🎈 perf: 图表简单调整

This commit is contained in:
砂糖
2025-09-22 14:44:36 +08:00
parent 75bd852c5b
commit 32a7bdfd6c
2 changed files with 106 additions and 166 deletions

View File

@@ -201,9 +201,9 @@ const handleQuery = () => {
// 重置按钮点击 // 重置按钮点击
const handleReset = () => { const handleReset = () => {
timeGranularity.value = ''; timeGranularity.value = 'week';
startDate.value = ''; startDate.value = new Date().toISOString().split('T')[0] + ' 00:00:00';
endDate.value = ''; calculateEndDate();
emit('reset'); emit('reset');
}; };
@@ -219,7 +219,7 @@ watch(timeGranularity, (newVal) => {
// 初始时间设置为今天触发一次qeury // 初始时间设置为今天触发一次qeury
onMounted(() => { onMounted(() => {
startDate.value = new Date().toISOString().split('T')[0] + ' 00:00:00'; handleReset();
handleQuery(); handleQuery();
}); });
</script> </script>

View File

@@ -3,11 +3,7 @@
<el-row :gutter="20" class="content-container"> <el-row :gutter="20" class="content-container">
<!-- 1. 时间选择区 - 使用封装的组件 --> <!-- 1. 时间选择区 - 使用封装的组件 -->
<el-col :span="24" class="time-filter"> <el-col :span="24" class="time-filter">
<TimeFilter <TimeFilter @query="fetchData" @reset="handleFilterReset" @dateChange="handleDateChange" />
@query="fetchData"
@reset="handleFilterReset"
@dateChange="handleDateChange"
/>
</el-col> </el-col>
<!-- 2. 指标卡区域 --> <!-- 2. 指标卡区域 -->
@@ -36,7 +32,7 @@
<el-col :span="6"> <el-col :span="6">
<el-card class="indicator-card net-cashflow"> <el-card class="indicator-card net-cashflow">
<div class="card-header"> <div class="card-header">
<span>现金流</span> <span>收益</span>
<i class="el-icon-refresh"></i> <i class="el-icon-refresh"></i>
</div> </div>
<div class="card-value">{{ netCashflow | formatCurrency }}</div> <div class="card-value">{{ netCashflow | formatCurrency }}</div>
@@ -60,7 +56,7 @@
<el-col :span="24" class="charts-container"> <el-col :span="24" class="charts-container">
<!-- 收入支出趋势图 --> <!-- 收入支出趋势图 -->
<el-row :gutter="20" class="chart-row"> <el-row :gutter="20" class="chart-row">
<el-col :span="24"> <el-col :span="12">
<el-card class="chart-card"> <el-card class="chart-card">
<div slot="header" class="chart-header"> <div slot="header" class="chart-header">
<span>收入支出趋势</span> <span>收入支出趋势</span>
@@ -70,6 +66,16 @@
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="12">
<el-card class="chart-card">
<div slot="header" class="chart-header">
<span>按供应商区分的支出</span>
</div>
<div class="chart-content">
<div ref="supplierChart" class="chart-wrapper"></div>
</div>
</el-card>
</el-col>
</el-row> </el-row>
<!-- 按订单和客户/供应商分析 --> <!-- 按订单和客户/供应商分析 -->
@@ -95,19 +101,6 @@
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="20" class="chart-row">
<el-col :span="24">
<el-card class="chart-card">
<div slot="header" class="chart-header">
<span>按供应商区分的支出</span>
</div>
<div class="chart-content">
<div ref="supplierChart" class="chart-wrapper"></div>
</div>
</el-card>
</el-col>
</el-row>
</el-col> </el-col>
<!-- 4. 回款应收未收任务信息表格 --> <!-- 4. 回款应收未收任务信息表格 -->
@@ -116,80 +109,25 @@
<div slot="header" class="table-header"> <div slot="header" class="table-header">
<span>回款应收未收任务信息</span> <span>回款应收未收任务信息</span>
</div> </div>
<el-table <el-table :data="receivableTasks" border style="width: 100%" v-loading="tableLoading">
:data="receivableTasks" <el-table-column prop="receivableId" label="应收ID" width="180" align="center"></el-table-column>
border <el-table-column prop="customerName" label="客户名称" width="150" align="center"></el-table-column>
style="width: 100%" <el-table-column prop="orderId" label="订单ID" width="180" align="center"></el-table-column>
v-loading="tableLoading" <el-table-column prop="dueDate" label="到期日期" width="150" align="center"></el-table-column>
> <el-table-column prop="amount" label="总金额" width="120" align="center"
<el-table-column :formatter="formatCurrency"></el-table-column>
prop="receivableId" <el-table-column prop="paidAmount" label="已付金额" width="120" align="center"
label="应收ID" :formatter="formatCurrency"></el-table-column>
width="180" <el-table-column prop="balanceAmount" label="未付金额" width="120" align="center"
align="center" :formatter="formatCurrency"></el-table-column>
></el-table-column> <el-table-column prop="status" label="状态" width="100" align="center"></el-table-column>
<el-table-column <el-table-column prop="remark" label="备注" align="center"></el-table-column>
prop="customerName"
label="客户名称"
width="150"
align="center"
></el-table-column>
<el-table-column
prop="orderId"
label="订单ID"
width="180"
align="center"
></el-table-column>
<el-table-column
prop="dueDate"
label="到期日期"
width="150"
align="center"
></el-table-column>
<el-table-column
prop="amount"
label="总金额"
width="120"
align="center"
:formatter="formatCurrency"
></el-table-column>
<el-table-column
prop="paidAmount"
label="已付金额"
width="120"
align="center"
:formatter="formatCurrency"
></el-table-column>
<el-table-column
prop="balanceAmount"
label="未付金额"
width="120"
align="center"
:formatter="formatCurrency"
></el-table-column>
<el-table-column
prop="status"
label="状态"
width="100"
align="center"
></el-table-column>
<el-table-column
prop="remark"
label="备注"
align="center"
></el-table-column>
</el-table> </el-table>
<div class="pagination-container"> <div class="pagination-container">
<el-pagination <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
@size-change="handleSizeChange" :current-page="currentPage" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize"
@current-change="handleCurrentChange" layout="total, sizes, prev, pager, next, jumper" :total="receivableTotal"></el-pagination>
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="receivableTotal"
></el-pagination>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
@@ -252,7 +190,7 @@ const initCharts = () => {
if (supplierChart.value) { if (supplierChart.value) {
supplierChartInstance.value = echarts.init(supplierChart.value); supplierChartInstance.value = echarts.init(supplierChart.value);
} }
// 监听窗口大小变化,调整图表 // 监听窗口大小变化,调整图表
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
trendChartInstance.value?.resize(); trendChartInstance.value?.resize();
@@ -266,21 +204,21 @@ const initCharts = () => {
const fetchData = async (timeParams) => { const fetchData = async (timeParams) => {
tableLoading.value = true; tableLoading.value = true;
currentTimeParams.value = timeParams || currentTimeParams.value; currentTimeParams.value = timeParams || currentTimeParams.value;
try { try {
// 获取应收数据 // 获取应收数据
const receivableRes = await listReceivable({ pageSize: 9999, pageNum: 1 }); const receivableRes = await listReceivable({ pageSize: 9999, pageNum: 1 });
receivableData.value = receivableRes.rows || []; receivableData.value = receivableRes.rows || [];
receivableTasks.value = receivableData.value.filter(item => item.status === '未结清'); receivableTasks.value = receivableData.value.filter(item => item.status === '未结清');
receivableTotal.value = receivableRes.total || 0; receivableTotal.value = receivableRes.total || 0;
// 获取应付数据 // 获取应付数据
const payableRes = await listPayable({ pageSize: 9999, pageNum: 1 }); const payableRes = await listPayable({ pageSize: 9999, pageNum: 1 });
payableData.value = payableRes.rows || []; payableData.value = payableRes.rows || [];
// 处理数据并更新指标 // 处理数据并更新指标
processData(); processData();
// 更新图表 // 更新图表
updateCharts(); updateCharts();
} catch (error) { } catch (error) {
@@ -297,15 +235,15 @@ const processData = () => {
totalIncome.value = receivableData.value.reduce((sum, item) => { totalIncome.value = receivableData.value.reduce((sum, item) => {
return sum + parseFloat(item.amount || 0); return sum + parseFloat(item.amount || 0);
}, 0); }, 0);
// 计算总支出(应付金额总和) // 计算总支出(应付金额总和)
totalExpense.value = payableData.value.reduce((sum, item) => { totalExpense.value = payableData.value.reduce((sum, item) => {
return sum + parseFloat(item.amount || 0); return sum + parseFloat(item.amount || 0);
}, 0); }, 0);
// 计算净现金流 // 计算净现金流
netCashflow.value = totalIncome.value - totalExpense.value; netCashflow.value = totalIncome.value - totalExpense.value;
// 计算未结清应收总额和数量 // 计算未结清应收总额和数量
const outstanding = receivableData.value.filter(item => item.status === '未结清'); const outstanding = receivableData.value.filter(item => item.status === '未结清');
outstandingReceivable.value = outstanding.reduce((sum, item) => { outstandingReceivable.value = outstanding.reduce((sum, item) => {
@@ -326,12 +264,12 @@ const updateCharts = () => {
const updateTrendChart = () => { const updateTrendChart = () => {
// 按时间粒度处理数据 // 按时间粒度处理数据
const timeGroups = groupDataByTime(); const timeGroups = groupDataByTime();
// 准备图表数据 // 准备图表数据
const xAxisData = Object.keys(timeGroups); const xAxisData = Object.keys(timeGroups);
const incomeData = xAxisData.map(key => timeGroups[key].income); const incomeData = xAxisData.map(key => timeGroups[key].income);
const expenseData = xAxisData.map(key => timeGroups[key].expense); const expenseData = xAxisData.map(key => timeGroups[key].expense);
// 设置图表配置 // 设置图表配置
const option = { const option = {
tooltip: { tooltip: {
@@ -391,7 +329,7 @@ const updateTrendChart = () => {
} }
] ]
}; };
trendChartInstance.value.setOption(option); trendChartInstance.value.setOption(option);
}; };
@@ -399,7 +337,7 @@ const updateTrendChart = () => {
const updateOrderChart = () => { const updateOrderChart = () => {
// 按订单ID合并数据 // 按订单ID合并数据
const orderMap = {}; const orderMap = {};
// 处理应收数据 // 处理应收数据
receivableData.value.forEach(item => { receivableData.value.forEach(item => {
if (!orderMap[item.orderId]) { if (!orderMap[item.orderId]) {
@@ -410,7 +348,7 @@ const updateOrderChart = () => {
} }
orderMap[item.orderId].income += parseFloat(item.amount || 0); orderMap[item.orderId].income += parseFloat(item.amount || 0);
}); });
// 处理应付数据 // 处理应付数据
payableData.value.forEach(item => { payableData.value.forEach(item => {
if (!orderMap[item.orderId]) { if (!orderMap[item.orderId]) {
@@ -421,7 +359,7 @@ const updateOrderChart = () => {
} }
orderMap[item.orderId].expense += parseFloat(item.amount || 0); orderMap[item.orderId].expense += parseFloat(item.amount || 0);
}); });
// 转换为图表数据 // 转换为图表数据
const orderList = Object.entries(orderMap) const orderList = Object.entries(orderMap)
.map(([orderId, data]) => ({ .map(([orderId, data]) => ({
@@ -432,11 +370,11 @@ const updateOrderChart = () => {
})) }))
.sort((a, b) => b.net - a.net) .sort((a, b) => b.net - a.net)
.slice(0, 10); // 只展示前10个订单 .slice(0, 10); // 只展示前10个订单
const xAxisData = orderList.map(item => `订单 ${item.orderId.slice(-6)}`); const xAxisData = orderList.map(item => `订单 ${item.orderId.slice(-6)}`);
const incomeData = orderList.map(item => item.income); const incomeData = orderList.map(item => item.income);
const expenseData = orderList.map(item => item.expense); const expenseData = orderList.map(item => item.expense);
// 设置图表配置 // 设置图表配置
const option = { const option = {
tooltip: { tooltip: {
@@ -494,7 +432,7 @@ const updateOrderChart = () => {
} }
] ]
}; };
orderChartInstance.value.setOption(option); orderChartInstance.value.setOption(option);
}; };
@@ -502,7 +440,7 @@ const updateOrderChart = () => {
const updateCustomerChart = () => { const updateCustomerChart = () => {
// 按客户分组计算收入 // 按客户分组计算收入
const customerMap = {}; const customerMap = {};
receivableData.value.forEach(item => { receivableData.value.forEach(item => {
if (!customerMap[item.customerId]) { if (!customerMap[item.customerId]) {
customerMap[item.customerId] = { customerMap[item.customerId] = {
@@ -512,12 +450,12 @@ const updateCustomerChart = () => {
} }
customerMap[item.customerId].amount += parseFloat(item.amount || 0); customerMap[item.customerId].amount += parseFloat(item.amount || 0);
}); });
// 转换为图表数据并排序 // 转换为图表数据并排序
const customerList = Object.values(customerMap) const customerList = Object.values(customerMap)
.sort((a, b) => b.amount - a.amount) .sort((a, b) => b.amount - a.amount)
.slice(0, 10); // 只展示前10个客户 .slice(0, 10); // 只展示前10个客户
// 设置图表配置 // 设置图表配置
const option = { const option = {
tooltip: { tooltip: {
@@ -560,7 +498,7 @@ const updateCustomerChart = () => {
} }
] ]
}; };
customerChartInstance.value.setOption(option); customerChartInstance.value.setOption(option);
}; };
@@ -568,7 +506,7 @@ const updateCustomerChart = () => {
const updateSupplierChart = () => { const updateSupplierChart = () => {
// 按供应商分组计算支出 // 按供应商分组计算支出
const supplierMap = {}; const supplierMap = {};
payableData.value.forEach(item => { payableData.value.forEach(item => {
if (!supplierMap[item.supplierId]) { if (!supplierMap[item.supplierId]) {
supplierMap[item.supplierId] = { supplierMap[item.supplierId] = {
@@ -578,12 +516,12 @@ const updateSupplierChart = () => {
} }
supplierMap[item.supplierId].amount += parseFloat(item.amount || 0); supplierMap[item.supplierId].amount += parseFloat(item.amount || 0);
}); });
// 转换为图表数据并排序 // 转换为图表数据并排序
const supplierList = Object.values(supplierMap) const supplierList = Object.values(supplierMap)
.sort((a, b) => b.amount - a.amount) .sort((a, b) => b.amount - a.amount)
.slice(0, 10); // 只展示前10个供应商 .slice(0, 10); // 只展示前10个供应商
// 设置图表配置 // 设置图表配置
const option = { const option = {
tooltip: { tooltip: {
@@ -623,7 +561,7 @@ const updateSupplierChart = () => {
} }
] ]
}; };
supplierChartInstance.value.setOption(option); supplierChartInstance.value.setOption(option);
}; };
@@ -631,37 +569,37 @@ const updateSupplierChart = () => {
const groupDataByTime = () => { const groupDataByTime = () => {
const timeGroups = {}; const timeGroups = {};
const granularity = currentTimeParams.value.timeGranularity || 'month'; const granularity = currentTimeParams.value.timeGranularity || 'month';
// 处理应收数据 // 处理应收数据
receivableData.value.forEach(item => { receivableData.value.forEach(item => {
const date = new Date(item.dueDate); const date = new Date(item.dueDate);
const timeKey = getTimeKey(date, granularity); const timeKey = getTimeKey(date, granularity);
if (!timeGroups[timeKey]) { if (!timeGroups[timeKey]) {
timeGroups[timeKey] = { timeGroups[timeKey] = {
income: 0, income: 0,
expense: 0 expense: 0
}; };
} }
timeGroups[timeKey].income += parseFloat(item.amount || 0); timeGroups[timeKey].income += parseFloat(item.amount || 0);
}); });
// 处理应付数据 // 处理应付数据
payableData.value.forEach(item => { payableData.value.forEach(item => {
const date = new Date(item.dueDate); const date = new Date(item.dueDate);
const timeKey = getTimeKey(date, granularity); const timeKey = getTimeKey(date, granularity);
if (!timeGroups[timeKey]) { if (!timeGroups[timeKey]) {
timeGroups[timeKey] = { timeGroups[timeKey] = {
income: 0, income: 0,
expense: 0 expense: 0
}; };
} }
timeGroups[timeKey].expense += parseFloat(item.amount || 0); timeGroups[timeKey].expense += parseFloat(item.amount || 0);
}); });
// 排序时间分组 // 排序时间分组
return Object.keys(timeGroups).sort().reduce((obj, key) => { return Object.keys(timeGroups).sort().reduce((obj, key) => {
obj[key] = timeGroups[key]; obj[key] = timeGroups[key];
@@ -673,7 +611,7 @@ const groupDataByTime = () => {
const getTimeKey = (date, granularity) => { const getTimeKey = (date, granularity) => {
const year = date.getFullYear(); const year = date.getFullYear();
const month = date.getMonth() + 1; const month = date.getMonth() + 1;
if (granularity === 'year') { if (granularity === 'year') {
return `${year}`; return `${year}`;
} else if (granularity === 'month') { } else if (granularity === 'month') {
@@ -719,14 +657,14 @@ const handleFilterReset = () => {
} }
.page-header { .page-header {
margin-bottom: 20px; margin-bottom: 10px;
h1 { h1 {
font-size: 24px; font-size: 24px;
color: #1f2d3d; color: #1f2d3d;
margin-bottom: 5px; margin-bottom: 5px;
} }
p { p {
font-size: 14px; font-size: 14px;
color: #8392a5; color: #8392a5;
@@ -735,51 +673,51 @@ const handleFilterReset = () => {
.content-container { .content-container {
.time-filter { .time-filter {
margin-bottom: 20px; margin-bottom: 10px;
} }
.indicator-cards { .indicator-cards {
margin-bottom: 20px; margin-bottom: 10px;
.indicator-card { .indicator-card {
height: 100%; height: 100%;
padding: 15px; padding: 5px;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
.card-header { .card-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 10px; margin-bottom: 5px;
color: #8392a5; color: #8392a5;
font-size: 14px; font-size: 14px;
i { i {
font-size: 16px; font-size: 12px;
} }
} }
.card-value { .card-value {
font-size: 24px; font-size: 24px;
font-weight: bold; font-weight: bold;
margin-bottom: 5px; margin-bottom: 5px;
color: #1f2d3d; color: #1f2d3d;
} }
.card-desc { .card-desc {
font-size: 12px; font-size: 12px;
color: #8392a5; color: #8392a5;
.rise { .rise {
color: #f56c6c; color: #f56c6c;
} }
.drop { .drop {
color: #409eff; color: #409eff;
} }
} }
&::after { &::after {
content: ''; content: '';
position: absolute; position: absolute;
@@ -791,70 +729,70 @@ const handleFilterReset = () => {
opacity: 0.1; opacity: 0.1;
} }
} }
.total-income { .total-income {
&::after { &::after {
background-color: #4e79a7; background-color: #4e79a7;
} }
} }
.total-expense { .total-expense {
&::after { &::after {
background-color: #e15759; background-color: #e15759;
} }
} }
.net-cashflow { .net-cashflow {
&::after { &::after {
background-color: #59a14f; background-color: #59a14f;
} }
} }
.outstanding-receivable { .outstanding-receivable {
&::after { &::after {
background-color: #9c755f; background-color: #9c755f;
} }
} }
} }
.charts-container { .charts-container {
margin-bottom: 20px; margin-bottom: 10px;
.chart-row { .chart-row {
margin-bottom: 20px; margin-bottom: 10px;
} }
.chart-card { .chart-card {
height: 100%; height: 100%;
.chart-header { .chart-header {
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
color: #1f2d3d; color: #1f2d3d;
padding: 15px 20px; padding: 5px 10px;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
} }
.chart-content { .chart-content {
padding: 20px; padding: 10px;
} }
.chart-wrapper { .chart-wrapper {
width: 100%; width: 100%;
height: 400px; height: 400px;
} }
} }
} }
.table-container { .table-container {
.table-header { .table-header {
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
color: #1f2d3d; color: #1f2d3d;
padding: 15px 20px; padding: 5px 10px;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
} }
.pagination-container { .pagination-container {
margin-top: 15px; margin-top: 15px;
text-align: right; text-align: right;
@@ -874,12 +812,14 @@ const handleFilterReset = () => {
@media (max-width: 992px) { @media (max-width: 992px) {
.indicator-cards { .indicator-cards {
.el-col { .el-col {
&:nth-child(1), &:nth-child(2) {
&:nth-child(1),
&:nth-child(2) {
margin-bottom: 20px; margin-bottom: 20px;
} }
} }
} }
.charts-container { .charts-container {
.chart-row { .chart-row {
.el-col { .el-col {
@@ -888,7 +828,7 @@ const handleFilterReset = () => {
} }
} }
} }
.chart-wrapper { .chart-wrapper {
height: 300px; height: 300px;
} }
@@ -903,7 +843,7 @@ const handleFilterReset = () => {
} }
} }
} }
.charts-container { .charts-container {
.chart-wrapper { .chart-wrapper {
height: 250px; height: 250px;