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 <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-07-30 10:48:55 +08:00
1 parent 43d117133b
commit 98074bf201
1 file changed
+191
@@ -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。
```