Files
ERP/ofbiz-framework/plugins/modern-ui/UI.md
T
QiufengandClaude Opus 4.8 5e51dc3f56 SNAPSHOT W7 已部署稳定态 — 凯迪ERP+OA一体化平台 (MET 73.3%)
恢复点(restore point)。别人改崩后可 git reset --hard 回到此提交。

== 此快照内容 ==
- 后端 oa-backend: 734 控制器 / 711 实体 (Spring Boot 3.2.5 + SQLite, 端口8091)
- 前端 modern-ui/app: Vue3+Vite, 约700页 (构建产物已在 oa-backend/src/main/resources/static)
- 数据库 oa-backend/data/oa.db: 含全部演示数据 (强制入库, 6.6MB)
- 交接文档 go.md + go-code-reference/endpoints/entities/database.md
- 多代理建设脚本 .claude/wf-*.js

== 状态 ==
- 对 凯迪科技ERP_20260507.xlsx 合规 MET ~73.3% (PARTIAL 75: 34可建+6种子/bug+35外部硬天花板)
- 安全: 5轮红队+5轮复检, default-deny分级鉴权, 连续零可利用
- W3~W7 累计补完436缺口; W8末轮(40缺口)为半成品(源码树可编译但未集成)
- 运行: cd oa-backend; java -jar build/libs/oa-backend-0.1.0.jar --server.port=8091; admin/123456

== 排除(gitignore, 可再生) ==
node_modules / oa-backend/build / .jdks / *.log / Backup-ERP-* / 弃用的OFBiz核心(只保留modern-ui)
完整文件夹备份见同目录 Backup-ERP-20260615-191517/ (含上述全部, 仅缺 node_modules)

时间戳: 20260615-191517

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 19:19:15 +08:00

