diff --git a/static/js/app.js b/static/js/app.js index b812a03..0595a05 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -332,6 +332,10 @@ const SubscriptionsView = {
年度约 {{ (v*12).toFixed(2) }}
+
+

按平台月度支出(换算为月均)

+ +
@@ -372,7 +376,37 @@ const SubscriptionsView = { } return m; }); - return { store, Fmt, subs, monthlyByCurrency }; + const byProviderMonthly = computed(() => { + const m = {}; + for (const a of subs.value) { + const prov = a.provider_name || a.provider; + m[prov] = (m[prov] || 0) + normalizedMonthly(a.cost, a.renewal_cycle); + } + return m; + }); + let billingChart = null; + function renderBillingChart() { + const canvas = document.getElementById('billingChart'); + if (!canvas || !window.Chart) return; + const entries = Object.entries(byProviderMonthly.value).sort((x, y) => y[1] - x[1]); + if (!entries.length) return; + if (billingChart) { billingChart.destroy(); billingChart = null; } + billingChart = new Chart(canvas, { + type: 'bar', + data: { + labels: entries.map(e => e[0]), + datasets: [{ label: '月度支出', data: entries.map(e => e[1].toFixed(2)), backgroundColor: '#6366f1', borderRadius: 6 }], + }, + options: { + responsive: true, + scales: { y: { beginAtZero: true } }, + plugins: { legend: { display: false } }, + }, + }); + } + watch([subs, byProviderMonthly], () => { nextTick(renderBillingChart); }, { deep: true }); + onMounted(() => { nextTick(renderBillingChart); }); + return { store, Fmt, subs, monthlyByCurrency, byProviderMonthly }; }, };