feat: 综合平台services多服务支持+SW网络优先缓存修复+适配层/定时任务完善

This commit is contained in:
gouki
2026-08-05 11:57:17 +00:00
parent 7f1268f508
commit 1b7d4823c3
47 changed files with 2895 additions and 1189 deletions
+60
View File
@@ -0,0 +1,60 @@
/* AI 账号视图 */
const AIAccountsView = {
template: `
<div class="space-y-4">
<div class="flex justify-between items-center flex-wrap gap-2">
<div class="text-sm text-slate-500">共 {{ totalAccounts }} 个账号 · 总余额 {{ totalBalance }}</div>
<button @click="addAny" class="text-sm px-3 py-1.5 rounded-lg bg-blue-600 text-white hover:bg-blue-700">+ 新增账号</button>
</div>
<div v-for="g in groups" :key="g.provider" class="bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-800 p-4">
<div class="flex justify-between items-center mb-3 flex-wrap gap-2">
<div class="flex items-center gap-2">
<span class="font-semibold text-sm">{{ g.provider }}</span>
<span class="text-xs px-2 py-0.5 rounded-full bg-violet-500/10 text-violet-600 dark:text-violet-400">{{ g.accounts.length }} 个账号</span>
</div>
<div class="flex items-center gap-3 text-xs">
<span class="text-slate-500">余额合计 <b class="text-slate-800 dark:text-slate-200">{{ g.totalBalance }}</b></span>
<button @click="add(g.provider)" class="text-blue-600 dark:text-blue-400 hover:underline">+ 添加账号</button>
</div>
</div>
<div class="space-y-2">
<div v-for="a in g.accounts" :key="a.id" class="flex items-center justify-between px-3 py-2 rounded-lg bg-slate-50 dark:bg-slate-800/50 text-sm gap-2">
<div class="min-w-0">
<span class="font-medium">{{ a.name }}</span>
<span v-if="a.ai_detail && a.ai_detail.plan" class="text-xs text-slate-400 ml-2">{{ a.ai_detail.plan }}</span>
</div>
<div class="flex items-center gap-3 text-xs shrink-0">
<span :class="balanceClass(a.ai_detail && a.ai_detail.balance)">余额 {{ fmtBal(a.ai_detail && a.ai_detail.balance) }}{{ a.ai_detail && a.ai_detail.balance !== null && a.ai_detail.balance !== undefined ? ' ' + a.ai_detail.currency : '' }}</span>
<span v-if="a.ai_detail && a.ai_detail.monthly_usage !== null && a.ai_detail.monthly_usage !== undefined" class="text-slate-500">用量 {{ a.ai_detail.monthly_usage }}/{{ a.ai_detail.monthly_limit ?? '∞' }}</span>
<button @click="edit(a)" class="text-blue-600 dark:text-blue-400 hover:underline">编辑</button>
</div>
</div>
</div>
</div>
<div v-if="!groups.length" class="bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-800 p-8 text-center text-slate-400 text-sm">暂无 AI 账号,点右上角「新增账号」添加(同一平台可添加多个)</div>
</div>`,
setup() {
const aiList = () => store.assets.filter(a => a.asset_type === 'ai_agent');
const groups = Vue.computed(() => {
const map = {};
for (const a of aiList()) {
const prov = (a.ai_detail && a.ai_detail.provider) || a.provider || '其他';
if (!map[prov]) map[prov] = { provider: prov, accounts: [], totalBalance: 0 };
map[prov].accounts.push(a);
const bal = a.ai_detail && a.ai_detail.balance;
if (bal) map[prov].totalBalance += Number(bal);
}
return Object.values(map).map(g => ({ ...g, totalBalance: Math.round(g.totalBalance * 100) / 100 })).sort((x, y) => y.totalBalance - x.totalBalance);
});
const totalAccounts = Vue.computed(() => aiList().length);
const totalBalance = Vue.computed(() => Math.round(aiList().reduce((s, a) => s + (a.ai_detail && a.ai_detail.balance ? Number(a.ai_detail.balance) : 0), 0) * 100) / 100);
function balanceClass(b) {
if (b === null || b === undefined) return 'text-slate-400';
if (Number(b) < 10) return 'text-red-600 dark:text-red-400 font-semibold';
if (Number(b) < 50) return 'text-amber-600 dark:text-amber-400';
return 'text-emerald-600 dark:text-emerald-400';
}
function fmtBal(b) { return (b === null || b === undefined) ? '—' : b; }
return { store, groups, totalAccounts, totalBalance, balanceClass, fmtBal, add: openAICreate, addAny: () => openAICreate(''), edit: openAssetEdit };
},
};