模型定价: 一致性检查与提示(渠道可用模型应全部纳入定价)
- /admin/models 返回每模型 used/needs_pricing/denied + summary(total/unpriced/missing) - missing 检测孤儿绑定(渠道提供但目录缺失) - 模型定价页: 缺失警告、在用未定价提示、已禁止/未定价徽章 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -65,6 +65,16 @@ export interface Model {
|
||||
enabled: boolean
|
||||
sort: number
|
||||
channels: ModelBinding[]
|
||||
used?: boolean
|
||||
needs_pricing?: boolean
|
||||
denied?: boolean
|
||||
}
|
||||
|
||||
export interface ModelSummary {
|
||||
total: number
|
||||
unpriced: number
|
||||
missing: { channel: string; model_id: number }[]
|
||||
denied_count: number
|
||||
}
|
||||
|
||||
export interface UsageLog {
|
||||
|
||||
@@ -6,11 +6,12 @@ import Button from '@/components/ui/Button.vue'
|
||||
import Input from '@/components/ui/Input.vue'
|
||||
import Modal from '@/components/ui/Modal.vue'
|
||||
import Badge from '@/components/ui/Badge.vue'
|
||||
import type { Channel, Model } from '@/types'
|
||||
import type { Channel, Model, ModelSummary } from '@/types'
|
||||
|
||||
const toast = useToastStore()
|
||||
const models = ref<Model[]>([])
|
||||
const channels = ref<Channel[]>([])
|
||||
const summary = ref<ModelSummary>({ total: 0, unpriced: 0, missing: [], denied_count: 0 })
|
||||
const editOpen = ref(false)
|
||||
const editing = ref<Model | null>(null)
|
||||
const saving = ref(false)
|
||||
@@ -33,6 +34,7 @@ async function load() {
|
||||
const [m, c] = await Promise.all([http.get('/admin/models'), http.get('/admin/channels')])
|
||||
models.value = m.data.data.items
|
||||
channels.value = c.data.data.items
|
||||
summary.value = m.data.data.summary
|
||||
} catch (e) {
|
||||
toast.err(errMsg(e))
|
||||
}
|
||||
@@ -135,12 +137,26 @@ onMounted(load)
|
||||
<Button @click="openCreate">添加模型</Button>
|
||||
</div>
|
||||
|
||||
<!-- 一致性提示:渠道提供的未禁止模型应全部纳入定价 -->
|
||||
<div v-if="summary.missing.length" class="card border-err/50 p-4">
|
||||
<p class="text-sm font-medium text-err">以下渠道提供了未纳入定价的模型</p>
|
||||
<p v-for="(x, i) in summary.missing" :key="i" class="mt-1 font-mono text-xs text-muted">
|
||||
{{ x.channel }} → 模型 id#{{ x.model_id }}(请在下方添加并定价)
|
||||
</p>
|
||||
</div>
|
||||
<p v-else-if="summary.unpriced > 0" class="text-xs text-muted">
|
||||
有 <span class="mono-num text-warn">{{ summary.unpriced }}</span> 个在用模型未定价,网关将按示例价计费
|
||||
</p>
|
||||
<p v-else class="text-xs text-muted">已启用渠道提供的全部模型均已纳入定价</p>
|
||||
|
||||
<div class="space-y-3">
|
||||
<div v-for="m in models" :key="m.id" class="card">
|
||||
<div class="flex items-center justify-between px-4 py-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span class="font-mono text-sm text-ink">{{ m.name }}</span>
|
||||
<Badge :variant="m.enabled ? 'ok' : 'neutral'">{{ m.enabled ? '启用' : '停用' }}</Badge>
|
||||
<Badge v-if="m.denied" variant="err">已禁止</Badge>
|
||||
<Badge v-if="m.needs_pricing" variant="warn">未定价</Badge>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="mono-num text-xs text-muted">入 {{ m.input_price }}</span>
|
||||
|
||||
Reference in New Issue
Block a user