From 98074bf20187f901faeacf2cc782312aec249ed5 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Thu, 30 Jul 2026 10:48:55 +0800 Subject: [PATCH] docs: add OA money field (amount + RMB uppercase) design. Captures DingTalk-aligned product decisions for the approval form designer amount control before implementation. Co-authored-by: Cursor --- .../specs/2026-07-30-oa-money-field-design.md | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-30-oa-money-field-design.md diff --git a/docs/superpowers/specs/2026-07-30-oa-money-field-design.md b/docs/superpowers/specs/2026-07-30-oa-money-field-design.md new file mode 100644 index 00000000..e70e150a --- /dev/null +++ b/docs/superpowers/specs/2026-07-30-oa-money-field-design.md @@ -0,0 +1,191 @@ +# OA「金额」控件设计文档 + +**日期:** 2026-07-30 +**范围:** `web-admin` 审批设计器步骤 2(表单设计)+ 发起/详情/打印运行时;条件分支已预留 `money`;后端发布白名单已含 `money` +**关联:** `docs/superpowers/specs/2026-07-14-oa-approval-dingtalk-design.md`;`docs/superpowers/specs/2026-07-15-oa-condition-from-form-design.md`;钉钉开放平台 `MoneyField` +**状态:** 待用户评审 + +--- + +## 1. 背景与目标 + +客户反馈钉钉审批有「金额」组件:可输入数值,并可选展示人民币大写。本仓库审批设计器「2 表单设计」左侧组件区尚无该控件;发布字典与条件候选已预留 `type: "money"`,但设计器与运行时未落地。 + +### 1.1 产品结论(已确认) + +| 项 | 结论 | +|----|------| +| 对齐对象 | 钉钉 `MoneyField`(金额) | +| 存值 | **仅存数字**(`number` / `null`);大写不落库,展示时实时换算 | +| 是否显示大写 | 设计器可配;**默认开启**(对齐钉钉 `upper` 默认需要大写) | +| 是否参与流程条件 | 设计器可配(复用「参与流程条件分支」);勾选后强制必填、可进条件比较 | +| 实现路径 | 自定义 FormCreate 控件 `type=money`(同部门 / 关联审批四层扩展) | +| 货币与规则 | 本期仅人民币大写(含角分);精度默认 2 位小数 | + +### 1.2 非目标 + +- 多币种、外币大写 +- 大写字符串单独字段落库 / 导出专用列 +- 负数金额(本期禁止输入;清洗后不允许为负) +- 小程序端本期实现(契约须可映射;PC 先落地) +- 用金额控件改写审批人解析策略 + +--- + +## 2. 架构 + +对齐 FormCreate / FcDesigner **四层扩展**(与「部门」「关联审批单」同路径): + +| 层 | 内容 | +|----|------| +| Runtime | `OaMoneyField.vue`:`v-model` 为 `number \| null`;按 `props.showUpper` 展示大写 | +| FormCreate 组件注册 | `formCreate.component('money', …)` | +| DragRule | `menu: 'main'`;`name/label` 为金额;`props.showUpper` 默认 `true` | +| 双侧注册 | 主应用(发起/详情)+ `oa-fc-host`(设计器 iframe)均调用 `setupOaMoneyField` | + +大写换算:新增纯函数工具(如 `utils/rmb-upper.ts`),输入数字 → 人民币大写字符串;不依赖后端。 + +``` +[设计器 DragRule] → form_schema_json.type = "money" + ↓ +[OaMoneyField] ← props.showUpper;值 = number|null + ↓ +form_data[field] = 1234.56 + ↓ +[条件引擎] 数值比较(已支持 money) +[详情/打印] 数字 +(可选)大写只读文案 +``` + +--- + +## 3. Schema 与属性 + +### 3.1 规则形态(示例) + +```json +{ + "type": "money", + "field": "Fxxxx", + "title": "金额(元)", + "info": "", + "$required": false, + "useInCondition": false, + "props": { + "showUpper": true, + "placeholder": "请输入金额" + } +} +``` + +### 3.2 设计器可配属性 + +| 属性 | 说明 | +|------|------| +| 标题 / 提示信息 / 必填 | 与现有控件一致 | +| 是否显示大写金额 | Switch,字段 `showUpper`,**默认 `true`** | +| 参与流程条件分支 | 复用全局 `formCreateUseInCondition` / `useInCondition`(`CONDITION_ELIGIBLE_TYPES` 已含 `money`) | +| 占位符 | 可选,默认「请输入金额」 | + +不在一期暴露:币种、精度(固定 2)、千分位开关(输入体验可内置千分位展示,值仍为 number)。 + +### 3.3 与钉钉字段映射(参考) + +| 钉钉 | 本系统 | +|------|--------| +| `MoneyField` | `type: "money"` | +| `upper` / `not_upper` | `props.showUpper`(布尔,默认 `true`) | +| 实例 `value` 数字字符串 | `form_data[field]: number \| null` | + +--- + +## 4. `form_data` 契约(锁定) + +```ts +// 金额字段值 +type MoneyFormValue = number | null +``` + +约束: + +- 有效值为非负有限数字;展示与存储精度按 **2 位小数**(输入清洗可复用 / 对齐 `money-format`)。 +- **禁止**把大写字符串写入 `form_data`。 +- 空值:`null` 或未填;参与条件时由必填规则保证有值。 +- 条件比较:引擎按数值比较(`ConditionRuleEditor` 已对 `money` 做数值 coerce)。 + +--- + +## 5. 运行时交互 + +### 5.1 填单(发起 / 退回修改) + +- 数字输入框(可复用 `AmountInput` 清洗与展示逻辑,或等价实现)。 +- `showUpper === true` 且有有效数字时,输入框**下方**展示只读大写,样式 muted、字号略小于正文(对齐钉钉观感,色值仍用 Fresh Mint 中性色)。 +- `showUpper === false` 时不渲染大写行。 +- 清空输入时大写行消失或显示占位「—」(推荐消失)。 + +### 5.2 审批详情 / 只读 + +- 展示格式化数字(如 `1,234.56`);若 `showUpper` 则附带大写行。 +- 无值时展示「—」。 + +### 5.3 打印 + +- 与详情一致:数字 +(可选)大写;大写不另占独立表单项标题。 + +### 5.4 人民币大写规则(一期) + +- 支持范围:`0`~`999999999999.99`(至「仟亿」元以下);超出显示「金额过大」,不写入非法大写。 +- 含角分:`1.23` → `壹元贰角叁分`;整数补「整」:`1` → `壹元整`。 +- `0` → `零元整`。 +- 与钉钉/银行常见惯例对齐即可,不要求与某一银行票据逐字公证;需单测覆盖样例集。 + +--- + +## 6. 流程条件 + +- `listConditionCandidates` / 后端 `CONDITION_ELIGIBLE_TYPES` **已包含** `money`,无需改类型集合(仅确保运行时控件落地后候选能扫到)。 +- 勾选「参与流程条件分支」⇒ 强制必填、锁定必填开关(现有 `oa-fc-host` 轮询逻辑对合格 type 已生效,确认 `money` 在 eligible 集合即可)。 +- 运算符:`>` `>=` `<` `<=` `==` `!=`(与数字字段相同)。 +- 取消勾选时若流程仍引用该 field:拦截(现有机制)。 + +--- + +## 7. 后端 + +- `oa_publish.ALLOWED_CONTROL_TYPES` / `FILLABLE_TYPES` **已含** `money`:发布字典无需新增 type。 +- 发起校验:增加 `validate_money_form_values`(非负有限 number 或 `null`;必填空值拒绝),与部门控件校验同级挂入发起路径。 +- 引擎条件匹配:沿用现有 `rules` 数值路径;不因大写展示改引擎。 + +--- + +## 8. 文件落点(预期) + +| 路径 | 职责 | +|------|------| +| `web-admin/src/components/oa-approval/OaMoneyField.vue` | 运行时控件 | +| `web-admin/src/utils/oa-money-field-rule.ts` | DragRule | +| `web-admin/src/utils/oa-form-create-money.ts` | `setupOaMoneyField` | +| `web-admin/src/utils/rmb-upper.ts` | 数字 → 人民币大写 | +| `web-admin/src/main.ts` + `oa-fc-host/main.ts` | 双侧注册 | +| 可选 `backend/app/services/oa_money_field.py` | 值形态校验 | + +--- + +## 9. 验收标准 + +1. 设计器左侧「组件」可见「金额」,可拖入画布。 +2. 属性可开关「是否显示大写金额」,默认开;开关后画布/预览即时生效。 +3. 可勾选「参与流程条件分支」;勾选后必填锁定,流程设计候选出现该字段,数值条件可发布并运行。 +4. 发起填单:输入 `1234.56`,下方显示正确大写;`form_data` 仅为数字。 +5. 关闭大写后填单/详情/打印均无大写行。 +6. 详情与打印展示与 Schema 配置一致。 +7. 大写工具函数有单元测试(零、整数、角分、较大金额等)。 + +--- + +## 10. 风险与备注 + +- FormCreate 若内置同名 `money` 控件,注册时需确认覆盖策略与 DragRule `name` 不冲突;以本仓库自定义组件为准。 +- 千分位仅影响输入展示,条件与存值必须是纯 number,避免字符串比较。 +- Tailwind 不进入 `oa-fc-host`;设计器内预览样式写在宿主局部 CSS。 +```