feat: 数据统计分析模块前端实现
前端实现:
- dashboard.js: API接口封装(13行)
- dashboard/index.vue: 仪表盘页面(576行)
页面布局:
1. 概览卡片(4个)
- 项目总数(含本月新增)
- 客户总数(含本月新增)
- 合同总数
- 需求工单数
2. 收支概览(3个)
- 总收入(含本月收入)
- 总支出(含本月支出)
- 净利润(含利润率)
3. 应收款概览(3个)
- 应收款总额
- 待收款金额(橙色警示)
- 逾期金额(红色警示)
4. 图表区域(4个)
- 收支趋势折线图(最近12个月)
- 项目状态分布饼图
- 支出类型分布饼图
- 应收款状态分布饼图
技术特点:
- ECharts图表库:折线图、环形饼图
- 响应式设计:窗口大小变化自动重绘
- 渐变色卡片:现代化UI设计
- 金额格式化:千分位分隔、保留2位小数
- 图表tooltip:金额/百分比格式化显示
- 组件销毁时清理资源
模块状态:✅ 完整(前端+后端)
This commit is contained in:
parent
39577a9b11
commit
81e919ad3c
12
fund-admin/src/api/dashboard.js
Normal file
12
fund-admin/src/api/dashboard.js
Normal file
@ -0,0 +1,12 @@
|
||||
import request from '../utils/request'
|
||||
|
||||
/**
|
||||
* 获取仪表盘统计数据
|
||||
*/
|
||||
export const getDashboardData = (params) => {
|
||||
return request({
|
||||
url: '/proj/api/v1/dashboard',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
@ -1,77 +1,139 @@
|
||||
<template>
|
||||
<div class="dashboard">
|
||||
<el-row :gutter="20">
|
||||
<div class="dashboard-container">
|
||||
<!-- 概览卡片 -->
|
||||
<el-row :gutter="20" class="overview-cards">
|
||||
<el-col :span="6">
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-icon" style="background: #409EFF;">
|
||||
<el-icon><User /></el-icon>
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-icon" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
|
||||
<el-icon size="28"><Folder /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">1,234</div>
|
||||
<div class="stat-label">客户总数</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-icon" style="background: #67C23A;">
|
||||
<el-icon><FolderOpened /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">56</div>
|
||||
<div class="stat-value">{{ data.projectCount || 0 }}</div>
|
||||
<div class="stat-label">项目总数</div>
|
||||
</div>
|
||||
<div class="stat-extra">本月新增 +{{ data.monthNewProjects || 0 }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-icon" style="background: #E6A23C;">
|
||||
<el-icon><Document /></el-icon>
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-icon" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">
|
||||
<el-icon size="28"><User /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">89</div>
|
||||
<div class="stat-value">{{ data.customerCount || 0 }}</div>
|
||||
<div class="stat-label">客户总数</div>
|
||||
</div>
|
||||
<div class="stat-extra">本月新增 +{{ data.monthNewCustomers || 0 }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-icon" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);">
|
||||
<el-icon size="28"><Document /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ data.contractCount || 0 }}</div>
|
||||
<div class="stat-label">合同总数</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-icon" style="background: #F56C6C;">
|
||||
<el-icon><Money /></el-icon>
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-icon" style="background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);">
|
||||
<el-icon size="28"><Tickets /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">¥2.5M</div>
|
||||
<div class="stat-label">合同总金额</div>
|
||||
<div class="stat-value">{{ data.requirementCount || 0 }}</div>
|
||||
<div class="stat-label">需求工单</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="mt-20">
|
||||
<el-col :span="12">
|
||||
<el-card>
|
||||
<!-- 收支概览 -->
|
||||
<el-row :gutter="20" class="finance-cards">
|
||||
<el-col :span="8">
|
||||
<el-card shadow="hover" class="finance-card income">
|
||||
<div class="finance-title">总收入</div>
|
||||
<div class="finance-value">¥ {{ formatMoney(data.totalIncome) }}</div>
|
||||
<div class="finance-sub">本月收入:¥ {{ formatMoney(data.monthIncome) }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card shadow="hover" class="finance-card expense">
|
||||
<div class="finance-title">总支出</div>
|
||||
<div class="finance-value">¥ {{ formatMoney(data.totalExpense) }}</div>
|
||||
<div class="finance-sub">本月支出:¥ {{ formatMoney(data.monthExpense) }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card shadow="hover" class="finance-card profit">
|
||||
<div class="finance-title">净利润</div>
|
||||
<div class="finance-value" :class="{ negative: (data.netProfit || 0) < 0 }">
|
||||
¥ {{ formatMoney(data.netProfit) }}
|
||||
</div>
|
||||
<div class="finance-sub">利润率:{{ getProfitRate() }}%</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 应收款概览 -->
|
||||
<el-row :gutter="20" class="receivable-cards">
|
||||
<el-col :span="8">
|
||||
<el-card shadow="hover" class="receivable-card">
|
||||
<div class="receivable-title">应收款总额</div>
|
||||
<div class="receivable-value">¥ {{ formatMoney(data.totalReceivable) }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card shadow="hover" class="receivable-card warning">
|
||||
<div class="receivable-title">待收款金额</div>
|
||||
<div class="receivable-value">¥ {{ formatMoney(data.totalUnpaid) }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card shadow="hover" class="receivable-card danger">
|
||||
<div class="receivable-title">逾期金额</div>
|
||||
<div class="receivable-value">¥ {{ formatMoney(data.overdueAmount) }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 图表区域 -->
|
||||
<el-row :gutter="20" class="chart-row">
|
||||
<el-col :span="16">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<span>收支趋势(最近12个月)</span>
|
||||
</template>
|
||||
<div ref="trendChartRef" class="chart-container"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<span>项目状态分布</span>
|
||||
</template>
|
||||
<div class="chart-placeholder">
|
||||
[图表区域]
|
||||
</div>
|
||||
<div ref="projectPieRef" class="chart-container"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="chart-row">
|
||||
<el-col :span="12">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<span>支出类型分布</span>
|
||||
</template>
|
||||
<div ref="expensePieRef" class="chart-container"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card>
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<span>最近活动</span>
|
||||
<span>应收款状态分布</span>
|
||||
</template>
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="(activity, index) in activities"
|
||||
:key="index"
|
||||
:timestamp="activity.time"
|
||||
>
|
||||
{{ activity.content }}
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div ref="receivablePieRef" class="chart-container"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@ -79,65 +141,498 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { User, FolderOpened, Document, Money } from '@element-plus/icons-vue'
|
||||
import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import { Folder, User, Document, Tickets } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import * as echarts from 'echarts'
|
||||
import { getDashboardData } from '../../api/dashboard'
|
||||
|
||||
const activities = [
|
||||
{ content: '创建了新项目 "XX企业资金管理系统"', time: '2026-02-15 10:30' },
|
||||
{ content: '客户 "ABC科技" 信息更新', time: '2026-02-15 09:15' },
|
||||
{ content: '合同 "HT2024001" 签署完成', time: '2026-02-14 16:45' },
|
||||
{ content: '新增用户 "张三"', time: '2026-02-14 14:20' }
|
||||
]
|
||||
// 统计数据
|
||||
const data = reactive({
|
||||
projectCount: 0,
|
||||
customerCount: 0,
|
||||
contractCount: 0,
|
||||
requirementCount: 0,
|
||||
totalIncome: 0,
|
||||
totalExpense: 0,
|
||||
netProfit: 0,
|
||||
totalReceivable: 0,
|
||||
totalUnpaid: 0,
|
||||
overdueAmount: 0,
|
||||
monthIncome: 0,
|
||||
monthExpense: 0,
|
||||
monthNewProjects: 0,
|
||||
monthNewCustomers: 0,
|
||||
incomeTrend: [],
|
||||
expenseTrend: [],
|
||||
projectStatusDistribution: [],
|
||||
expenseTypeDistribution: [],
|
||||
receivableStatusDistribution: []
|
||||
})
|
||||
|
||||
// 图表引用
|
||||
const trendChartRef = ref(null)
|
||||
const projectPieRef = ref(null)
|
||||
const expensePieRef = ref(null)
|
||||
const receivablePieRef = ref(null)
|
||||
|
||||
// 图表实例
|
||||
let trendChart = null
|
||||
let projectPieChart = null
|
||||
let expensePieChart = null
|
||||
let receivablePieChart = null
|
||||
|
||||
// 格式化金额
|
||||
const formatMoney = (value) => {
|
||||
if (value === null || value === undefined) return '0.00'
|
||||
return Number(value).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
||||
}
|
||||
|
||||
// 计算利润率
|
||||
const getProfitRate = () => {
|
||||
const income = data.totalIncome || 0
|
||||
if (income === 0) return '0.00'
|
||||
return ((data.netProfit / income) * 100).toFixed(2)
|
||||
}
|
||||
|
||||
// 加载数据
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await getDashboardData()
|
||||
Object.assign(data, res)
|
||||
|
||||
// 渲染图表
|
||||
await nextTick()
|
||||
renderTrendChart()
|
||||
renderProjectPieChart()
|
||||
renderExpensePieChart()
|
||||
renderReceivablePieChart()
|
||||
} catch (error) {
|
||||
console.error('加载仪表盘数据失败:', error)
|
||||
ElMessage.error(error.message || '加载数据失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染收支趋势图
|
||||
const renderTrendChart = () => {
|
||||
if (!trendChartRef.value) return
|
||||
|
||||
if (trendChart) {
|
||||
trendChart.dispose()
|
||||
}
|
||||
|
||||
trendChart = echarts.init(trendChartRef.value)
|
||||
|
||||
const months = data.incomeTrend?.map(item => item.month) || []
|
||||
const incomeData = data.incomeTrend?.map(item => item.amount || 0) || []
|
||||
const expenseData = data.expenseTrend?.map(item => item.amount || 0) || []
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: (params) => {
|
||||
let result = params[0].axisValue + '<br/>'
|
||||
params.forEach(param => {
|
||||
result += `${param.marker} ${param.seriesName}: ¥${formatMoney(param.value)}<br/>`
|
||||
})
|
||||
return result
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['收入', '支出'],
|
||||
bottom: 0
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '15%',
|
||||
top: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: months,
|
||||
axisLabel: {
|
||||
rotate: 45
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
formatter: (value) => {
|
||||
if (value >= 10000) return (value / 10000) + 'w'
|
||||
return value
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '收入',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: incomeData,
|
||||
itemStyle: { color: '#67c23a' },
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(103, 194, 58, 0.3)' },
|
||||
{ offset: 1, color: 'rgba(103, 194, 58, 0.05)' }
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '支出',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: expenseData,
|
||||
itemStyle: { color: '#f56c6c' },
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(245, 108, 108, 0.3)' },
|
||||
{ offset: 1, color: 'rgba(245, 108, 108, 0.05)' }
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
trendChart.setOption(option)
|
||||
}
|
||||
|
||||
// 渲染项目状态分布图
|
||||
const renderProjectPieChart = () => {
|
||||
if (!projectPieRef.value) return
|
||||
|
||||
if (projectPieChart) {
|
||||
projectPieChart.dispose()
|
||||
}
|
||||
|
||||
projectPieChart = echarts.init(projectPieRef.value)
|
||||
|
||||
const pieData = data.projectStatusDistribution?.map(item => ({
|
||||
name: item.name,
|
||||
value: item.count || 0
|
||||
})) || []
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
right: 10,
|
||||
top: 'center'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
center: ['40%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
data: pieData
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
projectPieChart.setOption(option)
|
||||
}
|
||||
|
||||
// 渲染支出类型分布图
|
||||
const renderExpensePieChart = () => {
|
||||
if (!expensePieRef.value) return
|
||||
|
||||
if (expensePieChart) {
|
||||
expensePieChart.dispose()
|
||||
}
|
||||
|
||||
expensePieChart = echarts.init(expensePieRef.value)
|
||||
|
||||
const pieData = data.expenseTypeDistribution?.map(item => ({
|
||||
name: item.name,
|
||||
value: item.amount || 0
|
||||
})) || []
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: (params) => {
|
||||
return `${params.name}: ¥${formatMoney(params.value)} (${params.percent}%)`
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
right: 10,
|
||||
top: 'center'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
center: ['40%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
data: pieData
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
expensePieChart.setOption(option)
|
||||
}
|
||||
|
||||
// 渲染应收款状态分布图
|
||||
const renderReceivablePieChart = () => {
|
||||
if (!receivablePieRef.value) return
|
||||
|
||||
if (receivablePieChart) {
|
||||
receivablePieChart.dispose()
|
||||
}
|
||||
|
||||
receivablePieChart = echarts.init(receivablePieRef.value)
|
||||
|
||||
const pieData = data.receivableStatusDistribution?.map(item => ({
|
||||
name: item.name,
|
||||
value: item.amount || 0
|
||||
})) || []
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: (params) => {
|
||||
return `${params.name}: ¥${formatMoney(params.value)} (${params.percent}%)`
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
right: 10,
|
||||
top: 'center'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
center: ['40%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
data: pieData
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
receivablePieChart.setOption(option)
|
||||
}
|
||||
|
||||
// 窗口大小变化时重绘图表
|
||||
const handleResize = () => {
|
||||
trendChart?.resize()
|
||||
projectPieChart?.resize()
|
||||
expensePieChart?.resize()
|
||||
receivablePieChart?.resize()
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
window.addEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
// 清理
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize)
|
||||
trendChart?.dispose()
|
||||
projectPieChart?.dispose()
|
||||
expensePieChart?.dispose()
|
||||
receivablePieChart?.dispose()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dashboard {
|
||||
.dashboard-container {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
/* 概览卡片 */
|
||||
.overview-cards {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.stat-card :deep(.el-card__body) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 8px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-size: 28px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.stat-info {
|
||||
margin-left: 15px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
margin-top: 5px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.mt-20 {
|
||||
margin-top: 20px;
|
||||
.stat-extra {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 20px;
|
||||
font-size: 12px;
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.chart-placeholder {
|
||||
height: 300px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f5f7fa;
|
||||
/* 收支卡片 */
|
||||
.finance-cards {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.finance-card {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.finance-card :deep(.el-card__body) {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.finance-title {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.finance-value {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.finance-value.negative {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.finance-sub {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.finance-card.income .finance-value {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.finance-card.expense .finance-value {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.finance-card.profit .finance-value {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
/* 应收款卡片 */
|
||||
.receivable-cards {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.receivable-card {
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.receivable-card :deep(.el-card__body) {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.receivable-title {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.receivable-value {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.receivable-card.warning .receivable-value {
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.receivable-card.danger .receivable-value {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
/* 图表区域 */
|
||||
.chart-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
height: 350px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user