Files
vps-manager/static/js/views/cloudflare.js
T

47 lines
3.0 KiB
JavaScript

/* Cloudflare 子资产视图 */
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 = Vue.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 };
},
};