/* 监控中心视图:默认到期倒计时,可按类别筛选 */ const MonitorView = { template: `
⏳ 所有设置了到期日的资产按剩余天数倒计时排序,越靠前越紧急 红=7天内 橙=30天内 绿=90天内
30 天内到期
{{ stats.soon30 }}
7 天内到期
{{ stats.soon7 }}
已过期
{{ stats.expired }}
有到期日的资产
{{ total }}
监控站点
{{ sslStats.total }}
证书正常
{{ sslStats.valid }}
即将到期
{{ sslStats.expiring }}
已过期 / 失败
{{ sslStats.expired + sslStats.error }}
`, setup() { const tabs = [ { key: '', label: '全部', tip: '所有有到期日的资产' }, { key: 'vps', label: 'VPS', tip: '仅看云服务器' }, { key: 'domain', label: '域名', tip: '仅看域名' }, { key: 'cloudflare', label: 'Cloudflare', tip: '仅看 Cloudflare 资源' }, { key: 'subscription', label: '付费资产', tip: '仅看有费用、需要续费的项目' }, { key: 'ai_agent', label: 'AI账号', tip: '仅看 AI 服务账号' }, { key: 'ssl', label: 'SSL证书', tip: '站点 HTTPS 证书到期倒计时(独立于资产)' }, ]; const hasExpiry = a => a.expiry_date && a.status !== 'cancelled'; const items = Vue.computed(() => { if (store.monitorTab === 'ssl') return []; let list = store.assets.filter(hasExpiry); const t = store.monitorTab; if (t === 'subscription') list = list.filter(a => a.cost > 0); else if (t) list = list.filter(a => a.asset_type === t); return list.sort((x, y) => (x.days_to_expiry ?? 9999) - (y.days_to_expiry ?? 9999)); }); // SSL 证书:按剩余天数倒计时排序 const sslList = Vue.computed(() => [...store.siteCerts].sort((x, y) => (x.days_to_expiry ?? 9999) - (y.days_to_expiry ?? 9999)) ); const sslStats = Vue.computed(() => { const s = { total: store.siteCerts.length, valid: 0, expiring: 0, expired: 0, error: 0 }; for (const c of store.siteCerts) if (s[c.status] !== undefined) s[c.status]++; return s; }); async function recheck(c) { try { await Api.post('/site-certs/' + c.id + '/check', {}); await loadSiteCerts(); } catch (e) { alert('重测失败:' + e.message); } } async function delCert(c) { if (!confirm('删除证书监控「' + c.hostname + '」?')) return; try { await Api.del('/site-certs/' + c.id); await loadSiteCerts(); } catch (e) { alert('删除失败:' + e.message); } } const total = Vue.computed(() => items.value.length); const stats = Vue.computed(() => { const s = { soon30: 0, soon7: 0, expired: 0 }; for (const a of store.assets.filter(hasExpiry)) { const d = a.days_to_expiry; if (d === null || d === undefined) continue; if (d < 0) s.expired++; else if (d <= 7) { s.soon7++; s.soon30++; } else if (d <= 30) s.soon30++; } return s; }); const emptyText = Vue.computed(() => store.monitorTab ? '该类别暂无到期资产' : '暂无到期资产(资产设置到期日即可出现在这里)'); function switchTab(k) { store.monitorTab = k; } function countdownText(d) { if (d === null || d === undefined) return '—'; if (d < 0) return '已过期 ' + Math.abs(d) + ' 天'; if (d === 0) return '今天到期'; return d + ' 天'; } function countdownBadge(d) { if (d === null || d === undefined) return 'bg-slate-500/10 text-slate-500'; if (d < 0 || d === 0 || d <= 7) return 'bg-red-500/10 text-red-600 dark:text-red-400'; if (d <= 30) return 'bg-amber-500/10 text-amber-600 dark:text-amber-400'; if (d <= 90) return 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400'; return 'bg-slate-500/10 text-slate-500'; } return { store, Fmt, tabs, items, total, stats, sslList, sslStats, emptyText, switchTab, countdownText, countdownBadge, recheck, delCert, edit: openAssetEdit, del: deleteAsset }; }, };