From 1c0863efe062af3ebcdecb8c10568d779f5c8295 Mon Sep 17 00:00:00 2001
From: zouyu <2723363702@qq.com>
Date: 星期一, 26 一月 2026 15:10:55 +0800
Subject: [PATCH] Merge remote-tracking branch 'refs/remotes/origin/dev_New' into dev_tide_mis_xindao
---
src/views/financialManagement/financialStatements/index.vue | 941 ++++++++++++++++++++++++++++++++++++++++++++--------------
1 files changed, 714 insertions(+), 227 deletions(-)
diff --git a/src/views/financialManagement/financialStatements/index.vue b/src/views/financialManagement/financialStatements/index.vue
index e5f9b23..cf4eee5 100644
--- a/src/views/financialManagement/financialStatements/index.vue
+++ b/src/views/financialManagement/financialStatements/index.vue
@@ -1,16 +1,16 @@
<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="daterange"
- format="YYYY-MM-DD"
- value-format="YYYY-MM-DD"
+ type="monthrange"
+ format="YYYY-MM"
+ value-format="YYYY-MM"
range-separator="鑷�"
- start-placeholder="寮�濮嬫棩鏈�"
- end-placeholder="缁撴潫鏃ユ湡"
- clearable
+ start-placeholder="寮�濮嬫湀浠�"
+ end-placeholder="缁撴潫鏈堜唤"
+ :disabled-date="disabledDate"
@change="handleDateChange"
class="w-full md:w-auto"
style="margin-right: 30px;"
@@ -28,109 +28,141 @@
<main class="container mx-auto px-4 pb-10">
<!-- 璐㈠姟鎸囨爣鍗$墖 -->
- <div class="grid-container">
- <!-- 鎬绘敹鍏� -->
- <el-card class="bg1">
- <p>鎬绘敹鍏�</p>
- <h3>
- 楼{{ pageInfo.totalIncome }}
- </h3>
- </el-card>
-
- <!-- 鏀跺叆绗旀暟 -->
- <el-card class="bg2">
- <p>鏀跺叆绗旀暟</p>
- <h3>
- {{ pageInfo.incomeNumber }}
- </h3>
- </el-card>
+ <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>
<!-- 鎬绘敮鍑� -->
- <el-card class="bg3">
- <p>鎬绘敮鍑�</p>
- <h3>
- 楼{{ pageInfo.totalExpense }}
- </h3>
- </el-card>
+ <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>
- <!-- 鏀嚭绗旀暟 -->
- <el-card class="bg4">
- <p>鏀嚭绗旀暟</p>
- <h3>
- {{ pageInfo.expenseNumber }}
- </h3>
- </el-card>
+ <!-- 鎬绘敹鍏ョ瑪鏁� -->
+ <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>
<!-- 鍑�鏀跺叆 -->
- <el-card class="bg5">
- <p>鍑�鏀跺叆</p>
- <h3>
- 楼{{ pageInfo.netRevenue }}
- </h3>
+ <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>
- <!-- 鏀跺叆缁熻鍥捐〃 -->
- <div class="grid-layout">
- <el-card style="margin-bottom: 20px;">
- <h2 class="section-title">鏀跺叆缁熻(鍏�)</h2>
- <div class="echarts">
- <Echarts :legend="pieLegend0" :chartStyle="chartStylePie"
- :series="materialPieSeries0"
- :tooltip="pieTooltip" style="height: 260px;width: 35%;">
- <div class="chart-num">
- <span style="font-size: 22px;">鏀跺叆</span>
- <span style="font-size: 36px;
- font-weight: 500;
- font-family: 'MyCustomFont', sans-serif;">{{ pageInfo.totalIncome }}</span>
- </div>
- </Echarts>
- <Echarts ref="chart"
- :chartStyle="chartStyle"
- :grid="grid"
- :legend="lineLegend"
- :series="lineSeries0"
- :tooltip="tooltip"
- :xAxis="xAxis0"
- :yAxis="yAxis0"
- style="height: 260px;width: 64%;"></Echarts>
- </div>
- </el-card>
-
- <!-- 鏀嚭缁熻鍥捐〃 -->
- <el-card>
- <h2 class="section-title">鏀嚭缁熻(鍏�)</h2>
- <div class="echarts">
- <Echarts ref="chart"
- :legend="pieLegend1"
- :chartStyle="chartStylePie"
- :series="materialPieSeries1"
- :tooltip="pieTooltip"
- style="height: 260px;width: 35%;">
- <div class="chart-num">
- <span style="font-size: 22px;">鏀嚭</span>
- <span style="font-size: 36px;
- font-weight: 500;
- font-family: 'MyCustomFont', sans-serif;">{{ pageInfo.totalExpense }}</span>
- </div></Echarts>
- <Echarts ref="chart"
- :chartStyle="chartStyle"
- :grid="grid"
- :legend="lineLegend"
- :series="lineSeries1"
- :tooltip="tooltip"
- :xAxis="xAxis1"
- :yAxis="yAxis1"
- style="height: 260px;width: 64%;"></Echarts>
- </div>
- </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>
-import { ref, computed, onMounted, reactive } from 'vue';
+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";
@@ -138,6 +170,7 @@
// 鏃ユ湡鑼冨洿
const dateRange = ref(null);
+const { proxy } = getCurrentInstance();
const chartStyle = {
width: '100%',
height: '100%', // 璁剧疆鍥捐〃瀹瑰櫒鐨勯珮搴�
@@ -172,22 +205,35 @@
return `<div>${axisLabel}</div><div>${rows}</div>`
}
})
-const months = ['1鏈�','2鏈�','3鏈�','4鏈�','5鏈�','6鏈�','7鏈�','8鏈�','9鏈�','10鏈�','11鏈�','12鏈�'];
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: months,
+ data: [],
},
]);
const xAxis1 = ref([
{
type: 'category',
axisTick: { show: true, alignWithLabel: true },
- data: months,
+ data: [],
},
]);
const yAxis0 = [
@@ -232,9 +278,10 @@
left: '60%',
orient: 'vertical',
icon: 'circle',
- data: pieData0.value.map(item => item.name),
+ data: (pieData0.value || []).filter(item => item && item.name).map(item => item.name),
formatter: function(name) {
- const item = pieData0.value.find(i => i.name === name);
+ if (!name) return '';
+ const item = pieData0.value.find(i => i && i.name === name);
if (!item) return name;
return `${name} | ${item.percent} ${item.amount}`;
},
@@ -250,9 +297,10 @@
left: '60%',
orient: 'vertical',
icon: 'circle',
- data: pieData1.value.map(item => item.name),
+ data: (pieData1.value || []).filter(item => item && item.name).map(item => item.name),
formatter: function(name) {
- const item = pieData1.value.find(i => i.name === name);
+ if (!name) return '';
+ const item = pieData1.value.find(i => i && i.name === name);
if (!item) return name;
return `${name} | ${item.percent} ${item.amount}`;
},
@@ -276,7 +324,7 @@
label: {
show: false
},
- data: pieData0.value,
+ data: (pieData0.value || []).filter(item => item && item.name),
color: pieColors
}
]);
@@ -293,7 +341,7 @@
label: {
show: false
},
- data: pieData1.value,
+ data: (pieData1.value || []).filter(item => item && item.name),
color: pieColors
}
]);
@@ -318,53 +366,337 @@
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 || !dateRange.value.length) {
+ 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:dateRange.value[0], entryDateEnd:dateRange.value[1]});
- if(code === 200) {
- pageInfo.value = data
- pieData0.value = data.incomeType.map(item=>({
- name:item.typeName,
- value:item.account,
- percent:`${item.proportion*100}%`,
- amount:`楼${item.account}`
+ 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.map(item=>({
- name:item.typeName,
- value:item.account,
- percent:`${item.proportion*100}%`,
- amount:`楼${item.account}`
+ 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();
- if(code==200){
- lineSeries0.value = data.map(item=>({
- name:item.typeName,
+ 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(item=>Number(item))
+ data:(item.account || []).map(val => Number(val) || 0)
}))
-
}
}catch (error) {
console.error('鑾峰彇璐㈠姟鎸囨爣鏁版嵁澶辫触锛�', error);
}
try{
- const {code,data} = await reportExpense();
- if(code==200){
- lineSeries1.value = data.map(item=>({
- name:item.typeName,
+ 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(item=>Number(item))
+ data:(item.account || []).map(val => Number(val) || 0)
}))
-
}
}catch (error) {
console.error('鑾峰彇璐㈠姟鎸囨爣鏁版嵁澶辫触锛�', error);
@@ -374,20 +706,66 @@
// 鍒濆鍖�
onMounted(() => {
- // 涓嶈缃粯璁ゆ棩鏈燂紝鐢辩敤鎴锋墜鍔ㄩ�夋嫨
+ // 璁剧疆榛樿鍊间负鏈�杩戝叚涓湀
+ const defaultRange = getLastSixMonths();
+ dateRange.value = defaultRange;
+ // 浣跨敤 nextTick 纭繚缁勪欢瀹屽叏娓叉煋鍚庡啀璋冪敤
+ nextTick(() => {
+ getData();
+ });
});
-// 澶勭悊鏃ユ湡鑼冨洿鍙樺寲
-const handleDateChange = (newRange) => {
- dateRange.value = newRange;
- if (newRange && newRange.length === 2) {
- 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 = null;
+ // 閲嶇疆涓烘渶杩戝叚涓湀
+ dateRange.value = getLastSixMonths();
+ getData();
};
</script>
@@ -397,111 +775,220 @@
:root {
--el-color-primary: #4f46e5;
}
-.el-card{
- position: relative;
- border-radius: 12px;
- padding: 14px 10px 10px 10px;
- box-shadow: 0 2px 8px #eee;
- :deep(.el-card__body){
- padding: 10px 20px !important;
- }
- &.bg1{
- background: url(@/assets/icons/png/1.png) no-repeat 100% 100% !important;
- }
- &.bg2{
- background: url(@/assets/icons/png/2.png) no-repeat 100% 100% !important;
- }
- &.bg3{
- background: url(@/assets/icons/png/3.png) no-repeat 100% 100% !important;
- }
- &.bg4{
- background: url(@/assets/icons/png/4.png) no-repeat 100% 100% !important;
- }
- &.bg5{
- background: url(@/assets/icons/png/5.png) no-repeat 100% 100% !important;
- }
-}
-.grid-container {
- /* grid 瀹瑰櫒鍩虹鏍峰紡 */
+/* 缁熻鍗$墖鏍峰紡 */
+.stats-cards {
display: grid;
- gap: 1rem; /* gap-4 瀵瑰簲 1rem (16px) */
- margin-bottom: 2rem; /* mb-8 瀵瑰簲 2rem (32px) */
+ 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;
- p{
- font-size: 22px;
- margin-top: 0px;
- color: #fff;
- }
- h3{
- font-size: 36px;
- font-weight: 500;
- font-family: 'MyCustomFont', sans-serif;
- margin: 10px 0;
- color: #fff;
+ &:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ transform: translateY(-2px);
}
-}
-
-/* 绉诲姩绔粯璁ゆ牱寮� (grid-cols-1) */
-.grid-container {
- grid-template-columns: repeat(1, minmax(0, 1fr));
-}
-
-/* 灏忓睆骞曞強浠ヤ笂 (sm:grid-cols-2) */
-@media (min-width: 640px) {
- .grid-container {
- grid-template-columns: repeat(2, minmax(0, 1fr));
+ .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;
+ }
}
}
-/* 澶у睆骞曞強浠ヤ笂 (lg:grid-cols-5) */
-@media (min-width: 1024px) {
- .grid-container {
- grid-template-columns: repeat(5, minmax(0, 1fr));
+/* 鍥捐〃琛屽竷灞� */
+.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;
}
}
-/* 鍗$墖鎮仠鏁堟灉澧炲己 */
-.el-card:hover {
- transform: translateY(-2px);
+.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;
+ }
}
-.echarts{
+
+/* 楗煎浘瀹瑰櫒 */
+.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;
+ }
+ }
}
-/* 鍥捐〃瀹瑰櫒鏍峰紡 */
-.el-chart {
- width: 100%;
- height: 100%;
-}
+/* 鏍囬鏍峰紡 */
.section-title {
- position: relative;
- font-size: 18px;
- color: #333;
- padding-left: 10px;
- margin-bottom: 10px;
- font-weight: 700;
+ 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;
+ }
}
-.section-title::before {
- position: absolute;
- left: 0;
- top: 0px;
- content: '';
- width: 4px;
- height: 18px;
- background-color: #002FA7;
- border-radius: 2px;
+/* 鍝嶅簲寮忚璁� */
+@media (max-width: 1400px) {
+ .stats-cards {
+ grid-template-columns: repeat(3, 1fr);
+ }
}
-.chart-num{
- position: absolute;
- z-index: 3;
- top: 92px;
- left: 92px;
- display: flex;
- flex-direction: column;
- justify-content: center;
+
+@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