feat(crm): 在订单异议页面添加异议内容和处理结果的HTML展示 feat(erp): 新增ERP仪表盘页面并优化采购订单表单 refactor(wms): 移除钢卷列表中的冗余代码并添加改判原因列
804 lines
21 KiB
Vue
804 lines
21 KiB
Vue
<template>
|
||
<div class="erp-dashboard-page">
|
||
<section class="stats-section">
|
||
<div class="stat-card">
|
||
<div class="stat-icon supplier-icon">
|
||
<i class="el-icon-office-building"></i>
|
||
</div>
|
||
<div class="stat-content">
|
||
<div class="stat-value">{{ supplierCount }}</div>
|
||
<div class="stat-label">供应商总数</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-icon order-icon">
|
||
<i class="el-icon-document"></i>
|
||
</div>
|
||
<div class="stat-content">
|
||
<div class="stat-value">{{ orderCount }}</div>
|
||
<div class="stat-label">采购订单总数</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-icon doing-icon">
|
||
<i class="el-icon-loading"></i>
|
||
</div>
|
||
<div class="stat-content">
|
||
<div class="stat-value">{{ doingOrderCount }}</div>
|
||
<div class="stat-label">执行中订单</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-icon complete-icon">
|
||
<i class="el-icon-circle-check"></i>
|
||
</div>
|
||
<div class="stat-content">
|
||
<div class="stat-value">{{ completeOrderCount }}</div>
|
||
<div class="stat-label">已完成订单</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="charts-section">
|
||
<div class="chart-card">
|
||
<div class="chart-header">
|
||
<span>订单状态分布</span>
|
||
</div>
|
||
<div ref="statusChart" class="chart-container"></div>
|
||
</div>
|
||
<div class="chart-card">
|
||
<div class="chart-header">
|
||
<span>供应商订单占比</span>
|
||
</div>
|
||
<div ref="supplierChart" class="chart-container"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="charts-section">
|
||
<div class="chart-card full-width">
|
||
<div class="chart-header">
|
||
<span>订单趋势(按天)</span>
|
||
</div>
|
||
<div ref="trendChart" class="chart-container"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="charts-section">
|
||
<div class="chart-card">
|
||
<div class="chart-header">
|
||
<span>业务员订单分布</span>
|
||
</div>
|
||
<div ref="salesmanBarChart" class="chart-container"></div>
|
||
</div>
|
||
<div class="chart-card">
|
||
<div class="chart-header">
|
||
<span>部门订单分布</span>
|
||
</div>
|
||
<div ref="deptBarChart" class="chart-container"></div>
|
||
</div>
|
||
<div class="chart-card">
|
||
<div class="chart-header">
|
||
<span>按业务员订单占比(饼图)</span>
|
||
</div>
|
||
<div ref="salesmanPieChart" class="chart-container"></div>
|
||
</div>
|
||
<div class="chart-card">
|
||
<div class="chart-header">
|
||
<span>按部门订单占比(饼图)</span>
|
||
</div>
|
||
<div ref="deptPieChart" class="chart-container"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="charts-section">
|
||
<div class="chart-card">
|
||
<div class="chart-header">
|
||
<span>供应商信用等级分布</span>
|
||
</div>
|
||
<div ref="creditChart" class="chart-container"></div>
|
||
</div>
|
||
<div class="chart-card">
|
||
<div class="chart-header">
|
||
<span>供应商类型分布</span>
|
||
</div>
|
||
<div ref="typeChart" class="chart-container"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { listSupplier, listPurchaseOrder } from '@/api/erp/purchase'
|
||
import * as echarts from 'echarts'
|
||
|
||
export default {
|
||
name: 'ErpDashboard',
|
||
data() {
|
||
return {
|
||
supplierCount: 0,
|
||
orderCount: 0,
|
||
doingOrderCount: 0,
|
||
completeOrderCount: 0,
|
||
supplierList: [],
|
||
orderList: [],
|
||
loading: false
|
||
}
|
||
},
|
||
mounted() {
|
||
this.loadData()
|
||
},
|
||
beforeDestroy() {
|
||
if (this.statusChart) this.statusChart.dispose()
|
||
if (this.supplierChart) this.supplierChart.dispose()
|
||
if (this.trendChart) this.trendChart.dispose()
|
||
if (this.creditChart) this.creditChart.dispose()
|
||
if (this.typeChart) this.typeChart.dispose()
|
||
if (this.salesmanBarChart) this.salesmanBarChart.dispose()
|
||
if (this.salesmanPieChart) this.salesmanPieChart.dispose()
|
||
if (this.deptBarChart) this.deptBarChart.dispose()
|
||
if (this.deptPieChart) this.deptPieChart.dispose()
|
||
if (this.supplierBarChart) this.supplierBarChart.dispose()
|
||
if (this.supplierPieChart) this.supplierPieChart.dispose()
|
||
window.removeEventListener('resize', this.handleResize)
|
||
},
|
||
methods: {
|
||
async loadData() {
|
||
this.loading = true
|
||
try {
|
||
const [supplierRes, orderRes] = await Promise.all([
|
||
listSupplier({ pageNum: 1, pageSize: 100000 }),
|
||
listPurchaseOrder({ pageNum: 1, pageSize: 100000 })
|
||
])
|
||
this.supplierList = supplierRes.rows || []
|
||
this.orderList = orderRes.rows || []
|
||
this.supplierCount = this.supplierList.length
|
||
this.orderCount = this.orderList.length
|
||
this.doingOrderCount = this.orderList.filter(o => o.orderStatus === 1).length
|
||
this.completeOrderCount = this.orderList.filter(o => o.orderStatus === 3).length
|
||
|
||
this.$nextTick(() => {
|
||
this.initCharts()
|
||
})
|
||
} catch (error) {
|
||
this.$message.error('加载数据失败')
|
||
console.error(error)
|
||
} finally {
|
||
this.loading = false
|
||
}
|
||
},
|
||
initCharts() {
|
||
this.initStatusChart()
|
||
this.initSupplierChart()
|
||
this.initTrendChart()
|
||
this.initCreditChart()
|
||
this.initTypeChart()
|
||
this.initSalesmanCharts()
|
||
this.initDeptCharts()
|
||
this.initSupplierNameCharts()
|
||
window.addEventListener('resize', this.handleResize)
|
||
},
|
||
handleResize() {
|
||
this.statusChart && this.statusChart.resize()
|
||
this.supplierChart && this.supplierChart.resize()
|
||
this.trendChart && this.trendChart.resize()
|
||
this.creditChart && this.creditChart.resize()
|
||
this.typeChart && this.typeChart.resize()
|
||
this.salesmanBarChart && this.salesmanBarChart.resize()
|
||
this.salesmanPieChart && this.salesmanPieChart.resize()
|
||
this.deptBarChart && this.deptBarChart.resize()
|
||
this.deptPieChart && this.deptPieChart.resize()
|
||
this.supplierBarChart && this.supplierBarChart.resize()
|
||
this.supplierPieChart && this.supplierPieChart.resize()
|
||
},
|
||
initStatusChart() {
|
||
this.statusChart = echarts.init(this.$refs.statusChart)
|
||
const statusMap = {
|
||
0: '草稿',
|
||
1: '执行中',
|
||
2: '部分到货',
|
||
3: '已完成',
|
||
4: '已取消'
|
||
}
|
||
const statusData = {}
|
||
this.orderList.forEach(order => {
|
||
const status = order.orderStatus || 0
|
||
statusData[status] = (statusData[status] || 0) + 1
|
||
})
|
||
const data = Object.keys(statusData).map(key => ({
|
||
name: statusMap[key],
|
||
value: statusData[key]
|
||
}))
|
||
const option = {
|
||
tooltip: {
|
||
trigger: 'item',
|
||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||
},
|
||
legend: {
|
||
orient: 'vertical',
|
||
left: 'left'
|
||
},
|
||
series: [
|
||
{
|
||
name: '订单状态',
|
||
type: 'pie',
|
||
radius: ['40%', '70%'],
|
||
avoidLabelOverlap: false,
|
||
itemStyle: {
|
||
borderRadius: 10,
|
||
borderColor: '#fff',
|
||
borderWidth: 2
|
||
},
|
||
label: {
|
||
show: true,
|
||
formatter: '{b}: {c}'
|
||
},
|
||
data: data
|
||
}
|
||
]
|
||
}
|
||
this.statusChart.setOption(option)
|
||
},
|
||
initSupplierChart() {
|
||
this.supplierChart = echarts.init(this.$refs.supplierChart)
|
||
const supplierOrderCount = {}
|
||
this.orderList.forEach(order => {
|
||
const supplierName = order.supplierName || '未知'
|
||
supplierOrderCount[supplierName] = (supplierOrderCount[supplierName] || 0) + 1
|
||
})
|
||
let data = Object.keys(supplierOrderCount).map(name => ({
|
||
name,
|
||
value: supplierOrderCount[name]
|
||
}))
|
||
data = data.sort((a, b) => b.value - a.value).slice(0, 10)
|
||
|
||
const option = {
|
||
tooltip: {
|
||
trigger: 'item',
|
||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||
},
|
||
legend: {
|
||
orient: 'vertical',
|
||
left: 'left'
|
||
},
|
||
series: [
|
||
{
|
||
name: '供应商订单',
|
||
type: 'pie',
|
||
radius: ['40%', '70%'],
|
||
avoidLabelOverlap: false,
|
||
itemStyle: {
|
||
borderRadius: 10,
|
||
borderColor: '#fff',
|
||
borderWidth: 2
|
||
},
|
||
label: {
|
||
show: true,
|
||
formatter: '{b}: {c}'
|
||
},
|
||
data: data
|
||
}
|
||
]
|
||
}
|
||
this.supplierChart.setOption(option)
|
||
},
|
||
initTrendChart() {
|
||
this.trendChart = echarts.init(this.$refs.trendChart)
|
||
const dayData = {}
|
||
this.orderList.forEach(order => {
|
||
if (order.orderDate) {
|
||
const day = order.orderDate
|
||
dayData[day] = (dayData[day] || 0) + 1
|
||
}
|
||
})
|
||
const days = Object.keys(dayData).sort()
|
||
const counts = days.map(d => dayData[d])
|
||
|
||
const option = {
|
||
tooltip: {
|
||
trigger: 'axis'
|
||
},
|
||
legend: {
|
||
data: ['订单数量']
|
||
},
|
||
grid: {
|
||
left: '3%',
|
||
right: '4%',
|
||
bottom: '3%',
|
||
containLabel: true
|
||
},
|
||
xAxis: {
|
||
type: 'category',
|
||
boundaryGap: false,
|
||
data: days,
|
||
axisLabel: {
|
||
rotate: 45
|
||
}
|
||
},
|
||
yAxis: {
|
||
type: 'value'
|
||
},
|
||
dataZoom: [
|
||
{
|
||
type: 'inside',
|
||
start: 0,
|
||
end: 100
|
||
},
|
||
{
|
||
start: 0,
|
||
end: 100
|
||
}
|
||
],
|
||
series: [
|
||
{
|
||
name: '订单数量',
|
||
type: 'line',
|
||
smooth: true,
|
||
data: counts,
|
||
areaStyle: {
|
||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||
{ offset: 0, color: 'rgba(80, 141, 255, 0.5)' },
|
||
{ offset: 1, color: 'rgba(80, 141, 255, 0.1)' }
|
||
])
|
||
},
|
||
lineStyle: {
|
||
color: '#508dff'
|
||
},
|
||
itemStyle: {
|
||
color: '#508dff'
|
||
}
|
||
}
|
||
]
|
||
}
|
||
this.trendChart.setOption(option)
|
||
},
|
||
initCreditChart() {
|
||
this.creditChart = echarts.init(this.$refs.creditChart)
|
||
const creditData = {}
|
||
this.supplierList.forEach(supplier => {
|
||
const credit = supplier.creditRating || '-'
|
||
creditData[credit] = (creditData[credit] || 0) + 1
|
||
})
|
||
const data = Object.keys(creditData).map(key => ({
|
||
name: key,
|
||
value: creditData[key]
|
||
}))
|
||
|
||
const option = {
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'shadow'
|
||
}
|
||
},
|
||
xAxis: {
|
||
type: 'category',
|
||
data: Object.keys(creditData)
|
||
},
|
||
yAxis: {
|
||
type: 'value'
|
||
},
|
||
series: [
|
||
{
|
||
name: '供应商数量',
|
||
type: 'bar',
|
||
data: Object.values(creditData),
|
||
itemStyle: {
|
||
color: function (params) {
|
||
const colorMap = {
|
||
'A': '#67c23a',
|
||
'B': '#e6a23c',
|
||
'C': '#f56c6c',
|
||
'D': '#909399',
|
||
'-': '#409eff'
|
||
}
|
||
return colorMap[params.name] || '#409eff'
|
||
}
|
||
}
|
||
}
|
||
]
|
||
}
|
||
this.creditChart.setOption(option)
|
||
},
|
||
initTypeChart() {
|
||
this.typeChart = echarts.init(this.$refs.typeChart)
|
||
const typeData = {}
|
||
this.supplierList.forEach(supplier => {
|
||
const type = supplier.type === 'RAW' ? '原料供应商' : '其他供应商'
|
||
typeData[type] = (typeData[type] || 0) + 1
|
||
})
|
||
const data = Object.keys(typeData).map(key => ({
|
||
name: key,
|
||
value: typeData[key]
|
||
}))
|
||
|
||
const option = {
|
||
tooltip: {
|
||
trigger: 'item',
|
||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||
},
|
||
legend: {
|
||
orient: 'vertical',
|
||
left: 'left'
|
||
},
|
||
series: [
|
||
{
|
||
name: '供应商类型',
|
||
type: 'pie',
|
||
radius: ['40%', '70%'],
|
||
avoidLabelOverlap: false,
|
||
itemStyle: {
|
||
borderRadius: 10,
|
||
borderColor: '#fff',
|
||
borderWidth: 2
|
||
},
|
||
label: {
|
||
show: true,
|
||
formatter: '{b}: {c}'
|
||
},
|
||
data: data
|
||
}
|
||
]
|
||
}
|
||
this.typeChart.setOption(option)
|
||
},
|
||
initSalesmanCharts() {
|
||
// 柱状图
|
||
this.salesmanBarChart = echarts.init(this.$refs.salesmanBarChart)
|
||
const salesmanData = {}
|
||
this.orderList.forEach(order => {
|
||
const salesman = order.salesman || '未知'
|
||
salesmanData[salesman] = (salesmanData[salesman] || 0) + 1
|
||
})
|
||
const salesmanList = Object.keys(salesmanData).sort((a, b) => salesmanData[b] - salesmanData[a])
|
||
const salesmanCounts = salesmanList.map(s => salesmanData[s])
|
||
|
||
const barOption = {
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'shadow'
|
||
}
|
||
},
|
||
grid: {
|
||
left: '3%',
|
||
right: '4%',
|
||
bottom: '3%',
|
||
containLabel: true
|
||
},
|
||
xAxis: {
|
||
type: 'category',
|
||
data: salesmanList,
|
||
axisLabel: {
|
||
rotate: 45
|
||
}
|
||
},
|
||
yAxis: {
|
||
type: 'value'
|
||
},
|
||
series: [
|
||
{
|
||
name: '订单数量',
|
||
type: 'bar',
|
||
data: salesmanCounts,
|
||
itemStyle: {
|
||
color: '#67c23a'
|
||
}
|
||
}
|
||
]
|
||
}
|
||
this.salesmanBarChart.setOption(barOption)
|
||
|
||
// 饼图
|
||
this.salesmanPieChart = echarts.init(this.$refs.salesmanPieChart)
|
||
let pieData = salesmanList.map(name => ({
|
||
name,
|
||
value: salesmanData[name]
|
||
}))
|
||
pieData = pieData.slice(0, 10)
|
||
|
||
const pieOption = {
|
||
tooltip: {
|
||
trigger: 'item',
|
||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||
},
|
||
legend: {
|
||
orient: 'vertical',
|
||
left: 'left'
|
||
},
|
||
series: [
|
||
{
|
||
name: '业务员订单',
|
||
type: 'pie',
|
||
radius: ['40%', '70%'],
|
||
avoidLabelOverlap: false,
|
||
itemStyle: {
|
||
borderRadius: 10,
|
||
borderColor: '#fff',
|
||
borderWidth: 2
|
||
},
|
||
label: {
|
||
show: true,
|
||
formatter: '{b}: {c}'
|
||
},
|
||
data: pieData
|
||
}
|
||
]
|
||
}
|
||
this.salesmanPieChart.setOption(pieOption)
|
||
},
|
||
initDeptCharts() {
|
||
// 柱状图
|
||
this.deptBarChart = echarts.init(this.$refs.deptBarChart)
|
||
const deptData = {}
|
||
this.orderList.forEach(order => {
|
||
const dept = order.deptName || '未知'
|
||
deptData[dept] = (deptData[dept] || 0) + 1
|
||
})
|
||
const deptList = Object.keys(deptData).sort((a, b) => deptData[b] - deptData[a])
|
||
const deptCounts = deptList.map(d => deptData[d])
|
||
|
||
const barOption = {
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'shadow'
|
||
}
|
||
},
|
||
grid: {
|
||
left: '3%',
|
||
right: '4%',
|
||
bottom: '3%',
|
||
containLabel: true
|
||
},
|
||
xAxis: {
|
||
type: 'category',
|
||
data: deptList,
|
||
axisLabel: {
|
||
rotate: 45
|
||
}
|
||
},
|
||
yAxis: {
|
||
type: 'value'
|
||
},
|
||
series: [
|
||
{
|
||
name: '订单数量',
|
||
type: 'bar',
|
||
data: deptCounts,
|
||
itemStyle: {
|
||
color: '#e6a23c'
|
||
}
|
||
}
|
||
]
|
||
}
|
||
this.deptBarChart.setOption(barOption)
|
||
|
||
// 饼图
|
||
this.deptPieChart = echarts.init(this.$refs.deptPieChart)
|
||
let pieData = deptList.map(name => ({
|
||
name,
|
||
value: deptData[name]
|
||
}))
|
||
pieData = pieData.slice(0, 10)
|
||
|
||
const pieOption = {
|
||
tooltip: {
|
||
trigger: 'item',
|
||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||
},
|
||
legend: {
|
||
orient: 'vertical',
|
||
left: 'left'
|
||
},
|
||
series: [
|
||
{
|
||
name: '部门订单',
|
||
type: 'pie',
|
||
radius: ['40%', '70%'],
|
||
avoidLabelOverlap: false,
|
||
itemStyle: {
|
||
borderRadius: 10,
|
||
borderColor: '#fff',
|
||
borderWidth: 2
|
||
},
|
||
label: {
|
||
show: true,
|
||
formatter: '{b}: {c}'
|
||
},
|
||
data: pieData
|
||
}
|
||
]
|
||
}
|
||
this.deptPieChart.setOption(pieOption)
|
||
},
|
||
initSupplierNameCharts() {
|
||
// 柱状图
|
||
this.supplierBarChart = echarts.init(this.$refs.supplierBarChart)
|
||
const supplierData = {}
|
||
this.orderList.forEach(order => {
|
||
const supplier = order.supplierName || '未知'
|
||
supplierData[supplier] = (supplierData[supplier] || 0) + 1
|
||
})
|
||
const supplierList = Object.keys(supplierData).sort((a, b) => supplierData[b] - supplierData[a])
|
||
const supplierCounts = supplierList.map(s => supplierData[s])
|
||
|
||
const barOption = {
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'shadow'
|
||
}
|
||
},
|
||
grid: {
|
||
left: '3%',
|
||
right: '4%',
|
||
bottom: '3%',
|
||
containLabel: true
|
||
},
|
||
xAxis: {
|
||
type: 'category',
|
||
data: supplierList.slice(0, 15),
|
||
axisLabel: {
|
||
rotate: 45
|
||
}
|
||
},
|
||
yAxis: {
|
||
type: 'value'
|
||
},
|
||
series: [
|
||
{
|
||
name: '订单数量',
|
||
type: 'bar',
|
||
data: supplierCounts.slice(0, 15),
|
||
itemStyle: {
|
||
color: '#f56c6c'
|
||
}
|
||
}
|
||
]
|
||
}
|
||
this.supplierBarChart.setOption(barOption)
|
||
|
||
// 饼图
|
||
this.supplierPieChart = echarts.init(this.$refs.supplierPieChart)
|
||
let pieData = supplierList.map(name => ({
|
||
name,
|
||
value: supplierData[name]
|
||
}))
|
||
pieData = pieData.slice(0, 10)
|
||
|
||
const pieOption = {
|
||
tooltip: {
|
||
trigger: 'item',
|
||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||
},
|
||
legend: {
|
||
orient: 'vertical',
|
||
left: 'left'
|
||
},
|
||
series: [
|
||
{
|
||
name: '供应商订单',
|
||
type: 'pie',
|
||
radius: ['40%', '70%'],
|
||
avoidLabelOverlap: false,
|
||
itemStyle: {
|
||
borderRadius: 10,
|
||
borderColor: '#fff',
|
||
borderWidth: 2
|
||
},
|
||
label: {
|
||
show: true,
|
||
formatter: '{b}: {c}'
|
||
},
|
||
data: pieData
|
||
}
|
||
]
|
||
}
|
||
this.supplierPieChart.setOption(pieOption)
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.erp-dashboard-page {
|
||
padding: 16px;
|
||
min-height: 100%;
|
||
background: #f0f2f5;
|
||
}
|
||
|
||
.stats-section {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||
gap: 16px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.stat-card {
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.stat-icon {
|
||
width: 60px;
|
||
height: 60px;
|
||
border-radius: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 28px;
|
||
|
||
&.supplier-icon {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: #fff;
|
||
}
|
||
|
||
&.order-icon {
|
||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||
color: #fff;
|
||
}
|
||
|
||
&.doing-icon {
|
||
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||
color: #fff;
|
||
}
|
||
|
||
&.complete-icon {
|
||
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
||
color: #fff;
|
||
}
|
||
}
|
||
|
||
.stat-content {
|
||
flex: 1;
|
||
}
|
||
|
||
.stat-value {
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
color: #1f2a37;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 14px;
|
||
color: #7a8694;
|
||
}
|
||
|
||
.charts-section {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 16px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.chart-card {
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
padding: 16px;
|
||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||
|
||
&.full-width {
|
||
grid-column: span 2;
|
||
}
|
||
}
|
||
|
||
.chart-header {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #1f2a37;
|
||
margin-bottom: 12px;
|
||
padding-bottom: 12px;
|
||
border-bottom: 1px solid #e4e7ed;
|
||
}
|
||
|
||
.chart-container {
|
||
height: 300px;
|
||
width: 100%;
|
||
}
|
||
</style> |