| | |
| | | |
| | | <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> |
| | |
| | | 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'); |
| | |
| | | :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> |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |