|
|
@@ -0,0 +1,241 @@
|
|
|
+<template>
|
|
|
+ <div
|
|
|
+ v-if="visibleCells.length > 0"
|
|
|
+ class="tiles-view"
|
|
|
+ :class="{ 'tiles-view--dense': props.cells.length > 4 }"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ v-for="cell in visibleCells"
|
|
|
+ :key="cell.key"
|
|
|
+ class="tile"
|
|
|
+ :class="{
|
|
|
+ 'tile--good': cell.trendTone === 'good',
|
|
|
+ 'tile--warn': cell.trendTone === 'warn',
|
|
|
+ 'tile--bad': cell.trendTone === 'bad',
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <span class="tile__accent" aria-hidden="true"></span>
|
|
|
+ <div class="tile__body">
|
|
|
+ <div class="tile__info">
|
|
|
+ <el-tooltip
|
|
|
+ v-if="cell.formulaText"
|
|
|
+ placement="top"
|
|
|
+ :show-after="200"
|
|
|
+ :content="cell.formulaText"
|
|
|
+ >
|
|
|
+ <div class="tile__label tile__label--formula">{{ cell.label }}</div>
|
|
|
+ </el-tooltip>
|
|
|
+ <div v-else class="tile__label">{{ cell.label }}</div>
|
|
|
+ <div class="tile__target">
|
|
|
+ 目标值: <span class="tile__target-val">{{ cell.targetDisplay || '—' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="tile__main">
|
|
|
+ <span class="tile__value">{{ cell.value }}</span>
|
|
|
+ <span v-if="cell.unit" class="tile__unit">{{ cell.unit }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-if="overflowCount > 0" class="tiles-view__overflow" :title="`还有 ${overflowCount} 个指标未显示`">
|
|
|
+ +{{ overflowCount }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="home-kpi-card__empty">
|
|
|
+ <span v-if="loading">加载中…</span>
|
|
|
+ <span v-else>暂无指标配置,点击卡片进入模块后到"运营指标建模"配置 L1 指标</span>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { computed } from 'vue'
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ moduleCode: { type: String, required: true },
|
|
|
+ cells: { type: Array, required: true },
|
|
|
+ loading: { type: Boolean, default: false },
|
|
|
+})
|
|
|
+
|
|
|
+/**
|
|
|
+ * 上限与 card_grid 对齐:<=4 走 2x2 大卡;>4 自动进 dense(2x4 = 8 格),
|
|
|
+ * 超出部分显示 +N 徽标,避免挤压与溢出。
|
|
|
+ */
|
|
|
+const MAX_DENSE = 8
|
|
|
+const MAX_NORMAL = 4
|
|
|
+
|
|
|
+const visibleCells = computed(() => {
|
|
|
+ const arr = Array.isArray(props.cells) ? props.cells : []
|
|
|
+ const cap = arr.length > MAX_NORMAL ? MAX_DENSE : MAX_NORMAL
|
|
|
+ return arr.slice(0, cap)
|
|
|
+})
|
|
|
+
|
|
|
+const overflowCount = computed(() => {
|
|
|
+ const arr = Array.isArray(props.cells) ? props.cells : []
|
|
|
+ const cap = arr.length > MAX_NORMAL ? MAX_DENSE : MAX_NORMAL
|
|
|
+ return Math.max(0, arr.length - cap)
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.tiles-view {
|
|
|
+ position: relative;
|
|
|
+ flex: 1;
|
|
|
+ min-height: 0;
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 1fr 1fr;
|
|
|
+ grid-template-rows: repeat(2, minmax(0, 1fr));
|
|
|
+ grid-auto-rows: 0;
|
|
|
+ gap: 10px;
|
|
|
+ align-content: stretch;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.tiles-view--dense {
|
|
|
+ grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
|
+ grid-template-rows: repeat(2, minmax(0, 1fr));
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.tile {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ align-items: stretch;
|
|
|
+ min-height: 0;
|
|
|
+ min-width: 0;
|
|
|
+ background: rgba(15, 23, 42, 0.55);
|
|
|
+ border: 1px solid rgba(51, 65, 85, 0.45);
|
|
|
+ border-radius: 8px;
|
|
|
+ overflow: hidden;
|
|
|
+ --tile-accent: #64748b;
|
|
|
+}
|
|
|
+
|
|
|
+.tile--good {
|
|
|
+ --tile-accent: #22c55e;
|
|
|
+}
|
|
|
+
|
|
|
+.tile--warn {
|
|
|
+ --tile-accent: #eab308;
|
|
|
+}
|
|
|
+
|
|
|
+.tile--bad {
|
|
|
+ --tile-accent: #ef4444;
|
|
|
+}
|
|
|
+
|
|
|
+.tile__accent {
|
|
|
+ flex: 0 0 4px;
|
|
|
+ background: var(--tile-accent);
|
|
|
+ box-shadow: 0 0 6px color-mix(in srgb, var(--tile-accent) 55%, transparent);
|
|
|
+}
|
|
|
+
|
|
|
+.tile__body {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ gap: 8px;
|
|
|
+ padding: 8px 10px 8px 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.tile__info {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 4px;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.tile__label {
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #cbd5e1;
|
|
|
+ line-height: 1.25;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.tile__label--formula {
|
|
|
+ cursor: help;
|
|
|
+ border-bottom: 1px dashed rgba(148, 163, 184, 0.55);
|
|
|
+ align-self: flex-start;
|
|
|
+ max-width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.tile__target {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #94a3b8;
|
|
|
+ line-height: 1.25;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.tile__target-val {
|
|
|
+ color: #cbd5e1;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.tile__main {
|
|
|
+ display: flex;
|
|
|
+ align-items: baseline;
|
|
|
+ gap: 3px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ max-width: 55%;
|
|
|
+ justify-content: flex-end;
|
|
|
+}
|
|
|
+
|
|
|
+.tile__value {
|
|
|
+ font-size: clamp(18px, 2.6vw, 26px);
|
|
|
+ font-weight: 800;
|
|
|
+ color: #ffffff;
|
|
|
+ line-height: 1.05;
|
|
|
+ letter-spacing: 0.02em;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.tile__unit {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #e2e8f0;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.tiles-view--dense .tile__value {
|
|
|
+ font-size: clamp(14px, 1.9vw, 18px);
|
|
|
+}
|
|
|
+
|
|
|
+.tiles-view--dense .tile__label {
|
|
|
+ font-size: 11px;
|
|
|
+}
|
|
|
+
|
|
|
+.tiles-view--dense .tile__target {
|
|
|
+ font-size: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.tiles-view__overflow {
|
|
|
+ position: absolute;
|
|
|
+ right: 4px;
|
|
|
+ bottom: 2px;
|
|
|
+ font-size: 10px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #93c5fd;
|
|
|
+ background: rgba(15, 23, 42, 0.75);
|
|
|
+ border: 1px solid rgba(96, 165, 250, 0.55);
|
|
|
+ border-radius: 10px;
|
|
|
+ padding: 1px 6px;
|
|
|
+ pointer-events: none;
|
|
|
+ line-height: 1.2;
|
|
|
+}
|
|
|
+
|
|
|
+.home-kpi-card__empty {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ color: #94a3b8;
|
|
|
+ font-size: 11px;
|
|
|
+ padding: 8px;
|
|
|
+ text-align: center;
|
|
|
+ line-height: 1.4;
|
|
|
+}
|
|
|
+</style>
|