From 511e822d121f8f21d6cd7ef26b03899d161771a4 Mon Sep 17 00:00:00 2001 From: yueli Date: Wed, 16 Sep 2026 16:46:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=E6=8C=89=E9=92=AE=E7=BA=A7?= =?UTF-8?q?=E6=9D=83=E9=99=90=E5=AE=88=E5=8D=AB=E4=B8=8E=E6=95=B0=E9=87=8F?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E6=A0=BC=E5=BC=8F=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 按钮级权限(与后端 @permission_required 用同一批权限码): - 出库记录页「退回」按钮 -> v-permission="'outbound_return'" - 不良品看板「修复回库」 -> v-permission="'defective_restock'" - 不良品看板「报废销毁」 -> v-permission="'defective_scrap'" 两个处置按钮是**独立权限**,可能有人只有其一,故分别判定、分别隐藏; 两者都无时显示「无处置权限」。均保留响应式的 v-if 兜底——v-permission 只在 mounted 执行一次,而 Element Plus 表格行会重渲染,两者用同一权限码 判定一致。hasPermission() 内部已对 SUPER_ADMIN 放行,与后端装饰器的 超管旁路对齐。 另外去掉了出库记录页原本套用的 username === 'IRIS' 特例放行:退回是实物 交接的 SOP 动作,后端不认这个特例,前端也不该认,否则会出现「按钮可见但 接口 403」的错位。 数量显示格式化(新增 utils/format.ts,该文件原本是 0 字节空文件): - formatQty() 显示用:1.0000 -> '1',2.5000 -> '2.5' - normalizeQty() 数值用:同上但返回 number,供 el-input-number 的 v-model / :max —— 喂字符串会让步进与边界比较退化 应用于 不良品看板(退回总数、剩余待处理、两个弹窗的在管数量与输入框)、 出库记录页(已退 N 提示、退回弹窗三项数字)、退回记录页(退回数量列)。 同时移除三个 el-input-number 上的 :precision="4" —— 它会把 1 强制渲染成 1.0000,是截图上多余小数点的直接来源。数量本身仍支持小数(后端按 numeric(19,4) 收),只是不再强制补零。 表格列原本不受影响(接口返回 JSON 数字,1.0 经 JS 解析即 1),但后端有一批 运算是 float 累加,可能吐出 0.30000000000000004 这类尾巴,格式化按 numeric(19,4) 量纲先收敛到 4 位再做归一。 --- inventory-web/src/utils/format.ts | 36 +++++++++ inventory-web/src/views/outbound/index.vue | 46 +++++++---- .../src/views/stock/defective/index.vue | 77 ++++++++++++++----- 3 files changed, 122 insertions(+), 37 deletions(-) diff --git a/inventory-web/src/utils/format.ts b/inventory-web/src/utils/format.ts index e69de29..1a84029 100644 --- a/inventory-web/src/utils/format.ts +++ b/inventory-web/src/utils/format.ts @@ -0,0 +1,36 @@ +// ============================================================================ +// 通用格式化工具 +// ============================================================================ + +/** + * 数量显示格式化 —— 整数不显示小数点,小数去除末尾多余的 0。 + * + * 1.0000 -> '1' + * 2.5000 -> '2.5' + * 0.30000000000000004 -> '0.3' (后端 float 累加产生的尾巴) + * null / undefined / '' -> fallback + * + * ★ 为什么先 toFixed(4) 再 Number: + * 本系统数量列在库中一律为 numeric(19,4),但后端有一批运算写成 + * `float(a) + float(b)`,累加后可能带出 0.30000000000000004 这类二进制 + * 浮点尾数。先按量纲精度收敛到 4 位,再交给 Number 归一 —— Number('0.3000') + * 即 0.3,String() 后自然就是 '0.3'。 + */ +export function formatQty(value: any, fallback = '0'): string { + if (value === null || value === undefined || value === '') return fallback + const n = Number(value) + if (!Number.isFinite(n)) return fallback + return String(Number(n.toFixed(4))) +} + +/** + * 数量归一化为 number —— 供 el-input-number 等**需要数值而非字符串**的 + * 组件使用。作用同 formatQty,只是保留 number 类型。 + * + * ★ 不要把 formatQty 的返回值喂给 el-input-number:传字符串会让 v-model + * 失去数值语义,组件内部的步进/边界比较也会退化。 + */ +export function normalizeQty(value: any, fallback = 0): number { + const n = Number(value) + return Number.isFinite(n) ? Number(n.toFixed(4)) : fallback +} diff --git a/inventory-web/src/views/outbound/index.vue b/inventory-web/src/views/outbound/index.vue index 8c9c88e..020c6fa 100644 --- a/inventory-web/src/views/outbound/index.vue +++ b/inventory-web/src/views/outbound/index.vue @@ -122,11 +122,18 @@ + 已退满的行(returnable_quantity <= 0)按钮置灰不可点。 + + ★ 权限双重保障,两者用同一个权限码 outbound_return,判定一致: + · v-if="canReturn" 响应式判定。v-permission 指令只在 mounted + 执行一次,而表格行会重渲染,用响应式判定兜底更稳。 + · v-permission 本系统规范的按钮级指令,声明式表达权限要求, + 无权限时直接把元素移出 DOM(普通领料员工完全看不到)。 --> - + + + @@ -104,19 +106,37 @@ - + @@ -145,14 +165,15 @@ {{ restockDialog.row?.material_name || '-' }} - {{ restockDialog.row?.remaining_qty ?? 0 }} + {{ formatQty(restockDialog.row?.remaining_qty) }} + @@ -190,14 +211,14 @@ {{ scrapDialog.row?.material_name || '-' }} - {{ scrapDialog.row?.remaining_qty ?? 0 }} + {{ formatQty(scrapDialog.row?.remaining_qty) }} + @@ -227,14 +248,23 @@