{{ item.name }}
- {{ item.actual != null ? fmtKpiVal(item.actual) : '—' }}
+ {{ item.actual != null ? fmtKpiVal(item.actual) : '—' }}
/ {{ item.target != null ? fmtKpiVal(item.target) : '—' }}
+ {{ kpiRateText(item) }}
-
- +{{ progressData.kpiList.length - 2 }} 更多
+
+ +{{ progressData.kpiList.length - 3 }} 更多
点击查看KPI详情
@@ -152,6 +157,8 @@ const props = defineProps<{
iconMap?: Record
kpiNameMap?: Record
allKpis?: any[]
+ /** 因果链查看模式下不在链上 → 变淡 */
+ chainDimmed?: boolean
}>()
const emit = defineEmits<{
@@ -202,12 +209,19 @@ function getKpiLevel(item: any): string {
if (item.actual != null && item.target) {
const ratio = item.actual / item.target
if (ratio >= 0.9) return 'green'
- if (ratio >= 0.7) return 'yellow'
+ if (ratio >= 0.6) return 'yellow'
return 'red'
}
return 'gray'
}
+/** KPI达成率文本(绿≥90% / 黄60-90% / 红<60%) */
+function kpiRateText(item: any): string {
+ if (item.actual == null || !item.target) return '—'
+ const ratio = item.actual / item.target
+ return Math.round(ratio * 100) + '%'
+}
+
function fmtKpiVal(val: any): string {
if (val == null) return '—'
if (typeof val === 'number') {
@@ -259,6 +273,11 @@ function getKrProgressText(kr: any): string {
transition: all 0.2s; position: relative;
}
.map-node:hover { box-shadow: 0 2px 10px rgba(0,0,0,0.08); }
+.map-node.chain-dimmed {
+ opacity: 0.3;
+ filter: saturate(0.35);
+ pointer-events: auto;
+}
.map-node.linking-source {
border-color: #e6a23c; box-shadow: 0 0 0 2px rgba(230,162,60,0.3);
}
@@ -289,9 +308,26 @@ function getKrProgressText(kr: any): string {
.snap-yellow { background: #fdf6ec; }
.snap-red { background: #fef0f0; }
.snap-gray { background: #f5f5f5; }
-.snap-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100px; color: #666; }
-.snap-value { font-weight: 600; color: #333; white-space: nowrap; }
+.snap-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 92px; color: #666; }
+.snap-value {
+ font-weight: 600; color: #333; white-space: nowrap;
+ font-family: 'JetBrains Mono', 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
+ font-variant-numeric: tabular-nums;
+}
+.snap-now-green { color: #67c23a; }
+.snap-now-yellow { color: #e6a23c; }
+.snap-now-red { color: #f56c6c; }
+.snap-now-gray { color: #333; }
.snap-target { font-weight: 400; color: #999; font-size: 10px; }
+.snap-rate {
+ font-size: 10px; font-weight: 700; flex-shrink: 0; min-width: 34px; text-align: right;
+ font-family: 'JetBrains Mono', 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
+ font-variant-numeric: tabular-nums;
+}
+.rate-green { color: #67c23a; }
+.rate-yellow { color: #e6a23c; }
+.rate-red { color: #f56c6c; }
+.rate-gray { color: #bbb; }
.kpi-snapshot-more { font-size: 10px; color: #409eff; text-align: center; cursor: pointer; padding: 1px; }
.node-click-hint { font-size: 10px; color: #bbb; text-align: center; margin-top: 2px; }
@@ -311,15 +347,23 @@ function getKrProgressText(kr: any): string {
.ps-done { background: #f0f9eb; color: #67c23a; }
.node-progress-bar {
- display: flex; align-items: center; gap: 6px; margin-top: 4px;
- height: 10px; position: relative;
+ display: flex; align-items: center; gap: 6px; margin-top: 6px;
+ justify-content: flex-end;
+}
+.node-progress-label {
+ font-size: 10px; color: #909399; flex-shrink: 0; line-height: 1;
+}
+.node-progress-track {
+ flex: 1; max-width: 90px; height: 6px; background: #e4e7ed;
+ border-radius: 3px; overflow: hidden;
}
.node-progress-fill {
- height: 6px; border-radius: 3px; transition: width 0.4s ease;
+ height: 100%; border-radius: 3px; transition: width 0.4s ease;
min-width: 4px;
}
.node-progress-text {
- font-size: 10px; font-weight: 600; color: #666;
+ font-size: 10px; font-weight: 700; min-width: 34px; text-align: right;
+ font-family: 'JetBrains Mono', 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
font-variant-numeric: tabular-nums; line-height: 1;
}
diff --git a/frontend/src/components/strategy-map/StrategyLayer.vue b/frontend/src/components/strategy-map/StrategyLayer.vue
index a521122d..fb4020f2 100644
--- a/frontend/src/components/strategy-map/StrategyLayer.vue
+++ b/frontend/src/components/strategy-map/StrategyLayer.vue
@@ -43,6 +43,7 @@
:icon-map="iconMap"
:kpi-name-map="kpiNameMap"
:all-kpis="allKpis"
+ :chain-dimmed="chainNodeKeys.length > 0 && !chainNodeKeys.includes(`${layerKey}-${index}`)"
@click="onNodeClick(layerKey, index, element)"
@edit="$emit('edit-objective', { dimKey: layerKey, idx: index, obj: element })"
@delete="$emit('delete-objective', { dimKey: layerKey, idx: index })"
@@ -91,6 +92,8 @@ const props = defineProps<{
iconMap?: Record
kpiNameMap?: Record
allKpis?: any[]
+ /** 因果链查看模式下,不在链上的节点变淡 */
+ chainNodeKeys?: string[]
getNodeLevel: (key: string) => string
getNodeProgress: (key: string) => any
}>()
diff --git a/frontend/src/components/strategy/ConnectionLines.vue b/frontend/src/components/strategy/ConnectionLines.vue
index 936775ec..1189313e 100644
--- a/frontend/src/components/strategy/ConnectionLines.vue
+++ b/frontend/src/components/strategy/ConnectionLines.vue
@@ -1,138 +1,141 @@