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 };
},
};