Files
erp-next/ruoyi-ui/src/views/bid/report/components/KpiCard.vue
王文昊 ba74618bea feat(bid): 新增投标报表统计分析模块
本次提交新增了完整的投标报表统计分析功能,包括:

添加用于数据检查与菜单初始化的 SQL 脚本

实现采购概览仪表板、采购成本分析及供应商绩效报告的后端服务、Mapper、Controller 及 VO 类

添加前端 API、路由配置以及使用 ECharts 可视化图表的页面组件

为仪表板添加通用的 KPI 卡片组件
2026-06-03 14:26:25 +08:00

105 lines
2.6 KiB
Vue

<template>
<el-card shadow="hover" class="kpi-card" :style="{ '--kpi-accent': accentColor }">
<div class="kpi-label">{{ label }}</div>
<div class="kpi-value">
<span class="kpi-number">{{ displayValue }}</span>
<span class="kpi-unit" v-if="unit">{{ unit }}</span>
</div>
<div class="kpi-trend" :class="trendClass" v-if="changeRate > 0">
<i :class="trendIcon"></i>
{{ changeRateText }}
<span class="trend-label">环比上月</span>
</div>
<div class="kpi-trend no-change" v-else>
<i class="el-icon-minus"></i> 持平
</div>
</el-card>
</template>
<script>
const COLORS = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399']
const CARD_NAMES = ['采购总额', 'RFQ总数', '采购单数', '活跃供应商']
export default {
name: 'KpiCard',
props: {
label: { type: String, required: true },
value: { type: [Number, String], default: 0 },
unit: { type: String, default: '' },
changeRate: { type: Number, default: 0 },
trend: { type: String, default: 'up' }
},
computed: {
safeValue() {
const v = Number(this.value)
return isNaN(v) ? 0 : v
},
displayValue() {
const v = this.safeValue
if (this.label === '采购总额') {
if (v >= 100000000) return '¥' + (v / 100000000).toFixed(2) + '亿'
if (v >= 10000) return '¥' + (v / 10000).toFixed(2) + '万'
return '¥' + v.toLocaleString()
}
return v.toLocaleString()
},
trendClass() { return this.trend === 'up' ? 'trend-up' : 'trend-down' },
trendIcon() { return this.trend === 'up' ? 'el-icon-top' : 'el-icon-bottom' },
changeRateText() {
const rate = Number(this.changeRate) || 0
return rate.toFixed(1) + '%'
},
accentColor() {
const idx = CARD_NAMES.indexOf(this.label)
return COLORS[idx >= 0 ? idx : 4]
}
}
}
</script>
<style scoped>
.kpi-card {
border-left: 4px solid var(--kpi-accent, #409EFF);
border-radius: 6px;
transition: transform .2s;
}
.kpi-card:hover {
transform: translateY(-2px);
}
.kpi-label {
font-size: 14px;
color: #909399;
margin-bottom: 8px;
}
.kpi-value {
margin-bottom: 8px;
display: flex;
align-items: baseline;
}
.kpi-number {
font-size: 28px;
font-weight: 700;
color: #303133;
line-height: 1.2;
}
.kpi-unit {
font-size: 14px;
color: #909399;
margin-left: 4px;
}
.kpi-trend {
font-size: 13px;
display: flex;
align-items: center;
gap: 4px;
}
.trend-up { color: #67C23A; }
.trend-down { color: #F56C6C; }
.no-change { color: #C0C4CC; }
.trend-label {
color: #C0C4CC;
margin-left: 4px;
font-size: 12px;
}
</style>