merge: origin/main 合入战略地图改造分支(解 MapCanvas 模板冲突:摘要条+编辑条+口径条并存)
ci/woodpecker/push/main Pipeline was successful

This commit is contained in:
wecom-fullstack
2026-10-20 11:34:29 +08:00
6 changed files with 213 additions and 1 deletions
+10 -1
View File
@@ -32,6 +32,8 @@
/>
</div>
<div class="header-right">
<!-- 口径标识(常驻):A 口径 v1.1 来自会计科目、缺票未闭 → 首屏固定可见 -->
<CaliberBadge v-if="caliberFacts" kind="account_invoice_gap" :facts="caliberFacts" style="margin-right: 8px" />
<span v-if="dataRange" class="data-range">📅 {{ dataRange }}</span>
<el-button text size="small" @click="showSidebar = !showSidebar; if(showSidebar) refreshAnalysis()">
{{ showSidebar ? '收起AI' : '🤖 AI分析' }}
@@ -186,7 +188,7 @@
<div class="stat-card blue" @click="$router.push('/deviations')" style="cursor:pointer;"><div class="stat-val">{{ finSummary.total_sales ? fmtValue(finSummary.total_sales, 'SALES_TOTAL') : '-' }}</div><div class="stat-label">本月销售额</div></div>
<div class="stat-card" :class="(finSummary.gross_profit_rate ?? 0) < 0 ? 'red' : 'green'" @click="$router.push('/deviations')" style="cursor:pointer;">
<div class="stat-val">{{ finSummary.gross_profit_rate != null ? finSummary.gross_profit_rate.toFixed(1) + '%' : '-' }}</div>
<div class="stat-label">毛利率</div>
<div class="stat-label">毛利率 <CaliberBadge v-if="caliberFacts" kind="estimate" :facts="caliberFacts" /></div>
</div>
<div class="stat-card yellow" @click="$router.push('/deviations')" style="cursor:pointer;"><div class="stat-val">{{ finSummary.receivable_turnover != null ? finSummary.receivable_turnover.toFixed(2) : '-' }}</div><div class="stat-label">应收周转率</div></div>
</div>
@@ -194,6 +196,8 @@
<div v-for="k in finKpis" :key="k.id" class="kpi-card-item wide" @click="showKPIDetail(k)">
<div class="card-item-head">
<span class="item-name">{{ k.kpi_name }}</span>
<!-- 缺票期:现值旁标测算/待票(不重算、不回填) -->
<CaliberBadge v-if="isCaliberKpiCode(k.kpi_code, caliberFacts)" kind="estimate" :facts="caliberFacts" />
<el-tag size="small" :type="alertType(k.alert_level)">{{ alertIcon(k.alert_level) }}</el-tag>
</div>
<div class="card-item-val" :style="{ color: alertColor(k.alert_level) }">
@@ -315,6 +319,8 @@ import BEPBreakEvenCard from '../components/BEPBreakEvenCard.vue'
import MyDialog from '../components/MyDialog.vue'
// 预估区标签(财务裁定 2026-10-02:预估行单列展示,不与实测相加/同线)
import ForecastTag from '../components/ForecastTag.vue'
import CaliberBadge from '../components/CaliberBadge.vue'
import { caliberGapFor, currentCaliberEntityId, isCaliberKpiCode, type CaliberGapFacts } from '../constants/caliber'
const router = useRouter()
@@ -365,6 +371,8 @@ const yellowKpis = computed(() => kpis.value.filter((k: any) => k.alert_level ==
const myKpis = ref<any[]>([])
const finKpis = ref<any[]>([])
const finSummary = ref<any>({})
// 缺票期标识:毛利率只显示现值 + 「测算/待票」,不按实账发票重算(仅登记账套挂载)
const caliberFacts = ref<CaliberGapFacts | null>(caliberGapFor(currentCaliberEntityId()))
// 预估区(单列,不计入实测汇总;财务裁定 2026-10-02)
const forecastList = ref<any[]>([])
const enabledChannels = ref(0)
@@ -495,6 +503,7 @@ function onCustomRangeChange(val: any) {
async function loadData() {
try {
const entityId = Number(localStorage.getItem('cma_entity_id') || 1)
caliberFacts.value = caliberGapFor(entityId)
const params = { ...getPeriodParams(), entity_id: entityId }
// include_forecast=true:预估行**单列返回**(forecast 字段),实测取值不受影响
if (userRole.value === 'ceo') {
+13
View File
@@ -54,6 +54,11 @@
@completeness="showCompleteness = true"
/>
<!-- 口径标识(常驻,非 hover 临时提示):A 口径 v1.1 来自会计科目、非发票同票列明 -->
<div v-if="caliberFacts" class="caliber-strip">
<CaliberBadge kind="account_invoice_gap" :facts="caliberFacts" />
</div>
<!-- A3 三维切片(时间/门店/产品):切换即重算;空态显式,不回退总额 -->
<MapDimensionFilter :map-id="selectedMap" @slice="onDimensionSlice" />
@@ -316,6 +321,8 @@ 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 CaliberBadge from "../components/CaliberBadge.vue"
import { caliberGapFor, currentCaliberEntityId } from "../constants/caliber"
import MapCanvasDialogs from "../components/map-canvas/MapCanvasDialogs.vue"
import ConnectionManagerDialog from "../components/map-canvas/ConnectionManagerDialog.vue"
import MyDialog from "../components/MyDialog.vue"
@@ -351,6 +358,8 @@ const kpiNameMap = reactive<Record<string, string>>({})
// 维度数据(对应backend的dimensions)
const dimensions = reactive<any[]>([])
const loading = ref(true)
// 口径标识:仅登记过缺票事实的账套(当前为 entity 2 陕西博海)才挂「科目口径 · 缺票」
const caliberFacts = ref(caliberGapFor(currentCaliberEntityId()))
// ── 仪表盘视图状态(2026-10-18 V1+V3 合版) ──
/** 四层视觉色(本页按设计规范呈现,不改动地图存储的颜色值) */
@@ -790,6 +799,7 @@ watch(zoomLevel, () => {
// ── 画布加载 ──
function loadCanvas() {
caliberFacts.value = caliberGapFor(currentCaliberEntityId())
if (!selectedMap.value) return
dimensions.splice(0, dimensions.length)
connections.value = []
@@ -2053,6 +2063,9 @@ onUnmounted(() => {
/* ── 页面布局 ── */
.map-canvas-page { padding: 16px; height: 100vh; display: flex; flex-direction: column; position: relative; overflow: hidden; }
/* 口径标识条:常驻首屏,右侧对齐,不占画布高度(flex-shrink: 0) */
.caliber-strip { display: flex; justify-content: flex-end; align-items: center; margin: -4px 0 8px; flex-shrink: 0; }
.linking-bar {
display: flex; align-items: center; gap: 12px;
padding: 8px 16px; background: #fdf6ec; border: 1px solid #f5dab1;
+12
View File
@@ -27,6 +27,8 @@
<!-- ── 口径/统计条 ── -->
<div class="meta-bar">
<!-- 口径标识位(常驻,非 hover):与看板/KPI 列表共用同一组件与文案常量 -->
<CaliberBadge v-if="caliberFacts" kind="account_invoice_gap" :facts="caliberFacts" />
<span class="axis-hint">泳道自下而上:学习 → 流程 → 客户 → 财务</span>
<span class="legend">
<span v-for="lg in legend" :key="lg.level" class="legend-item">
@@ -110,6 +112,8 @@
>
<div class="card-head">
<span class="card-name" :title="n.name">{{ n.name }}</span>
<!-- 缺票期:只标现值 + 测算/待票,不重算、不回填 -->
<CaliberBadge v-if="isCaliberKpiCode(n.kpi_code, caliberFacts)" kind="estimate" :facts="caliberFacts" />
<el-tag size="small" effect="plain" class="unit-tag">{{ n.unit || '—' }}</el-tag>
</div>
@@ -163,6 +167,8 @@ import {
Money, User, Setting, Reading, TrendCharts,
} from '@element-plus/icons-vue'
import { mapApi } from '../api/index'
import CaliberBadge from '../components/CaliberBadge.vue'
import { caliberGapFor, currentCaliberEntityId, isCaliberKpiCode, type CaliberGapFacts } from '../constants/caliber'
// ── 状态 ──
const boardRef = ref<HTMLElement | null>(null)
@@ -176,6 +182,12 @@ const edgePaths = ref<any[]>([])
const MAP_ID = 65 // 陕西酣客三年战略规划(e1);map 列表为空时回退到「最新非归档地图」
// 口径标识:以看板地图所属账套为准(接口返回 map.entity_id),缺省回退登录账套
const caliberFacts = computed<CaliberGapFacts | null>(() => {
const eid = board.value?.map?.entity_id
return caliberGapFor(eid != null ? Number(eid) : currentCaliberEntityId())
})
const displayLayers = computed<any[]>(() => {
const layers = (board.value?.layers || []).slice()
// 后端已按 学习→流程→客户→财务 返回(自下而上);页面自上而下渲染需反转