From 9f8dbf77df7ca4a216ca0bf395b48a21918eb5a9 Mon Sep 17 00:00:00 2001 From: MaysWind Date: Thu, 30 Oct 2025 00:18:52 +0800 Subject: [PATCH] add bubble chart for trends analysis --- src/components/desktop/MonthlyTrendsChart.vue | 11 +++++++++++ src/core/statistics.ts | 13 +++++++++++++ src/desktop-main.ts | 3 ++- src/locales/de.json | 1 + src/locales/en.json | 1 + src/locales/es.json | 1 + src/locales/fr.json | 1 + src/locales/it.json | 1 + src/locales/ja.json | 1 + src/locales/ko.json | 1 + src/locales/nl.json | 1 + src/locales/pt_BR.json | 1 + src/locales/ru.json | 1 + src/locales/th.json | 1 + src/locales/uk.json | 1 + src/locales/vi.json | 1 + src/locales/zh_Hans.json | 1 + src/locales/zh_Hant.json | 1 + src/stores/statistics.ts | 6 ++---- .../statistics/StatisticsTransactionPageBase.ts | 11 +++++++++-- 20 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/components/desktop/MonthlyTrendsChart.vue b/src/components/desktop/MonthlyTrendsChart.vue index 301a5221..18acfe68 100644 --- a/src/components/desktop/MonthlyTrendsChart.vue +++ b/src/components/desktop/MonthlyTrendsChart.vue @@ -59,6 +59,7 @@ interface MonthlyTrendsChartDataItem { type: string; areaStyle?: object; stack?: string; + symbolSize?: (data: number) => number; animation: boolean; data: number[]; } @@ -152,6 +153,7 @@ const allDisplayDateRanges = computed(() => { const allSeries = computed(() => { const allSeries: MonthlyTrendsChartDataItem[] = []; + let maxAmount: number = 0; for (const [item, index] of itemAndIndex(props.items)) { if (props.hiddenField && item[props.hiddenField]) { @@ -208,6 +210,10 @@ const allSeries = computed(() => { } } + if (amount > maxAmount) { + maxAmount = amount; + } + allAmounts.push(amount); } @@ -233,6 +239,11 @@ const allSeries = computed(() => { finalItem.areaStyle = {}; } else if (props.type === TrendChartType.Column.type) { finalItem.type = 'bar'; + } else if (props.type === TrendChartType.Bubble.type) { + finalItem.type = 'scatter'; + finalItem.symbolSize = (data: number): number => { + return Math.sqrt(data) / Math.sqrt(maxAmount) * 80 + 5; + } } allSeries.push(finalItem); diff --git a/src/core/statistics.ts b/src/core/statistics.ts index 528fa109..abfb24db 100644 --- a/src/core/statistics.ts +++ b/src/core/statistics.ts @@ -9,6 +9,7 @@ export enum StatisticsAnalysisType { export class CategoricalChartType implements TypeAndName { private static readonly allInstancesForAll: CategoricalChartType[] = []; private static readonly allInstancesForDesktop: CategoricalChartType[] = []; + private static readonly allInstancesByType: Record = {}; public static readonly Pie = new CategoricalChartType(0, 'Pie Chart', false); public static readonly Bar = new CategoricalChartType(1, 'Bar Chart', false); @@ -30,6 +31,11 @@ export class CategoricalChartType implements TypeAndName { } CategoricalChartType.allInstancesForDesktop.push(this); + CategoricalChartType.allInstancesByType[type] = this; + } + + public static isValidType(type: number): boolean { + return !!CategoricalChartType.allInstancesByType[type]; } public static values(withDesktopOnlyChart: boolean): CategoricalChartType[] { @@ -43,9 +49,11 @@ export class CategoricalChartType implements TypeAndName { export class TrendChartType implements TypeAndName { private static readonly allInstances: TrendChartType[] = []; + private static readonly allInstancesByType: Record = {}; public static readonly Area = new TrendChartType(0, 'Area Chart'); public static readonly Column = new TrendChartType(1, 'Column Chart'); + public static readonly Bubble = new TrendChartType(2, 'Bubble Chart'); public static readonly Default = TrendChartType.Column; @@ -57,6 +65,11 @@ export class TrendChartType implements TypeAndName { this.name = name; TrendChartType.allInstances.push(this); + TrendChartType.allInstancesByType[type] = this; + } + + public static isValidType(type: number): boolean { + return !!TrendChartType.allInstancesByType[type]; } public static values(): TrendChartType[] { diff --git a/src/desktop-main.ts b/src/desktop-main.ts index 73397bfa..4b089f5a 100644 --- a/src/desktop-main.ts +++ b/src/desktop-main.ts @@ -52,7 +52,7 @@ import 'vuetify/styles'; import * as echarts from 'echarts/core'; import { CanvasRenderer } from 'echarts/renderers'; -import { LineChart, BarChart, PieChart, CandlestickChart, RadarChart, SankeyChart } from 'echarts/charts'; +import { LineChart, BarChart, PieChart, ScatterChart, CandlestickChart, RadarChart, SankeyChart } from 'echarts/charts'; import { GridComponent, TooltipComponent, @@ -497,6 +497,7 @@ echarts.use([ LineChart, BarChart, PieChart, + ScatterChart, CandlestickChart, RadarChart, SankeyChart, diff --git a/src/locales/de.json b/src/locales/de.json index 3ce0ecf2..a6676986 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -1516,6 +1516,7 @@ "Radar Chart": "Radar Chart", "Area Chart": "Flächendiagramm", "Column Chart": "Säulendiagramm", + "Bubble Chart": "Bubble Chart", "Candlestick Chart": "Candlestick Chart", "Sankey Chart": "Sankey Chart", "Sort by": "Sortieren nach", diff --git a/src/locales/en.json b/src/locales/en.json index c394bc71..0ed9c50f 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1516,6 +1516,7 @@ "Radar Chart": "Radar Chart", "Area Chart": "Area Chart", "Column Chart": "Column Chart", + "Bubble Chart": "Bubble Chart", "Candlestick Chart": "Candlestick Chart", "Sankey Chart": "Sankey Chart", "Sort by": "Sort by", diff --git a/src/locales/es.json b/src/locales/es.json index 0fbaaff1..8196d40a 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -1516,6 +1516,7 @@ "Radar Chart": "Radar Chart", "Area Chart": "Gráfico de área", "Column Chart": "Gráfico de columnas", + "Bubble Chart": "Bubble Chart", "Candlestick Chart": "Candlestick Chart", "Sankey Chart": "Sankey Chart", "Sort by": "Ordenar por", diff --git a/src/locales/fr.json b/src/locales/fr.json index 4829664c..ef7a6a2e 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -1516,6 +1516,7 @@ "Radar Chart": "Radar Chart", "Area Chart": "Graphique en aires", "Column Chart": "Graphique en colonnes", + "Bubble Chart": "Bubble Chart", "Candlestick Chart": "Graphique en chandelier", "Sankey Chart": "Sankey Chart", "Sort by": "Trier par", diff --git a/src/locales/it.json b/src/locales/it.json index 4f2296d2..9303c60a 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -1516,6 +1516,7 @@ "Radar Chart": "Radar Chart", "Area Chart": "Grafico ad area", "Column Chart": "Grafico a colonne", + "Bubble Chart": "Bubble Chart", "Candlestick Chart": "Candlestick Chart", "Sankey Chart": "Sankey Chart", "Sort by": "Ordina per", diff --git a/src/locales/ja.json b/src/locales/ja.json index 4899fa99..bb6df200 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -1516,6 +1516,7 @@ "Radar Chart": "Radar Chart", "Area Chart": "エリアチャート", "Column Chart": "列チャート", + "Bubble Chart": "Bubble Chart", "Candlestick Chart": "Candlestick Chart", "Sankey Chart": "Sankey Chart", "Sort by": "ソート順", diff --git a/src/locales/ko.json b/src/locales/ko.json index b63feb0a..7471efb5 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -1516,6 +1516,7 @@ "Radar Chart": "Radar Chart", "Area Chart": "영역 차트", "Column Chart": "세로 막대 차트", + "Bubble Chart": "Bubble Chart", "Candlestick Chart": "캠들스틱 차트", "Sankey Chart": "Sankey Chart", "Sort by": "정렬 기준", diff --git a/src/locales/nl.json b/src/locales/nl.json index 44820471..460ebe8e 100644 --- a/src/locales/nl.json +++ b/src/locales/nl.json @@ -1516,6 +1516,7 @@ "Radar Chart": "Radar Chart", "Area Chart": "Vlakdiagram", "Column Chart": "Kolomdiagram", + "Bubble Chart": "Bubble Chart", "Candlestick Chart": "Candlestickdiagram", "Sankey Chart": "Sankey Chart", "Sort by": "Sorteren op", diff --git a/src/locales/pt_BR.json b/src/locales/pt_BR.json index e8025ad0..7a8813ff 100644 --- a/src/locales/pt_BR.json +++ b/src/locales/pt_BR.json @@ -1516,6 +1516,7 @@ "Radar Chart": "Radar Chart", "Area Chart": "Gráfico de Área", "Column Chart": "Gráfico de Colunas", + "Bubble Chart": "Bubble Chart", "Candlestick Chart": "Candlestick Chart", "Sankey Chart": "Sankey Chart", "Sort by": "Ordenar por", diff --git a/src/locales/ru.json b/src/locales/ru.json index 47d4b12b..aa3b2a51 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -1516,6 +1516,7 @@ "Radar Chart": "Radar Chart", "Area Chart": "Диаграмма с областями", "Column Chart": "Столбчатая диаграмма", + "Bubble Chart": "Bubble Chart", "Candlestick Chart": "Candlestick Chart", "Sankey Chart": "Sankey Chart", "Sort by": "Сортировать по", diff --git a/src/locales/th.json b/src/locales/th.json index bf05211b..aed3d9b1 100644 --- a/src/locales/th.json +++ b/src/locales/th.json @@ -1516,6 +1516,7 @@ "Radar Chart": "Radar Chart", "Area Chart": "กราฟพื้นที่", "Column Chart": "กราฟคอลัมน์", + "Bubble Chart": "Bubble Chart", "Candlestick Chart": "กราฟแท่งเทียน", "Sankey Chart": "Sankey Chart", "Sort by": "จัดเรียงตาม", diff --git a/src/locales/uk.json b/src/locales/uk.json index 34d6b744..31c9a2b1 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -1516,6 +1516,7 @@ "Radar Chart": "Radar Chart", "Area Chart": "Діаграма з областями", "Column Chart": "Стовпчикова діаграма", + "Bubble Chart": "Bubble Chart", "Candlestick Chart": "Candlestick Chart", "Sankey Chart": "Sankey Chart", "Sort by": "Сортувати за", diff --git a/src/locales/vi.json b/src/locales/vi.json index 414c6268..c16e0fa0 100644 --- a/src/locales/vi.json +++ b/src/locales/vi.json @@ -1516,6 +1516,7 @@ "Radar Chart": "Radar Chart", "Area Chart": "Biểu đồ diện tích", "Column Chart": "Biểu đồ cột", + "Bubble Chart": "Bubble Chart", "Candlestick Chart": "Candlestick Chart", "Sankey Chart": "Sankey Chart", "Sort by": "Sắp xếp theo", diff --git a/src/locales/zh_Hans.json b/src/locales/zh_Hans.json index c4be9a15..17655d0b 100644 --- a/src/locales/zh_Hans.json +++ b/src/locales/zh_Hans.json @@ -1516,6 +1516,7 @@ "Radar Chart": "雷达图", "Area Chart": "面积图", "Column Chart": "柱状图", + "Bubble Chart": "气泡图", "Candlestick Chart": "K线图", "Sankey Chart": "桑基图", "Sort by": "排序方式", diff --git a/src/locales/zh_Hant.json b/src/locales/zh_Hant.json index 7053a82d..1f3572a5 100644 --- a/src/locales/zh_Hant.json +++ b/src/locales/zh_Hant.json @@ -1516,6 +1516,7 @@ "Radar Chart": "雷達圖", "Area Chart": "面積圖", "Column Chart": "柱狀圖", + "Bubble Chart": "氣泡圖", "Candlestick Chart": "K線圖", "Sankey Chart": "桑基圖", "Sort by": "排序方式", diff --git a/src/stores/statistics.ts b/src/stores/statistics.ts index baadda75..45e01c46 100644 --- a/src/stores/statistics.ts +++ b/src/stores/statistics.ts @@ -812,9 +812,7 @@ export const useStatisticsStore = defineStore('statistics', () => { transactionStatisticsFilter.value.categoricalChartType = settingsStore.appSettings.statistics.defaultCategoricalChartType; } - if (transactionStatisticsFilter.value.categoricalChartType !== CategoricalChartType.Pie.type && - transactionStatisticsFilter.value.categoricalChartType !== CategoricalChartType.Bar.type && - transactionStatisticsFilter.value.categoricalChartType !== CategoricalChartType.Radar.type) { + if (!CategoricalChartType.isValidType(transactionStatisticsFilter.value.categoricalChartType)) { transactionStatisticsFilter.value.categoricalChartType = CategoricalChartType.Default.type; } @@ -859,7 +857,7 @@ export const useStatisticsStore = defineStore('statistics', () => { transactionStatisticsFilter.value.trendChartType = settingsStore.appSettings.statistics.defaultTrendChartType; } - if (transactionStatisticsFilter.value.trendChartType !== TrendChartType.Area.type && transactionStatisticsFilter.value.trendChartType !== TrendChartType.Column.type) { + if (!TrendChartType.isValidType(transactionStatisticsFilter.value.trendChartType)) { transactionStatisticsFilter.value.trendChartType = TrendChartType.Default.type; } diff --git a/src/views/base/statistics/StatisticsTransactionPageBase.ts b/src/views/base/statistics/StatisticsTransactionPageBase.ts index 87e5e169..4af9a029 100644 --- a/src/views/base/statistics/StatisticsTransactionPageBase.ts +++ b/src/views/base/statistics/StatisticsTransactionPageBase.ts @@ -9,7 +9,13 @@ import { type TransactionStatisticsFilter, useStatisticsStore } from '@/stores/s import type { TypeAndDisplayName } from '@/core/base.ts'; import { type LocalizedDateRange, type WeekDayValue, DateRangeScene, DateRange } from '@/core/datetime.ts'; import type { ColorStyleValue } from '@/core/color.ts'; -import { StatisticsAnalysisType, ChartDataType, ChartSortingType, ChartDateAggregationType } from '@/core/statistics.ts'; +import { + StatisticsAnalysisType, + ChartDataType, + ChartSortingType, + ChartDateAggregationType, + TrendChartType +} from '@/core/statistics.ts'; import { DISPLAY_HIDDEN_AMOUNT } from '@/consts/numeral.ts'; @@ -229,7 +235,8 @@ export function useStatisticsTransactionPageBase() { }); const showStackedInTrendsChart = computed(() => { - return query.value.chartDataType !== ChartDataType.OutflowsByAccount.type && + return (query.value.trendChartType === TrendChartType.Area.type || query.value.trendChartType === TrendChartType.Column.type) && + query.value.chartDataType !== ChartDataType.OutflowsByAccount.type && query.value.chartDataType !== ChartDataType.InflowsByAccount.type; });