merge: origin/main (A3 三维切片) 合入战略地图改造分支
This commit is contained in:
@@ -360,6 +360,8 @@
|
||||
<el-button type="primary" @click="savePlan" :loading="savingPlan">保存</el-button>
|
||||
</template>
|
||||
</MyDialog>
|
||||
<!-- KBFF-v1.1: 按 related_page 关联知识接线 · /alerts -->
|
||||
<KnowledgePanel related-page="/alerts" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -372,6 +374,7 @@ import BotSourceTag from '../components/BotSourceTag.vue'
|
||||
import RiskCell from '../components/charts/RiskCell.vue'
|
||||
import { sessionEntityName } from '../utils/entity'
|
||||
import MyDialog from '../components/MyDialog.vue'
|
||||
import KnowledgePanel from '../components/KnowledgePanel.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const activeTab = ref((route.meta?.tab as string) || 'alerts')
|
||||
|
||||
@@ -769,6 +769,8 @@
|
||||
</el-form>
|
||||
<template #footer><el-button @click="showDerivationRuleDialog = false">取消</el-button><el-button type="primary" @click="saveDerivationRule">保存</el-button></template>
|
||||
</MyDialog>
|
||||
<!-- KBFF-v1.1: 按 related_page 关联知识接线 · /budget -->
|
||||
<KnowledgePanel related-page="/budget" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -778,6 +780,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { budgetApi, kpiApi, mapApi, actionPlanApi } from '../api/index'
|
||||
import MyDialog from '../components/MyDialog.vue'
|
||||
import DriverFactorBudget from '../components/DriverFactorBudget.vue'
|
||||
import KnowledgePanel from '../components/KnowledgePanel.vue'
|
||||
|
||||
const activeTab = ref('input')
|
||||
const currentYear = new Date().getFullYear()
|
||||
|
||||
@@ -184,6 +184,8 @@
|
||||
|
||||
<!-- 无数据提示 -->
|
||||
<el-empty v-if="!loading && hierarchyData.length === 0 && reportData.length === 0" description="暂无差异分析数据,请先在预算管理中录入预算" style="padding:40px 0;" />
|
||||
<!-- KBFF-v1.1: 按 related_page 关联知识接线 · /deviations -->
|
||||
<KnowledgePanel related-page="/deviations" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -191,6 +193,7 @@
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { budgetApi, kpiApi } from '../api/index'
|
||||
import KnowledgePanel from '../components/KnowledgePanel.vue'
|
||||
|
||||
const currentYear = new Date().getFullYear()
|
||||
const yearOptions = computed(() => {
|
||||
|
||||
@@ -398,6 +398,8 @@
|
||||
<el-button type="primary" :disabled="!selectedTemplate" @click="doInstantiate">从模板创建</el-button>
|
||||
</template>
|
||||
</MyDialog>
|
||||
<!-- KBFF-v1.1: 按 related_page 关联知识接线 · /kpis -->
|
||||
<KnowledgePanel related-page="/kpis" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -407,6 +409,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { kpiApi, templateApi, dashboardApi, userApi, orgApi } from '../api/index'
|
||||
import MyDialog from '../components/MyDialog.vue'
|
||||
import { useFormGuard, trackDialogForm } from '../composables/useFormGuard'
|
||||
import KnowledgePanel from '../components/KnowledgePanel.vue'
|
||||
|
||||
// ── 数据 ──
|
||||
const kpis = ref<any[]>([])
|
||||
|
||||
@@ -54,6 +54,9 @@
|
||||
@completeness="showCompleteness = true"
|
||||
/>
|
||||
|
||||
<!-- A3 三维切片(时间/门店/产品):切换即重算;空态显式,不回退总额 -->
|
||||
<MapDimensionFilter :map-id="selectedMap" @slice="onDimensionSlice" />
|
||||
|
||||
<!-- 因果链查看模式提示 -->
|
||||
<div v-if="chainMode" class="chain-mode-bar">
|
||||
<span class="chain-ico">
|
||||
@@ -312,6 +315,7 @@ import MapNodeDetail from "../components/strategy-map/MapNodeDetail.vue"
|
||||
import MapCompleteness from "../components/strategy-map/MapCompleteness.vue"
|
||||
import ConnectionLines from "../components/strategy/ConnectionLines.vue"
|
||||
import MapCanvasToolbar from "../components/map-canvas/MapCanvasToolbar.vue"
|
||||
import MapDimensionFilter from "../components/map-canvas/MapDimensionFilter.vue"
|
||||
import MapCanvasDialogs from "../components/map-canvas/MapCanvasDialogs.vue"
|
||||
import ConnectionManagerDialog from "../components/map-canvas/ConnectionManagerDialog.vue"
|
||||
import MyDialog from "../components/MyDialog.vue"
|
||||
@@ -573,6 +577,44 @@ const visibleObjectiveCount = computed(() =>
|
||||
treeGroups.value.reduce((n: number, g: any) => n + g.objectives.length, 0)
|
||||
)
|
||||
|
||||
// ── A3 维度切片(时间/门店/产品)──────────────────────────────
|
||||
// 切片激活时用它覆盖 objectiveLevels / kpiProgressMap 的展示值:
|
||||
// - available=false 的 KPI 一律 null,不用总额或其它期间冒充
|
||||
const dimSlice = ref<any | null>(null)
|
||||
|
||||
/** 切片结果 → 节点键(dimKey-index);按 目标名 对齐 map_objectives 与本地 dimensions */
|
||||
const sliceByKey = computed<Record<string, any>>(() => {
|
||||
const out: Record<string, any> = {}
|
||||
const payload = dimSlice.value
|
||||
if (!payload || !Array.isArray(payload.objectives)) return out
|
||||
for (const o of payload.objectives) {
|
||||
const dim = dimensions.find((d: any) => d.key === o.dimension_key)
|
||||
if (!dim) continue
|
||||
const idx = (dim.objectives || []).findIndex((x: any) => x.name === o.name)
|
||||
if (idx < 0) continue
|
||||
const kpis = (o.kpis || []).map((k: any) => ({
|
||||
code: k.kpi_code, name: k.kpi_name, actual: k.actual_value, target: k.target_value,
|
||||
unit: k.unit, available: k.available, reason: k.reason,
|
||||
}))
|
||||
const ratios = (o.kpis || [])
|
||||
.filter((k: any) => k.available && k.achievement_pct != null)
|
||||
.map((k: any) => Number(k.achievement_pct))
|
||||
out[`${o.dimension_key}-${idx}`] = {
|
||||
ratio: ratios.length ? Math.round(ratios.reduce((a: number, b: number) => a + b, 0) / ratios.length) : 0,
|
||||
level: o.has_data ? (o.level || 'gray') : 'gray',
|
||||
kpiList: kpis,
|
||||
slice: true,
|
||||
}
|
||||
}
|
||||
return out
|
||||
})
|
||||
|
||||
function onDimensionSlice(payload: any | null) {
|
||||
dimSlice.value = payload
|
||||
// 切回全景(payload=null)时恢复 /review 的原始状态
|
||||
if (!payload) loadObjectiveLevels()
|
||||
}
|
||||
|
||||
// 弹窗
|
||||
const showNodeDialog = ref(false)
|
||||
const editingNodeData = ref<any>(null)
|
||||
@@ -679,9 +721,20 @@ function setLayerRef(key: string, el: any) {
|
||||
}
|
||||
|
||||
function getNodeLevel(key: string): string {
|
||||
if (dimSlice.value) return sliceByKey.value[key]?.level || 'gray'
|
||||
return objectiveLevels[key] || 'gray'
|
||||
}
|
||||
function getNodeProgress(key: string): any {
|
||||
if (dimSlice.value) {
|
||||
const s = sliceByKey.value[key]
|
||||
return {
|
||||
ratio: s?.ratio ?? 0,
|
||||
level: s?.level || 'gray',
|
||||
kpiList: s?.kpiList || [],
|
||||
planSummary: kpiProgressMap[key]?.planSummary ?? null,
|
||||
slice: true,
|
||||
}
|
||||
}
|
||||
return kpiProgressMap[key] || null
|
||||
}
|
||||
|
||||
|
||||
@@ -340,6 +340,8 @@
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</MyDialog>
|
||||
<!-- KBFF-v1.1: 按 related_page 关联知识接线 · /maps-review -->
|
||||
<KnowledgePanel related-page="/maps-review" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -349,6 +351,7 @@ import { useRoute, useRouter } from "vue-router"
|
||||
import { ElMessage } from "element-plus"
|
||||
import api from "../api/index"
|
||||
import MyDialog from '../components/MyDialog.vue'
|
||||
import KnowledgePanel from '../components/KnowledgePanel.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const $router = useRouter()
|
||||
|
||||
@@ -434,6 +434,8 @@
|
||||
<CostIntelligence />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<!-- KBFF-v1.1: 按 related_page 关联知识接线 · /predict -->
|
||||
<KnowledgePanel related-page="/predict" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -442,6 +444,7 @@ import { ref, nextTick } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { predictApi } from '../api/index'
|
||||
import CostIntelligence from './CostIntelligence.vue'
|
||||
import KnowledgePanel from '../components/KnowledgePanel.vue'
|
||||
|
||||
const activeTab = ref('cvp')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user