From bb886d353ae6e59b91b428911eab8baa8cbc338f Mon Sep 17 00:00:00 2001 From: spring <2396852758@qq.com> Date: 星期五, 08 八月 2025 11:31:09 +0800 Subject: [PATCH] 完成财务报表 --- src/views/financialManagement/financialStatements/index.vue | 513 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 513 insertions(+), 0 deletions(-) diff --git a/src/views/financialManagement/financialStatements/index.vue b/src/views/financialManagement/financialStatements/index.vue index e69de29..430fe6a 100644 --- a/src/views/financialManagement/financialStatements/index.vue +++ b/src/views/financialManagement/financialStatements/index.vue @@ -0,0 +1,513 @@ +<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" + range-separator="鑷�" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + :default-value="[new Date(firstDayOfMonth), new Date()]" + @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="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> + + <!-- 鎬绘敮鍑� --> + <el-card class="bg3"> + <p>鎬绘敮鍑�</p> + <h3> + 楼{{ pageInfo.totalExpense }} + </h3> + </el-card> + + <!-- 鏀嚭绗旀暟 --> + <el-card class="bg4"> + <p>鏀嚭绗旀暟</p> + <h3> + {{ pageInfo.expenseNumber }} + </h3> + </el-card> + + <!-- 鍑�鏀跺叆 --> + <el-card class="bg5"> + <p>鍑�鏀跺叆</p> + <h3> + 楼{{ pageInfo.netRevenue }} + </h3> + </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> + </main> + </div> +</template> + +<script setup> +import { ref, computed, onMounted, reactive } 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([]); +const firstDayOfMonth = ref(null); +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 months = ['1鏈�','2鏈�','3鏈�','4鏈�','5鏈�','6鏈�','7鏈�','8鏈�','9鏈�','10鏈�','11鏈�','12鏈�']; +const lineSeries0 = ref([]) +const lineSeries1 = ref([]) + +const xAxis0 = ref([ + { + type: 'category', + axisTick: { show: true, alignWithLabel: true }, + data: months, + }, +]); +const xAxis1 = ref([ + { + type: 'category', + axisTick: { show: true, alignWithLabel: true }, + data: months, + }, +]); +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.map(item => item.name), + formatter: function(name) { + const item = pieData0.value.find(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.map(item => item.name), + formatter: function(name) { + const item = pieData1.value.find(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, + 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, + 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 getData = async () => { + 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}` + })) + pieData1.value = data.expenseType.map(item=>({ + name:item.typeName, + value:item.account, + percent:`${item.proportion*100}%`, + amount:`楼${item.account}` + })) + + } + } catch (error) { + console.error('鑾峰彇璐㈠姟鎸囨爣鏁版嵁澶辫触锛�', error); + } + try{ + const {code,data} = await reportIncome(); + if(code==200){ + lineSeries0.value = data.map(item=>({ + name:item.typeName, + type: 'line', + data:item.account.map(item=>Number(item)) + })) + + } + }catch (error) { + console.error('鑾峰彇璐㈠姟鎸囨爣鏁版嵁澶辫触锛�', error); + } + try{ + const {code,data} = await reportExpense(); + if(code==200){ + lineSeries1.value = data.map(item=>({ + name:item.typeName, + type: 'line', + data:item.account.map(item=>Number(item)) + })) + + } + }catch (error) { + console.error('鑾峰彇璐㈠姟鎸囨爣鏁版嵁澶辫触锛�', error); + } +}; + + +// 鍒濆鍖栨棩鏈熻寖鍥达紙榛樿褰撴湀锛� +onMounted(() => { + const today = new Date(); + const firstDay = new Date(today.getFullYear(), today.getMonth(), 1); + firstDayOfMonth.value = firstDay; + dateRange.value = [dayjs(firstDay).format("YYYY-MM-DD"), dayjs(today).format("YYYY-MM-DD")]; + getData() + +}); + +// 澶勭悊鏃ユ湡鑼冨洿鍙樺寲 +const handleDateChange = (newRange) => { + if (newRange && newRange.length === 2) { + dateRange.value = newRange; + getData() + } +}; + +// 閲嶇疆鏃ユ湡鑼冨洿 +const resetDateRange = () => { + const today = new Date(); + const firstDay = new Date(today.getFullYear(), today.getMonth(), 1); + dateRange.value = [dayjs(firstDay).format("YYYY-MM-DD"), dayjs(today).format("YYYY-MM-DD")]; + getData() +}; + +</script> + +<style scoped lang="scss"> +/* 鍩虹鏍峰紡琛ュ厖 */ +: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 瀹瑰櫒鍩虹鏍峰紡 */ + display: grid; + gap: 1rem; /* gap-4 瀵瑰簲 1rem (16px) */ + margin-bottom: 2rem; /* mb-8 瀵瑰簲 2rem (32px) */ + + 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; + } + +} + +/* 绉诲姩绔粯璁ゆ牱寮� (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)); + } +} + +/* 澶у睆骞曞強浠ヤ笂 (lg:grid-cols-5) */ +@media (min-width: 1024px) { + .grid-container { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } +} + +/* 鍗$墖鎮仠鏁堟灉澧炲己 */ +.el-card:hover { + transform: translateY(-2px); +} +.echarts{ + display: flex; + justify-content: space-between; +} + +/* 鍥捐〃瀹瑰櫒鏍峰紡 */ +.el-chart { + width: 100%; + height: 100%; +} +.section-title { + position: relative; + font-size: 18px; + color: #333; + padding-left: 10px; + margin-bottom: 10px; + font-weight: 700; +} + +.section-title::before { + position: absolute; + left: 0; + top: 0px; + content: ''; + width: 4px; + height: 18px; + background-color: #002FA7; + border-radius: 2px; +} +.chart-num{ + position: absolute; + z-index: 3; + top: 92px; + left: 92px; + display: flex; + flex-direction: column; + justify-content: center; +} +</style> -- Gitblit v1.9.3