@qfei-design/make-app-approval 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PUBLIC_API.md +129 -0
- package/README.md +65 -2
- package/capabilities.json +76 -0
- package/dist/detail/approval-task-detail.d.ts +2 -0
- package/dist/detail/diagnostics.d.ts +2 -0
- package/dist/detail/field-value.d.ts +2 -0
- package/dist/detail/index.d.ts +3 -0
- package/dist/detail/task-policy.d.ts +18 -0
- package/dist/detail/types.d.ts +111 -0
- package/dist/detail.cjs +1 -0
- package/dist/detail.mjs +500 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.mjs +30 -0
- package/dist/list/approval-table.d.ts +14 -0
- package/dist/list/diagnostics.d.ts +3 -0
- package/dist/list/index.d.ts +3 -0
- package/dist/list/my-approvals-page.d.ts +2 -0
- package/dist/list/page-contract.d.ts +3 -0
- package/dist/list/status-label.d.ts +1 -0
- package/dist/list/types.d.ts +71 -0
- package/dist/list/ui-components.d.ts +44 -0
- package/dist/list/use-approval-page.d.ts +12 -0
- package/dist/list/virtual-page-loader.d.ts +26 -0
- package/dist/list.cjs +1 -0
- package/dist/list.mjs +381 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react.cjs +1 -0
- package/dist/react.mjs +17 -0
- package/dist/styles.css +94 -0
- package/dist/styles.css.d.ts +1 -0
- package/dist/withdrawal.d.ts +14 -0
- package/docs/host-integration.md +217 -0
- package/docs/implementation-plan.md +61 -0
- package/docs/publishing.md +108 -0
- package/examples/antd-components.tsx +38 -0
- package/examples/detail-integration.tsx +80 -0
- package/examples/host-integration.tsx +36 -0
- package/package.ai.json +85 -0
- package/package.json +117 -4
- package/recipes.json +114 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** 宿主提供业务记录定位,App 范围由 Service 注入;后端最终校验发起人和流程状态。 */
|
|
2
|
+
export type ApprovalWithdrawalInput = Readonly<{
|
|
3
|
+
bpmKey: string;
|
|
4
|
+
comment: string;
|
|
5
|
+
}>;
|
|
6
|
+
export type ApprovalWithdrawalResult = Readonly<{
|
|
7
|
+
processInstanceId: string;
|
|
8
|
+
recordId: string;
|
|
9
|
+
status: string;
|
|
10
|
+
processStatus: string;
|
|
11
|
+
processEnded: boolean;
|
|
12
|
+
}>;
|
|
13
|
+
export declare const APPROVAL_WITHDRAW_COMMENT_MAX_LENGTH = 1024;
|
|
14
|
+
export declare const getApprovalWithdrawalCommentError: (value: unknown) => string | undefined;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# 宿主接入与能力分工
|
|
2
|
+
|
|
3
|
+
本文是 `@qfei-design/make-app-approval` 的接入依据。先读当前能力,再接入对应入口;不要把“后续计划”当作已经存在的组件。
|
|
4
|
+
|
|
5
|
+
## 当前交付范围
|
|
6
|
+
|
|
7
|
+
目前已提供桌面/平板“我的审批”列表 `MyApprovalsPage`、流程状态 `ApprovalStatus`、普通记录编辑限制工具。列表使用 `@qfei-design/canvas-table@^1.3.1`,本项目以 `1.3.1` 验证。
|
|
8
|
+
|
|
9
|
+
列表拥有页签、搜索、状态和日期筛选、查询/重置、虚拟滚动/刷新、加载/空/失败/重试、连续序号和查看详情入口。页面通过滚动加载记录,没有上一页/下一页按钮。底层仍使用后端分页接口,包根据当前滚动位置请求所需数据页;不下载全量再分页,不自行归类。
|
|
10
|
+
|
|
11
|
+
筛选区按钮依次为“查询、重置、刷新”。刷新沿用已生效的查询条件,不提交尚未查询的输入;加载期间禁用刷新。页面不显示底部状态栏或“共多少条”,加载提示和失败重试位于表格区域。`total` 仍是接口必填字段,用于 CanvasTable 虚拟滚动。
|
|
12
|
+
|
|
13
|
+
真实 BPM 接口尚未联调;`demo` 使用明确标注的模拟数据。ExpensePoc 目前安装的旧打包文件只有状态/禁编基础,本次列表源码不会自动进入该宿主,需重新打包并按本文升级。
|
|
14
|
+
|
|
15
|
+
## 包、宿主、后端的职责
|
|
16
|
+
|
|
17
|
+
| 能力 | 包侧提供 | 宿主 UI / App Service 提供 | 后端提供 | 当前情况 |
|
|
18
|
+
| --- | --- | --- | --- | --- |
|
|
19
|
+
| 审批列表页面 | 两个页签、筛选布局、CanvasTable 列、虚拟滚动和状态反馈 | 安装组件、导入 CSS、挂载到路由 | 无 | 已实现 |
|
|
20
|
+
| 基础控件 | 固定页签/筛选功能、受控值、日期校验、查询/重置/刷新/重试逻辑 | 必传 `components`:Input、Select、DatePicker、Button、Tabs;适配事件并继承宿主组件库/主题 | 无 | 已实现,无原生控件兜底 |
|
|
21
|
+
| 列表查询 | 调用 `provider.loadPage`,管理滚动事件、数据页请求、取消、缓存、防旧结果覆盖与重试 | UI 复用统一请求层访问 App Service;Service 转换接口参数和结果 | 当前用户范围、页签归类、筛选、排序、分页和总数 | 包接口已实现,真实接口待确认 |
|
|
22
|
+
| 应用与身份隔离 | `scopeKey` 改变时取消旧请求,重置数据/页签/筛选 | 提供包含应用、身份会话、环境的稳定上下文标识;退出时卸载 | 验证实际身份和 App 访问权限 | 已实现 |
|
|
23
|
+
| 流程状态 | 已确认状态码的统一文字;未知码显示“状态未知” | 提供原始流程状态;只传后端支持的状态筛选项 | 返回状态码,不以个人任务状态替代流程状态 | 已实现;CANCELLED 显示已撤回 |
|
|
24
|
+
| 打开详情 | 行头查看入口、键盘可达的文字入口、防连续重复打开、失败提示 | `onOpenApproval(row)` 接入宿主路由/抽屉 | 详情数据与操作授权 | 列表提供回调;宿主可在容器内挂载 /detail 的 ApprovalTaskDetail |
|
|
25
|
+
| 办理后刷新 | `refreshKey` 改变后重新查询当前条件;总数减少时限制到有效滚动范围 | 动作成功后增加刷新标识,并刷新相关业务详情/列表;仅关闭详情不触发刷新 | 返回动作结果和最新列表 | 列表刷新和任务动作编排已实现,真实联调未完成 |
|
|
26
|
+
| 导航 | 不创建菜单、不检查宿主 IAM 菜单权限 | 增加“我的审批”菜单和路由,依接入约定处理可见性;不另加 IAM 菜单授权 | App 访问权限、当前用户数据范围 | 宿主接入项 |
|
|
27
|
+
| 普通记录禁编 | `getRecordApprovalWriteRestriction` 返回限制原因 | 在编辑入口、URL、表格快捷编辑、批量操作、保存前落实限制;仍检查原操作/行级权限 | 锁与状态、最终写入校验 | 工具已实现,入口接线属于宿主 |
|
|
28
|
+
| 审批新建表单 | `hasApprovalDefinition` 识别标识 | Schema 有 key 时获取 `bpm-start-config.fields`,UI/Service 均切换审批字段规则;无 key 走原规则 | 只对有效且启用流程返回 key,返回完整字段及授权 | 标识工具已实现;ExpensePoc 新建表单代码已接入,真实发起未联调 |
|
|
29
|
+
| 审批办理详情与操作 | 统一详情内容、字段权限、意见与操作交互 | 业务表单控件、请求层、路由及动作成功后的关联刷新 | 节点字段、允许操作、并发/最终授权 | /detail 已提供,真实接口未联调 |
|
|
30
|
+
| 业务详情的审批流程表 | 后续统一表格展示 | 传入业务对象/记录上下文,通过 Service 查询 | 一个统一接口返回完整表格行及开始/结束时间 | 按用户约定暂缓 |
|
|
31
|
+
| 移动端 | 后续移动审批列表/详情,复用同一数据约定 | 现有 make-app-mobile 导航与业务表单 | 同一审批能力 | 移动审批页面尚未交付;窄屏换行不代表移动验收完成 |
|
|
32
|
+
| 接入 Skill | 当前提供 API、职责、示例和验收步骤 | 按文档接入与验证 | 无 | 审批专项 Skill 在首次联调和另一宿主验证后固化 |
|
|
33
|
+
|
|
34
|
+
## 宿主最小接入步骤
|
|
35
|
+
|
|
36
|
+
1. 安装审批包和 CanvasTable。未发布前使用本项目生成的 `.tgz`,不能直接安装不存在的 npm 版本。UI 从 npmjs 安装 `@qfei-design/canvas-table@1.3.1`;宿主默认使用公司安装源时,可在自己的 `.npmrc` 添加 `@qfei-design:registry=https://registry.npmjs.org/`。只有根入口工具需求的 Service 不必安装 CanvasTable。
|
|
37
|
+
2. 在 UI 入口导入一次 `@qfei-design/make-app-approval/styles.css`。
|
|
38
|
+
3. 在现有 App Service 增加审批查询适配,再通过 UI 已有鉴权/Trace/错误处理请求层调用它。不要在包里放 Token、环境 URL,也不要绕过 Service 直连工作流后端。
|
|
39
|
+
4. 提供 `provider.loadPage(query, { signal })`,返回标准 `ApprovalPage`。把取消信号传入既有 UI 请求链路;Service 按自身规范继续处理取消。
|
|
40
|
+
5. 以宿主组件库实现 `ApprovalUIComponents`,挂载 `MyApprovalsPage` 时传入 `components`、`scopeKey` 和 `onOpenApproval`。包内部已封装表格和筛选逻辑,宿主只提供组件适配,不重写查询功能。Antd 宿主可复制 `examples/antd-components.tsx`。
|
|
41
|
+
6. 真实办理成功后更新 `refreshKey`,并由宿主刷新其他相关业务页面。失败时不伪造成功状态、不自行将行搬到“已审批”。
|
|
42
|
+
7. 验证文末场景,之后才开放真实导航。
|
|
43
|
+
|
|
44
|
+
完整可类型检查示例见 [examples/host-integration.tsx](../examples/host-integration.tsx)。文件中的 `ApprovalHostService` 是宿主需实现的接口,不是包提供的网络客户端;它没有预设任何尚未确认的 HTTP 路径。
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
import { MyApprovalsPage } from "@qfei-design/make-app-approval/list";
|
|
48
|
+
import type { ApprovalListProvider } from "@qfei-design/make-app-approval/list";
|
|
49
|
+
import "@qfei-design/make-app-approval/styles.css";
|
|
50
|
+
// 从随包 examples/antd-components.tsx 复制到宿主;其他组件库提供相同合同。
|
|
51
|
+
import { antdApprovalComponents } from "./approval/antd-components";
|
|
52
|
+
|
|
53
|
+
// service 是宿主既有请求层;应稳定维护 provider,而不是在这里写 fetch 或 Token。
|
|
54
|
+
const provider: ApprovalListProvider = {
|
|
55
|
+
loadPage: (query, { signal }) => service.queryApprovals(appKey, query, signal),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
<MyApprovalsPage
|
|
59
|
+
components={antdApprovalComponents}
|
|
60
|
+
scopeKey={JSON.stringify([appKey, sessionScope, environment])}
|
|
61
|
+
provider={provider}
|
|
62
|
+
onOpenApproval={openApprovalDetail}
|
|
63
|
+
refreshKey={approvalRevision}
|
|
64
|
+
statusOptions={supportedStatusOptions}
|
|
65
|
+
/>;
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
示例中的 `service`、应用上下文、详情回调、刷新标识和筛选项都由宿主提供。`scopeKey` 不会作为鉴权凭据或自动请求参数发送给后端;后端必须验证真实会话。不要把 Token 放入 `scopeKey`。当前宿主上下文变化要同时更新 provider 与 scopeKey,不能只换 provider。
|
|
69
|
+
|
|
70
|
+
## 组件与主题如何对接
|
|
71
|
+
|
|
72
|
+
控件合同见 [PUBLIC_API.md](../PUBLIC_API.md#宿主基础组件合同),可运行 Antd 示例见 [examples/antd-components.tsx](../examples/antd-components.tsx)。本仓库以 Antd 6.3.2 / dayjs 1.11.20 验证示例;它们只作为开发依赖,不进入审批包运行时依赖。其他组件库宿主无需安装 Antd。
|
|
73
|
+
|
|
74
|
+
- 输入、状态和日期都接收包提供的受控值,并通过 `onChange(value)` 回传;不要把 DOM Event、Dayjs 或组件库的选项对象直接回传给包。Antd Input 取 `event.target.value`,DatePicker 取 `date.format("YYYY-MM-DD")`,清空均回传空字符串。
|
|
75
|
+
- 包负责筛选草稿与已查询条件、查询/重置、日期合法性及起止校验、刷新/重试和虚拟滚动;宿主控件不得另起请求或根据流程状态归类记录。
|
|
76
|
+
- 所有按钮包括查询、重置、刷新、首屏/后续页/表格重试和键盘详情入口都使用注入的 Button。查询映射为 `htmlType="submit"`,其他按钮为 `button`,保证键盘提交与操作不会混淆。
|
|
77
|
+
- Tabs 只渲染一次活动内容;不得在每个面板同时挂载 children,否则会创建多个表格。键盘、焦点、tab 与 panel 关联沿用宿主组件库。
|
|
78
|
+
- 组件适配器定义在模块外,继承宿主既有 ConfigProvider/主题上下文。包不新建第二个主题 Provider,不覆盖控件的边框、圆角、品牌色、高度、焦点和弹层样式。包只持有筛选布局及 CanvasTable 样式;如需对齐 Canvas 和容器色,可从宿主 token 映射 `--make-approval-*`,动态改变后传 `themeKey`。
|
|
79
|
+
- 未发布版本升级后,原列表调用方必须补传完整 `components`。缺失时明确报错,不再静默使用原生 HTML 控件。根入口工具和状态组件不受影响;当前 ExpensePoc 仅消费基础能力,尚未挂载该列表。
|
|
80
|
+
|
|
81
|
+
## 数据如何对接
|
|
82
|
+
|
|
83
|
+
调用链固定为:`MyApprovalsPage → provider → 宿主 UI 请求层 → App Service → BPM 后端`。
|
|
84
|
+
|
|
85
|
+
`ApprovalListQuery` 是包侧标准参数,不等于后端 DTO:
|
|
86
|
+
|
|
87
|
+
| 字段 | 包传出的含义 | 宿主需要做的适配 |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| `tab` | `pending` 待我处理;`approved` 已审批 | 转成后端确认的页签参数;不要拼两次单状态查询后分页 |
|
|
90
|
+
| `keyword` | 已去掉首尾空格的申请单号/申请人关键字;空串不限 | 转成后端查询字段,交后端搜索 |
|
|
91
|
+
| `processStatus` | 用户所选原始状态码;空串不限 | 原样传给支持该筛选的后端,不自行重分类 |
|
|
92
|
+
| `startDate` / `endDate` | `YYYY-MM-DD`,空串不限 | 按应用时区转换边界;结束日期表示包含当日,必要时转为下一日的排他上界 |
|
|
93
|
+
| `page` / `pageSize` | 接口数据页从 1 开始;默认每批 20,支持 1–200;无翻页 UI | 若后端从 0 开始或用 offset,仅在 Service 适配一次 |
|
|
94
|
+
|
|
95
|
+
`ApprovalPage` 返回 `{ rows, total }`。`rows` 是当前一页,`total` 是后端按相同用户/页签/筛选计算的全部匹配数,不能用本页长度冒充。后端负责稳定排序。
|
|
96
|
+
|
|
97
|
+
每行 `ApprovalListItem`:
|
|
98
|
+
|
|
99
|
+
| 字段 | 是否必填 | 用途 |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| `id` | 是,非空字符串 | 后端稳定行标识,同一页唯一;不能用行下标或申请单号代替 |
|
|
102
|
+
| `entityKey` / `recordId` | 是,非空字符串 | 定位业务对象和记录;统一使用 `recordId`,宿主原来的 `recordID` 在适配时转换 |
|
|
103
|
+
| `taskId` | 否;传入时非空 | 后端提供任务标识时保留,待办详情/办理通常需要 |
|
|
104
|
+
| `processInstanceId` | 否;传入时非空 | 后端有实例标识时保留;不为当前列表强制新增业务记录定位实例接口 |
|
|
105
|
+
| `applicationNo` / `applicationType` / `applicantName` | 是,字符串 | 三列展示值,缺少展示值可用空串,但不编造编号 |
|
|
106
|
+
| `applicationTime` | 是,字符串 | 宿主按统一时区格式化后的文字;包不猜测时间戳单位或隐式使用浏览器时区 |
|
|
107
|
+
| `processStatus` | 是,原始流程状态字符串 | 我的审批必须提供真实流程状态;兼容输入 null/UNSUBMITTED 时保留原行并显示“状态未知”,不显示短横线,也不作为合法审批结果 |
|
|
108
|
+
|
|
109
|
+
列表会校验行标识、分页数量和必需字段。结构不合格时显示加载失败,而不是把有问题的数据静默当成空列表。类型转换和响应外层解包由宿主适配负责。保持后端返回顺序;禁止客户端去重后改总数、按状态二次过滤或合并分页。
|
|
110
|
+
|
|
111
|
+
## 滚动加载由包处理,宿主只提供数据
|
|
112
|
+
|
|
113
|
+
1. 包先调用一次 `loadPage({ page: 1, ... }, { signal })`,取得首批记录和真实 `total`。无需新增独立 count 接口。
|
|
114
|
+
2. 总数已知后才创建虚拟表格,设置 `virtualOptions.enabled/totalRowCount/pageSize/maxCachedRows`。首批数据保存在一次性缓存,等 Canvas 发出自己的第 0 页请求并认领身份后回填,不重复查首屏。
|
|
115
|
+
3. 后续 Canvas 发出 `data:load(tablePage, request)`,包同步认领请求;将零基 `tablePage` 转为宿主一基 `page`,调用同一个 provider。宿主无需订阅 Canvas 事件或持有表格实例。
|
|
116
|
+
4. 快速滚动先合并 120ms 内的请求,仅保留视口及相邻一页范围内的排队请求,最多同时请求 2 页;页面离开视口或上下文失效时取消对应请求。取消信号组合传给 provider,忽略迟到结果。
|
|
117
|
+
5. 返回数据与总数通过 `setVirtualPageData(rows, tablePage, total, request)` 原子提交。失败、取消和越界页均释放对应请求身份,防止某一批一直处于加载中。
|
|
118
|
+
6. Canvas 缓存按完整页向上取整,至少约 300 行,并根据视口高度为可见页和预加载页留出容量;尺寸更新后通过公开 `scroll:change` 同步当前视口,补齐新增可见页。回滚到已淘汰的数据时重新请求。行快照读取 `getTableData()` 的实际缓存,用于打开详情和键盘文字入口,不另设淘汰顺序,也不当成全量数据。详情回调仍使用宿主原始记录,不附加内部展示字段。
|
|
119
|
+
7. 首屏失败显示整页重试;普通后续批次失败保留已加载记录,显示“部分审批记录加载失败”,重试时通过公开 `scroll:change` 事件同步当前位置,等待表格发出缺页的新请求身份,不调用会回到顶部的 `refreshCanvas()`。取消不显示为错误。
|
|
120
|
+
8. 动态数据导致跨页记录交叠等缓存校验失败时,停止当前代次的排队和在途请求,忽略迟到结果,并暂时移除详情入口;滚动不会清除这类失败提示。重试沿用列表刷新流程,重新取得首批数据与当前视口所需页,保留查询条件和有效范围内的滚动位置。宿主无需增加接口或事件处理。
|
|
121
|
+
|
|
122
|
+
接口中的 `page/pageSize` 是传输协议。它们不代表宿主或包要展示分页控件,也不需要宿主实现“加载下一页”按钮。后端继续提供稳定排序、同条件总数和按页查询能力。
|
|
123
|
+
|
|
124
|
+
## 状态、身份与权限边界
|
|
125
|
+
|
|
126
|
+
- “待我处理”只包含后端返回的当前用户真实待办。
|
|
127
|
+
- 本人同意而下一审批人尚未处理时,这条先离开待办;整个流程结束后才进入已审批。包不会用 `processStatus` 或 `taskId` 计算所属页签。
|
|
128
|
+
- 列表不合并、不去重、不按状态过滤返回行。“我的审批”不显示“—”,此前原型中的短横杠已由用户确认为图片错误;业务对象未配置审批时仍可显示“—”。
|
|
129
|
+
- 新建表单保存必须检查必填,失败时标红并阻止保存。当前用户是发起人且后端流程状态为审批中时显示撤回,其他状态隐藏;无需额外的允许撤回标识。撤回接口已补充:bpm-process + UpdateResource,必填 bpmKey、recordId、comment,成功状态 CANCELLED;宿主撤回入口等待发起人和流程 key 来源确认,最终身份、状态及并发校验由后端负责。
|
|
130
|
+
- 筛选选项来自 `statusOptions`,只传后端支持的真实状态码。该属性只定义筛选项,不覆盖包的状态显示规则;新增流程状态应在确认后统一更新公共包。
|
|
131
|
+
- `bpmDefinitionKey` 有值即有效且启用;无值使用普通业务规则。审批新建字段源是 `bpm-start-config.fields`,不可再与普通 IAM 字段取交集;对象/操作/行级权限仍保留。
|
|
132
|
+
- 普通业务禁编与审批任务中的授权编辑不同。不得通过解锁普通记录来实现审批办理。
|
|
133
|
+
- 包不提供登录、不持有 Token、不订阅权限缓存、不把“显示按钮”当作最终授权。Service/后端保留所有必要校验。
|
|
134
|
+
|
|
135
|
+
## 生命周期、样式和异常
|
|
136
|
+
|
|
137
|
+
- provider 和打开详情回调变化不会自动重查或重建表格。应用/身份/环境变化更新 `scopeKey`;同一上下文数据变化更新 `refreshKey` 或点包内“刷新”,重新取总数和当前视口数据并恢复原滚动位置;若总数减少则定位到有效范围。
|
|
138
|
+
- 切页签保留已应用的筛选,清空缓存并回到顶部重新加载;筛选输入是草稿,点“查询”才请求;“重置”清空筛选、保留当前页签。
|
|
139
|
+
- 新请求会取消旧请求。即便宿主未及时响应取消,旧结果也不能写回。卸载取消请求并销毁 CanvasTable。
|
|
140
|
+
- `onOpenApproval` 可返回 Promise;异步打开期间忽略重复点击。拒绝时包显示通用失败提示;宿主负责错误细节、登录失效处理和全局 Trace。
|
|
141
|
+
- 列表按整份查询结果显示连续序号,只读,无普通记录快捷编辑、删除、批量编辑或前端排序入口。
|
|
142
|
+
- CSS 局部作用于 `.make-app-approval`;复用宿主 `--background`、`--foreground`、`--primary`、`--border` 等变量。可在 `.make-app-approval` 上覆盖 `--make-approval-bg`、`--make-approval-header`、`--make-approval-border`、`--make-approval-foreground`、`--make-approval-primary`、`--make-approval-primary-foreground`、`--make-approval-muted`、`--make-approval-radius`、`--make-approval-table-height`。表格默认高 440px,宿主容器必须有可测量宽度;动态主题变化传新的 `themeKey`,Canvas 重读主题。
|
|
143
|
+
- 包内提供局部日志 `logger(event)`,内容只有操作、阶段、页码、每页数、行数,不包含账号、申请人、关键词、记录内容或原始错误。默认输出安全控制台日志;宿主可转接现有日志系统。
|
|
144
|
+
- `/list` 用于浏览器挂载,CanvasTable 挂载时才加载;根入口与 `/react` 状态组件保留无 Canvas 依赖与 SSR 能力。SSR 框架应按自身客户端组件机制加载列表;不要在 Service 中挂载 UI。
|
|
145
|
+
- CanvasTable 为可选 peer,仅“根入口工具消费者”可以不安装;使用列表的宿主必须安装。打包不内嵌第二份 CanvasTable 或 React。
|
|
146
|
+
|
|
147
|
+
## 验收清单
|
|
148
|
+
|
|
149
|
+
- 普通 Make App 提供 components、provider、路由、详情回调和样式导入后即可显示统一列表,无需宿主私有列配置。
|
|
150
|
+
- 宿主注入的 Input/Select/DatePicker/Button/Tabs 实际生效;主题、禁用、焦点与弹层样式跟随宿主,输入/清空/Enter 查询/日期倒置/重置行为保持一致。
|
|
151
|
+
- 待我处理、已审批请求参数正确,前端显示后端原始顺序和总数;滚动加载后续批次,查询/重置回到顶部。
|
|
152
|
+
- 切换页签、应用、账号、环境及卸载时没有旧数据回流;详情开关不重复加载,办理成功才刷新。
|
|
153
|
+
- 状态/日期/关键字由 Service 透传正确;空数据保留表头;查询失败能重试,401/403 不能包装成成功空列表。
|
|
154
|
+
- 行头打开详情能定位正确记录和任务;键盘可到达等价文字入口;超长列文本省略;列表不可快捷编辑。
|
|
155
|
+
- 共享 CanvasTable 与 React 版本,根入口能在 Service 导入;冻结安装、测试、构建、类型检查通过。
|
|
156
|
+
- 真正的待办办理、流程表、移动页面、审批新建字段切换须按各自阶段单独验收,不能由列表示例通过推断完成。
|
|
157
|
+
|
|
158
|
+
## 已确认的 Workflow 接入约定
|
|
159
|
+
|
|
160
|
+
2026-10-16 用户转述后端确认:上游地址统一为网关 origin + `/make/workflow/v1/`。启动配置使用 `POST /make/workflow/v1/bpm-start-config`、`X-Make-Target: MakeService.GetResource` 和 `{ appKey, entityKey }`;首次发起、任务及其他 Workflow 资源共用此前缀,各自的 Target 与参数仍按正式接口文档。
|
|
161
|
+
|
|
162
|
+
`fields[].field` 与 Schema 一样包含完整元数据,截图只是简写。宿主保留类型、必填、校验、选项、关联等信息,复用现有字段渲染器;`readable` 决定展示,`editable` 决定编辑及提交,不能再与普通 IAM 字段集合求交。Service 保存校验也使用该来源,保留对象/操作资格及平台最终授权。字段配置失败时阻止提交,不降级为普通字段。
|
|
163
|
+
|
|
164
|
+
宿主 UI 经已有请求层访问本应用 Service;Service 复用网关配置、登录上下文、Trace 与取消信号,提供包所需的 provider。包继续提供统一 UI、状态展示、CanvasTable 滚动加载及请求调度,不增加直接访问 Workflow 的网络客户端。已确认的地址和字段完整性不再列为开发阻塞;实际发起、字段保存及任务查询仍需分别联调。
|
|
165
|
+
|
|
166
|
+
## 接入真实后端前还缺什么
|
|
167
|
+
|
|
168
|
+
1. “我的审批”两个页签的最终参数、统一总数、稳定行标识、申请单编号、申请人、流程状态及筛选能力。
|
|
169
|
+
2. BPM 任务列表的最终返回;访问前缀及 CANCELLED 撤回状态已确认。
|
|
170
|
+
3. 业务详情流程表与单独保存接口待补;撤回接口已提供,宿主入口的数据来源及撤回后编辑规则待确认。
|
|
171
|
+
|
|
172
|
+
以上属于真实接口联调前置条件。当前 provider 数据约定可以先在包和宿主之间实现,但不能冒充已经确认的后端接口。
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
2026-10-16 实测补充:部署后的内部网关仍用 `/make/workflow/v1/`;本地预览访问公网网关需 `/api/make/workflow/v1/`。dev 公网无 `/api` 的地址返回 HTML,加 `/api` 后返回正式业务 JSON;此差异由宿主 Service 适配,公共包不持有地址或凭据。dev beta 的 `ExpensePoc_beta_`/`expense_claim` 在用户补充配置后已返回 bpmDefinitionKey,启动配置成功,但 fields 为空数组;宿主新建页已进入审批分支并禁用保存,等待确认发起节点字段配置,真实发起仍待联调。
|
|
176
|
+
|
|
177
|
+
## 审批详情接入(本轮新增)
|
|
178
|
+
|
|
179
|
+
宿主列表的 `onOpenApproval(row)` 打开既有抽屉,使用 row 的 `processInstanceId/taskId` 挂载 `ApprovalTaskDetail`。两者缺失时必须显示详情不可用,不拿 recordId 替代。包不根据普通记录编辑禁令禁用节点允许的任务编辑;提交只走任务动作接口。
|
|
180
|
+
|
|
181
|
+
| 能力 | 包提供 | 宿主提供 |
|
|
182
|
+
| --- | --- | --- |
|
|
183
|
+
| 节点授权 | 仅展示 actions 中的操作;按 viewPermission/editPermission 限制字段和提交内容 | 读取当前任务详情,以完整字段元数据补齐 fields;不拿普通 IAM 字段取交集 |
|
|
184
|
+
| 业务表单 | 逐字段调用 renderField,提供值、只读、禁用、错误、受控 onChange;隐藏字段不调用 | 复用字段控件与格式化;只读字段不得提供编辑,关联与附件保留自身请求和序列化链路 |
|
|
185
|
+
| 基础控件 | 管理操作选择、确认、意见与退回目标 | 必传 Button/Select/TextArea,复用宿主主题;Antd 示例已有适配 |
|
|
186
|
+
| 校验 | 三个操作统一只校验可编辑且必填的字段,先检查空值并标红;意见在退回/拒绝时必填、同意时选填;退回目标按 returnTargets | 必传 validateFields,仅校验包传入的字段,不追加只读、隐藏或非必填字段 |
|
|
187
|
+
| 权限变化 | 提交前重读节点配置;变更后清空旧修改并更新按钮/字段 | 保留请求取消、鉴权、Trace;后端执行最终授权和并发校验 |
|
|
188
|
+
| 可见内容变化 | 停止此次提交,展示最新值并重新确认;保留未冲突草稿,清除冲突草稿并提示 | 保持详情读取的数据独立且不可变;提交接口负责最终并发校验,前端重读不能替代事务或版本校验 |
|
|
189
|
+
| 操作结果 | 双击保护、发送前确认、失败后重新加载核查、成功后锁定当前办理界面 | submitAction 访问任务动作接口;onActionSuccess 刷新列表及关联业务页 |
|
|
190
|
+
|
|
191
|
+
详情组件不包含“单独保存”、撤回或真实流程历史查询。流程历史和单独保存等待接口;申请人撤回由宿主业务详情接入已补充的接口。审批意见必填与业务字段必填是两个独立规则:退回/拒绝意见必填,同意意见选填,SUBMIT 沿用 commentPolicy;同意、拒绝、退回修改的业务字段均按下表执行。
|
|
192
|
+
|
|
193
|
+
代码示例见 `examples/detail-integration.tsx` 与 `examples/antd-components.tsx`。`loadDetail` 统一返回包的字段描述和授权结构;公开结构并不是直接照搬后端 DTO,尤其 fields 需要宿主补齐。
|
|
194
|
+
|
|
195
|
+
`validateFields` 只接收可编辑且必填的字段描述及这些字段的当前值,没有需要校验的字段时不调用。发生网络/权限读取失败时不降级为普通字段。动作成功回调失败只提示刷新未完成,不把成功操作当成失败重发。切换 scopeKey 或任务标识时卸载旧表单并取消未完成请求;取消不能撤销后端已接受的操作。
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
### 详情字段校验与布局(2026-10-16 确认)
|
|
199
|
+
|
|
200
|
+
| 当前节点可编辑 | 字段必填 | 同意 / 拒绝 / 退回修改 |
|
|
201
|
+
| --- | --- | --- |
|
|
202
|
+
| 是 | 是 | 校验,未通过则标红并阻止动作 |
|
|
203
|
+
| 是 | 否 | 不校验;用户的修改仍按授权提交 |
|
|
204
|
+
| 否 | 任意 | 不校验,仅展示 |
|
|
205
|
+
|
|
206
|
+
包在重读最新授权后检查空值(null、undefined、空字符串、纯空白字符串、空数组);0 和 false 不是空值。空值检查通过后调用宿主 validateFields,以便复用必填字段的类型、范围等校验。禁止从全量表单补充非必填字段校验。字段与意见校验均不会调用普通 CRUD。
|
|
207
|
+
|
|
208
|
+
`renderLayout({ title, actions, content, footer })` 用于接入宿主抽屉:title 为“审批详情”及流程状态,actions 为当前节点授权按钮,content 为申请信息、可选流程区域以及随正文滚动的审批意见和动作确认。footer 为兼容旧接线保留且始终为空。将 actions 放在关闭 X 左侧,不设置固定底栏;不要再复制一套按钮或意见输入框。加载和失败时也会调用布局函数,首次加载完成不会重建宿主容器。
|
|
209
|
+
|
|
210
|
+
未传 renderLayout 时使用包内普通页面布局。Antd 6 宿主示例 `AntdApprovalDetailDrawer` 使用 `extra={actions}`、`closable={{ placement: "end" }}`,不传 Drawer footer,颜色来自宿主 ConfigProvider;其他组件库实现同样的布局槽即可。申请信息使用两列标签/值与分隔线,窄屏降为一列;基础控件仍由宿主注入。
|
|
211
|
+
|
|
212
|
+
审批意见位于可办理详情正文中,随内容滚动,切换动作不清空;退回/拒绝时必填、同意时选填。字段名后用红色星号表示必填,只读必填字段也保留标识但不参与校验;意见的红星随当前动作切换,不再显示“(必填)”文字。点击顶部操作聚焦意见输入框,宿主 TextArea 必须透传 id。保留动作确认及退回目标选择。`history?: ReactNode` 只接收宿主已取得的流程展示内容,省略时不显示流程区域;不得将普通任务时间线拼成尚未确认的表格接口。demo 的 CanvasTable 流程表仅为模拟样式,不随包导出、不访问真实后端。
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
### 2026-10-16 撤回接口更新
|
|
216
|
+
|
|
217
|
+
Workflow 已提供 `POST /make/workflow/v1/bpm-process`,target `MakeService.UpdateResource`,请求 `appKey/entityKey/bpmKey/recordId/comment`;comment trim 后必填且不超过 1024 字。成功返回 `CANCELLED`,业务记录状态和锁由后端 consumer 异步更新。公共包已补状态与意见校验;宿主 Service 保留原始状态、Content-Type 和正文,不代替后端判断能否撤回。不要将请求成功直接等同于业务记录已解锁,也不要自动重发失败的撤回请求。
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# 我的审批公共包实施清单
|
|
2
|
+
|
|
3
|
+
## 已完成的基础
|
|
4
|
+
|
|
5
|
+
独立项目、流程状态展示、普通编辑限制、CanvasTable 桌面审批列表及 provider、独立构建与类型声明、CSS、打包产物检查、接入说明和 GitLab CI 模板。列表具有页签/筛选/虚拟滚动/分批加载/重试/取消旧请求/详情回调。职责和宿主模板见 [host-integration.md](host-integration.md)。ExpensePoc 目前仍消费上一阶段打包文件,本次尚未升级该宿主。
|
|
6
|
+
|
|
7
|
+
列表可使用模拟 provider 验证;审批详情与节点授权动作组件已实现;流程表和移动页面未完成,真实 BPM 接口未联调。
|
|
8
|
+
|
|
9
|
+
## 后续按以下顺序开发
|
|
10
|
+
|
|
11
|
+
| 模块 | 包内工作 | 宿主/后端配合 | 是否需要等待 |
|
|
12
|
+
| --- | --- | --- | --- |
|
|
13
|
+
| 我的审批列表 | 已实现待我处理/已审批页签、申请单号/申请人搜索、状态和日期筛选、CanvasTable 虚拟滚动、分批加载、加载/空/错误/重试 | 后端提供当前用户范围内两类列表、统一分页总数、申请单号与查询能力;宿主 provider 通过 Service 获取 | UI 已完成本轮实现,真实对接等待最终接口约定 |
|
|
14
|
+
| 办理详情 | 已实现公共详情内容、逐字段宿主插槽、节点字段/动作授权、意见、退回目标、确认、提交中与失败处理 | 已有任务详情/动作接口需核对真实响应;字段由任务节点配置授权;单独保存仍待接口;撤回已提供,宿主入口数据来源待确认 | 组件与已知动作已实现;真实联调及单独保存待接口;撤回底层合同已补 |
|
|
15
|
+
| 审批流程表 | 节点、开始/结束时间、处理人、结果、备注 | 用户已明确后端会提供业务记录对应的统一表格接口 | 按约定暂缓,不拼时间线和当前任务代替 |
|
|
16
|
+
| 移动端 | 与 PC 共用状态、请求和审批规则,提供移动布局及操作区 | 复用 make-app-mobile,宿主提供移动导航上下文 | 当前未实现,后续单独验收 |
|
|
17
|
+
| 应用接入 | 稳定的公共组件、数据接口与业务表单插槽 | 宿主提供“我的审批”导航、路由和 Service;无需为该导航另加 IAM 权限判断,显示边界按最终接入约定落实 | 基础可先对接,完整页面待上面模块 |
|
|
18
|
+
| Skill 标准 | 包版本、CSS、provider、表单、导航、异常与验收步骤 | ExpensePoc 首次联调后,用另一应用验证;补审批专项 Skill 与既有 Skill 引用 | 先记录边界,联调后固化可执行规范 |
|
|
19
|
+
|
|
20
|
+
## 基础控件接入已实现
|
|
21
|
+
|
|
22
|
+
宿主必须传入 `components`(Input、Select、DatePicker、Button、Tabs)。包固定筛选/校验/查询/重置/刷新/重试与 CanvasTable 滚动功能,宿主仅适配组件值和事件、提供主题。没有原生控件兜底;Antd 示例随包交付,其他组件库按相同合同对接。此前列表接入方式需补传此属性,根入口工具不受影响。
|
|
23
|
+
|
|
24
|
+
## 已确认的后端责任
|
|
25
|
+
|
|
26
|
+
- 待我处理只返回当前用户真实待办。
|
|
27
|
+
- 本人点同意后,下一位仍未处理时,该条先移出待我处理;整个流程结束后才进入已审批。
|
|
28
|
+
- 后端在排序、分页和总数计算前完成归类。公共包与 App Service 不自行合并 `COMPLETED/CLOSED` 分页,不按个人任务完成推断流程结束。
|
|
29
|
+
- 前端展示后端状态码,不补造已撤回状态。
|
|
30
|
+
- 列表逐条展示后端结果,不合并、不去重、不按业务状态过滤;同一申请对应多条任务时也不由前端调整行数。
|
|
31
|
+
- 用户已纠正原型:“我的审批”流程状态不出现“—”;未配置审批对象在业务列表/详情中的流程状态可以显示“—”。审批列表状态缺失不能按普通业务记录的空状态处理。
|
|
32
|
+
- 新建表单点击“保存”也必须校验必填,未通过时标红并阻止保存;不能以保存草稿为由跳过校验。
|
|
33
|
+
- 当前用户是发起人且后端流程状态为审批中时显示撤回,其他状态隐藏;不再要求后端另加允许撤回标识。审批中禁止普通编辑;撤回接口已补充;宿主入口等待发起人、流程 key 来源及撤回后编辑规则确认。
|
|
34
|
+
- Schema 有 `bpmDefinitionKey` 就表示有效且启用;不新增前端有效性判定。
|
|
35
|
+
- 全部 Workflow 接口使用网关 origin + `/make/workflow/v1/`。启动配置为 `/make/workflow/v1/bpm-start-config`,首次发起为 `/make/workflow/v1/bpm-process`;宿主 Service 承接请求,包不直接调用网关。
|
|
36
|
+
- `bpm-start-config.fields[].field` 与 Schema 一样包含完整字段元数据,示例只是简写。保留类型、必填、校验、选项、关联等返回信息,按 `readable/editable` 展示和编辑;UI 与 Service 保存校验均切换审批字段来源,不与普通 IAM 字段再取交集。此信息已足够开始发起侧开发,ExpensePoc 发起侧代码已接入,等待真实启用对象联调。
|
|
37
|
+
|
|
38
|
+
## 目前待提供的信息
|
|
39
|
+
|
|
40
|
+
1. “我的审批”列表最终合同:两类页签参数、分页总数、申请单号和申请人、状态码、关键字和日期筛选。现有只按单个任务状态查询不足以表达已确认规则。
|
|
41
|
+
2. 后端后续提供的详情流程表和单独保存接口;撤回所需的宿主身份/key 来源及撤回后编辑规则。
|
|
42
|
+
|
|
43
|
+
2026-10-16 早期核对时仅列出 APPROVE、REJECT、RETURN、SUBMIT;现已由下文“撤回接口更新”确认撤回请求和 CANCELLED 业务状态。本地没有 Workflow 运行时代码,不能据此断言服务端未实现。本次已修正列表空状态:null/UNSUBMITTED 展示为状态未知,保留原行;不影响业务记录的短横线。
|
|
44
|
+
|
|
45
|
+
已导出的 provider 是包与宿主的标准数据约定,不是后端原始 DTO;实际字段映射由宿主根据确认后的接口完成。地址与启动字段完整性已经确认,不能继续列为缺失信息;也不能据此把模拟数据验证表述为真实接口联调通过。
|
|
46
|
+
|
|
47
|
+
## 发布前还需完成
|
|
48
|
+
|
|
49
|
+
本项目已建立远端仓库并配置本地 origin 地址。首次发布前仍需接入 CI 安装/发布权限、完成页面功能和真实接口联调、验证 PC/移动体验、验证另一宿主接入,再发布适当版本。仓库中的 CI 文件仅是配置,远端流水线和 npm 发布结果需以实际运行记录为准。
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
2026-10-16 实测补充:部署后的内部网关仍用 `/make/workflow/v1/`;本地预览访问公网网关需 `/api/make/workflow/v1/`。dev 公网无 `/api` 的地址返回 HTML,加 `/api` 后返回正式业务 JSON;此差异由宿主 Service 适配,公共包不持有地址或凭据。dev beta 的 `ExpensePoc_beta_`/`expense_claim` 在用户补充配置后已返回 bpmDefinitionKey,启动配置成功,但 fields 为空数组;宿主新建页已进入审批分支并禁用保存,等待确认发起节点字段配置,真实发起仍待联调。
|
|
53
|
+
|
|
54
|
+
## 详情展示与校验进展(2026-10-16)
|
|
55
|
+
|
|
56
|
+
已按用户确认完成同意、拒绝、退回的统一可编辑必填校验;新增宿主布局槽、顶部动作与正文内滚动意见、两列申请信息、可选流程内容插槽。必填字段名称后统一使用红色星号,退回/拒绝意见必填、同意意见选填。演示流程表使用 CanvasTable 模拟数据,真实历史查询依旧等待后端。审批公共包本轮未升级至 ExpensePoc,也未发布 npm。
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
### 2026-10-16 撤回接口更新
|
|
60
|
+
|
|
61
|
+
Workflow 已提供 `POST /make/workflow/v1/bpm-process`,target `MakeService.UpdateResource`,请求 `appKey/entityKey/bpmKey/recordId/comment`;comment trim 后必填且不超过 1024 字。成功返回 `CANCELLED`,业务记录状态和锁由后端 consumer 异步更新。公共包已补状态与意见校验;宿主 Service 保留原始状态、Content-Type 和正文,不代替后端判断能否撤回。不要将请求成功直接等同于业务记录已解锁,也不要自动重发失败的撤回请求。
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# 发布规范
|
|
2
|
+
|
|
3
|
+
本包采用 `@qfei/component-release-tools@^0.5.2` 的单包发布流程,以
|
|
4
|
+
`make-app-observability` 的 `e9ed50a00aa2fd467dafbbc4c84577689c400816`
|
|
5
|
+
版本为对齐基准。`.gitlab-ci.yml` 与该基准逐字节一致,
|
|
6
|
+
`package.json.qfeiRelease` 配置一致。按当前需求,`.npmrc` 在标准安装认证配置上增加
|
|
7
|
+
`@qfei-design` 的 npmjs 安装源,使 CanvasTable 从公网安装。发布统一在 GitLab 任务中执行。
|
|
8
|
+
|
|
9
|
+
## 安装源与发布目标
|
|
10
|
+
|
|
11
|
+
- CanvasTable 安装源:`https://registry.npmjs.org/`,通过 `@qfei-design` 作用域指定;
|
|
12
|
+
锁文件中的 `@qfei-design/canvas-table@1.3.1` tarball 也指向 npmjs,冻结安装不再访问内网下载地址。
|
|
13
|
+
- 其他依赖的默认安装源:`https://nexus.qtech.cn/repository/npm-group/`,包括内部公共发布工具。
|
|
14
|
+
- 内部发布目标:`https://nexus.qtech.cn/repository/npm-hosted/`,由公共工具默认配置管理。
|
|
15
|
+
- 正式版额外发布目标:`https://registry.npmjs.org/`,访问级别为 `public`。
|
|
16
|
+
- alpha/beta 只发布内部仓库;latest 使用同一份 `.tgz` 发布到内部仓库和 npmjs。
|
|
17
|
+
|
|
18
|
+
`.npmrc` 只包含以下配置,不能将安装源改成发布用的 `npm-hosted`:
|
|
19
|
+
|
|
20
|
+
```ini
|
|
21
|
+
registry=https://nexus.qtech.cn/repository/npm-group/
|
|
22
|
+
@qfei-design:registry=https://registry.npmjs.org/
|
|
23
|
+
always-auth=true
|
|
24
|
+
//nexus.qtech.cn/repository/npm-group/:_auth=${NEXUS_NPM_READ_AUTH}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
安装源设置不改变发布目标。公共发布工具为每个目标显式指定包作用域的 registry,
|
|
28
|
+
因此上述 npmjs 安装配置不会将内部发布重定向到 npmjs。当前审批包版本为 `0.1.0`,
|
|
29
|
+
CanvasTable 依赖版本保持 `1.3.1`。
|
|
30
|
+
|
|
31
|
+
## 分支、验证与通道
|
|
32
|
+
|
|
33
|
+
| 事件 | 验证与发布行为 |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| MR | 执行两个验证任务,不发布 |
|
|
36
|
+
| 普通 push 或合并到 `dev` / `test` | 不创建分支发布流水线 |
|
|
37
|
+
| 在 GitLab 手动 Run `dev` pipeline | 验证通过后自动运行 `publish_alpha` |
|
|
38
|
+
| 在 GitLab 手动 Run `test` pipeline | 验证通过后自动运行 `publish_beta` |
|
|
39
|
+
| `release/*` / `master` 分支流水线 | 执行两个验证任务,不发布 |
|
|
40
|
+
| `vX.Y.Z` 标签 | 创建 `publish_latest` 手动任务,点击后执行发布前验证并双仓库发布 |
|
|
41
|
+
|
|
42
|
+
两个验证任务为默认 React 19 的 `verify` 和 React 18.3.1 的 `verify_react_18`。
|
|
43
|
+
均先用锁文件安装依赖,再运行 CI 配置检查、测试、类型检查和构建。
|
|
44
|
+
React 18 任务在自己的工作目录替换 React、React DOM 及类型包,不改写锁文件,
|
|
45
|
+
不改变主开发环境的 React 19 依赖声明。
|
|
46
|
+
|
|
47
|
+
每个发布通道通过 `resource_group` 串行执行。发布工具会在发布任务内再次运行
|
|
48
|
+
`test`、`typecheck`、`build` 和打包预检,因此标签任务也包含发布前验证。
|
|
49
|
+
标签任务使用默认 React 19 依赖;React 18 兼容性在合并前的分支验证中检查。
|
|
50
|
+
|
|
51
|
+
## 初始化与正式发布
|
|
52
|
+
|
|
53
|
+
1. 建立远端 GitLab 项目和 `master` 分支,配置共享 CI 模板访问权限及 Node 22 Runner。
|
|
54
|
+
2. 配置下面的 CI/CD 变量,并将 `v*` 标签设为受保护标签。
|
|
55
|
+
3. 使用 `pnpm run sprint:init -- X.Y.Z` 初始化迭代。公共工具会创建缺少的
|
|
56
|
+
`dev/test` 分支,或基于各自远端 HEAD 更新版本;默认不触发流水线。
|
|
57
|
+
该命令会提交并推送远端分支,不属于本地验证命令。
|
|
58
|
+
4. 通过 MR 验证改动,在 GitLab 手动运行 dev/test 流水线验证 alpha/beta 包。
|
|
59
|
+
5. 使用 `pnpm run release:init -- X.Y.Z` 准备正式版本分支。该命令默认从
|
|
60
|
+
master 创建 `release/X.Y.Z`,更新版本、提交并推送。将待发布改动完成验证并合入 master。
|
|
61
|
+
6. 确认 `package.json.version` 为 `X.Y.Z`,在对应提交创建受保护的 `vX.Y.Z` 标签,
|
|
62
|
+
在标签流水线中手动运行 `publish_latest`。
|
|
63
|
+
|
|
64
|
+
`X.Y.Z` 表示实际目标版本,不能原样执行。`release:check` 保留为公共工具的兼容入口,
|
|
65
|
+
正式版任务直接运行 `publish:latest`,由它完成标签、版本与多个发布目标的检查。
|
|
66
|
+
不要在开发机直接执行 `publish:alpha`、`publish:beta`、`publish:latest` 或 `npm publish`。
|
|
67
|
+
|
|
68
|
+
## GitLab CI/CD 变量
|
|
69
|
+
|
|
70
|
+
| 变量 | 用途与可见范围 |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| `NEXUS_NPM_READ_AUTH` | 从 npm-group 安装依赖的只读 `_auth` 值;所有验证和发布任务都需要,包含允许安装依赖的 MR 流水线 |
|
|
73
|
+
| `NEXUS_NPM_DEPLOY_AUTH` | 向内部 npm-hosted 发布的 `_auth` 值;alpha、beta、latest 发布任务需要 |
|
|
74
|
+
| `NPMJS_TOKEN` | 向 npmjs 的 `@qfei-design` 范围发布;latest 任务需要,使用受保护变量 |
|
|
75
|
+
| `NPM_PACKAGE_NOTIFY_WEBHOOK_URL` | 可选,所有通道共用的发布通知地址 |
|
|
76
|
+
| `NPM_ALPHA_NOTIFY_WEBHOOK_URL` | 可选,覆盖 alpha 通知地址 |
|
|
77
|
+
| `NPM_BETA_NOTIFY_WEBHOOK_URL` | 可选,覆盖 beta 通知地址 |
|
|
78
|
+
| `NPM_LATEST_NOTIFY_WEBHOOK_URL` | 可选,覆盖 latest 通知地址 |
|
|
79
|
+
|
|
80
|
+
凭据值由 GitLab CI/CD Variables 注入并设为 Masked,不写入仓库或日志。
|
|
81
|
+
两个 Nexus `_auth` 变量使用对应账号与密码组成的 Base64 认证值,npmjs 使用 Token。
|
|
82
|
+
变量的 Protected 设置需与分支、标签和 MR 的实际可见范围配套;仅给受保护标签开放
|
|
83
|
+
读取凭据会使普通 MR 的依赖安装失败。部署变量只开放给有发布权限的分支和标签。
|
|
84
|
+
|
|
85
|
+
共享通知模板在发布任务结束后发送通知;未配置 webhook 时跳过通知,不影响发布结果。
|
|
86
|
+
安装认证与发布认证分开管理;只配置部署凭据不能替代安装前的只读变量。
|
|
87
|
+
|
|
88
|
+
## 本地验证与失败重试
|
|
89
|
+
|
|
90
|
+
使用 Node.js `>=22.12.0` 和 pnpm `10.20.0`,先在当前终端环境注入
|
|
91
|
+
`NEXUS_NPM_READ_AUTH`;项目不会自动加载 `.env`。
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
corepack pnpm install --frozen-lockfile
|
|
95
|
+
corepack pnpm run verify
|
|
96
|
+
npm pack --dry-run --ignore-scripts
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
配置约束由 `tests/release-contract.test.ts` 检查,公共工具的 `ci:check` 检查共享 CI 规则。
|
|
100
|
+
构建会验证 ESM、CommonJS、类型声明和 CSS 公开入口。上述命令不发布 npm 包。
|
|
101
|
+
|
|
102
|
+
双仓库发布部分成功时,排除失败原因后重试原标签的发布任务。公共工具检查已有版本的
|
|
103
|
+
完整性:与本次产物一致则跳过该目标,不一致则失败,不能用同版本覆盖不同内容。
|
|
104
|
+
不要通过修改目标列表绕过失败目标;需要改动产物时使用新版本。
|
|
105
|
+
|
|
106
|
+
本地检查通过不代表远端 Runner、保护策略或发布账号权限已配置完成。本地 `origin`
|
|
107
|
+
已配置为 `https://git.qtech.cn/front-end/make/make-app-approval.git`,首次发布前仍需落实
|
|
108
|
+
上述外部配置。
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Button, DatePicker, Input, Select, Tabs } from "antd";
|
|
2
|
+
import dayjs from "dayjs";
|
|
3
|
+
import type { ApprovalUIComponents } from "@qfei-design/make-app-approval/list";
|
|
4
|
+
|
|
5
|
+
/** 复制到 Antd 宿主;组件继承宿主 ConfigProvider,不创建第二个主题。 */
|
|
6
|
+
export const antdApprovalComponents: ApprovalUIComponents = {
|
|
7
|
+
Input: ({ id, ariaLabel, value, onChange, placeholder, disabled }) => <Input
|
|
8
|
+
id={id} aria-label={ariaLabel} value={value} placeholder={placeholder} disabled={disabled}
|
|
9
|
+
allowClear onChange={event => onChange(event.target.value)} style={{ width: "100%" }}
|
|
10
|
+
/>,
|
|
11
|
+
Select: ({ id, ariaLabel, value, onChange, options, disabled }) => <Select<string>
|
|
12
|
+
id={id} aria-label={ariaLabel} value={value} disabled={disabled}
|
|
13
|
+
options={options.map(option => ({ ...option }))} onChange={value => onChange(value ?? "")} style={{ width: "100%" }}
|
|
14
|
+
/>,
|
|
15
|
+
DatePicker: ({ id, ariaLabel, value, onChange, placeholder, disabled }) => <DatePicker
|
|
16
|
+
id={id} aria-label={ariaLabel} value={value ? dayjs(value) : null} placeholder={placeholder} disabled={disabled}
|
|
17
|
+
format="YYYY-MM-DD" allowClear onChange={date => onChange(date ? date.format("YYYY-MM-DD") : "")} style={{ width: "100%" }}
|
|
18
|
+
/>,
|
|
19
|
+
Button: ({ children, htmlType, variant, onClick, disabled, loading, ariaLabel }) => <Button
|
|
20
|
+
type={variant} htmlType={htmlType} onClick={onClick} disabled={disabled} loading={loading} aria-label={ariaLabel}
|
|
21
|
+
>{children}</Button>,
|
|
22
|
+
Tabs: ({ items, value, onChange, children, ariaLabel }) => <Tabs
|
|
23
|
+
aria-label={ariaLabel} activeKey={value}
|
|
24
|
+
onChange={key => { if (key === "pending" || key === "approved") onChange(key); }}
|
|
25
|
+
items={items.map(item => ({ key: item.value, label: item.label, children: item.value === value ? children : null }))}
|
|
26
|
+
tabBarStyle={{ margin: 0, paddingInline: 20 }}
|
|
27
|
+
/>,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
import type { ApprovalDetailComponents } from "@qfei-design/make-app-approval/detail";
|
|
31
|
+
export const antdApprovalDetailComponents: ApprovalDetailComponents = {
|
|
32
|
+
Button: antdApprovalComponents.Button,
|
|
33
|
+
Select: antdApprovalComponents.Select,
|
|
34
|
+
TextArea: ({ id, ariaLabel, value, onChange, placeholder, disabled, maxLength, required }) => <Input.TextArea
|
|
35
|
+
id={id} aria-label={ariaLabel} value={value} placeholder={placeholder} disabled={disabled}
|
|
36
|
+
maxLength={maxLength} required={required} rows={3} onChange={event => onChange(event.target.value)}
|
|
37
|
+
/>,
|
|
38
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ApprovalTaskDetail,
|
|
3
|
+
type ApprovalTaskDetailProps,
|
|
4
|
+
} from "@qfei-design/make-app-approval/detail";
|
|
5
|
+
import { Drawer, theme } from "antd";
|
|
6
|
+
import type { CSSProperties } from "react";
|
|
7
|
+
|
|
8
|
+
/** 放进宿主已有 Drawer/Page;不复制包内权限、按钮或请求状态逻辑。 */
|
|
9
|
+
export function ApprovalDetailRoute(props: ApprovalTaskDetailProps) {
|
|
10
|
+
return <ApprovalTaskDetail {...props} />;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Antd 宿主示例:包提供行为与内容,宿主控制抽屉、关闭位置和主题。 */
|
|
14
|
+
export function AntdApprovalDetailDrawer({
|
|
15
|
+
onClose,
|
|
16
|
+
...props
|
|
17
|
+
}: ApprovalTaskDetailProps & { onClose: () => void }) {
|
|
18
|
+
const { token } = theme.useToken();
|
|
19
|
+
const variables = {
|
|
20
|
+
"--make-approval-bg": token.colorBgContainer,
|
|
21
|
+
"--make-approval-header": token.colorFillAlter,
|
|
22
|
+
"--make-approval-surface": token.colorFillAlter,
|
|
23
|
+
"--make-approval-border": token.colorBorderSecondary,
|
|
24
|
+
"--make-approval-foreground": token.colorText,
|
|
25
|
+
"--make-approval-muted": token.colorTextSecondary,
|
|
26
|
+
"--make-approval-error": token.colorError,
|
|
27
|
+
"--make-approval-primary": token.colorPrimary,
|
|
28
|
+
"--make-approval-radius": `${token.borderRadius}px`,
|
|
29
|
+
} as CSSProperties;
|
|
30
|
+
return (
|
|
31
|
+
<ApprovalTaskDetail
|
|
32
|
+
{...props}
|
|
33
|
+
renderLayout={({ title, actions, content }) => (
|
|
34
|
+
<Drawer
|
|
35
|
+
open
|
|
36
|
+
size={960}
|
|
37
|
+
title={title}
|
|
38
|
+
extra={actions}
|
|
39
|
+
closable={{ placement: "end" }}
|
|
40
|
+
onClose={onClose}
|
|
41
|
+
destroyOnHidden
|
|
42
|
+
rootStyle={variables}
|
|
43
|
+
styles={{
|
|
44
|
+
header: { padding: token.paddingLG },
|
|
45
|
+
body: { padding: 0 },
|
|
46
|
+
}}
|
|
47
|
+
>
|
|
48
|
+
{content}
|
|
49
|
+
</Drawer>
|
|
50
|
+
)}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
宿主 provider 的固定调用链:
|
|
57
|
+
loadDetail(reference, { signal })
|
|
58
|
+
-> UI 统一请求层 -> App Service
|
|
59
|
+
-> POST /make/workflow/v1/bpm-task (MakeService.GetResource)
|
|
60
|
+
-> 以返回的 fieldPermission 为授权来源,补齐任务字段的完整元数据
|
|
61
|
+
-> 返回 ApprovalTaskDetailData。不得拿 IAM 截断后的 Schema 缺失字段当不可见。
|
|
62
|
+
submitAction(input, { signal })
|
|
63
|
+
-> 同一 UI/Service 请求层
|
|
64
|
+
-> POST /make/workflow/v1/bpm-task (MakeService.CreateResource)
|
|
65
|
+
-> 返回动作后的真实状态,不由宿主推断流程结束。
|
|
66
|
+
本地公网网关路径为 /api/make/workflow/v1/,部署内部为 /make/workflow/v1/。
|
|
67
|
+
|
|
68
|
+
renderField({ field, value, readOnly, disabled, error, onChange })
|
|
69
|
+
-> 根据 field.schema 复用宿主字段控件;只读使用展示控件或 readOnly/disabled。
|
|
70
|
+
-> 复杂字段、附件、关联候选的权限与值序列化仍由宿主负责。
|
|
71
|
+
-> 禁止自行从缓存补出包未传入的隐藏字段。
|
|
72
|
+
validateFields({ fields, values, actionType, signal })
|
|
73
|
+
-> fields/values 仅包含当前可编辑且必填的字段。三种动作执行同一规则。
|
|
74
|
+
-> 包先收集必填空值错误,宿主补充这些字段的类型/范围等校验,返回 { [fieldKey]: "错误文字" }。
|
|
75
|
+
-> 不校验只读字段和非必填字段,不从全量 Schema 再追加校验字段。
|
|
76
|
+
-> 不创建记录、不推进流程;不得返回隐藏字段值或在校验中发起任务动作。
|
|
77
|
+
onActionSuccess(result)
|
|
78
|
+
-> 关闭宿主抽屉、增加列表 refreshKey,并刷新关联业务详情。
|
|
79
|
+
-> 必须返回执行过程的 Promise,便于包区分动作成功与宿主刷新失败。
|
|
80
|
+
*/
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { MyApprovalsPage, type ApprovalListItem, type ApprovalListProvider, type ApprovalListQuery, type ApprovalPage, type ApprovalLogger, type ApprovalStatusOption, type ApprovalUIComponents } from "@qfei-design/make-app-approval/list";
|
|
3
|
+
// 宿主入口导入一次 @qfei-design/make-app-approval/styles.css。
|
|
4
|
+
|
|
5
|
+
/** 由宿主实现:复用既有 UI -> App Service 请求层,不在包中配置 URL/Token。 */
|
|
6
|
+
export type ApprovalHostService = {
|
|
7
|
+
queryApprovals: (appKey: string, query: ApprovalListQuery, signal: AbortSignal) => Promise<ApprovalPage>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type ApprovalRouteProps = {
|
|
11
|
+
/** 传入稳定的宿主适配器,例如 antd-components.tsx 中的 antdApprovalComponents。 */
|
|
12
|
+
components: ApprovalUIComponents;
|
|
13
|
+
appKey: string;
|
|
14
|
+
/** 当前身份或环境变化时更新的无敏感信息上下文标识。 */
|
|
15
|
+
sessionScope: string;
|
|
16
|
+
service: ApprovalHostService;
|
|
17
|
+
openApproval: (row: ApprovalListItem) => void | Promise<void>;
|
|
18
|
+
revision: number;
|
|
19
|
+
statusOptions: readonly ApprovalStatusOption[];
|
|
20
|
+
logger?: ApprovalLogger;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function ApprovalRoute({ components, appKey, sessionScope, service, openApproval, revision, statusOptions, logger }: ApprovalRouteProps) {
|
|
24
|
+
const provider = useMemo<ApprovalListProvider>(() => ({
|
|
25
|
+
loadPage: (query, { signal }) => service.queryApprovals(appKey, query, signal),
|
|
26
|
+
}), [appKey, service]);
|
|
27
|
+
return <MyApprovalsPage
|
|
28
|
+
components={components}
|
|
29
|
+
scopeKey={JSON.stringify([appKey, sessionScope])}
|
|
30
|
+
provider={provider}
|
|
31
|
+
onOpenApproval={openApproval}
|
|
32
|
+
refreshKey={revision}
|
|
33
|
+
statusOptions={statusOptions}
|
|
34
|
+
logger={logger}
|
|
35
|
+
/>;
|
|
36
|
+
}
|