feat: Cloudflare视图(按账号分组展示zone/worker/r2/tunnel/mail子资产)
This commit is contained in:
+49
-1
@@ -416,6 +416,52 @@ const SubscriptionsView = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SUB_TYPE_LABELS = { zone: '解析', worker: 'Worker', r2: 'R2', tunnel: 'Tunnel', mail: 'Mail', dns_record: 'DNS记录', other: '其他' };
|
||||||
|
|
||||||
|
const CloudflareView = {
|
||||||
|
template: `
|
||||||
|
<div class="space-y-4">
|
||||||
|
<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">
|
||||||
|
暂无 Cloudflare 子资产(新增资产时选 Cloudflare 类型即可)
|
||||||
|
</div>
|
||||||
|
<div v-for="g in groups" :key="g.account" 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">
|
||||||
|
<div class="font-semibold text-sm">☁️ {{ g.account || '未指定账号' }}</div>
|
||||||
|
<span class="text-xs text-slate-400">{{ g.items.length }} 个子资产</span>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<div v-for="a in g.items" :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">
|
||||||
|
<div class="min-w-0 flex items-center gap-2">
|
||||||
|
<span class="text-xs px-2 py-0.5 rounded-full shrink-0" :class="subTypeBadge(a.cloudflare_detail.sub_type)">{{ SUB_TYPE_LABELS[a.cloudflare_detail.sub_type] || a.cloudflare_detail.sub_type }}</span>
|
||||||
|
<span class="font-medium truncate">{{ a.cloudflare_detail.sub_name || a.cloudflare_detail.zone_name || a.name }}</span>
|
||||||
|
<span v-if="a.cloudflare_detail.zone_name && a.cloudflare_detail.sub_type !== 'zone'" class="text-xs text-slate-400">@{{ a.cloudflare_detail.zone_name }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-3 text-xs shrink-0">
|
||||||
|
<span v-if="a.cloudflare_detail.status" class="text-slate-500">{{ a.cloudflare_detail.status }}</span>
|
||||||
|
<button @click="edit(a)" class="text-blue-600 dark:text-blue-400 hover:underline">编辑</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`,
|
||||||
|
setup() {
|
||||||
|
const groups = computed(() => {
|
||||||
|
const cfAssets = store.assets.filter(a => a.asset_type === 'cloudflare' && a.cloudflare_detail);
|
||||||
|
const map = {};
|
||||||
|
for (const a of cfAssets) {
|
||||||
|
const acc = a.cloudflare_detail.account_email || '';
|
||||||
|
if (!map[acc]) map[acc] = { account: a.cloudflare_detail.account_email, items: [] };
|
||||||
|
map[acc].items.push(a);
|
||||||
|
}
|
||||||
|
return Object.values(map);
|
||||||
|
});
|
||||||
|
function subTypeBadge(t) {
|
||||||
|
return { zone: 'bg-blue-500/10 text-blue-600 dark:text-blue-400', worker: 'bg-orange-500/10 text-orange-600 dark:text-orange-400', r2: 'bg-purple-500/10 text-purple-600 dark:text-purple-400', tunnel: 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400', mail: 'bg-pink-500/10 text-pink-600 dark:text-pink-400', dns_record: 'bg-slate-500/10 text-slate-600', other: 'bg-slate-500/10 text-slate-500' }[t] || 'bg-slate-500/10 text-slate-500';
|
||||||
|
}
|
||||||
|
return { store, groups, subTypeBadge, SUB_TYPE_LABELS, edit: openAssetEdit };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const ProvidersView = {
|
const ProvidersView = {
|
||||||
template: `
|
template: `
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
@@ -979,12 +1025,13 @@ const AppRoot = {
|
|||||||
{ key: 'assets', label: '资产', icon: '📦' },
|
{ key: 'assets', label: '资产', icon: '📦' },
|
||||||
{ key: 'servers', label: '服务器', icon: '🖥️' },
|
{ key: 'servers', label: '服务器', icon: '🖥️' },
|
||||||
{ key: 'domains', label: '域名', icon: '🌐' },
|
{ key: 'domains', label: '域名', icon: '🌐' },
|
||||||
|
{ key: 'cloudflare', label: 'Cloudflare', icon: '☁️' },
|
||||||
{ key: 'subscriptions', label: '订阅', icon: '💳' },
|
{ key: 'subscriptions', label: '订阅', icon: '💳' },
|
||||||
{ key: 'ai', label: 'AI账号', icon: '🤖' },
|
{ key: 'ai', label: 'AI账号', icon: '🤖' },
|
||||||
{ key: 'providers', label: '平台', icon: '🏢' },
|
{ key: 'providers', label: '平台', icon: '🏢' },
|
||||||
{ key: 'settings', label: '设置', icon: '⚙️' },
|
{ key: 'settings', label: '设置', icon: '⚙️' },
|
||||||
];
|
];
|
||||||
const viewMap = { dashboard: 'dashboard-view', assets: 'assets-view', servers: 'servers-view', domains: 'domains-view', subscriptions: 'subscriptions-view', ai: 'ai-view', providers: 'providers-view', settings: 'settings-view' };
|
const viewMap = { dashboard: 'dashboard-view', assets: 'assets-view', servers: 'servers-view', domains: 'domains-view', cloudflare: 'cloudflare-view', subscriptions: 'subscriptions-view', ai: 'ai-view', providers: 'providers-view', settings: 'settings-view' };
|
||||||
const currentNav = computed(() => navs.find(n => n.key === store.view) || navs[0]);
|
const currentNav = computed(() => navs.find(n => n.key === store.view) || navs[0]);
|
||||||
const viewComponent = computed(() => viewMap[store.view] || 'dashboard-view');
|
const viewComponent = computed(() => viewMap[store.view] || 'dashboard-view');
|
||||||
function go(key) { store.view = key; if (key !== 'servers') store.serverMetrics = {}; if (key === 'ai') { store.filterType = ''; store.filterStatus = ''; store.search = ''; loadAssets(); } }
|
function go(key) { store.view = key; if (key !== 'servers') store.serverMetrics = {}; if (key === 'ai') { store.filterType = ''; store.filterStatus = ''; store.search = ''; loadAssets(); } }
|
||||||
@@ -1006,6 +1053,7 @@ app.component('assets-view', AssetsView);
|
|||||||
app.component('providers-view', ProvidersView);
|
app.component('providers-view', ProvidersView);
|
||||||
app.component('servers-view', ServersView);
|
app.component('servers-view', ServersView);
|
||||||
app.component('domains-view', DomainsView);
|
app.component('domains-view', DomainsView);
|
||||||
|
app.component('cloudflare-view', CloudflareView);
|
||||||
app.component('subscriptions-view', SubscriptionsView);
|
app.component('subscriptions-view', SubscriptionsView);
|
||||||
app.component('ai-view', AIAccountsView);
|
app.component('ai-view', AIAccountsView);
|
||||||
app.component('settings-view', SettingsView);
|
app.component('settings-view', SettingsView);
|
||||||
|
|||||||
Reference in New Issue
Block a user