Files
chaos-api/web/image-gen/src/App.vue
T
2026-06-15 06:16:16 +08:00

140 lines
4.0 KiB
Vue
Vendored

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import SettingsPanel from './components/SettingsPanel.vue'
import ImageGenerator from './components/ImageGenerator.vue'
import Gallery from './components/Gallery.vue'
import { probeSession, listModels, getSelf, type NewApiUser, type Auth } from './lib/api'
import { loadSettings } from './lib/settings'
const user = ref<NewApiUser | null>(null)
const auth = ref<Auth>({ kind: 'session' })
const baseUrl = ref<string>(loadSettings().baseUrl)
const models = ref<string[]>([])
const error = ref<string>('')
const settingsRef = ref<InstanceType<typeof SettingsPanel> | null>(null)
const galleryRef = ref<InstanceType<typeof Gallery> | null>(null)
const checking = ref<boolean>(true)
async function refreshSession() {
checking.value = true
try {
const u = await probeSession(baseUrl.value)
user.value = u
error.value = ''
if (u) {
try {
models.value = await listModels(baseUrl.value, { kind: 'session' })
} catch (e) {
error.value = `已登录,但拉取模型失败: ${e}`
}
} else {
models.value = []
}
} finally {
checking.value = false
}
}
onMounted(refreshSession)
// Re-probe when the tab regains focus (e.g. user came back from /sign-in).
// `visibilitychange` covers mobile / tab-switch too; `focus` covers desktop.
window.addEventListener('focus', refreshSession)
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') refreshSession()
})
async function onSessionChanged(u: NewApiUser | null) {
user.value = u
if (u) {
try {
models.value = await listModels(baseUrl.value, { kind: 'session' })
} catch (e) {
error.value = `已登录,但拉取模型失败: ${e}`
}
} else {
models.value = []
}
}
function onAuthOverridden(a: Auth) {
auth.value = a
if (a.kind === 'sk') {
// refresh user info using the new auth
getSelf(baseUrl.value, a)
.then((u) => {
user.value = u
})
.catch((e) => {
error.value = `sk-key 无效: ${e}`
})
}
}
function onGenerated(img: { prompt: string; model: string; urls: string[] }) {
galleryRef.value?.add(img)
// refresh quota after a generation (best-effort)
if (user.value) {
probeSession(baseUrl.value)
.then((u) => {
if (u) user.value = u
})
.catch(() => {
/* ignore */
})
}
}
function onError(msg: string) {
error.value = msg
}
</script>
<template>
<div class="min-h-full max-w-5xl mx-auto p-6 space-y-5">
<header class="flex items-baseline justify-between">
<div>
<h1 class="text-2xl font-semibold tracking-tight">newapi-image-gen</h1>
<p class="text-xs text-zinc-500 mt-0.5">
new-api <code class="text-zinc-400">/v1/images/generations</code>,额度扣 new-api 账户
</p>
</div>
<a
v-if="user"
href="/"
class="text-xs text-zinc-500 hover:text-zinc-300"
> 返回 new-api 主页</a>
</header>
<p v-if="error" class="rounded-md border border-rose-800 bg-rose-950/40 px-3 py-2 text-sm text-rose-300">
{{ error }}
</p>
<SettingsPanel
ref="settingsRef"
:user="user"
@session-changed="onSessionChanged"
@auth-overridden="onAuthOverridden"
/>
<div v-if="checking" class="text-sm text-zinc-500">检查登录态</div>
<ImageGenerator
v-else-if="user"
:base-url="baseUrl"
:auth="auth"
:models="models"
@generated="onGenerated"
@error="onError"
/>
<div v-else class="rounded-xl border border-dashed border-zinc-800 p-6 text-center text-sm text-zinc-500">
先在上方登录,登录后会自动出现生图面板
</div>
<Gallery ref="galleryRef" :base-url="baseUrl" />
<footer class="pt-4 pb-2 text-center text-xs text-zinc-600">
登录走 new-api 自己的 OAuth,会话 cookie new-api 主站共享;本前端不会碰你的密码或 token
</footer>
</div>
</template>