593 lines
30 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OFBiz Modern UI 使用手册
## 定位
`plugins/modern-ui` 是 OFBiz 现代管理端的生产 UI。目标界面是 `/modern/app/` 下的管理员工作台、领域管理页、业务操作页和系统维护页,服务对象是每天处理订单、库存、财务、客户、内容、系统维护等任务的后台操作员。
本手册说明如何用 Vue 3、Element Plus 和 ERP wrapper 构建 OFBiz 管理端。原有 FreeMarker、widget XML、controller XML 和 service XML 是元数据来源、兼容路由和回退依据;生产页面要面向业务操作员,使用订单、商品、客户、财务、库存、履约、采购、人事、内容、POS、电商、系统维护等业务语言。
`/Users/qiu/Desktop/ERP/element-plus-lab` 可以作为样式试验来源,但不能作为 modern-ui 的产品目标。生产 UI 的入口、样式、组件调用和验收边界都以本目录代码为准。
## 代码入口
| 关注点 | 文件 |
| --- | --- |
| 应用启动、Element Plus 注册、全局图标注册、全局样式导入 | `plugins/modern-ui/app/src/main.ts` |
| Hash 路由、登录门禁、视图选择、业务页参数传递 | `plugins/modern-ui/app/src/App.vue` |
| 侧栏、顶栏、全局命令、快捷动作、会话身份 | `plugins/modern-ui/app/src/components/erp/ErpAppShell.vue` |
| 模块目录、模块路由、标签、图标、业务前缀、快捷页、工作流 | `plugins/modern-ui/app/src/data/moduleCatalog.ts` |
| 领域管理页模板 | `plugins/modern-ui/app/src/components/erp/ErpDomainAdminView.vue` |
| 生成业务页路由 `#/pages/:pageId` | `plugins/modern-ui/app/src/views/BusinessPageView.vue` |
| `PageDefinition` block 渲染、页面动作、回执 | `plugins/modern-ui/app/src/components/erp/ErpPageRenderer.vue` |
| 非普通 form/table/action block 的适配与 workspace 分发 | `plugins/modern-ui/app/src/components/erp/ErpAdapterBlock.vue` |
| ERP wrapper 组件 | `plugins/modern-ui/app/src/components/erp/*.vue` |
| API、action、lookup、option、entity rows、fallback 数据 | `plugins/modern-ui/app/src/services/api.ts`, `plugins/modern-ui/app/src/services/fallback.ts` |
| 共享类型 | `plugins/modern-ui/app/src/types/api.ts` |
| 标签清洗和业务文案格式化 | `plugins/modern-ui/app/src/utils/display.ts`, `plugins/modern-ui/app/src/utils/erpMetadata.ts` |
| 样式入口 | `plugins/modern-ui/app/src/styles/erp-ui.css`, `plugins/modern-ui/app/src/styles/modern.css` |
| 设计变量、Element Plus 覆盖、ERP 可复用模式 | `plugins/modern-ui/app/src/styles/tokens.css`, `plugins/modern-ui/app/src/styles/element-overrides.css`, `plugins/modern-ui/app/src/styles/erp-patterns.css` |
| 生成页面 JSON | `plugins/modern-ui/app/public/generated`, `plugins/modern-ui/webapp/modern/app/generated` |
应用入口只导入一次全局样式:
```ts
import './styles/erp-ui.css'
import './styles/modern.css'
```
业务视图不要重复导入 Element Plus CSS,不要在页面内重新定义主题变量,也不要自建第二套视觉体系。
## 全局样式入口
`erp-ui.css` 是可复用 ERP 视觉系统入口,顺序为:
1. Element Plus 官方 CSS。
2. `tokens.css`:颜色、字号、间距、圆角、阴影、边框等全局变量。
3. `base.css`:基础页面和元素规则。
4. `element-overrides.css`Element Plus 变量映射和窄范围覆盖。
5. `erp-patterns.css`:ERP 表单、表格、状态、上传、lookup、菜单、详情、分页、空状态等可复用类。
`modern.css` 负责管理端 shell、领域页、生成业务页和少量页面级布局。规则归属如下:
- 改颜色、字号、间距、圆角、阴影:优先改 `tokens.css`
- 改 Element Plus 全局变量或基础组件密度:放在 `element-overrides.css`
- 可复用 ERP 模式:放在 `erp-patterns.css`
- 某个页面或某类业务页的布局:放在 `modern.css`,使用明确的页面或 block class。
- 组件私有且不可复用的小范围布局:留在组件 `<style scoped>`,但避免硬编码主题值。
不要在业务页面里散落一次性颜色、深层选择器、任意 padding、超出 8px 的圆角或装饰性阴影。页面章节应是工作区域,不要做卡片套卡片。
### Token 和密度契约
生产样式以 `tokens.css` 为唯一变量来源:
| 维度 | Token / 规则 |
| --- | --- |
| 主色 | `--erp-color-primary` 用于全局导航、主按钮、选中态和可操作焦点 |
| 状态色 | `success` 表示完成/健康/已连接,`warning` 表示待处理/需复核/后台处理中,`danger` 表示错误/拒绝/取消/删除,`info` 表示中性元数据 |
| 字号 | `24px` 页面标题,`18px` 大面板标题,`14px` 正文和主要控件,`13px` 紧凑文本,`12px` 标签和元数据 |
| 间距 | 全站使用 4px grid`--erp-space-1``--erp-space-8` |
| 控件高度 | 标准 `32px`,紧凑 `28px`,大控件 `36px` |
| 圆角 | 常规 `4px` / `6px`,上限 `8px` |
| 阴影 | 后台工作区默认不用装饰阴影,只在浮层或确需层级时使用 |
| 表格 | 表头/行高 `34px`cell 垂直 padding `5px`,横向 padding `8px` |
`element-overrides.css` 只把 Element Plus 变量映射到 ERP token,不承载业务页面布局。`erp-patterns.css` 承载可复用的 `.erp-table``.erp-dense-pagination``.erp-status-tag``.erp-state-line``.erp-work-area``.erp-form--search``.erp-form--entity``.erp-action-stack` 等模式。
### 样式依赖规则
业务页只能走以下路径:
```text
Element Plus primitive -> ERP wrapper / ERP pattern -> 业务页面
```
- 表单、表格、动作、状态、lookup、上传、抽屉、审计优先调用 wrapper。
- 没有 wrapper 时,先复用 `erp-patterns.css` 中的模式类;确实缺失时补窄 pattern,再让页面使用。
- `modern.css` 只放 shell、领域页和生成页的布局组合,不新增颜色体系、字号体系、阴影体系或按钮体系。
- `<style scoped>` 只能处理组件私有排版,且必须引用 token;不能散写主题色、任意圆角、装饰阴影或深层覆盖 Element Plus。
- 业务页面不得重复手写 form/table/action/status/lookup/upload/detail drawer/audit timeline 的样式组合。
### 按钮层级
- 每个工作区最多一个 primary action;其余为 default / link / text。
- 删除、取消、移除、拒绝等危险动作使用 danger,并在高风险动作前加确认。
- 普通动作区最多展示 4 个动作,紧凑动作区最多 2 个,其余进入 `更多`
- 表格行操作使用 link button,短标签优先:查看、审计、编辑、打开。
- 成功、失败、不可执行、需人工处理等结果必须留在工作区内;toast 只能补充。
## Element Plus 与 ERP Wrapper
生产页面采用:
```text
Element Plus primitive -> ERP wrapper / ERP pattern -> OFBiz business page
```
直接使用 Element Plus 的场景:
- `el-tabs`:同一业务对象的不同表面,如总览、数据、动作、流程、审计、权限。
- `el-menu`:页面内子导航或横向菜单。
- `el-alert``el-empty``el-skeleton`loading、empty、permission、unavailable、error、success receipt。
- `el-descriptions``el-timeline`:只读事实和历史,但优先由 wrapper 承载。
- `el-dialog``el-popconfirm`:短确认、短阻塞表单、危险动作确认。
- `el-button``el-tag`:局部小控件,且没有对应业务 wrapper 时。
业务对象优先使用 wrapper
| 业务需要 | 使用 |
| --- | --- |
| 管理端 shell、全局导航、会话 | `ErpAppShell` |
| 标题、说明、面包屑、头部动作 | `ErpPageHeader` |
| 查询、筛选、报表参数 | `ErpSearchForm` |
| 新建、编辑、维护表单 | `ErpEntityForm` |
| 业务列表、实体表、分页、排序、行详情 | `ErpDataTable` |
| 保存、提交、审批、取消、导出、打印等动作 | `ErpActionBar` |
| 业务状态、队列状态、回执状态 | `ErpStatusTag` |
| Party/Product/Order 等实体查找 | `ErpLookup` |
| 行详情、审计、附件、权限上下文侧检 | `ErpDrawer` |
| 审计历史 | `ErpAuditTimeline` |
| 附件、媒体、导入文件 | `ErpUpload`, `ErpMediaWorkspace` |
| 生成页面 block 渲染 | `ErpPageRenderer` |
| 非普通 form/table/action 的生成 block | `ErpAdapterBlock` |
| 领域流程工作台 | `ErpOrderWorkspace`, `ErpFinanceWorkspace`, `ErpInventoryWorkspace`, `ErpProcurementWorkspace`, `ErpCatalogWorkspace`, `ErpPartyWorkspace`, `ErpContentWorkspace`, `ErpSystemWorkspace`, `ErpCommerceSurface`, `ErpPosWorkspace` 等 |
如果一个业务视图重复手写 form、table、action bar、status tag、lookup、upload、detail drawer 或 audit timeline,应该改为调用 wrapper,或在 `components/erp` 新增窄 wrapper 后再复用。
### 当前覆盖事实
截至 2026-06-09 的审计口径:
- 1772 个 legacy view route 映射到 1767 个 unique `PageDefinition`,有 5 个重复 route alias。
- 1767 个 split PageDefinition JSON 文件位于 `plugins/modern-ui/webapp/modern/app/generated/pages`
- `ErpPageRenderer` / `ErpAdapterBlock` 结构支持 1767 个生成页,当前没有 unsupported block type。
- 553 个 table block 都有 data-source contract,其中 505 个 entity-backed48 个 derived/report/history。
- 1409 个 form block 都有 submit/read contract,其中 602 个 service-ready146 个 event-ready369 个 navigation-action,其余为 readonly、dynamic、local、navigation-target 或 preserved unmapped target。
- 当前 verification 截图目录有 112 张 PNG;这不是全量 1767 页浏览器截图。
- 当前 parity artifact 只有 4 个 Accounting 页面 runtime smoke 通过;它不能代表全域 E2E parity。
- 当前浏览器运行时验收使用本地 Google Chrome + CDP fallback`browser-runtime-report.json``http://127.0.0.1:8080/modern/app/` 通过,截图为 `plugins/modern-ui/verification/browser-runtime-admin.png`
- Codex in-app browser / `iab` 当前不可用,根因为 `agent.browsers.get('iab') -> Browser is not available: iab`。不要把 `iab` 不可用解释为 Modern UI 失败,也不要无限重试;按 Chrome/CDP 验证链路处理。
- 1721 个页面仍是 pending business E2E876 个是 high-risk pending 页面。
这组事实只允许声明“结构覆盖/renderer 覆盖/contract 覆盖已建立”。不要在产品文案、PR、交付说明或验收报告里写“full parity complete”、“全量业务等价完成”或类似结论。
### Wrapper 使用边界
生成页默认由 `BusinessPageView -> ErpPageRenderer -> ErpAdapterBlock` 承载。只有在以下情况才新增或扩展 dedicated Vue/workspace
- 页面需要跨多个实体编排流程,而不是单个 form/table/action block。
- 需要真实业务状态机、步骤条、异常交接、审计、上传、导入、导出、PDF/CSV/print、POS/cart/payment、report output 等行为。
- legacy 页面依赖复杂 JavaScript、FreeMarker/Groovy 模板语义或多请求副作用,metadata renderer 只能保留结构。
- 同一领域已有 workspace 可复用,如 `ErpFinanceWorkspace``ErpOrderWorkspace``ErpProcurementWorkspace``ErpContentWorkspace``ErpPosWorkspace``ErpBirtReportingWorkspace``ErpSystemWorkspace`
不要为了单个普通 legacy form/table 复制一套页面结构。先把元数据送进 wrapper;当 E2E 证明 metadata renderer 无法表达业务行为时,再把那段行为提升成窄 workspace 或 wrapper。
## 表单规则
查询、筛选、报表参数和生成 legacy form block 使用 `ErpSearchForm`
```vue
<ErpSearchForm
:fields="formBlock.fields"
:submit-action="formBlock.submitAction"
:initial-values="routePayload"
@submit="handleSubmit"
@reset="handleReset"
@update:model="mergePayload"
/>
```
新建、编辑、维护业务对象使用 `ErpEntityForm`
```vue
<ErpEntityForm
:fields="editBlock.fields"
:submit-action="editBlock.submitAction"
:initial-values="currentRecord"
@submit="handleSubmit"
@update:model="mergePayload"
/>
```
表单布局规则:
- 搜索表单使用四列密集网格,编辑表单使用两列;抽屉或窄面板内使用一列。
- 身份字段优先:party、product、order、invoice、payment、facility、content、work effort、employee 等。
- 只读事实用 `el-descriptions``.erp-detail-descriptions`,不要用 disabled form 假装详情。
- `ErpSearchForm` 当前最多展示 16 个可见字段,`ErpEntityForm` 当前最多展示 24 个可见字段。需要更多字段时分组、分 tab 或高级区,不要绕过 wrapper。
- hidden、ignored、submit widget 不作为普通输入控件显示。
- disabled、readonly、requires-login、contract-only、backend-only 等状态必须可见并有业务含义,不要直接隐藏整块表单。
## Select、Radio、Lookup 与 Option
来自 OFBiz widget XML 的 `drop-down``select``radio` 必须继续使用生成元数据,不要在业务页面手写 `<el-select>` options。
选择控件的三类数据契约:
```text
field.options: 静态 <option key="" description=""> 值
field.optionSources[type=entity-options]: 动态 <entity-options> 来源
field.optionSources[type=list-options]: 旧 list-options 上下文,等待页面数据注入
```
`ErpSearchForm``ErpEntityForm` 通过 `fieldOptions.ts` 合并静态选项和远程 entity options。实体选项调用:
```text
GET /api/v1/options/:entityName
?keyFieldName=geoId
&description=${geoName} [${geoId}]
&constraint=geoTypeId:STATE
&pageSize=60
```
lookup 规则:
- Party、Product、OrderHeader 或可推断实体引用使用 `ErpLookup`
- 描述性文本可以用普通 input;实体身份字段不要用自由文本代替 lookup。
- lookup 数据通过 `services/api.ts#getLookup` 获取。
- lookup 弹层、autocomplete、选中值展示不要在业务页重复手写。
上传规则:
- 普通字段上传由表单 wrapper 的 file widget 或 `ErpUpload` 承载。
- 媒体资产、内容关联、审核工作流使用 `ErpMediaWorkspace`
- 上传、导入、PDF、CSV、print、export 都属于业务验收敏感路径,不能只验证页面可渲染。
## 表格和列表
业务列表使用 `ErpDataTable`
```vue
<ErpDataTable
:columns="tableBlock.fields"
:rows="tableBlock.rows"
:data-source="tableBlock.dataSource"
/>
```
数据契约示例:
```ts
{ type: 'entity', entityName: 'Party', endpoint: '/api/v1/entities/Party' }
{ type: 'derived', derivedType: 'report-list', reason: '需要进入对应业务流程后加载数据' }
```
`ErpDataTable` 负责:
- provided rows 和 remote entity rows。
- 搜索、刷新、服务端排序、分页。
- 默认 page size 20,可选 10 / 20 / 50 / 100。
- 默认最多 12 个可见列。
- hidden、ignored、submit widget 不显示为列。
- `amount``total``price` 等金额列右对齐。
- `status` 类列用 `ErpStatusTag`
- 固定右侧操作列,短动作如 `查看``审计`
- 420px 详情抽屉,显示 `el-descriptions` 和审计时间线。
- loading、empty、unavailable、error、derived/fallback 状态。
当用户需要扫描、排序、比较、分页或执行行级动作时,不要用装饰卡片替代表格。空数据必须解释业务状态:当前条件无匹配记录、权限不足、会话缺失、服务不可用、或数据需要进入业务流程后加载。
## 动作和按钮
页面动作使用 `ErpActionBar`
```vue
<ErpActionBar
:actions="page.actions"
:payload="pagePayload"
@executed="setActionResult"
/>
```
动作规则:
- 一个区域只保留一个 primary action`ErpActionBar` 默认第一个可见动作是 primary。
- 删除、取消、移除、拒绝、delete、cancel、remove、reject 等危险动作使用 danger,并在高风险时加 `el-popconfirm`
- 普通模式最多显示 4 个动作,compact 模式最多显示 2 个,其余进入 `更多`
- 表格行操作使用 link button,标签短而明确:查看、审计、编辑、打开。
- 成功、失败、不可执行、需人工处理等结果要留在页面工作区或 `ErpPageRenderer` action receipt 中;`ElMessage` 只能作为补充。
- 不要做长横向按钮带,不要在多个区域重复同一个 submit,不要在 API 或 OFBiz 返回结果不支持时宣称动作成功。
`ErpPageRenderer` 只在 `submitAction.apiExecutable` 为 true 且存在 `actionId` 时调用 `/api/v1/actions/:actionId`。其他 action contract 要在页面里显示原因和下一步。
## 菜单、面包屑和路由
`ErpAppShell` 拥有全局导航。业务页面不要重建侧栏、顶栏、全局命令、会话身份或支持链接。
模块导航来自:
```text
plugins/modern-ui/app/src/data/moduleCatalog.ts
```
新增或调整模块时先维护 `id``navLabel``landingPath``title``eyebrow``description``icon``tone``prefixes``legacyIncludes``quickPages``workflows``App.vue``ErpAppShell.vue``DashboardView.vue``ModuleWorkspaceView.vue` 都依赖该目录。
导航规则:
- 侧栏:全局模块和系统入口。
- 顶栏快捷动作:高频跨模块跳转,标签保持短。
- `el-tabs`:同一业务对象的多个表面。
- 横向 `el-menu`:页内路由或锚点,active state 是 1px 下划线,不是圆角 pill。
- 面包屑:通过 `ErpPageHeader``crumbs`,使用业务层级,如 `OFBiz 管理 / 订单履约 / 订单查询`,不要暴露组件路径、JSON 文件名或生成来源。
- legacy GET 路由进入现代页时必须保留 query 参数,如 `partyId``orderId``productId`
## 抽屉、弹窗和状态
抽屉用于非阻塞业务检查:
- 行详情
- 审计历史
- 附件和媒体审查
- 权限上下文
- side-by-side 数据检查
- 低频编辑
弹窗只用于确认和短阻塞表单。不要把完整业务流程、大表、多 tab、长审计或复杂上传塞进 dialog。popover 和 tooltip 只放轻量上下文,不能承载关键错误或多步工作。
状态规则:
| 状态 | 表达 |
| --- | --- |
| Loading | 工作区内保留骨架或 loading 文案 |
| Empty | 说明业务含义和下一步 |
| Unavailable | 说明权限、会话、服务、数据源或流程依赖 |
| Error | 页面内保留错误原因,不只 toast |
| Success | 页面内回执或结果区,toast 只补充 |
| Disabled | 尽量显示为何不能操作 |
状态是文字第一、颜色第二。蓝色用于主导航和主动作,绿色用于完成/健康/已连接,黄色用于待处理/需复核/后台处理,红色用于错误/拒绝/取消/删除,灰色用于中性元数据和不可用说明。
## 页面排版
后台页面是桌面优先的密集工作台,当前宽度目标约 1180px。POS 和 commerce 可以更适合触控,但 back-office 页面仍保持紧凑、稳定、可扫描。
推荐结构:
1. `ErpPageHeader`:面包屑、短说明、有限头部动作。
2. tabs:当页面有总览、数据、动作、流程、审计、权限等不同表面。
3. 查询/命令/动作带:只在能帮助操作员时出现。
4. 主工作区:form、table、action bar、domain workspace。
5. 侧向上下文:文档摘要、流程、审计、权限、交接状态。
密度规则:
- 24px 用于页面 `h1`,18px 用于面板标题,14px 用于正文和主要控件,13px 用于菜单/面包屑/紧凑文本,12px 用于状态、标签和元数据。
- 使用 4px spacing grid 和 `--erp-space-*`
- 圆角不超过 `--erp-radius-md` 8px。
- 表格用 1px soft border、紧凑 cell、稳定分页。
- cards 只用于重复项、摘要指标、队列行和真正 framed tools;不要卡片套卡片。
- 不做营销 hero,不用大段解释挡住主工作区,第一屏要看到真实业务工作。
## PageDefinition 业务页
`PageDefinition` 是业务操作页的主要元数据契约,包含:
- `pageId``title``component``domain``layout`
- `blocks`: form、table、actions、menu、links、permission、section、workspace、report、adapter 等 `PageBlock[]`
- `actions`: 字符串 action 或 `ActionDefinition[]`
- `permissions`: OFBiz 继承权限。
- `legacy`: 旧 source path 和 widget traceability。
- `acceptance`: renderability、mapping、risk、checklist、scenario 等验收线索。
路由和渲染流程:
```text
controller.xml + widget XML + service XML
-> plugins/modern-api/generated/ui-inventory.json
-> plugins/modern-ui/app/public/generated/ui-inventory.json
-> /modern/app/generated/ui-inventory.json
-> GET /api/v1/pages/:pageId 或 generated page JSON fallback
-> BusinessPageView
-> ErpPageRenderer
-> Element Plus ERP wrappers
```
`BusinessPageView` 接收 `pageId` 和 hash query,解析 `PageDefinition`,保留 route payload,并传给 `ErpPageRenderer``ErpPageRenderer` 负责页面动作状态、`ErpActionBar`、上传处理、动作回执、domain workspace 选择和 block 渲染。
不要在自定义 view 中按 route id 手工重建生成业务页。优先修 `PageDefinition` 元数据、renderer 映射或 workspace 映射,让同类页面一起受益。
legacy query 不能丢:
```text
/accounting/control/EditBillingAccount?partyId=DemoCustomer
-> /modern/app/#/pages/accounting__EditBillingAccount?partyId=DemoCustomer
```
这些参数要进入 `initialPayload`,并预填匹配的 `ErpSearchForm` / `ErpEntityForm` 字段。
## Block 和 Workspace 选择
常见 block 映射:
| Block 类型 | 使用 |
| --- | --- |
| `form` | `ErpSearchForm`;编辑语义明确时用 `ErpEntityForm` |
| `table` | `ErpDataTable` |
| `actions` | `ErpActionBar` |
| `menu` | Element Plus `el-menu` 或业务链接组 |
| `links` | Element Plus button/link group |
| `permission` | `el-descriptions` / `el-alert` 展示业务权限含义 |
| `section` | 业务摘要或只读事实 |
| `report` | `ErpReportWorkspace` 或 report adapter |
| `search-workspace` | 查询表单 + 结果表格 |
| `entity-editor` | `ErpEntityForm` + 校验/审计上下文 |
| `domain-workspace` | 摘要、tabs、line tables、领域动作 |
| `tree-workspace` | 树导航 + 详情面板 |
| `calendar` | `el-calendar` + `.erp-calendar` |
| `lookup-workspace` | lookup 搜索 + 可选结果 |
| `commerce-surface` | `ErpCommerceSurface` |
| `pos-workspace` | `ErpPosWorkspace` |
| `client-behavior` | dependent selects、多选、前端校验说明 |
| `route-workspace` | 没有 widget block 的旧路由工作区 |
| `template-adapter`, `html-template`, `legacy-screen` | 临时结构承载;业务签收前必须确认行为或替换成领域实现 |
领域流程优先使用 dedicated workspace,例如:
- 订单、购物车、发运、履约:`ErpOrderWorkspace`, `ErpFulfillmentWorkspace`, `ErpReturnWorkspace`
- 财务、手工分录、对账:`ErpFinanceWorkspace`, `ErpFinanceOperationsWorkspace`
- 库存、采购、制造:`ErpInventoryWorkspace`, `ErpProcurementWorkspace`, `ErpManufacturingWorkspace`
- 商品、目录、促销、媒体:`ErpCatalogWorkspace`, `ErpMediaWorkspace`
- 客户、关系、沟通:`ErpPartyWorkspace`
- 内容、报表、BI、系统、扩展、网关、门户:对应 `Erp*Workspace`
Dedicated workspace 仍然要消费 `PageBlock``PageDefinition`、actions、fields、items、capabilities 和 dataSource,不要把业务规则写成孤立静态布局。
## 页面配方
### 领域管理页
适用于订单、客户、商品、财务、库存、生产、人事、采购、内容、营销、电商、POS、扩展、系统等模块。
1.`moduleCatalog.ts` 维护模块身份和导航。
2. 使用 `ErpDomainAdminView` 或领域 admin view。
3. 顶部使用 `ErpPageHeader`
4. 指标用 `.metrics-grid``.erp-metric-card`
5. 主体展示队列、近期记录、风险、交接、治理信息。
6. 记录列表使用 `ErpDataTable` 或领域 workspace 内表格。
7. 原始诊断信息只放系统/诊断区域,不进第一屏。
### 查询和列表维护
1. `ErpPageHeader`
2. `ErpSearchForm`
3. `ErpActionBar`,当存在查询、导出、打印或批处理动作
4. `ErpDataTable`
5. 内置详情抽屉或 `ErpDrawer`
6. 需要审计时使用 `ErpAuditTimeline`
### 新建和编辑
1. `ErpPageHeader`
2. 权限、来源或后台契约 `el-alert`
3. `ErpEntityForm`
4. `ErpActionBar` 或 form submit action
5. 既有记录的详情/审计侧栏
必填和身份字段前置,实体引用用 lookup,大表单拆成 tabs 或 sections。
### 流程、审批、履约
1. 当前对象摘要和状态标签
2. 一个明确的 primary next action
3. 主流程 workspace:行项目、数量、付款、发运、退货、凭证或审批数据
4. 侧栏显示审计、权限、风险、交接
5. 提交后保留 action receipt
流程页必须说明当前状态、下一步、阻塞原因和审计相关结果。
### 报表和导出
使用 `ErpReportWorkspace``ErpAdapterBlock` 的 report surface
1. 参数表单
2. 输出模式:页面预览、PDF、CSV、print、export
3. 结果表格或不可用原因
4. 导出状态或 action receipt
报表、PDF、CSV、print、export 要验证参数、权限、输出内容和文件行为。
### 系统、安全、运维
1. `ErpPageHeader`
2. 服务、权限、缓存、job、安全状态 alert
3. logs、jobs、services、users、groups、rules 使用密集表格
4. 危险动作必须确认
5. 只有代码、日志、配置值使用 monospace
## 文案规则
使用短、具体、可操作的中文业务文案。
推荐:
- 当前单据
- 业务数据
- 提交动作
- 权限上下文
- 待处理队列
- 异常交接
- 处理回执
- 业务流转
- 审计记录
生产页面避免使用内部工程叙事作为主文案。操作员可见区域必须围绕业务对象、业务状态、下一步动作、权限原因和处理回执;生成来源、适配状态、覆盖率、签收阶段、路由映射和工程报告只放内部验证、开发诊断或交付说明。
文档和交付说明可以客观描述从原有 OFBiz 页面到现代管理端的迁移策略,但必须以生产管理站点为中心:哪些业务对象可操作、哪些动作可执行、哪些流程仍需业务签收、哪些验证命令证明了什么。
## 验收边界
可以声明“结构已覆盖”的条件:
- 有 route manifest entry 和 `PageDefinition`
- 能进入 Vue + Element Plus renderer。
- block 映射到 form、table、action、adapter 或 workspace。
- 表单控件有明确 read/submit contract。
- action 有 `ActionDefinition` 或明确不可执行状态。
- 代表性 runtime 检查无 console/runtime 错误。
不能把结构覆盖等同于业务完成。业务签收需要旧流程和现代流程在以下行为上匹配:
- query 参数和表单预填
- 查询结果、分页和排序
- lookup 和 option loading
- create/edit/submit/action side effects
- service/event 返回、错误和跳转
- upload、media、import、export、PDF、print、report output
- permission、login、session、validation
- 状态流转、审计相关影响、导航目标
`template-adapter``html-template``legacy-screen`、derived dataSource、local submit contract 等都只能说明页面有结构承载;涉及业务副作用时必须做 old-vs-new E2E 或改成 dedicated Vue/workspace。
## 内部验证
代码或 UI 行为改动后,从 `plugins/modern-ui/app` 运行:
```bash
npm run build
npm run verify:browser-runtime
npm run verify:coverage
npm run verify:preview
npm run verify:parity
```
命令含义:
| 命令 | 能证明 | 不能证明 |
| --- | --- | --- |
| `npm run build` | TypeScript 和 Vite 构建成功 | 业务行为正确 |
| `npm run verify:browser-runtime` | `/modern/app/` 管理端在浏览器渲染,收集 console/runtime 健康 | 全路由和业务等价 |
| `npm run verify:coverage` | route/page/action/form/table/API contract 的结构覆盖 | old-vs-new 功能等价 |
| `npm run verify:preview` | 代表性页面渲染、交互、生成资源和场景覆盖 | 所有业务页已签收 |
| `npm run verify:parity` | rewrite gate 和 parity accounting 报告 | 所有业务副作用已匹配 |
文档-only 改动可做轻量检查:
```bash
git status --short -- plugins/modern-ui/UI.md plugins/modern-ui/DESIGN.md plugins/modern-ui/app/src/styles
rg -n "Token 和密度契约|样式依赖规则|按钮层级|表单规则|表格和列表|验收边界|内部验证" plugins/modern-ui/UI.md
rg -n "<本次任务禁用的三组中文主导词>" plugins/modern-ui/UI.md plugins/modern-ui/DESIGN.md plugins/modern-ui/app/src/styles
```
最后一条把占位内容替换成审查清单中的禁用主导词后应无结果。
当前 Codex in-app browser / `iab` 不可用时,浏览器验收命令仍应走本地 Chrome/CDP fallback
```bash
CHROME_BIN="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" npm run verify:browser-runtime
```
验收报告路径是 `plugins/modern-ui/verification/browser-runtime-report.json`,截图路径是 `plugins/modern-ui/verification/browser-runtime-admin.png`。如果该命令失败,优先检查 `/modern/app/` 是否可达、`CHROME_BIN` 是否存在、CDP 是否能启动;不要改用无限 `iab` 重试作为替代。
## Review Checklist
审核 modern-ui 页面或组件改动时确认:
- 页面使用合适的 `ErpPageHeader``ErpSearchForm``ErpEntityForm``ErpDataTable``ErpActionBar``ErpStatusTag``ErpLookup``ErpUpload``ErpDrawer``ErpPageRenderer``ErpAdapterBlock`
- 颜色、字号、间距、圆角、边框、阴影来自 tokens 和 ERP pattern。
- 表格密集、可分页、状态真实,不发明业务记录。
- 动作有 primary、secondary、danger、overflow 层级。
- loading、empty、unavailable、permission、error、success、disabled 都在页面内有表达。
- 面包屑和文案使用业务语言,不把生成来源、组件路径或内部验证状态暴露为主 UI。
- `PageDefinition` 页面保持 renderer-driven,除非有明确领域 workspace 理由。
- 没有把内部验证状态、覆盖率或适配状态说成最终产品目标。