Просмотр исходного кода

feat: 首页移除质检通过率卡片,KPI行改均分布局并优化图表提示

lihaiting 5 часов назад
Родитель
Сommit
3f05a7fa63

+ 4 - 3
src/views/warehouseManagement/index/components/DashboardChart.vue

@@ -6,7 +6,8 @@ import { isIdleTrendDay, trendQuantity, agingColorOf, formatAgingRatio, isAgingU
 // 荧光青主色 + 青/蓝/紫/橙语义扩展,深底下的发光感靠柱体渐变与 tooltip 描边实现。
 const CHART_COLORS = ['#00eaff', '#4fd1c5', '#3d8bff', '#a78bfa', '#ffb020', '#ff7a59', '#7af6ff', '#f5d76e'];
 // HR 同款深色 tooltip:近黑蓝底 + 青色描边 + 亮青文字。
-const TOOLTIP_STYLE = { backgroundColor: 'rgba(4,18,36,.94)', borderColor: 'rgba(0,210,255,.35)', borderWidth: 1, textStyle: { color: '#e8f7ff' } };
+// confine: tooltip 默认渲染在图表容器内,靠边卡片(如库龄结构)悬浮时会被容器裁掉一半,限定在容器内完整显示。
+const TOOLTIP_STYLE = { backgroundColor: 'rgba(4,18,36,.94)', borderColor: 'rgba(0,210,255,.35)', borderWidth: 1, confine: true, textStyle: { color: '#e8f7ff' } };
 const MUTED = '#8fc4de';
 export default {
   name: 'WarehouseDashboardChart',
@@ -122,7 +123,7 @@ export default {
     // unknown 段用中性灰并明确「不参与四段占比」,避免把「日期待核实」读成第五段库龄。
     agingOption() {
       const rows = this.rows;
-      const unitSuffix = this.unit ? ' ' + this.unit : '';
+      // 2026-11-06 用户要求:悬浮提示里的数量不再带单位后缀(kg),数量本身已够明确。
       const numberText = (value) => {
         const number = Number(value);
         return Number.isFinite(number) ? number.toLocaleString('zh-CN') : '—';
@@ -134,7 +135,7 @@ export default {
         const unknown = isAgingUnknown(row);
         return [
           nameOf(row),
-          '在库数量:' + numberText(row.quantity) + unitSuffix,
+          '在库数量:' + numberText(row.quantity),
           unknown ? '占比:不参与 0-30 / 31-90 / 91-180 / >180 四段占比' : '占比:' + formatAgingRatio(row.ratio),
           unknown ? '该段批次日期待核实,不计入四段占比合计' : '点击查看该段明细'
         ].join('<br/>');

+ 3 - 88
src/views/warehouseManagement/index/components/HomeOverview.vue

@@ -200,25 +200,8 @@ const STAGNANT_NEUTRAL_COLOR = '#9FB8D4';
 // 天数展示:最多 2 位小数且不补零(整数天不加小数位);null 显示「—」,绝不显示 0。
 const formatTurnoverDays = (value) => (value == null ? '—' : Number(value).toLocaleString('zh-CN', { maximumFractionDigits: 2 }));
 
-// 质检通过率分级 → 配色与状态文案:阈值来自业务确认(≥98 绿 / 95~98 橙 / <95 红),
-// 与后端 qualityPassRate 同一口径。本次只是把原先内联在 kpis() 里的三元表达式抽成常量并补注释,
-// 判定边界(98、95 含等于)与三条文案逐位不变,不引入新的判定依据。
-const QUALITY_LEVEL_STYLE = {
-  pass: { color: '#3BD48A', text: '达标' },
-  near: { color: '#ffb020', text: '接近阈值' },
-  fail: { color: '#F25E5E', text: '低于目标' }
-};
-// 后端没给通过率(qualityPassRate 为 null,例如 qualityInspectedCount 为 0)时的取值色:
-// 中性蓝不表达健康度,避免把「本月没有已检批次」误报成不达标。
-const QUALITY_NEUTRAL_COLOR = '#9FB8D4';
-// 通过率 → 分级:阈值来自业务确认(≥98 达标绿 / 95~98 接近阈值橙 / <95 低于目标红)。
-// 边界值按 >= 归档:98 归绿、95 归橙,与改动前的内联三元逐位一致;null(或非数字)返回 null,
-// 调用方据此走中性色且状态留空,绝不把「算不出来」显示成健康或不健康。
-const resolveQualityStyle = (rate) => {
-  if (rate == null) return null;
-  if (rate >= 98) return QUALITY_LEVEL_STYLE.pass;
-  return rate >= 95 ? QUALITY_LEVEL_STYLE.near : QUALITY_LEVEL_STYLE.fail;
-};
+// 2026-11-06 用户要求去掉首页「质检通过率」卡片(含序号 06),相关分级常量与解析一并移除;
+// qc 下钻视图仍保留(汇总接口字段不动),只是首页不再有这张卡的入口。
 
 // 待办条日志去重:todoCards 是计算属性,父组件重渲染、筛选联动都会让它反复求值,
 // 每次求值都 homeLog 会把控制台刷满、反而看不见真实变化。这里用模块级快照记住上一次打印的
@@ -547,69 +530,6 @@ export default {
       };
     },
 
-    // 质检通过率 KPI:通过率、较上月差值(单位 pp)、让步接收批次全部由后端下发,前端只做展示映射。
-    // 口径:通过率=合格批次÷已检批次(合格+不合格,让步接收进分母但不计合格),
-    // 时间口径固定为「检验完成日期落在本月」,由后端判定,前端不再自行改时间窗。
-    // qualityInspectedCount 可能为 0 而 qualityPassRate 为 null:此时显示「—」、中性色、状态留空,绝不把 0 当率。
-    qualityCard() {
-      const s = this.summary;
-      const rate = parseBackendNumber(s.qualityPassRate);
-      const inspected = parseBackendNumber(s.qualityInspectedCount);
-      const passed = parseBackendNumber(s.qualityPassedCount);
-      const concession = parseBackendNumber(s.qualityConcessionCount);
-      const change = parseBackendNumber(s.qualityChange);
-      const style = resolveQualityStyle(rate);
-      // 联调日志:只打印质检卡片真正用到的字段,禁止打印整包 summary。
-      homeLog('质检通过率字段已解析', { qualityPassRate: s.qualityPassRate, qualityInspectedCount: s.qualityInspectedCount, qualityPassedCount: s.qualityPassedCount, qualityConcessionCount: s.qualityConcessionCount, qualityChange: s.qualityChange, qualityChangeReason: s.qualityChangeReason });
-
-      // 较上月(单位 pp,复用 dashboard-utils 的 formatPpChange:带符号 + 2 位小数 + pp,0 只给「持平」)。
-      // 质检通过率上升是好转(good 绿 ▲)、下降是恶化(bad 红 ▼)、持平不带健康度(dim);
-      // good/bad/dim 与同屏「库存周转天数」「呆滞品占比」完全同一套取值,同一块屏不教两种视觉语言。
-      // qualityChange 为 null 表示上月不可比:只显示占位符「—」(与 formatPpChange(null) 同一语义),
-      // 不可比原因写进 title 与控制台日志,绝不渲染成 +0.00pp、0 或「持平」。
-      let delta = '';
-      let deltaTitle = '';
-      let deltaTone = 'dim';
-      if (change == null) {
-        // 用户要求:不可比原因不显示在卡片上(之前显示「—」),delta 留空。
-        delta = '';
-        deltaTitle = s.qualityChangeReason ? String(s.qualityChangeReason) : '较上月不可比(上月无已检批次或口径不可比)';
-        homeWarn('质检通过率环比不可比', { qualityPassRate: s.qualityPassRate, qualityInspectedCount: s.qualityInspectedCount, qualityChange: s.qualityChange, qualityChangeReason: s.qualityChangeReason });
-      } else {
-        const formatted = formatPpChange(change);
-        // ▲/▼ 是上升/下降的显式标识(颜色仍由 deltaTone 决定);持平不挂箭头。
-        const arrow = change > 0 ? '▲' : change < 0 ? '▼' : '';
-        delta = (arrow ? arrow + ' ' : '') + '较上月' + (formatted === '持平' ? '持平' : ' ' + formatted);
-        deltaTone = change > 0 ? 'good' : change < 0 ? 'bad' : 'dim';
-        deltaTitle = '本月质检通过率 − 上月质检通过率(单位:百分点 pp,可为负);正值=通过率上升、负值=下降;口径:检验完成日期落在本月';
-      }
-
-      // 第二行小字(与周转/呆滞同一写法):分级状态 + 让步接收批次 + 分子分母,每段缺值时不占位。
-      // 让步接收批次只在 > 0 时出现(0 或缺失=本月没有让步单,写「让步接收 0 批」只是噪音)。
-      const parts = [];
-      if (style) parts.push(style.text);
-      if (concession != null && concession > 0) parts.push('让步接收 ' + concession + ' 批 · 进分母不计合格');
-      if (inspected != null && inspected > 0) parts.push('已检 ' + inspected + ' 批 / 合格 ' + (passed == null ? '—' : passed) + ' 批');
-      const subtotal = parts.length ? {
-        text: parts.join(' · '),
-        title: '分级阈值来自业务确认:≥98 达标(绿)/ 95~98 接近阈值(橙)/ <95 低于目标(红);'
-          + '通过率=合格批次 ÷ 已检批次(合格+不合格,让步接收进分母但不计合格),未检批次不计入分母'
-          + (concession != null && concession > 0 ? ';本月让步接收 ' + concession + ' 批次已计入分母' : '')
-      } : null;
-
-      return {
-        color: style ? style.color : QUALITY_NEUTRAL_COLOR,
-        status: style ? style.text : '',
-        delta,
-        deltaTitle,
-        deltaTone,
-        subtotal,
-        // info 提示:口径固定说明,分子分母只在 subtotal 里出现一次,避免同一张卡两处数字打架。
-        info: '质检通过率=合格批次数 ÷ 已检批次数 × 100%(合格+不合格,让步接收进分母但不计合格,未检批次不计入分母);口径:检验完成日期落在本月;较上月单位 pp',
-        progress: rate
-      };
-    },
-
     userName() { return (this.$store.state.user.info || {}).nickname || (this.$store.state.user.info || {}).name || ''; },
     monthLabel() { return new Intl.DateTimeFormat('zh-CN', { year: 'numeric', month: '2-digit' }).format(new Date()); },
     todayLabel() { return new Intl.DateTimeFormat('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', weekday: 'long' }).format(new Date()); },
@@ -653,8 +573,6 @@ export default {
       const turnover = this.turnoverCard;
       // 呆滞卡片同理:一次解析同时喂颜色、状态、环比与口径提示,日志只打一次。
       const stagnant = this.stagnantCard;
-      // 质检卡片同理:一次解析同时喂颜色、状态、较上月 pp 与让步接收小字,日志只打一次。
-      const quality = this.qualityCard;
       return [
         { key: 'stock', code: '01', symbol: '库', label: '库存总量', value: s.inventoryQuantity, unit: this.unit, reason: s.inventoryReason, delta: this.stockWeekOverWeek.text, deltaTitle: this.stockWeekOverWeek.title, subtotal: this.stockUnitSubtotal, locked: this.stockLocked, info: '当前在库数量含待检、剔除锁定;不同基本单位分别展示,不跨单位加总', color: '#00eaff', target: 'stockLedger' },
         // 金额为空时 delta 留空(原因文案不再显示在卡片上,控制台日志保留)。
@@ -664,10 +582,7 @@ export default {
         { key: 'turnover', code: '03', symbol: '转', label: '库存周转天数', value: s.turnoverDays, unit: '天', reason: s.turnoverReason, delta: turnover.delta, deltaTitle: turnover.deltaTitle, deltaTone: turnover.deltaTone, status: turnover.status, subtotal: turnover.subtotal, info: turnover.info, color: turnover.color, target: 'turnover' },
         // 呆滞品占比:颜色与状态只认后端 stagnantLevel(null 走中性色、状态留空),
         // 阈值/环比/可核实分母同样由后端下发,前端不再自行用 8%/10% 判级(判级依据在仓库配置与上月快照里)。
-        { key: 'stagnant', code: '04', symbol: '滞', label: '呆滞品占比', value: s.stagnantRate, unit: '%', reason: s.stagnantReason, delta: stagnant.delta, deltaTitle: stagnant.deltaTitle, deltaTone: stagnant.deltaTone, status: stagnant.status, subtotal: stagnant.subtotal, info: stagnant.info, color: stagnant.color, progress: stagnant.progress, target: 'dead' },
-        // 质检通过率:颜色与文案按业务确认的 ≥98/95 阈值分级(null 走中性色、状态留空,0 已检批次不显示 0%),
-        // 较上月复用 qualityChange(单位 pp,上升为好 → good 绿 ▲),让步接收批次走第二行小字。
-        { key: 'quality', code: '06', symbol: '检', label: '质检通过率', value: s.qualityPassRate, unit: '%', reason: s.qualityReason, delta: quality.delta, deltaTitle: quality.deltaTitle, deltaTone: quality.deltaTone, status: quality.status, subtotal: quality.subtotal, info: quality.info, color: quality.color, progress: quality.progress, target: 'qc' }
+        { key: 'stagnant', code: '04', symbol: '滞', label: '呆滞品占比', value: s.stagnantRate, unit: '%', reason: s.stagnantReason, delta: stagnant.delta, deltaTitle: stagnant.deltaTitle, deltaTone: stagnant.deltaTone, status: stagnant.status, subtotal: stagnant.subtotal, info: stagnant.info, color: stagnant.color, progress: stagnant.progress, target: 'dead' }
       ];
     }
   },

+ 7 - 3
src/views/warehouseManagement/index/dashboard-theme.scss

@@ -175,12 +175,14 @@
 .badge-overdue{font-size:13px;color:var(--red);background:rgba(242,94,94,.15);border:1px solid rgba(242,94,94,.5);border-radius:2px;padding:1px 7px;font-weight:400;text-shadow:none;}
 
 /* ========== KPI 卡(HR metric-card 结构:左圆环图标 + 右文案列) ========== */
-.kpi-row{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:14px;margin-bottom:14px;}
+/* 2026-11-06 质检卡删除后剩 4 张:固定 5 列会空出一块,改为 flex 均分——
+   卡片数量无论几张都自动拉平铺满整行(等宽等间距)。 */
+.kpi-row{display:flex;gap:14px;margin-bottom:14px;}
 .kpi{padding:14px 16px;cursor:pointer;transition:transform .25s ease, box-shadow .25s ease, border-color .25s ease;position:relative;overflow:hidden;}
 .kpi:hover{transform:translateY(-4px);border-color:rgba(0,230,255,.55);box-shadow:0 0 0 1px rgba(0,200,255,.25) inset, 0 16px 40px rgba(0,0,0,.4), 0 0 36px rgba(0,200,255,.22);}
 .kpi:hover .kpi-go{opacity:1;}
 /* 首页 KPI 行才用「图标 + 文案」横排;下钻页 kpi-strip 的 KPI 保持纵向堆叠 */
-.kpi-row .kpi{display:flex;align-items:center;gap:14px;padding:16px 18px;min-height:108px;}
+.kpi-row .kpi{display:flex;align-items:center;gap:14px;padding:16px 18px;min-height:108px;flex:1 1 0;min-width:0;}
 .kpi-ico{position:relative;flex:0 0 52px;width:52px;height:52px;border-radius:50%;display:flex;align-items:center;justify-content:center;
   font-size:22px;font-weight:600;text-shadow:0 0 12px currentColor;
   background:radial-gradient(circle at 35% 30%, rgba(0,230,255,.3), rgba(0,40,70,.15) 55%, transparent);}
@@ -415,7 +417,9 @@ table.tbl tr:hover td{background:rgba(0,200,255,.06);}
 }
 @media (max-width:1400px){
   .greet{flex-wrap:wrap;gap:12px;}
-  .kpi-row{grid-template-columns:repeat(3,1fr);}
+  /* 窄屏 KPI 行 2x2:4 张卡换行后两两等宽铺满,不留孤块 */
+  .kpi-row{flex-wrap:wrap;}
+  .kpi-row .kpi{flex:1 1 calc(50% - 14px);}
   .grid-2-1,.grid-3,.grid-2{grid-template-columns:1fr;}
   .kpi-strip{grid-template-columns:repeat(3,1fr);}
   /* 待办条任何宽度都强制一行,只收紧间距不允许换行 */