feat(caliber-label): 科目口径·缺票 常驻标识 + 测算/待票 数值标识(展示层 P2)
ci/woodpecker/push/main Pipeline was canceled
ci/woodpecker/push/main Pipeline was canceled
- 新增 constants/caliber.ts(文案单一真源)+ components/CaliberBadge.vue(常驻,非 hover) - 经营看板/Dashboard:页头常驻「科目口径 · 缺票」(tooltip 含 四件套 0/4、invoice_check 0 行)+ 毛利率与 A 口径 KPI 卡「测算/待票」 - 战略地图看板/StrategyMap:meta-bar 常驻标识位 + 节点级测算标识(与 v4 §2/§4 共用同一组件) - 战略地图画布/MapCanvas、KPI 列表 KPIListView 同步复用同一组件 - 只做展示层:不改库、不重算历史值、不静默按实账回填
This commit is contained in:
@@ -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') {
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
@toggle-chain-mode="toggleChainMode"
|
||||
/>
|
||||
|
||||
<!-- 口径标识(常驻,非 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" />
|
||||
|
||||
@@ -206,6 +211,8 @@ import StrategyLayer from "../components/strategy-map/StrategyLayer.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"
|
||||
@@ -241,6 +248,8 @@ const kpiNameMap = reactive<Record<string, string>>({})
|
||||
// 维度数据(对应backend的dimensions)
|
||||
const dimensions = reactive<any[]>([])
|
||||
const loading = ref(true)
|
||||
// 口径标识:仅登记过缺票事实的账套(当前为 entity 2 陕西博海)才挂「科目口径 · 缺票」
|
||||
const caliberFacts = ref(caliberGapFor(currentCaliberEntityId()))
|
||||
|
||||
// 连线
|
||||
const connections = ref<any[]>([])
|
||||
@@ -540,6 +549,7 @@ watch(zoomLevel, () => {
|
||||
|
||||
// ── 画布加载 ──
|
||||
function loadCanvas() {
|
||||
caliberFacts.value = caliberGapFor(currentCaliberEntityId())
|
||||
if (!selectedMap.value) return
|
||||
dimensions.splice(0, dimensions.length)
|
||||
connections.value = []
|
||||
@@ -1621,6 +1631,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;
|
||||
|
||||
@@ -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()
|
||||
// 后端已按 学习→流程→客户→财务 返回(自下而上);页面自上而下渲染需反转
|
||||
|
||||
Reference in New Issue
Block a user