From 32f10392ab00b07e85c0b4229037c30af7c3f28e Mon Sep 17 00:00:00 2001
From: zouyu <2723363702@qq.com>
Date: 星期二, 27 一月 2026 10:42:49 +0800
Subject: [PATCH] 浪潮对接单点登录:mis调整2
---
src/views/financialManagement/financialStatements/index.vue | 994 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 992 insertions(+), 2 deletions(-)
diff --git a/src/views/financialManagement/financialStatements/index.vue b/src/views/financialManagement/financialStatements/index.vue
index c272707..cf4eee5 100644
--- a/src/views/financialManagement/financialStatements/index.vue
+++ b/src/views/financialManagement/financialStatements/index.vue
@@ -1,4 +1,994 @@
-<template>
+ <template>
+ <div style="padding: 20px;">
+ <!-- 椤甸潰鏍囬鍜屾湀浠界瓫閫� -->
+ <div class="w-full md:w-auto flex items-center gap-3" style="margin-bottom: 20px;">
+ <el-date-picker
+ v-model="dateRange"
+ type="monthrange"
+ format="YYYY-MM"
+ value-format="YYYY-MM"
+ range-separator="鑷�"
+ start-placeholder="寮�濮嬫湀浠�"
+ end-placeholder="缁撴潫鏈堜唤"
+ :disabled-date="disabledDate"
+ @change="handleDateChange"
+ class="w-full md:w-auto"
+ style="margin-right: 30px;"
+ />
+
+ <el-button
+ type="primary"
+ icon="Refresh"
+ @click="resetDateRange"
+ size="default"
+ >
+ 閲嶇疆
+ </el-button>
+ </div>
+
+ <main class="container mx-auto px-4 pb-10">
+ <!-- 璐㈠姟鎸囨爣鍗$墖 -->
+ <div class="stats-cards">
+ <!-- 鎬昏惀鏀� -->
+ <div class="stat-card stat-card-blue">
+ <div class="stat-icon">
+ <img src="@/assets/icons/png/walletBlue@2x.png" alt="鎬昏惀鏀�" />
+ </div>
+ <div class="stat-content">
+ <div class="stat-label">鎬昏惀鏀�</div>
+ <div class="stat-value">{{ formatMoney(pageInfo.totalIncome || 0) }} 鍏�</div>
+ </div>
+ </div>
+
+ <!-- 鎬绘敮鍑� -->
+ <div class="stat-card stat-card-orange">
+ <div class="stat-icon">
+ <img src="@/assets/icons/png/walletOrange@2x.png" alt="鎬绘敮鍑�" />
+ </div>
+ <div class="stat-content">
+ <div class="stat-label">鎬绘敮鍑�</div>
+ <div class="stat-value">{{ formatMoney(pageInfo.totalExpense || 0) }} 鍏�</div>
+ </div>
+ </div>
+
+ <!-- 鎬绘敹鍏ョ瑪鏁� -->
+ <div class="stat-card stat-card-green">
+ <div class="stat-icon">
+ <img src="@/assets/icons/png/walletGreen@2x.png" alt="鎬绘敹鍏ョ瑪鏁�" />
+ </div>
+ <div class="stat-content">
+ <div class="stat-label">鎬绘敹鍏ョ瑪鏁�</div>
+ <div class="stat-value">{{ pageInfo.incomeNumber || 0 }} 绗�</div>
+ </div>
+ </div>
+
+ <!-- 鎬绘敮鍑虹瑪鏁� -->
+ <div class="stat-card stat-card-red">
+ <div class="stat-icon">
+ <img src="@/assets/icons/png/walletRed@2x.png" alt="鎬绘敮鍑虹瑪鏁�" />
+ </div>
+ <div class="stat-content">
+ <div class="stat-label">鎬绘敮鍑虹瑪鏁�</div>
+ <div class="stat-value">{{ pageInfo.expenseNumber || 0 }} 绗�</div>
+ </div>
+ </div>
+
+ <!-- 鍑�鏀跺叆 -->
+ <div class="stat-card stat-card-yellow">
+ <div class="stat-icon">
+ <img src="@/assets/icons/png/walletYellow@2x.png" alt="鍑�鏀跺叆" />
+ </div>
+ <div class="stat-content">
+ <div class="stat-label">鍑�鏀跺叆</div>
+ <div class="stat-value">{{ formatMoney(pageInfo.netRevenue || 0) }} 鍏�</div>
+ </div>
+ </div>
+ </div>
+
+ <!-- 涓棿鍥捐〃鍖哄煙 -->
+ <div class="charts-row">
+ <!-- 宸︿晶锛氭敹鍏ユ敮鍑哄垎鏋� -->
+ <el-card class="chart-card">
+ <h2 class="section-title">鏀跺叆鏀嚭鍒嗘瀽</h2>
+ <div class="pie-chart-container">
+ <Echarts
+ :legend="pieLegendIncomeExpense"
+ :chartStyle="chartStylePie"
+ :series="pieSeriesIncomeExpense"
+ :tooltip="pieTooltipIncomeExpense"
+ style="height: 320px; width: 100%;">
+ </Echarts>
+ <div class="pie-stats">
+ <div class="bar-stat-item">
+ <span class="bar-stat-label">鏀跺叆鏁伴噺</span>
+ <span class="bar-stat-value">{{ pageInfo.incomeNumber || 0 }}</span>
+ </div>
+ <div class="bar-stat-item">
+ <span class="bar-stat-label">鏀嚭鏁伴噺</span>
+ <span class="bar-stat-value">{{ pageInfo.expenseNumber || 0 }}</span>
+ </div>
+ </div>
+ </div>
+ </el-card>
+
+ <!-- 鍙充晶锛氳椤圭泩鍒╁垎鏋� -->
+ <el-card class="chart-card">
+ <h2 class="section-title">琛岄」鐩堝埄鍒嗘瀽</h2>
+ <div class="bar-chart-header">
+ <div class="bar-stat-item">
+ <span class="bar-stat-label">褰撳墠鎬讳釜鏁�</span>
+ <span class="bar-stat-value">{{ allBarTypes.value?.length || 0 }}</span>
+ </div>
+ <div class="bar-stat-item">
+ <span class="bar-stat-label">鏀嚭閲戦</span>
+ <span class="bar-stat-value">{{ formatMoney(pageInfo.totalExpense || 0) }}</span>
+ </div>
+ <div class="bar-stat-item">
+ <span class="bar-stat-label">鏀跺叆閲戦</span>
+ <span class="bar-stat-value">{{ formatMoney(pageInfo.totalIncome || 0) }}</span>
+ </div>
+ </div>
+ <Echarts
+ ref="barChart"
+ :chartStyle="chartStyle"
+ :grid="barGrid"
+ :legend="barLegend"
+ :series="barSeries"
+ :tooltip="barTooltip"
+ :xAxis="barXAxis"
+ :yAxis="barYAxis"
+ style="height: 300px; width: 100%;">
+ </Echarts>
+ </el-card>
+ </div>
+
+ <!-- 搴曢儴锛氳惀鏀惰秼鍔垮垎鏋� -->
+ <el-card class="trend-chart-card">
+ <h2 class="section-title">钀ユ敹瓒嬪娍鍒嗘瀽</h2>
+ <Echarts
+ ref="trendChart"
+ :chartStyle="chartStyle"
+ :grid="grid"
+ :legend="trendLegend"
+ :series="trendSeries"
+ :tooltip="tooltip"
+ :xAxis="xAxis0"
+ :yAxis="trendYAxis"
+ style="height: 350px; width: 100%;">
+ </Echarts>
+ </el-card>
+ </main>
+ </div>
</template>
+
<script setup>
-</script>
\ No newline at end of file
+import { ref, computed, onMounted, reactive, nextTick, getCurrentInstance } from 'vue';
+import 'element-plus/dist/index.css';
+import Echarts from "@/components/Echarts/echarts.vue";
+import { reportForms,reportIncome,reportExpense } from "@/api/financialManagement/financialStatements";
+import dayjs from "dayjs";
+
+// 鏃ユ湡鑼冨洿
+const dateRange = ref(null);
+const { proxy } = getCurrentInstance();
+const chartStyle = {
+ width: '100%',
+ height: '100%', // 璁剧疆鍥捐〃瀹瑰櫒鐨勯珮搴�
+ position:'relative',
+}
+const grid = {
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ containLabel: true
+}
+const lineLegend = {
+ show: false,
+}
+// 鎶樼嚎鍥炬彁绀烘
+const tooltip = reactive({
+ trigger: 'axis',
+ axisPointer: {
+ type: 'line',
+ lineStyle: { color: '#aaa' }
+ },
+ // 鑷畾涔夊唴瀹�
+ formatter: function (params) {
+ if (!params || !params.length) return ''
+ const axisLabel = params[0].axisValueLabel || params[0].axisValue || ''
+ const rows = params
+ .map(p => {
+ const colorDot = `<span style="display:inline-block;margin-right:6px;width:8px;height:8px;border-radius:50%;background:${p.color}"></span>`
+ return `${colorDot}${p.seriesName}: ${p.value}`
+ })
+ .join('<br/>')
+ return `<div>${axisLabel}</div><div>${rows}</div>`
+ }
+})
+const lineSeries0 = ref([])
+const lineSeries1 = ref([])
+
+// 鏍规嵁鏈堜唤鑼冨洿鐢熸垚 x 杞存暟鎹�
+const generateMonthLabels = (startMonth, endMonth) => {
+ const labels = [];
+ let current = dayjs(startMonth);
+ const end = dayjs(endMonth);
+
+ while (current.isBefore(end) || current.isSame(end, 'month')) {
+ labels.push(`${current.month() + 1}鏈坄);
+ current = current.add(1, 'month');
+ }
+
+ return labels;
+};
+
+const xAxis0 = ref([
+ {
+ type: 'category',
+ axisTick: { show: true, alignWithLabel: true },
+ data: [],
+ },
+]);
+const xAxis1 = ref([
+ {
+ type: 'category',
+ axisTick: { show: true, alignWithLabel: true },
+ data: [],
+ },
+]);
+const yAxis0 = [
+{
+ type: 'value',
+ name: '鏀跺叆缁熻', // 宸︿晶y杞�
+ position: 'left',
+ min: 0,
+ // 鍧愭爣杞村悕绉版牱寮�
+ nameTextStyle: {
+ color: '#000',
+ fontSize: 14,
+ },
+ }
+]
+
+const yAxis1 = [
+{
+ type: 'value',
+ name: '鏀嚭缁熻', // 宸︿晶y杞�
+ position: 'left',
+ min: 0,
+ // 鍧愭爣杞村悕绉版牱寮�
+ nameTextStyle: {
+ color: '#000',
+ fontSize: 14,
+ },
+ }
+]
+
+const chartStylePie = {
+ width: '100%',
+ height: '100%' // 璁剧疆鍥捐〃瀹瑰櫒鐨勯珮搴�
+}
+const pieColors = ['#F04864','#FACC14', '#8543E0', '#1890FF', '#13C2C2','#2FC25B']; // 鍙牴鎹疄闄呰皟鏁�
+const pieData0 = ref([]);
+const pieData1 = ref([]);
+
+const pieLegend0 = computed(() => ({
+ show: true,
+ top: 'center',
+ left: '60%',
+ orient: 'vertical',
+ icon: 'circle',
+ data: (pieData0.value || []).filter(item => item && item.name).map(item => item.name),
+ formatter: function(name) {
+ if (!name) return '';
+ const item = pieData0.value.find(i => i && i.name === name);
+ if (!item) return name;
+ return `${name} | ${item.percent} ${item.amount}`;
+ },
+ textStyle: {
+ color: '#333',
+ fontSize: 14,
+ lineHeight: 26,
+ }
+}));
+const pieLegend1 = computed(() => ({
+ show: true,
+ top: 'center',
+ left: '60%',
+ orient: 'vertical',
+ icon: 'circle',
+ data: (pieData1.value || []).filter(item => item && item.name).map(item => item.name),
+ formatter: function(name) {
+ if (!name) return '';
+ const item = pieData1.value.find(i => i && i.name === name);
+ if (!item) return name;
+ return `${name} | ${item.percent} ${item.amount}`;
+ },
+ textStyle: {
+ color: '#333',
+ fontSize: 14,
+ lineHeight: 26,
+ }
+}));
+
+const materialPieSeries0 = computed(() => [
+ {
+ type: 'pie',
+ radius: ['50%', '65%'],
+ center: ['25%', '50%'],
+ avoidLabelOverlap: false,
+ itemStyle: {
+ borderColor: '#fff',
+ borderWidth: 2
+ },
+ label: {
+ show: false
+ },
+ data: (pieData0.value || []).filter(item => item && item.name),
+ color: pieColors
+ }
+]);
+const materialPieSeries1 = computed(() => [
+ {
+ type: 'pie',
+ radius: ['50%', '65%'],
+ center: ['25%', '50%'],
+ avoidLabelOverlap: false,
+ itemStyle: {
+ borderColor: '#fff',
+ borderWidth: 2
+ },
+ label: {
+ show: false
+ },
+ data: (pieData1.value || []).filter(item => item && item.name),
+ color: pieColors
+ }
+]);
+const pieTooltip = reactive({
+ trigger: 'item',
+ formatter: function(params) {
+ // 妫�鏌ユ暟鎹槸鍚﹀瓨鍦�
+ if (!params.data) return params.name;
+ // 鎷兼帴瀹屾暣鍐呭
+ return `
+ <div>
+ <div style="color:${params.color};font-size:16px;">鈼�</div>
+ <div>${params.name}</div>
+ <div>鍗犳瘮锛�${params.data.percent}</div>
+ <div>閲戦锛�${params.data.amount}</div>
+ </div>
+ `;
+ }
+})
+
+
+const pageInfo = ref({
+})
+
+// 鏍煎紡鍖栭噾棰�
+const formatMoney = (value) => {
+ if (!value && value !== 0) return '0';
+ return Number(value).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
+};
+
+// 鏀跺叆鏀嚭鍒嗘瀽楗煎浘
+const pieDataIncomeExpense = computed(() => {
+ const totalIncome = Number(pageInfo.value.totalIncome) || 0;
+ const totalExpense = Number(pageInfo.value.totalExpense) || 0;
+ const total = totalIncome + totalExpense;
+ if (total === 0) {
+ return [
+ { name: '鏀跺叆', value: 0, percent: '0%' },
+ { name: '鏀嚭', value: 0, percent: '0%' }
+ ];
+ }
+ const incomePercent = ((totalIncome / total) * 100).toFixed(0);
+ const expensePercent = ((totalExpense / total) * 100).toFixed(0);
+ return [
+ { name: '鏀跺叆', value: totalIncome, percent: `${incomePercent}%` },
+ { name: '鏀嚭', value: totalExpense, percent: `${expensePercent}%` }
+ ];
+});
+
+const pieLegendIncomeExpense = computed(() => ({
+ show: false
+}));
+
+const pieTooltipIncomeExpense = reactive({
+ trigger: 'item',
+ formatter: function(params) {
+ if (!params.data) return params.name;
+ return `${params.name}鍗犳瘮 ${params.percent}%`;
+ }
+});
+
+const pieSeriesIncomeExpense = computed(() => [
+ {
+ type: 'pie',
+ radius: ['0%', '70%'],
+ center: ['50%', '50%'],
+ avoidLabelOverlap: true,
+ itemStyle: {
+ borderColor: '#fff',
+ borderWidth: 2
+ },
+ label: {
+ show: true,
+ position: 'outside',
+ formatter: function(params) {
+ return `${params.name}鍗犳瘮 ${params.percent}%`;
+ },
+ fontSize: 14,
+ color: '#333'
+ },
+ labelLine: {
+ show: true,
+ length: 15,
+ length2: 10,
+ lineStyle: {
+ color: '#333'
+ }
+ },
+ emphasis: {
+ label: {
+ show: true,
+ fontSize: 16,
+ fontWeight: 'bold'
+ }
+ },
+ data: pieDataIncomeExpense.value,
+ color: ['#1890FF', '#FACC14']
+ }
+]);
+
+// 琛岄」鐩堝埄鍒嗘瀽鏌辩姸鍥�
+const barXAxis = computed(() => {
+ return [{
+ type: 'category',
+ data: (allBarTypes.value && allBarTypes.value.length > 0) ? allBarTypes.value : ['椤圭洰1', '椤圭洰2', '椤圭洰3', '椤圭洰4', '椤圭洰5', '椤圭洰6', '椤圭洰7'],
+ axisTick: { show: true, alignWithLabel: true },
+ }];
+});
+
+const barYAxis = [{
+ type: 'value',
+ name: '鍗曚綅: 鍏�',
+ position: 'left',
+ min: 0,
+ nameTextStyle: {
+ color: '#000',
+ fontSize: 14,
+ },
+}];
+
+const barGrid = {
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ containLabel: true
+};
+
+const barLegend = {
+ show: true,
+ top: 10,
+ right: 10,
+};
+
+// 鑾峰彇鎵�鏈夌被鍨嬪悕绉�
+const allBarTypes = computed(() => {
+ const incomeTypes = (lineSeries0.value || []).map(item => item.name || item.typeName).filter(Boolean);
+ const expenseTypes = (lineSeries1.value || []).map(item => item.name || item.typeName).filter(Boolean);
+ return [...new Set([...incomeTypes, ...expenseTypes])];
+});
+
+const barSeries = computed(() => {
+ if (allBarTypes.value.length === 0) {
+ return [
+ {
+ name: '鏀嚭',
+ type: 'bar',
+ data: [],
+ itemStyle: { color: '#1890FF' }
+ },
+ {
+ name: '鏀跺叆',
+ type: 'bar',
+ data: [],
+ itemStyle: { color: '#13C2C2' }
+ }
+ ];
+ }
+
+ // 璁$畻姣忎釜椤圭洰鐨勬�绘敹鍏ワ紙姹囨�绘墍鏈夋湀浠斤級
+ const incomeData = allBarTypes.value.map(typeName => {
+ const incomeItem = (lineSeries0.value || []).find(item => (item.name || item.typeName) === typeName);
+ if (incomeItem && incomeItem.data && Array.isArray(incomeItem.data)) {
+ return incomeItem.data.reduce((sum, val) => sum + (Number(val) || 0), 0);
+ }
+ return 0;
+ });
+
+ // 璁$畻姣忎釜椤圭洰鐨勬�绘敮鍑猴紙姹囨�绘墍鏈夋湀浠斤級
+ const expenseData = allBarTypes.value.map(typeName => {
+ const expenseItem = (lineSeries1.value || []).find(item => (item.name || item.typeName) === typeName);
+ if (expenseItem && expenseItem.data && Array.isArray(expenseItem.data)) {
+ return expenseItem.data.reduce((sum, val) => sum + (Number(val) || 0), 0);
+ }
+ return 0;
+ });
+
+ return [
+ {
+ name: '鏀嚭',
+ type: 'bar',
+ data: expenseData,
+ itemStyle: { color: '#1890FF' }
+ },
+ {
+ name: '鏀跺叆',
+ type: 'bar',
+ data: incomeData,
+ itemStyle: { color: '#13C2C2' }
+ }
+ ];
+});
+
+const barTooltip = reactive({
+ trigger: 'axis',
+ axisPointer: {
+ type: 'shadow'
+ },
+ formatter: function (params) {
+ if (!params || !params.length) return '';
+ const axisLabel = params[0].axisValueLabel || params[0].axisValue || '';
+ const rows = params
+ .map(p => {
+ const colorDot = `<span style="display:inline-block;margin-right:6px;width:8px;height:8px;border-radius:50%;background:${p.color}"></span>`;
+ const value = typeof p.value === 'number' ? p.value.toFixed(2) : p.value;
+ return `${colorDot}${p.seriesName} ${value}`;
+ })
+ .join('<br/>');
+ return `<div>${axisLabel}</div><div>${rows}</div>`;
+ }
+});
+
+// 钀ユ敹瓒嬪娍鍒嗘瀽
+const trendLegend = {
+ show: true,
+ top: 10,
+ right: 10,
+};
+
+const trendYAxis = [{
+ type: 'value',
+ name: '鍗曚綅: 鍏�',
+ position: 'left',
+ min: 0,
+ nameTextStyle: {
+ color: '#000',
+ fontSize: 14,
+ },
+}];
+
+const trendSeries = computed(() => {
+ // 姹囨�绘墍鏈夋敮鍑虹被鍨嬬殑鏁版嵁
+ let expenseTrend = [];
+ if (lineSeries1.value.length > 0) {
+ const monthCount = Math.max(...lineSeries1.value.map(item => item.data?.length || 0));
+ expenseTrend = Array(monthCount).fill(0);
+ lineSeries1.value.forEach(item => {
+ if (item.data && Array.isArray(item.data)) {
+ item.data.forEach((val, index) => {
+ if (index < monthCount) {
+ expenseTrend[index] += Number(val) || 0;
+ }
+ });
+ }
+ });
+ }
+
+ // 姹囨�绘墍鏈夋敹鍏ョ被鍨嬬殑鏁版嵁
+ let incomeTrend = [];
+ if (lineSeries0.value.length > 0) {
+ const monthCount = Math.max(...lineSeries0.value.map(item => item.data?.length || 0));
+ incomeTrend = Array(monthCount).fill(0);
+ lineSeries0.value.forEach(item => {
+ if (item.data && Array.isArray(item.data)) {
+ item.data.forEach((val, index) => {
+ if (index < monthCount) {
+ incomeTrend[index] += Number(val) || 0;
+ }
+ });
+ }
+ });
+ }
+
+ return [
+ {
+ name: '鏀嚭',
+ type: 'line',
+ data: expenseTrend,
+ itemStyle: { color: '#1890FF' },
+ smooth: true
+ },
+ {
+ name: '鏀跺叆',
+ type: 'line',
+ data: incomeTrend,
+ itemStyle: { color: '#13C2C2' },
+ smooth: true
+ }
+ ];
+});
+
+// 鑾峰彇鏈�杩戝叚涓湀鐨勮寖鍥�
+const getLastSixMonths = () => {
+ const endMonth = dayjs().format('YYYY-MM');
+ const startMonth = dayjs().subtract(5, 'month').format('YYYY-MM');
+ return [startMonth, endMonth];
+};
+
+const getData = async () => {
+ if (!dateRange.value || !Array.isArray(dateRange.value) || dateRange.value.length !== 2) {
+ return;
+ }
+ const startDateStr = dateRange.value[0];
+ const endDateStr = dateRange.value[1];
+ if (!startDateStr || !endDateStr) {
+ return;
+ }
+
+ // 楠岃瘉鏃ユ湡鏍煎紡骞惰浆鎹负瀹屾暣鏃ユ湡
+ const startDate = dayjs(startDateStr);
+ const endDate = dayjs(endDateStr);
+ if (!startDate.isValid() || !endDate.isValid()) {
+ console.error('鏃犳晥鐨勬棩鏈熸牸寮�');
+ return;
+ }
+
+ // 鏇存柊 x 杞存暟鎹�
+ const monthLabels = generateMonthLabels(startDateStr, endDateStr);
+ xAxis0.value[0].data = monthLabels;
+ xAxis1.value[0].data = monthLabels;
+
+ // 寮�濮嬫湀浠芥嫾鎺ョ涓�澶╋紝缁撴潫鏈堜唤鎷兼帴鏈�鍚庝竴澶�
+ const entryDateStart = startDate.startOf('month').format('YYYY-MM-DD');
+ const entryDateEnd = endDate.endOf('month').format('YYYY-MM-DD');
+
+ try {
+ const {code,data} = await reportForms({entryDateStart, entryDateEnd});
+ if(code === 200 && data) {
+ pageInfo.value = data || {};
+ // 瀹夊叏澶勭悊鏁版嵁锛岃繃婊ゆ帀 null 鎴� undefined
+ pieData0.value = (data.incomeType || []).filter(item => item && item.typeName).map(item=>({
+ name:item.typeName || '',
+ value:item.account || 0,
+ percent:`${((item.proportion || 0) * 100).toFixed(2)}%`,
+ amount:`楼${(item.account || 0).toFixed(2)}`
+ }))
+ pieData1.value = (data.expenseType || []).filter(item => item && item.typeName).map(item=>({
+ name:item.typeName || '',
+ value:item.account || 0,
+ percent:`${((item.proportion || 0) * 100).toFixed(2)}%`,
+ amount:`楼${(item.account || 0).toFixed(2)}`
+ }))
+ }
+ } catch (error) {
+ console.error('鑾峰彇璐㈠姟鎸囨爣鏁版嵁澶辫触锛�', error);
+ }
+ try{
+ const {code,data} = await reportIncome({entryDateStart, entryDateEnd});
+ if(code==200 && data && Array.isArray(data)){
+ lineSeries0.value = data.filter(item => item && item.typeName).map(item=>({
+ name:item.typeName || '',
+ type: 'line',
+ data:(item.account || []).map(val => Number(val) || 0)
+ }))
+ }
+ }catch (error) {
+ console.error('鑾峰彇璐㈠姟鎸囨爣鏁版嵁澶辫触锛�', error);
+ }
+ try{
+ const {code,data} = await reportExpense({entryDateStart, entryDateEnd});
+ if(code==200 && data && Array.isArray(data)){
+ lineSeries1.value = data.filter(item => item && item.typeName).map(item=>({
+ name:item.typeName || '',
+ type: 'line',
+ data:(item.account || []).map(val => Number(val) || 0)
+ }))
+ }
+ }catch (error) {
+ console.error('鑾峰彇璐㈠姟鎸囨爣鏁版嵁澶辫触锛�', error);
+ }
+};
+
+
+// 鍒濆鍖�
+onMounted(() => {
+ // 璁剧疆榛樿鍊间负鏈�杩戝叚涓湀
+ const defaultRange = getLastSixMonths();
+ dateRange.value = defaultRange;
+ // 浣跨敤 nextTick 纭繚缁勪欢瀹屽叏娓叉煋鍚庡啀璋冪敤
+ nextTick(() => {
+ getData();
+ });
+});
+
+// 闄愬埗鏈堜唤閫夋嫨鑼冨洿锛堟渶澶�12涓湀锛�
+const disabledDate = (time) => {
+ // 濡傛灉娌℃湁閫夋嫨寮�濮嬫湀浠斤紝涓嶇鐢ㄤ换浣曟棩鏈�
+ if (!dateRange.value || !Array.isArray(dateRange.value) || !dateRange.value[0]) {
+ return false;
+ }
+
+ const startMonth = dayjs(dateRange.value[0]);
+ const currentMonth = dayjs(time);
+
+ // 濡傛灉褰撳墠鏈堜唤鍦ㄥ紑濮嬫湀浠戒箣鍓嶏紝绂佺敤
+ if (currentMonth.isBefore(startMonth, 'month')) {
+ return true;
+ }
+
+ // 璁$畻鏈�澶у厑璁哥殑鏈堜唤锛堝紑濮嬫湀浠� + 11涓湀 = 12涓湀锛�
+ const maxMonth = startMonth.add(11, 'month');
+
+ // 绂佺敤瓒呰繃12涓湀鐨勬湀浠�
+ return currentMonth.isAfter(maxMonth, 'month');
+};
+
+// 澶勭悊鏈堜唤鑼冨洿鍙樺寲
+const handleDateChange = (newRange) => {
+ if (!newRange || !Array.isArray(newRange) || newRange.length !== 2) {
+ return;
+ }
+
+ // 楠岃瘉鏈堜唤鑼冨洿涓嶈秴杩�12涓湀
+ const startDate = dayjs(newRange[0]);
+ const endDate = dayjs(newRange[1]);
+ const monthDiff = endDate.diff(startDate, 'month');
+
+ if (monthDiff > 11) {
+ proxy.$modal.msgWarning('鏈�澶氬彧鑳介�夋嫨12涓湀浠�');
+ // 鑷姩璋冩暣涓�12涓湀
+ const adjustedEnd = startDate.add(11, 'month').format('YYYY-MM');
+ dateRange.value = [newRange[0], adjustedEnd];
+ getData();
+ return;
+ }
+
+ dateRange.value = newRange;
+ getData();
+};
+
+// 閲嶇疆鏈堜唤鑼冨洿
+const resetDateRange = () => {
+ // 閲嶇疆涓烘渶杩戝叚涓湀
+ dateRange.value = getLastSixMonths();
+ getData();
+};
+
+</script>
+
+<style scoped lang="scss">
+/* 鍩虹鏍峰紡琛ュ厖 */
+:root {
+ --el-color-primary: #4f46e5;
+}
+
+/* 缁熻鍗$墖鏍峰紡 */
+.stats-cards {
+ display: grid;
+ grid-template-columns: repeat(5, 1fr);
+ gap: 20px;
+ margin-bottom: 20px;
+}
+
+.stat-card {
+ background: #fff;
+ border: 1px solid #e4e7ed;
+ border-radius: 8px;
+ padding: 20px;
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+ transition: all 0.3s;
+
+ &:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ transform: translateY(-2px);
+ }
+
+ .stat-icon {
+ width: 48px;
+ height: 48px;
+ flex-shrink: 0;
+
+ img {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ }
+ }
+
+ .stat-content {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+
+ .stat-label {
+ font-size: 14px;
+ color: #666;
+ line-height: 1.2;
+ }
+
+ .stat-value {
+ font-size: 24px;
+ font-weight: 600;
+ color: #333;
+ line-height: 1.2;
+ }
+
+ .stat-trend {
+ font-size: 12px;
+ line-height: 1.2;
+
+ &.trend-up {
+ color: #f56c6c;
+ }
+
+ &.trend-down {
+ color: #67c23a;
+ }
+ }
+}
+
+/* 鍥捐〃琛屽竷灞� */
+.charts-row {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 20px;
+ margin-bottom: 20px;
+}
+
+.chart-card {
+ border: 1px solid #e4e7ed;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+
+ :deep(.el-card__body) {
+ padding: 20px !important;
+ }
+}
+
+.trend-chart-card {
+ border: 1px solid #e4e7ed;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+
+ :deep(.el-card__body) {
+ padding: 20px !important;
+ }
+}
+
+/* 楗煎浘瀹瑰櫒 */
+.pie-chart-container {
+ position: relative;
+
+ .pie-stats {
+ display: flex;
+ justify-content: space-between;
+ gap: 20px;
+ margin-top: 20px;
+
+ .bar-stat-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 8px;
+ padding: 15px;
+ background: #f5f7fa;
+ border-radius: 6px;
+ flex: 1;
+
+ .bar-stat-label {
+ font-size: 14px;
+ color: #666;
+ }
+
+ .bar-stat-value {
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ }
+ }
+ }
+}
+
+/* 鏌辩姸鍥惧ご閮ㄧ粺璁� */
+.bar-chart-header {
+ display: flex;
+ justify-content: space-between;
+ gap: 20px;
+ margin-bottom: 20px;
+
+ .bar-stat-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 8px;
+ padding: 15px;
+ background: #f5f7fa;
+ border-radius: 6px;
+ flex: 1;
+
+ .bar-stat-label {
+ font-size: 14px;
+ color: #666;
+ }
+
+ .bar-stat-value {
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ }
+ }
+}
+
+/* 鏍囬鏍峰紡 */
+.section-title {
+ position: relative;
+ font-size: 18px;
+ color: #333;
+ padding-left: 12px;
+ margin-bottom: 20px;
+ font-weight: 700;
+
+ &::before {
+ position: absolute;
+ left: 0;
+ top: 2px;
+ content: '';
+ width: 4px;
+ height: 18px;
+ background-color: #002FA7;
+ border-radius: 2px;
+ }
+}
+
+/* 鍝嶅簲寮忚璁� */
+@media (max-width: 1400px) {
+ .stats-cards {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+
+@media (max-width: 1024px) {
+ .stats-cards {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .charts-row {
+ grid-template-columns: 1fr;
+ }
+}
+
+@media (max-width: 640px) {
+ .stats-cards {
+ grid-template-columns: 1fr;
+ }
+}
+</style>
+
+
+
+
+
+
+
+
+
+
--
Gitblit v1.9.3