61 lines
4.0 KiB
JavaScript
61 lines
4.0 KiB
JavaScript
/* 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 };
|
|
},
|
|
};
|