三页面重设计与移动端适配;接入 PWA 与构建分包
CI / test (push) Successful in 47s

This commit is contained in:
2026-07-21 19:26:22 +08:00
parent ac457282ce
commit 6129173fe6
64 changed files with 6243 additions and 549 deletions
+19 -5
View File
@@ -11,6 +11,7 @@ import AppInputNumber from '@/components/AppInputNumber.vue'
import FormField from '@/components/FormField.vue'
import FormModal from '@/components/FormModal.vue'
import StatusBadge from '@/components/StatusBadge.vue'
import TenantHoverCard from '@/components/TenantHoverCard.vue'
import TenantPicker from '@/components/TenantPicker.vue'
import { useAsync } from '@/composables/useAsync'
import { fmtRelative } from '@/composables/useFormat'
@@ -26,6 +27,20 @@ const groupHints = computed(() => [...new Set(props.channels.map((c) => c.group)
const toast = useToast()
const dialog = useDialog()
const scope = useScopeStore()
/** 名称列:渠道名关联租户,悬停出统一租户卡(别名/名称/主区域/前往租户);
* 名称本身不承载跳转,单元格内截断 */
function nameCell(r: AiChannel) {
// scope 经 Pinia reactive 包装,useAsync 的 data ref 已深层解包,不能再 .value
const cfg = (scope.configs.data ?? []).find((c) => c.id === r.ociConfigId)
return h(TenantHoverCard, {
cfgId: r.ociConfigId,
label: r.name,
alias: cfg?.alias,
tenancyName: cfg?.tenancyName,
region: cfg?.region,
labelClass: 'inline-block max-w-full truncate align-bottom font-medium',
})
}
const PROBE_META: Record<Exclude<AiProbeStatus, ''>, { kind: 'run' | 'warn' | 'term' | 'stop'; label: string }> = {
ok: { kind: 'run', label: '可用' },
@@ -169,7 +184,7 @@ function probeBadge(r: AiChannel) {
}
const columns = computed<DataTableColumns<AiChannel>>(() => [
{ title: '名称', key: 'name', minWidth: 200, ellipsis: { tooltip: true }, render: (r) => h('span', { class: 'text-[13px] font-medium' }, r.name) },
{ title: '名称', key: 'name', minWidth: 200, render: nameCell },
{ title: '区域', key: 'region', width: 150, ellipsis: { tooltip: true }, render: (r) => h('span', { class: 'mono text-xs text-ink-2' }, r.region) },
{
title: '分组', key: 'group', width: 100,
@@ -201,14 +216,13 @@ const columns = computed<DataTableColumns<AiChannel>>(() => [
<div class="panel">
<PanelHeader
title="渠道(号池)"
desc="渠道 = 租户 × 区域;请求按优先级分组、组内权重随机,429/超时自动换渠道重试,连续失败指数退避熔断"
desc="渠道 = 租户 × 区域;按优先级与权重分流,失败自动重试并熔断"
>
<NButton size="small" type="primary" @click="openCreate">添加渠道</NButton>
</PanelHeader>
<NDataTable :columns="columns" :data="channels" :loading="loading" :bordered="false" size="small" />
<NDataTable :columns="columns" :data="channels" :loading="loading" :bordered="false" size="small" :scroll-x="960" />
<div class="border-t border-line-soft bg-wash px-4.5 py-2.5 text-xs leading-relaxed text-ink-3">
探测三步:列模型(服务可见性) 同步模型缓存 max_tokens=16 试调(配额)· 探测成功自动复位熔断 ·
有渠道时系统自动维护AI渠道探测后台任务(每天 00:00 逐渠道探测,渠道清空自动暂停)
探测 = 列模型 同步缓存 试调验证配额;成功自动复位熔断 · 后台任务每日 00:00 自动逐渠道探测
</div>
<AiChannelModelsModal