feat: Add TokenRecords finance management system

- Created new finance application based on Vue Vben Admin
- Implemented transaction management, category management, and loan tracking
- Added person management for tracking financial relationships
- Integrated budget management and financial analytics
- Added data import/export functionality
- Implemented responsive design for mobile support
- Added comprehensive testing with Playwright

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
你的用户名
2025-08-06 20:09:48 +08:00
parent b93e22c45a
commit 4b4616de1e
193 changed files with 17756 additions and 16 deletions

View File

@@ -0,0 +1,147 @@
import type * as echarts from 'echarts';
import type { Ref } from 'vue';
import { computed, nextTick, onMounted, onUnmounted, ref, unref, watch } from 'vue';
import { useDebounceFn } from '@vueuse/core';
import * as echartCore from 'echarts/core';
import { BarChart, LineChart, PieChart } from 'echarts/charts';
import {
DataZoomComponent,
GridComponent,
LegendComponent,
TitleComponent,
ToolboxComponent,
TooltipComponent,
} from 'echarts/components';
import { LabelLayout, UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
// 注册必要的组件
echartCore.use([
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
ToolboxComponent,
DataZoomComponent,
BarChart,
LineChart,
PieChart,
CanvasRenderer,
UniversalTransition,
LabelLayout,
]);
export type EChartsOption = echarts.EChartsOption;
export type EChartsInstance = echarts.ECharts;
export interface UseChartOptions {
theme?: string | object;
initOptions?: echarts.EChartsCoreOption;
loading?: boolean;
loadingOptions?: object;
}
export function useChart(
elRef: Ref<HTMLDivElement | null>,
options: UseChartOptions = {},
) {
const { theme = 'light', initOptions = {}, loading = false, loadingOptions = {} } = options;
let chartInstance: EChartsInstance | null = null;
const cacheOptions = ref<EChartsOption>({});
const isDisposed = ref(false);
// 获取图表实例
const getChartInstance = (): EChartsInstance | null => {
if (!elRef.value || isDisposed.value) {
return null;
}
if (!chartInstance) {
chartInstance = echartCore.init(elRef.value, theme, initOptions);
}
return chartInstance;
};
// 设置图表配置
const setOptions = (options: EChartsOption, clear = true) => {
cacheOptions.value = options;
nextTick(() => {
if (!isDisposed.value) {
const instance = getChartInstance();
if (instance) {
clear && instance.clear();
instance.setOption(options);
}
}
});
};
// 获取图表配置
const getOptions = (): EChartsOption => {
return cacheOptions.value;
};
// 调整图表大小
const resize = useDebounceFn(() => {
const instance = getChartInstance();
instance?.resize();
}, 200);
// 销毁图表
const dispose = () => {
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
isDisposed.value = true;
}
};
// 监听 loading 状态
watch(
() => loading,
(val) => {
const instance = getChartInstance();
if (instance) {
if (val) {
instance.showLoading(loadingOptions);
} else {
instance.hideLoading();
}
}
},
);
// 监听元素变化,重新初始化
watch(
elRef,
(el) => {
if (el) {
isDisposed.value = false;
setOptions(cacheOptions.value);
}
},
);
// 挂载时初始化
onMounted(() => {
window.addEventListener('resize', resize);
});
// 卸载时清理
onUnmounted(() => {
window.removeEventListener('resize', resize);
dispose();
});
return {
getInstance: getChartInstance,
setOptions,
getOptions,
resize,
dispose,
};
}