| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- <template>
- <div
- class="adm-stat-card"
- :class="['adm-stat-card--' + resolvedColor, { 'is-clickable': clickable }]"
- @click="handleClick"
- >
- <div v-if="icon" class="stat-card__icon">
- <i :class="icon"></i>
- </div>
- <div class="stat-card__body">
- <div class="stat-card__value">
- <span v-if="prefix" class="stat-card__affix">{{ prefix }}</span>
- <span class="stat-card__num">{{ displayValue }}</span>
- <span v-if="suffix" class="stat-card__affix">{{ suffix }}</span>
- </div>
- <div class="stat-card__label">{{ label }}</div>
- </div>
- </div>
- </template>
- <script>
- /**
- * StatCard —— 企业级 KPI 统计卡
- *
- * 设计目的:统一后台 19 个页面各自手写的 .stat-card / .stat-icon-wrap / .stat-value / .stat-label,
- * 消除内联色值与圆角/阴影漂移,收敛到 Ant Design 5 语义色令牌。
- *
- * 兼容旧数据形态 { color, icon, value, label }:
- * 旧写法 <el-card shadow="hover" class="stat-card" :class="'stat-' + card.color"> ... </el-card>
- * 新写法 <StatCard :label="c.label" :value="c.value" :icon="c.icon" :color="c.color" />
- * 旧 color 名(blue/purple/green/orange/red/cyan 等)由 COLOR_MAP 自动映射,无需改数据。
- */
- const COLOR_MAP = {
- primary: 'primary',
- blue: 'primary',
- purple: 'primary', // 旧品牌紫统一收敛为品牌蓝
- success: 'success',
- green: 'success',
- warning: 'warning',
- orange: 'warning',
- amber: 'warning',
- yellow: 'warning',
- danger: 'danger',
- red: 'danger',
- cyan: 'cyan',
- teal: 'cyan',
- info: 'info',
- gray: 'info',
- grey: 'info',
- default: 'info'
- }
- export default {
- name: 'StatCard',
- props: {
- /** 指标名称 */
- label: { type: String, default: '' },
- /** 指标数值 */
- value: { type: [String, Number], default: '' },
- /** Element UI 图标 class,如 el-icon-user */
- icon: { type: String, default: '' },
- /** 语义色:primary/success/warning/danger/cyan/info(旧名自动映射) */
- color: { type: String, default: 'primary' },
- /** 数值前缀,如 ¥ */
- prefix: { type: String, default: '' },
- /** 数值后缀,如 元 / % */
- suffix: { type: String, default: '' },
- /** 数字是否按千分位格式化(仅对 number 生效) */
- thousand: { type: Boolean, default: false },
- /** 是否可点击(开启后带 hover 反馈并派发 click) */
- clickable: { type: Boolean, default: false }
- },
- computed: {
- resolvedColor() {
- return COLOR_MAP[String(this.color || 'primary').toLowerCase()] || 'primary'
- },
- displayValue() {
- const v = this.value
- if (v === null || v === undefined || v === '') return '-'
- if (this.thousand && typeof v === 'number' && isFinite(v)) {
- return v.toLocaleString('en-US')
- }
- return v
- }
- },
- methods: {
- handleClick() {
- if (this.clickable) this.$emit('click')
- }
- }
- }
- </script>
- <style scoped>
- .adm-stat-card {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 16px;
- background: #fff;
- border: 1px solid #f0f0f0;
- border-radius: 8px;
- height: 100%;
- box-sizing: border-box;
- transition: border-color 0.2s ease;
- }
- .adm-stat-card.is-clickable {
- cursor: pointer;
- }
- .adm-stat-card.is-clickable:hover {
- border-color: var(--adm-primary);
- }
- .adm-stat-card__icon {
- flex: 0 0 auto;
- width: 44px;
- height: 44px;
- border-radius: 8px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 20px;
- }
- .adm-stat-card__body {
- flex: 1 1 auto;
- min-width: 0;
- }
- .adm-stat-card__value {
- font-size: 24px;
- font-weight: 600;
- line-height: 1.2;
- color: #262626;
- font-variant-numeric: tabular-nums;
- font-feature-settings: 'tnum';
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .adm-stat-card__affix {
- font-size: 14px;
- font-weight: 400;
- color: #8c8c8c;
- margin: 0 2px;
- }
- .adm-stat-card__label {
- font-size: 12px;
- color: #8c8c8c;
- margin-top: 2px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- /* ---- 语义色(Ant Design 5 浅底 + 深字) ---- */
- .adm-stat-card--primary .adm-stat-card__icon {
- background: #e6f4ff;
- color: var(--adm-primary);
- }
- .adm-stat-card--success .adm-stat-card__icon {
- background: #f6ffed;
- color: #389e0d;
- }
- .adm-stat-card--warning .adm-stat-card__icon {
- background: #fffbe6;
- color: #d48806;
- }
- .adm-stat-card--danger .adm-stat-card__icon {
- background: #fff1f0;
- color: #cf1322;
- }
- .adm-stat-card--cyan .adm-stat-card__icon {
- background: #e6fffb;
- color: #08979c;
- }
- .adm-stat-card--info .adm-stat-card__icon {
- background: #f5f5f5;
- color: #595959;
- }
- </style>
|