feat: 财务报表初版

This commit is contained in:
砂糖
2025-09-22 14:13:48 +08:00
parent f51731113f
commit 75bd852c5b
16 changed files with 1769 additions and 12 deletions

View File

@@ -0,0 +1,248 @@
<template>
<div>
<el-row :gutter="15">
<el-col :span="6">
<el-form-item label="时间粒度">
<el-select
v-model="timeGranularity"
@change="handleGranularityChange"
clearable
>
<el-option label="按周" value="week"></el-option>
<el-option label="按月" value="month"></el-option>
<el-option label="按年" value="year"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="dateLabel">
<template v-if="timeGranularity === 'week'">
<el-date-picker
v-model="startDate"
type="date"
placeholder="选择起始日期"
value-format="YYYY-MM-DD 00:00:00"
@change="handleStartDateChange"
></el-date-picker>
</template>
<template v-if="timeGranularity === 'month'">
<el-date-picker
v-model="startDate"
type="month"
placeholder="选择起始月份"
value-format="YYYY-MM-01 00:00:00"
@change="handleStartDateChange"
></el-date-picker>
</template>
<template v-if="timeGranularity === 'year'">
<el-date-picker
v-model="startDate"
type="year"
placeholder="选择起始年份"
value-format="YYYY-01-01 00:00:00"
@change="handleStartDateChange"
></el-date-picker>
</template>
<template v-if="!timeGranularity">
<div class="placeholder-text">请先选择时间粒度</div>
</template>
</el-form-item>
</el-col>
<el-col :span="6" class="filter-button">
<el-button
type="primary"
@click="handleQuery"
:disabled="!timeGranularity || !startDate"
>
查询
</el-button>
<el-button @click="handleReset">重置</el-button>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue';
import { ElMessage } from 'element-plus';
// 时间粒度week, month, year
const timeGranularity = ref('week');
// 起始日期
const startDate = ref('');
// 结束日期(自动计算)
const endDate = ref('');
// 计算日期标签文本
const dateLabel = computed(() => {
if (timeGranularity.value === 'week') return '选择周';
if (timeGranularity.value === 'month') return '选择月';
if (timeGranularity.value === 'year') return '选择年';
return '时间选择';
});
// 时间粒度变化处理
const handleGranularityChange = (val) => {
// 重置日期选择
startDate.value = '';
endDate.value = '';
// 如果选择了粒度,设置默认起始时间
if (val) {
setDefaultStartDate(val);
}
};
// 设置默认起始时间
const setDefaultStartDate = (granularity) => {
const now = new Date();
// 统一格式化为YYYY-MM-DD 00:00:00
if (granularity === 'week') {
// 默认当前周的周一
const day = now.getDay() || 7; // 转换为周一为1周日为7
const monday = new Date(now);
monday.setDate(now.getDate() - (day - 1));
startDate.value = `${monday.getFullYear()}-${(monday.getMonth() + 1).toString().padStart(2, '0')}-${monday.getDate().toString().padStart(2, '0')} 00:00:00`;
calculateEndDate();
} else if (granularity === 'month') {
// 默认当前月
startDate.value = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}-01 00:00:00`;
calculateEndDate();
} else if (granularity === 'year') {
// 默认当前年
startDate.value = `${now.getFullYear()}-01-01 00:00:00`;
calculateEndDate();
}
};
// 起始日期变化处理
const handleStartDateChange = () => {
if (startDate.value) {
calculateEndDate();
} else {
endDate.value = '';
}
};
// 计算结束日期
const calculateEndDate = () => {
if (!timeGranularity.value || !startDate.value) return;
if (timeGranularity.value === 'week') {
// 周粒度:起始日期 + 6天周日
const start = new Date(startDate.value);
const end = new Date(start);
end.setDate(start.getDate() + 6);
endDate.value = formatDate(end, 'date');
} else if (timeGranularity.value === 'month') {
// 月粒度:下个月第一天
const [year, month] = startDate.value.split('-').map(Number);
const end = new Date(year, month + 1, 0);
endDate.value = formatDate(end, 'month');
} else if (timeGranularity.value === 'year') {
// 年粒度:下一年第一天
const year = parseInt(startDate.value);
const end = new Date(year + 1, 0, 1);
endDate.value = formatDate(end, 'year');
}
// 通知父组件日期已变更
emit('dateChange', getTimeParams());
};
// 格式化日期
const formatDate = (date, type) => {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
if (type === 'date') return `${year}-${month}-${day}`;
if (type === 'month') return `${year}-${month}`;
if (type === 'year') return `${year}`;
return `${year}-${month}-${day}`;
};
// 获取时间参数
const getTimeParams = () => {
let endTime = '';
if (timeGranularity.value === 'week') {
endTime = `${endDate.value} 23:59:59`;
} else if (timeGranularity.value === 'month') {
endTime = `${endDate.value}-01 00:00:00`;
} else if (timeGranularity.value === 'year') {
endTime = `${endDate.value}-01-01 00:00:00`;
}
return {
startTime: startDate.value,
endTime: endTime,
}
};
// 查询按钮点击
const handleQuery = () => {
if (!timeGranularity.value) {
ElMessage.warning('请选择时间粒度');
return;
}
if (!startDate.value) {
ElMessage.warning('请选择起始时间');
return;
}
emit('query', getTimeParams());
};
// 重置按钮点击
const handleReset = () => {
timeGranularity.value = '';
startDate.value = '';
endDate.value = '';
emit('reset');
};
// 定义组件事件
const emit = defineEmits(['query', 'reset', 'dateChange']);
// 初始化默认值
watch(timeGranularity, (newVal) => {
if (newVal && !startDate.value) {
setDefaultStartDate(newVal);
}
}, { immediate: true });
// 初始时间设置为今天触发一次qeury
onMounted(() => {
startDate.value = new Date().toISOString().split('T')[0] + ' 00:00:00';
handleQuery();
});
</script>
<style lang="scss" scoped>
.time-filter-card {
padding: 15px 0;
.date-separator {
margin: 0 10px;
color: #606266;
}
.filter-button {
display: flex;
align-items: flex-end;
gap: 10px;
padding-bottom: 22px;
}
.placeholder-text {
line-height: 40px;
color: #909399;
}
}
</style>

View File

@@ -0,0 +1,913 @@
<template>
<div class="finance-analysis-page">
<el-row :gutter="20" class="content-container">
<!-- 1. 时间选择区 - 使用封装的组件 -->
<el-col :span="24" class="time-filter">
<TimeFilter
@query="fetchData"
@reset="handleFilterReset"
@dateChange="handleDateChange"
/>
</el-col>
<!-- 2. 指标卡区域 -->
<el-col :span="24" class="indicator-cards">
<el-row :gutter="20">
<el-col :span="6">
<el-card class="indicator-card total-income">
<div class="card-header">
<span>总收入</span>
<i class="el-icon-arrow-up"></i>
</div>
<div class="card-value">{{ totalIncome | formatCurrency }}</div>
<!-- <div class="card-desc">较上期 <span class="rise">+5.2%</span></div> -->
</el-card>
</el-col>
<el-col :span="6">
<el-card class="indicator-card total-expense">
<div class="card-header">
<span>总支出</span>
<i class="el-icon-arrow-down"></i>
</div>
<div class="card-value">{{ totalExpense | formatCurrency }}</div>
<!-- <div class="card-desc">较上期 <span class="drop">-2.8%</span></div> -->
</el-card>
</el-col>
<el-col :span="6">
<el-card class="indicator-card net-cashflow">
<div class="card-header">
<span>净现金流</span>
<i class="el-icon-refresh"></i>
</div>
<div class="card-value">{{ netCashflow | formatCurrency }}</div>
<!-- <div class="card-desc">较上期 <span class="rise">+8.0%</span></div> -->
</el-card>
</el-col>
<el-col :span="6">
<el-card class="indicator-card outstanding-receivable">
<div class="card-header">
<span>未结清应收</span>
<i class="el-icon-info"></i>
</div>
<div class="card-value">{{ outstandingReceivable | formatCurrency }}</div>
<div class="card-desc"> {{ receivableCount }} 笔未结清</div>
</el-card>
</el-col>
</el-row>
</el-col>
<!-- 3. echarts图表区域 -->
<el-col :span="24" class="charts-container">
<!-- 收入支出趋势图 -->
<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="trendChart" class="chart-wrapper"></div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 按订单和客户/供应商分析 -->
<el-row :gutter="20" class="chart-row">
<el-col :span="12">
<el-card class="chart-card">
<div slot="header" class="chart-header">
<span>按订单区分的收支</span>
</div>
<div class="chart-content">
<div ref="orderChart" class="chart-wrapper"></div>
</div>
</el-card>
</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="customerChart" class="chart-wrapper"></div>
</div>
</el-card>
</el-col>
</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>
<!-- 4. 回款应收未收任务信息表格 -->
<el-col :span="24" class="table-container">
<el-card>
<div slot="header" class="table-header">
<span>回款应收未收任务信息</span>
</div>
<el-table
:data="receivableTasks"
border
style="width: 100%"
v-loading="tableLoading"
>
<el-table-column
prop="receivableId"
label="应收ID"
width="180"
align="center"
></el-table-column>
<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>
<div class="pagination-container">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="receivableTotal"
></el-pagination>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup>
import * as echarts from 'echarts';
import { listReceivable } from '@/api/finance/receivable';
import { listPayable } from '@/api/finance/payable';
import TimeFilter from './components/TimeFilter.vue'; // 引入封装的时间筛选组件
// 分页相关
const currentPage = ref(1);
const pageSize = ref(10);
// 数据相关
const receivableData = ref([]); // 应收数据
const payableData = ref([]); // 应付数据
const receivableTasks = ref([]); // 回款任务数据
const receivableTotal = ref(0); // 回款任务总数
const tableLoading = ref(false);
const currentTimeParams = ref({}); // 当前时间参数
// 指标数据
const totalIncome = ref(0);
const totalExpense = ref(0);
const netCashflow = ref(0);
const outstandingReceivable = ref(0);
const receivableCount = ref(0);
// 图表实例
const trendChart = ref(null);
const orderChart = ref(null);
const customerChart = ref(null);
const supplierChart = ref(null);
const trendChartInstance = ref(null);
const orderChartInstance = ref(null);
const customerChartInstance = ref(null);
const supplierChartInstance = ref(null);
// 初始化
onMounted(() => {
// 初始化图表
initCharts();
});
// 初始化图表
const initCharts = () => {
if (trendChart.value) {
trendChartInstance.value = echarts.init(trendChart.value);
}
if (orderChart.value) {
orderChartInstance.value = echarts.init(orderChart.value);
}
if (customerChart.value) {
customerChartInstance.value = echarts.init(customerChart.value);
}
if (supplierChart.value) {
supplierChartInstance.value = echarts.init(supplierChart.value);
}
// 监听窗口大小变化,调整图表
window.addEventListener('resize', () => {
trendChartInstance.value?.resize();
orderChartInstance.value?.resize();
customerChartInstance.value?.resize();
supplierChartInstance.value?.resize();
});
};
// 获取数据
const fetchData = async (timeParams) => {
tableLoading.value = true;
currentTimeParams.value = timeParams || currentTimeParams.value;
try {
// 获取应收数据
const receivableRes = await listReceivable({ pageSize: 9999, pageNum: 1 });
receivableData.value = receivableRes.rows || [];
receivableTasks.value = receivableData.value.filter(item => item.status === '未结清');
receivableTotal.value = receivableRes.total || 0;
// 获取应付数据
const payableRes = await listPayable({ pageSize: 9999, pageNum: 1 });
payableData.value = payableRes.rows || [];
// 处理数据并更新指标
processData();
// 更新图表
updateCharts();
} catch (error) {
console.error('获取数据失败:', error);
ElMessage.error('获取数据失败,请重试');
} finally {
tableLoading.value = false;
}
};
// 处理数据计算指标
const processData = () => {
// 计算总收入(应收金额总和)
totalIncome.value = receivableData.value.reduce((sum, item) => {
return sum + parseFloat(item.amount || 0);
}, 0);
// 计算总支出(应付金额总和)
totalExpense.value = payableData.value.reduce((sum, item) => {
return sum + parseFloat(item.amount || 0);
}, 0);
// 计算净现金流
netCashflow.value = totalIncome.value - totalExpense.value;
// 计算未结清应收总额和数量
const outstanding = receivableData.value.filter(item => item.status === '未结清');
outstandingReceivable.value = outstanding.reduce((sum, item) => {
return sum + parseFloat(item.balanceAmount || 0);
}, 0);
receivableCount.value = outstanding.length;
};
// 更新图表
const updateCharts = () => {
updateTrendChart();
updateOrderChart();
updateCustomerChart();
updateSupplierChart();
};
// 更新收入支出趋势图
const updateTrendChart = () => {
// 按时间粒度处理数据
const timeGroups = groupDataByTime();
// 准备图表数据
const xAxisData = Object.keys(timeGroups);
const incomeData = xAxisData.map(key => timeGroups[key].income);
const expenseData = xAxisData.map(key => timeGroups[key].expense);
// 设置图表配置
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: '{b}<br/>收入: {c0} 元<br/>支出: {c1} 元'
},
legend: {
data: ['收入', '支出'],
top: 0
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: xAxisData,
axisLabel: {
rotate: 45,
interval: 0
}
},
yAxis: {
type: 'value',
name: '金额 (元)',
axisLabel: {
formatter: '{value}'
}
},
series: [
{
name: '收入',
type: 'bar',
data: incomeData,
itemStyle: {
color: '#4e79a7'
},
emphasis: {
focus: 'series'
}
},
{
name: '支出',
type: 'bar',
data: expenseData,
itemStyle: {
color: '#e15759'
},
emphasis: {
focus: 'series'
}
}
]
};
trendChartInstance.value.setOption(option);
};
// 更新按订单区分的收支图表
const updateOrderChart = () => {
// 按订单ID合并数据
const orderMap = {};
// 处理应收数据
receivableData.value.forEach(item => {
if (!orderMap[item.orderId]) {
orderMap[item.orderId] = {
income: 0,
expense: 0
};
}
orderMap[item.orderId].income += parseFloat(item.amount || 0);
});
// 处理应付数据
payableData.value.forEach(item => {
if (!orderMap[item.orderId]) {
orderMap[item.orderId] = {
income: 0,
expense: 0
};
}
orderMap[item.orderId].expense += parseFloat(item.amount || 0);
});
// 转换为图表数据
const orderList = Object.entries(orderMap)
.map(([orderId, data]) => ({
orderId,
income: data.income,
expense: data.expense,
net: data.income - data.expense
}))
.sort((a, b) => b.net - a.net)
.slice(0, 10); // 只展示前10个订单
const xAxisData = orderList.map(item => `订单 ${item.orderId.slice(-6)}`);
const incomeData = orderList.map(item => item.income);
const expenseData = orderList.map(item => item.expense);
// 设置图表配置
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
data: ['收入', '支出'],
top: 0
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: xAxisData,
axisLabel: {
rotate: 45,
interval: 0
}
},
yAxis: {
type: 'value',
name: '金额 (元)'
},
series: [
{
name: '收入',
type: 'bar',
stack: 'total',
emphasis: {
focus: 'series'
},
data: incomeData,
itemStyle: {
color: '#4e79a7'
}
},
{
name: '支出',
type: 'bar',
stack: 'total',
emphasis: {
focus: 'series'
},
data: expenseData.map(v => -v), // 支出用负值表示
itemStyle: {
color: '#e15759'
}
}
]
};
orderChartInstance.value.setOption(option);
};
// 更新按客户区分的收入图表
const updateCustomerChart = () => {
// 按客户分组计算收入
const customerMap = {};
receivableData.value.forEach(item => {
if (!customerMap[item.customerId]) {
customerMap[item.customerId] = {
name: item.customerName,
amount: 0
};
}
customerMap[item.customerId].amount += parseFloat(item.amount || 0);
});
// 转换为图表数据并排序
const customerList = Object.values(customerMap)
.sort((a, b) => b.amount - a.amount)
.slice(0, 10); // 只展示前10个客户
// 设置图表配置
const option = {
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 10,
top: 'center'
},
series: [
{
name: '客户收入',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 16,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: customerList.map(item => ({
name: item.name,
value: item.amount
}))
}
]
};
customerChartInstance.value.setOption(option);
};
// 更新按供应商区分的支出图表
const updateSupplierChart = () => {
// 按供应商分组计算支出
const supplierMap = {};
payableData.value.forEach(item => {
if (!supplierMap[item.supplierId]) {
supplierMap[item.supplierId] = {
name: item.supplierName,
amount: 0
};
}
supplierMap[item.supplierId].amount += parseFloat(item.amount || 0);
});
// 转换为图表数据并排序
const supplierList = Object.values(supplierMap)
.sort((a, b) => b.amount - a.amount)
.slice(0, 10); // 只展示前10个供应商
// 设置图表配置
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value',
name: '金额 (元)'
},
yAxis: {
type: 'category',
data: supplierList.map(item => item.name),
axisLabel: {
interval: 0
}
},
series: [
{
name: '支出金额',
type: 'bar',
data: supplierList.map(item => item.amount),
itemStyle: {
color: '#e15759'
},
emphasis: {
focus: 'series'
}
}
]
};
supplierChartInstance.value.setOption(option);
};
// 按时间粒度分组数据
const groupDataByTime = () => {
const timeGroups = {};
const granularity = currentTimeParams.value.timeGranularity || 'month';
// 处理应收数据
receivableData.value.forEach(item => {
const date = new Date(item.dueDate);
const timeKey = getTimeKey(date, granularity);
if (!timeGroups[timeKey]) {
timeGroups[timeKey] = {
income: 0,
expense: 0
};
}
timeGroups[timeKey].income += parseFloat(item.amount || 0);
});
// 处理应付数据
payableData.value.forEach(item => {
const date = new Date(item.dueDate);
const timeKey = getTimeKey(date, granularity);
if (!timeGroups[timeKey]) {
timeGroups[timeKey] = {
income: 0,
expense: 0
};
}
timeGroups[timeKey].expense += parseFloat(item.amount || 0);
});
// 排序时间分组
return Object.keys(timeGroups).sort().reduce((obj, key) => {
obj[key] = timeGroups[key];
return obj;
}, {});
};
// 根据时间粒度生成时间键
const getTimeKey = (date, granularity) => {
const year = date.getFullYear();
const month = date.getMonth() + 1;
if (granularity === 'year') {
return `${year}`;
} else if (granularity === 'month') {
return `${year}-${month.toString().padStart(2, '0')}`;
} else {
// 按周分组 (ISO周数)
const week = getWeekNumber(date);
return `${year}-W${week.toString().padStart(2, '0')}`;
}
};
// 获取ISO周数
const getWeekNumber = (date) => {
const temp = new Date(date.getTime());
temp.setHours(0, 0, 0, 0);
// 把周一作为一周的第一天
temp.setDate(temp.getDate() + 3 - (temp.getDay() + 6) % 7);
const firstDay = new Date(temp.getFullYear(), 0, 4);
return Math.round((temp - firstDay) / (7 * 24 * 60 * 60 * 1000)) + 1;
};
// 格式化金额
const formatCurrency = (row, column, value) => {
if (!value) return '0.00';
return parseFloat(value).toFixed(2);
};
// 事件处理函数
const handleDateChange = (timeParams) => {
currentTimeParams.value = timeParams;
};
const handleFilterReset = () => {
currentPage.value = 1;
};
</script>
<style lang="scss" scoped>
.finance-analysis-page {
padding: 20px;
background-color: #f5f7fa;
min-height: 100vh;
}
.page-header {
margin-bottom: 20px;
h1 {
font-size: 24px;
color: #1f2d3d;
margin-bottom: 5px;
}
p {
font-size: 14px;
color: #8392a5;
}
}
.content-container {
.time-filter {
margin-bottom: 20px;
}
.indicator-cards {
margin-bottom: 20px;
.indicator-card {
height: 100%;
padding: 15px;
position: relative;
overflow: hidden;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
color: #8392a5;
font-size: 14px;
i {
font-size: 16px;
}
}
.card-value {
font-size: 24px;
font-weight: bold;
margin-bottom: 5px;
color: #1f2d3d;
}
.card-desc {
font-size: 12px;
color: #8392a5;
.rise {
color: #f56c6c;
}
.drop {
color: #409eff;
}
}
&::after {
content: '';
position: absolute;
right: -30px;
top: -30px;
width: 80px;
height: 80px;
border-radius: 50%;
opacity: 0.1;
}
}
.total-income {
&::after {
background-color: #4e79a7;
}
}
.total-expense {
&::after {
background-color: #e15759;
}
}
.net-cashflow {
&::after {
background-color: #59a14f;
}
}
.outstanding-receivable {
&::after {
background-color: #9c755f;
}
}
}
.charts-container {
margin-bottom: 20px;
.chart-row {
margin-bottom: 20px;
}
.chart-card {
height: 100%;
.chart-header {
font-size: 16px;
font-weight: 500;
color: #1f2d3d;
padding: 15px 20px;
border-bottom: 1px solid #eee;
}
.chart-content {
padding: 20px;
}
.chart-wrapper {
width: 100%;
height: 400px;
}
}
}
.table-container {
.table-header {
font-size: 16px;
font-weight: 500;
color: #1f2d3d;
padding: 15px 20px;
border-bottom: 1px solid #eee;
}
.pagination-container {
margin-top: 15px;
text-align: right;
}
}
}
// 响应式调整
@media (max-width: 1200px) {
.charts-container {
.chart-wrapper {
height: 350px;
}
}
}
@media (max-width: 992px) {
.indicator-cards {
.el-col {
&:nth-child(1), &:nth-child(2) {
margin-bottom: 20px;
}
}
}
.charts-container {
.chart-row {
.el-col {
&:nth-child(1) {
margin-bottom: 20px;
}
}
}
.chart-wrapper {
height: 300px;
}
}
}
@media (max-width: 768px) {
.indicator-cards {
.el-col {
&:nth-child(3) {
margin-bottom: 20px;
}
}
}
.charts-container {
.chart-wrapper {
height: 250px;
}
}
}
</style>