diff --git a/static/js/app.js b/static/js/app.js
index 8665e35..a1d4623 100644
--- a/static/js/app.js
+++ b/static/js/app.js
@@ -83,6 +83,17 @@ function openAssetCreate(presetType) {
if (presetType) form.asset_type = presetType;
store.assetModal = { show: true, editing: null, form };
}
+function openAICreate(provider) {
+ const form = emptyAssetForm();
+ form.asset_type = 'ai_agent';
+ if (provider) {
+ form.provider = provider;
+ form.ai_detail.provider = provider;
+ const p = store.providers.find(x => x.slug === provider);
+ if (p) form.provider_id = p.id;
+ }
+ store.assetModal = { show: true, editing: null, form };
+}
function openAssetEdit(a) {
const form = emptyAssetForm();
['name', 'asset_type', 'provider', 'provider_id', 'renewal_cycle', 'account', 'expiry_date', 'auto_renew', 'cost', 'currency', 'status', 'is_archived', 'remark'].forEach(k => { form[k] = a[k]; });
@@ -406,6 +417,66 @@ const DomainsView = {
},
};
+const AIAccountsView = {
+ template: `
+
+
+
共 {{ totalAccounts }} 个账号 · 总余额 {{ totalBalance }}
+
+
+
+
+
+ {{ g.provider }}
+ {{ g.accounts.length }} 个账号
+
+
+ 余额合计 {{ g.totalBalance }}
+
+
+
+
+
+
+ {{ a.name }}
+ {{ a.ai_detail.plan }}
+
+
+ 余额 {{ 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 : '' }}
+ 用量 {{ a.ai_detail.monthly_usage }}/{{ a.ai_detail.monthly_limit ?? '∞' }}
+
+
+
+
+
+
暂无 AI 账号,点右上角「新增账号」添加(同一平台可添加多个)
+
`,
+ setup() {
+ const aiList = () => store.assets.filter(a => a.asset_type === 'ai_agent');
+ const groups = 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 = computed(() => aiList().length);
+ const totalBalance = 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 };
+ },
+};
+
const SettingsView = {
template: `
@@ -621,17 +692,19 @@ const AppRoot = {
{ key: 'assets', label: '资产', icon: '📦' },
{ key: 'servers', label: '服务器', icon: '🖥️' },
{ key: 'domains', label: '域名', icon: '🌐' },
+ { key: 'ai', label: 'AI账号', icon: '🤖' },
{ key: 'providers', label: '平台', icon: '🏢' },
{ key: 'settings', label: '设置', icon: '⚙️' },
];
- const viewMap = { dashboard: 'dashboard-view', assets: 'assets-view', servers: 'servers-view', domains: 'domains-view', providers: 'providers-view', settings: 'settings-view' };
+ const viewMap = { dashboard: 'dashboard-view', assets: 'assets-view', servers: 'servers-view', domains: 'domains-view', ai: 'ai-view', providers: 'providers-view', settings: 'settings-view' };
const currentNav = computed(() => navs.find(n => n.key === store.view) || navs[0]);
const viewComponent = computed(() => viewMap[store.view] || 'dashboard-view');
- function go(key) { store.view = key; if (key !== 'servers') store.serverMetrics = {}; }
+ function go(key) { store.view = key; if (key !== 'servers') store.serverMetrics = {}; if (key === 'ai') { store.filterType = ''; store.filterStatus = ''; store.search = ''; loadAssets(); } }
function quickAdd() {
- const preset = store.view === 'domains' ? 'domain' : (store.view === 'providers' ? null : 'vps');
- if (store.view === 'providers') openProviderCreate();
- else openAssetCreate(preset);
+ if (store.view === 'providers') return openProviderCreate();
+ if (store.view === 'ai') return openAICreate('');
+ const preset = store.view === 'domains' ? 'domain' : 'vps';
+ openAssetCreate(preset);
}
function toggleDark() { store.dark = !store.dark; applyDark(); }
return { store, appName, navs, currentNav, viewComponent, go, quickAdd, toggleDark, reload: loadAssets };
@@ -645,6 +718,7 @@ app.component('assets-view', AssetsView);
app.component('providers-view', ProvidersView);
app.component('servers-view', ServersView);
app.component('domains-view', DomainsView);
+app.component('ai-view', AIAccountsView);
app.component('settings-view', SettingsView);
applyDark();
loadAll();