业务人员在录入或核对大额「单价」「金额」时,需要千分位逗号辅助阅读,但输入过程中插入逗号会干扰光标与连续键入。渠道服务订单「发起充值」已在 channel-service-order/index.vue 用页面内局部函数实现「聚焦纯数字、失焦千分位」,尚未复用到其他页面。
本设计将交互固化为统一工具 + 统一组件,并明确可编辑输入与只读展示两类场景,避免再次在 .vue 中复制正则或误用前瞻表达式导致编译错误。
| 时机 | 可编辑输入框 | 列表/查看等只读展示 |
|---|---|---|
| 聚焦输入中 | 纯数字,如 1234567.89(无逗号) |
— |
| 失焦后 | 千分位,如 1,234,567.89 |
始终千分位(同上格式) |
| 再次聚焦 | 去掉逗号,恢复纯数字 | — |
| 提交/API | number 或约定精度的小数,不受逗号影响 |
— |
不在范围: 数量、份数、排序号、百分比系数等非金额字段(仍用 el-input-number 整数或原控件)。
中试服务(pilot-service)
| 页面 | 路由 | 可编辑 | 只读展示 |
|---|---|---|---|
| 渠道服务购买订单 | /#/pilot-service/channel-service-order |
充值金额、退款金额、产品明细单价;发起充值已改 | 列表余额/购买总额、查看流水与持仓、合计 |
| 渠道服务订单财务确认 | /#/pilot-service/channel-service-order-finance |
— | 列表/详情金额、单价 |
| 渠道信息管理 | /#/pilot-service/channel |
协议价(元)行内编辑 | 价格选择器单价列 |
| 细胞产品价格体系 | /#/pilot-service/cell-product-price |
表单单价 | 列表单价 |
| 细胞产品项目价格 | /#/pilot-service/project-price-system |
表格单价 | 查看单价 |
| 体检机构及项目 | /#/pilot-service/checkup-institution |
项目费用等 | 套餐合计费用 |
细胞存储(cell-storage)
| 页面 | 路由 | 可编辑 | 只读展示 |
|---|---|---|---|
| 采集协议单录入 | /#/cell-storage/protocol |
合同金额、应缴费、转账金额、采集金额等 | 查看弹窗同字段 |
| 缴费信息 | /#/cell-storage/payment-info |
退费金额 | 应缴/实缴列 |
| 缴费/退费确认(财务) | /#/cell-storage/payment-confirm、refund-confirm |
— | 金额列 |
其他(第二批)
| 页面 | 说明 |
|---|---|
订单 Demo system/order/index.vue |
供货价/售价及金额(若仍为可编辑控件则纳入) |
研究捐赠 DonationFormDialog |
采集金额等 |
| 第三方检测公司等 | feeAmount 等 :precision="2" 的金额 |
miniprogram/el-input-number(数量、排序、priceCoefficient 百分比等)formatAmount 与输入逻辑不一致AmountInput 组件(推荐)@/utils/money-format.ts:sanitizeMoneyInput、formatMoneyDisplay、parseMoneyToNumber@/components/common/amount-input/index.vue:v-model 绑定 number | null,封装 focus/blur/input只读:formatMoneyDisplay(n) 或增强现有列表 formatAmount 的展示路径
优点:单测友好、符合 Fresh Mint 组件化、表格/弹窗同一套
缺点:需逐页替换 el-input-number(金额类)
v-money-inputmin/max/precision、表格行绑定不清晰、与 Element Plus 集成弱结论:采用方案 B。
web-admin/src/utils/money-format.ts// 展示:整数千分位 + 最多 precision 位小数(默认 2)
export function formatMoneyDisplay(value: string | number | null | undefined, precision?: number): string
// 输入清洗:去逗号、仅保留数字与一个点
export function sanitizeMoneyInput(value: string, precision?: number): string
// 提交:number | null;空串 / 仅 '.' → null
export function parseMoneyToNumber(value: string, precision?: number): number | null
实现要点:
addThousandSeparators),禁止在 .vue 内联复杂前瞻正则。el-input-number 的 :precision="2" 行为对齐。AmountInput路径:web-admin/src/components/common/amount-input/index.vue
| Prop | 类型 | 默认 | 说明 |
|---|---|---|---|
modelValue |
number \| null |
— | 双向绑定数值 |
min |
number |
— | 可选下限 |
max |
number |
— | 可选上限 |
precision |
number |
2 |
小数位数;单价/金额均为 2 |
placeholder |
string |
— | |
disabled |
boolean |
false |
|
clearable |
boolean |
true |
事件:update:modelValue、change(失焦且数值变化时,便于触发行内 calcAmount)
内部状态:
displayText: ref<string> — 绑定 el-input@focus → displayText = sanitize(modelValue)@input → 清洗 + 临时更新 displayText;可选同步 modelValue 为中间值@blur → emit(update:modelValue, parseMoneyToNumber(...));displayText = formatMoneyDisplay(...)校验:min/max 在 blur 或 submit 时由父级 el-form rules 负责;组件可在 blur 时 ElMessage.warning 可选(默认不弹,避免表格内刷屏)。
样式:宽度 100%;与现有 el-input 高度 36px 规范一致。
formatMoneyDisplay(v),前缀 ¥ 仍在模板侧拼接(与现网 ¥{{ formatAmount(...) }} 一致)。money-format.ts 导出 formatMoneyDisplay;各页逐步将只读 formatAmount 替换为 formatMoneyDisplay(或全局别名 formatAmount = formatMoneyDisplay 仅用于金额列——需逐文件审查,避免误改非金额 toFixed)。建议新增 formatMoneyAmount(v) 专用于金额展示,保留原 formatAmount 为两位小数无逗号直至迁移完成,P0 页面直接使用 formatMoneyDisplay。
在 openspec/changes/zkxw-engineering-standards/specs/ui-conventions/spec.md(或归档后 openspec/specs/ui-conventions/spec.md)增补一节:
AmountInput(或经评审的等价封装),SHALL 满足聚焦/失焦/提交行为表。formatMoneyDisplay,整数部分千分位、小数位默认 2 位。.vue 中复制千分位正则实现。addThousandSeparators、formatRechargeAmountDisplay 等重复函数,改为 import 工具 + AmountInput。AmountInput。AmountInput(@change 仍调 calcItemAmount)。单元(建议) web-admin/src/utils/money-format.spec.ts 或 vitest:
formatMoneyDisplay(1234567.89) → 1,234,567.89sanitizeMoneyInput('1,234.5') → 1234.5parseMoneyToNumber('1,234.56') → 1234.56手工验收(每批至少 1 页)
1000000.5,框内无逗号1,000,000.51000000.51000000.5| 阶段 | 内容 | 产出 |
|---|---|---|
| P0 | 工具 + 组件 + 规范 + 渠道服务订单全页 | 可复用基座 |
| P1 | 渠道信息、细胞价格、项目价格、财务确认页只读列 | 中试服务闭环 |
| P2 | 细胞存储协议/缴费/退费 | 存储线金额 |
| P3 | Demo 订单、捐赠、第三方费用等 | 余量 |
AmountInput:须 row-key 稳定,避免 Vue 复用 DOM 导致显示串行。el-form 校验:rules 仍针对 number 字段,blur 后须已 emit 最新值。max=currentBalance,在 AmountInput 上透传并在 blur 校验。quantity 等整数字段使用本组件。¥)状态: 待评审
关联实现: docs/superpowers/plans/2026-05-25-money-input-thousand-separator.md