@qfei-design/make-app-approval 0.1.0 → 0.2.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 CHANGED
@@ -19,16 +19,16 @@
19
19
 
20
20
  | 平台状态码 | 展示 |
21
21
  | --- | --- |
22
- | `UNSUBMITTED` | — |
22
+ | `UNSUBMITTED` | - |
23
23
  | `IN_APPROVAL` / `RETURNING` | 审批中 |
24
24
  | `APPROVED` | 已同意 |
25
25
  | `REJECTED` | 已拒绝 |
26
26
  | `CANCELLED` | 已撤回 |
27
27
  | 未识别值 | 状态未知 |
28
28
 
29
- 根入口的显示方法对 `null/undefined` 返回“—”,用于业务记录无审批状态的场景;已知绑定审批的记录状态缺失,应由宿主传入 `UNKNOWN` 展示异常。编辑限制方法对绑定记录的状态缺失采取禁编。
29
+ 根入口的显示方法对 `null/undefined/UNSUBMITTED` 返回 `-`,用于业务记录无审批状态的场景;未知非空状态显示“状态未知”。宿主传入原始记录状态,不根据实体绑定补造状态或锁定。
30
30
 
31
- 普通记录无审批元数据时返回 `null`,由原权限决定是否可写;明确锁定、审批中、回退中、结束的原单返回限制。审批记录仅 `UNSUBMITTED` 且 `qfei_is_locked === false` 时允许继续走普通权限校验。异常状态或异常锁值返回 `unknown` 限制。不要将 `null` 结果视为最终授权。
31
+ 仅 `qfei_is_locked === true` 或流程状态为 `IN_APPROVAL` / `RETURNING` 时返回普通编辑限制。其他状态(包括已结束、已撤回、未提交及缺失状态)返回 `null`,继续由普通 IAM 对象、记录和字段权限决定是否可写;不根据异常元数据推断锁定。实体启用审批本身不会锁定全部记录。第二个参数和返回类型中的历史原因值保留兼容,但当前只返回 `locked` / `in-progress` 限制。不要将 `null` 结果视为最终授权,后端仍负责真实审批状态、并发锁和权限校验。
32
32
 
33
33
  ## React 与 CSS
34
34
 
@@ -58,13 +58,13 @@ type ApprovalStatusProps = {
58
58
 
59
59
  `MyApprovalsPageProps`、`ApprovalListProvider`、`ApprovalListQuery`、`ApprovalListFilters`、`ApprovalListTab`、`ApprovalListItem`、`ApprovalPage`、`ApprovalStatusOption`、`ApprovalLogger`、`ApprovalDiagnostic`。
60
60
 
61
- 宿主控件类型也从 `/list` 导入:`ApprovalUIComponents`、`ApprovalInputProps`、`ApprovalSelectProps`、`ApprovalDatePickerProps`、`ApprovalButtonProps`、`ApprovalTabsProps`。
61
+ 宿主控件类型也从 `/list` 导入:`ApprovalUIComponents`、`ApprovalInputProps`、`ApprovalSelectProps`、`ApprovalDateRangePickerProps`、`ApprovalButtonProps`、`ApprovalTabsProps`。
62
62
 
63
63
  | 属性 | 必填 | 说明 |
64
64
  | --- | --- | --- |
65
65
  | `scopeKey` | 是 | 非空上下文标识;应用/身份/环境变化时改变,清空旧请求和页面状态,不用作鉴权 |
66
66
  | `provider` | 是 | `{ loadPage(query, { signal }): Promise<ApprovalPage> }`;宿主请求函数,不是 HTTP 客户端配置 |
67
- | `components` | 是 | `ApprovalUIComponents`,包含 Input / Select / DatePicker / Button / Tabs 的宿主适配组件;缺失时报错,无原生控件回退 |
67
+ | `components` | 是 | `ApprovalUIComponents`,包含 Input / Select / DateRangePicker / Button / Tabs 的宿主适配组件;缺失时报错,无原生控件回退 |
68
68
  | `onOpenApproval` | 是 | `(row: ApprovalListItem) => void \| Promise<void>`,宿主打开详情;Promise 拒绝时包展示安全错误 |
69
69
  | `statusOptions` | 否 | `{ value, label }[]`,只填真实后端支持的状态筛选项;默认只有“全部流程状态”,不覆盖状态显示映射 |
70
70
  | `pageSize` | 否 | 每批加载条数,1–200 的整数,默认 20;变更重置列表,不生成翻页按钮 |
@@ -91,7 +91,7 @@ CanvasTable 的数据页从 0 开始,包在 provider 边界加 1;宿主始
91
91
  | --- | --- | --- |
92
92
  | `Input` | `id`、`ariaLabel`、`value`、`onChange(value)`、可选 `placeholder/disabled` | 输入/清空转换为字符串,保留 id、可访问名称、禁用状态和宿主输入样式 |
93
93
  | `Select` | Input 的公共属性,加 `options: readonly {value,label}[]` | 渲染后端支持选项,空字符串代表全部;将组件库事件转换为字符串值 |
94
- | `DatePicker` | 与 Input 相同;值为 `YYYY-MM-DD` 或空字符串 | 日期对象转换为日期字符串,清空回传 `""`;不转换成 UTC 时间戳,不自行处理 Service 日期边界 |
94
+ | `DateRangePicker` | `id`、`ariaLabel`、`disabled`;`value`/`onChange` 为 `readonly [string, string]`,`placeholder` 为可选起止文案二元组 | 使用宿主真正的日期范围组件;日期为 `YYYY-MM-DD`,完整范围一起回传,清空回传 `["", ""]`;不转换 UTC 时间戳或自行处理 Service 日期边界 |
95
95
  | `Button` | `children`、`htmlType: button/submit`、`variant: primary/default/text`,可选 `onClick/disabled/loading/ariaLabel` | 映射宿主按钮属性;查询使用真正的 submit 按钮,其他操作使用 button;保留键盘、禁用和加载行为 |
96
96
  | `Tabs` | `items: {value,label}[]`、`value`、`onChange(value)`、`ariaLabel`、`children` | 只在当前活动面板挂载一次 children;提供组件库页签样式、键盘操作与 tab/panel 可访问关联 |
97
97
 
@@ -101,7 +101,17 @@ CanvasTable 的数据页从 0 开始,包在 provider 边界加 1;宿主始
101
101
 
102
102
  包 CSS 只控制页面/字段容器、间距、CanvasTable 和状态区域,不再对后代 button/input/select 或组件库内部结构写通用覆盖。控件的颜色、圆角、高度、焦点、弹层及禁用样式由宿主主题管理。Antd/dayjs 仅用于本仓库演示、测试和随包示例,不是审批包的运行时或 peer 依赖。
103
103
 
104
- 这是未发布 `0.1.0` 开发版本的接入调整:升级列表产物时必须补传 `components`。根入口和 `/react` 状态组件用法不变。完整 Antd 适配器见 [examples/antd-components.tsx](examples/antd-components.tsx),其他 React 组件库实现同一合同即可。
104
+ ### 列表布局与剩余高度
105
+
106
+ - 列表外层不绘制卡片边框和圆角;筛选区使用 `padding: 0 4px 12px`,左右 4px 用于容纳宿主控件的聚焦外框,工作区留白仍由宿主提供。表格网格线保留。
107
+ - 宿主传入 `className="make-app-approval--fill"` 时,包内结果区、表格框架和 Canvas 占满剩余高度;加载、空列表和失败状态共用这条高度链。不使用固定视口减数。
108
+ - 宿主须提供确定的内容区高度,并让注入的 Tabs 根节点、中间容器和活动面板使用 `display: flex; flex-direction: column; flex: 1; min-height: 0; min-width: 0`。页签栏不收缩,列表页面不承担滚动;CanvasTable 自身管理滚动。
109
+ - Tabs 内部样式由宿主设置,包不引用 `.ant-tabs-*` 等组件库内部选择器。容器变化由已有 ResizeObserver 自动通知 CanvasTable,无需宿主重新创建表格。
110
+ - 未使用撑满类时,保留嵌入式表格的 `--make-approval-table-height`(默认 440px);撑满模式覆盖此固定高度。
111
+
112
+ 以上无边框及撑满布局属于当前源码更新,公网 0.1.0 尚未包含。旧版宿主可临时添加局部兼容样式,升级后移除包侧覆盖,保留宿主 Tabs 高度链。
113
+
114
+ 使用列表时必须传入 `components`。根入口和 `/react` 状态组件用法不变。完整 Antd 适配器见 [examples/antd-components.tsx](examples/antd-components.tsx),其他 React 组件库实现同一合同即可。
105
115
 
106
116
  列表入口将缺失状态和 UNSUBMITTED 显示为“状态未知”,不显示“—”,不合并、去重或过滤后端业务行。该兼容展示不代表这些值是合法审批结果。
107
117
 
@@ -126,4 +136,12 @@ CanvasTable 的数据页从 0 开始,包在 provider 边界加 1;宿主始
126
136
 
127
137
  根入口新增 `ApprovalWithdrawalInput`(`bpmKey/comment`)、`ApprovalWithdrawalResult`(`processInstanceId/recordId/status/processStatus/processEnded`)、`APPROVAL_WITHDRAW_COMMENT_MAX_LENGTH`(1024)和 `getApprovalWithdrawalCommentError(value)`。校验返回中文错误或 `undefined`;按 trim 后的非空内容和长度检查,宿主提交 trim 后的意见。
128
138
 
129
- 包提供统一“已撤回”状态和意见校验,不请求接口、不推断流程发起人。宿主负责业务详情撤回入口、意见输入/确认、UI → Service 请求、错误 Toast、成功后刷新详情和列表;后端负责实例定位、发起人校验、结束/并发校验与业务记录异步解锁。撤回请求使用 `bpmKey` 而非 `processInstanceId`。发起人标识、停用流程的 key 来源及撤回后原单编辑规则仍需宿主与产品确认,不允许用业务申请人字段猜测。
139
+ 包提供统一“已撤回”状态和意见校验,不请求接口、不推断流程发起人。宿主负责业务详情撤回入口、意见输入/确认、UI → Service 请求、错误 Toast、成功后刷新详情和列表;后端负责实例定位、发起人校验、结束/并发校验与业务记录异步解锁。撤回请求使用 `bpmKey` 而非 `processInstanceId`。发起人标识和停用流程的 key 来源由宿主按真实接口提供,不允许用业务申请人字段猜测;撤回后的普通编辑按根入口限制和普通 IAM 共同判断。
140
+
141
+ ## 0.2.0 日期范围组件迁移
142
+
143
+ 申请时间现改为必传 `components.DateRangePicker`,旧 `components.DatePicker` / `ApprovalDatePickerProps` 合同不再使用。宿主应改为组件库原生范围控件(Antd 为 `DatePicker.RangePicker`),并采用 `ApprovalDateRangePickerProps`,同一事件回传起止字符串二元组。缺少范围适配器直接报错,不回退到双单日期输入框。公开查询 DTO 的 startDate/endDate 不变。
144
+
145
+ 重置会清空筛选值,并重新挂载范围组件以丢弃尚未提交的半选草稿;普通输入及列表刷新不会重新挂载。宿主无需额外实现重置接口。
146
+
147
+ 该合同从 0.2.0 开始提供,公网原始 0.1.0 不包含这项改动。使用 0.1.0 补丁的宿主升级时,应同时升级 UI/Service 到 0.2.0,并移除对应 pnpm 补丁。
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Make App 审批公共 npm 包,统一审批相关 UI、交互和样式。源码由本独立项目维护;ExpensePoc 是首个接入应用。
4
4
 
5
- **尚未发布 npm。** 已有基于 CanvasTable 的桌面审批列表、流程状态组件和普通记录编辑限制。已提供按节点授权的办理详情与动作组件;真实接口联调、流程表和移动审批页面尚未完成。接入先读 [包与宿主职责及接入步骤](docs/host-integration.md),再读 [公开 API](PUBLIC_API.md)。
5
+ **本次版本为 0.2.0。** 日期范围控件合同相较 0.1.0 有不兼容调整,升级前请阅读 [迁移说明](PUBLIC_API.md#020-日期范围组件迁移)。 已有基于 CanvasTable 的桌面审批列表、流程状态组件和普通记录编辑限制。已提供按节点授权的办理详情与动作组件;真实接口联调、流程表和移动审批页面尚未完成。接入先读 [包与宿主职责及接入步骤](docs/host-integration.md),再读 [公开 API](PUBLIC_API.md)。
6
6
 
7
7
  ## 当前提供
8
8
 
@@ -30,7 +30,7 @@ corepack pnpm pack --pack-destination /tmp/make-app-approval
30
30
 
31
31
  ## 接入示例
32
32
 
33
- 未发布前,在 Make App 的 `apps/vendor` 放入本项目生成的 `.tgz`,UI 和 Service 使用同一文件依赖。这样远端构建只需要应用仓库,不依赖开发者电脑上的兄弟目录。正式发布后改为已发布的版本依赖。
33
+ Make App 的 UI 和 Service 固定使用相同的已发布版本。升级到 0.2.0 时移除旧版审批包的 pnpm 补丁;远端依赖安装直接从 npmjs 获取发布产物。
34
34
 
35
35
  ```tsx
36
36
  import {
@@ -43,16 +43,16 @@ import "@qfei-design/make-app-approval/styles.css";
43
43
 
44
44
  const approvalEnabled = hasApprovalDefinition(entity);
45
45
  const restriction = getRecordApprovalWriteRestriction(record, { approvalEnabled });
46
- const status = <ApprovalStatus value={record[APPROVAL_STATUS_FIELD] ?? "UNKNOWN"} />;
46
+ const status = <ApprovalStatus value={record[APPROVAL_STATUS_FIELD]} />;
47
47
  ```
48
48
 
49
- `restriction` 为空时仍须检查原有操作、字段和行级权限。已知存在审批但状态缺失时传入 `UNKNOWN` 展示异常;不要把加载失败当作未发起。已打开的表单在保存前仍需检查最新状态,后端写接口负责最终校验。
49
+ `restriction` 为空时仍须检查原有操作、字段和行级权限。业务记录状态缺失时展示 `-`,未知非空状态展示“状态未知”;不要把加载失败当作未发起。已打开的表单在保存前仍需检查最新状态,后端写接口负责最终校验。
50
50
 
51
51
  ## 包与宿主的分工
52
52
 
53
53
  - 包目前统一列表、任务详情内容、节点字段权限和审批操作;宿主提供抽屉/页面容器及业务字段控件。
54
54
  - 宿主处理导航、路由、应用上下文、登录及 App Service 请求。包处理列表刷新;宿主用 `refreshKey` 通知办理成功并刷新其他业务页面。包不读取 Token,不直接访问 Make 后端。
55
- - `components` 必传:`Input`、`Select`、`DatePicker`、`Button`、`Tabs`。包固定查询/重置/日期校验/刷新功能,宿主适配控件事件和主题;没有原生控件兜底,也不要求宿主安装 Antd。Antd 宿主可复制 [适配示例](examples/antd-components.tsx),继承既有 `ConfigProvider`。
55
+ - `components` 必传:`Input`、`Select`、`DateRangePicker`、`Button`、`Tabs`。包固定查询/重置/日期校验/刷新功能,宿主适配控件事件和主题;没有原生控件兜底,也不要求宿主安装 Antd。Antd 宿主可复制 [适配示例](examples/antd-components.tsx),继承既有 `ConfigProvider`。
56
56
  - 后端决定“待我处理 / 已审批”的归类、筛选、总数、排序和分页;前端不合并多次分页结果再过滤。
57
57
  - 新建审批字段按 `bpm-start-config.fields` 接入 UI 和 Service,不能与普通 IAM 字段再取交集。已确认上游为网关 origin + `/make/workflow/v1/bpm-start-config`,字段元数据完整、与 Schema 一致;ExpensePoc 新建表单已完成代码接入、尚待真实字段和发起联调;该新建表单不属于包组件。全部 Workflow 资源共用 `/make/workflow/v1/` 前缀,网络适配仍由宿主提供。
58
58
  - 普通业务入口禁编与任务内审批/退回修改是不同入口;包不会通过解锁业务记录来办理审批。
@@ -63,4 +63,4 @@ const status = <ApprovalStatus value={record[APPROVAL_STATUS_FIELD] ?? "UNKNOWN"
63
63
 
64
64
  采用与 `make-app-observability` 一致的 `@qfei/component-release-tools` 和 GitLab CI 流程。alpha/beta 由 dev/test 手动流水线触发,只发布内部 Nexus;latest 由受保护的 `vX.Y.Z` 标签手动任务触发,将同一份产物发布到内部 Nexus 和 npmjs。分支验证同时覆盖 React 19 和 React 18。
65
65
 
66
- 完整的分支流程、初始化命令、CI 变量和失败重试说明见 [发布规范](docs/publishing.md)。远端仓库为 [make-app-approval](https://git.qtech.cn/front-end/make/make-app-approval),本地通过 `origin` 关联。当前尚未发布 npm;首次发布前需接入 CI 权限及凭据、确认发布版本和功能范围。本地只运行验证和打包,不运行发布命令。不要将本地 `0.1.0` 版本号视为已存在的 npm 版本。
66
+ 完整的分支流程、初始化命令、CI 变量和失败重试说明见 [发布规范](docs/publishing.md)。远端仓库为 [make-app-approval](https://git.qtech.cn/front-end/make/make-app-approval),本地通过 `origin` 关联。0.1.0 已发布;本次 0.2.0 的发布状态以 GitLab 任务和 registry 查询为准。本地只运行验证和打包,不运行发布命令。不要仅凭本地版本号认定 registry 已有对应版本。
package/capabilities.json CHANGED
@@ -30,7 +30,7 @@
30
30
  {
31
31
  "id": "host-ui-components",
32
32
  "status": "implemented",
33
- "reason": "宿主必传 Input/Select/DatePicker/Button/Tabs;包固定功能并仅提供布局,包含可验证的 Antd 适配示例",
33
+ "reason": "宿主必传 Input/Select/DateRangePicker/Button/Tabs;包固定功能并仅提供布局,包含可验证的 Antd 适配示例",
34
34
  "exports": [
35
35
  "ApprovalUIComponents"
36
36
  ],
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=1024,p=e=>{if(typeof e!="string"||!e.trim())return"请填写撤回意见";if(e.trim().length>s)return`撤回意见不能超过 ${s} 个字符`},i="qfei_process_instance_status",l="qfei_is_locked",r={UNSUBMITTED:{label:"—",tone:"neutral"},IN_APPROVAL:{label:"审批中",tone:"processing"},RETURNING:{label:"审批中",tone:"processing"},APPROVED:{label:"已同意",tone:"success"},REJECTED:{label:"已拒绝",tone:"danger"},CANCELLED:{label:"已撤回",tone:"neutral"}},A=e=>e==null?r.UNSUBMITTED:typeof e=="string"&&Object.hasOwn(r,e)?r[e]:{label:"状态未知",tone:"unknown"},c=e=>!!e?.bpmDefinitionKey?.trim(),u=e=>e===i||e===l,t={locked:{reason:"locked",message:"该数据正在审批中,已被锁定,暂不可编辑"},"in-progress":{reason:"in-progress",message:"该数据正在审批中,暂不可编辑"},ended:{reason:"ended",message:"流程已结束,原单只读"},unknown:{reason:"unknown",message:"审批状态暂不可确认,请刷新后重试"}},E=(e,a={})=>{const n=e[i],o=e[l];return o===!0?t.locked:n==="IN_APPROVAL"||n==="RETURNING"?t["in-progress"]:n==="APPROVED"||n==="REJECTED"?t.ended:o!==void 0&&typeof o!="boolean"?t.unknown:n==="UNSUBMITTED"?o===!1?null:t.unknown:n!==void 0||a.approvalEnabled?t.unknown:null};exports.APPROVAL_LOCK_FIELD=l;exports.APPROVAL_STATUS_FIELD=i;exports.APPROVAL_WITHDRAW_COMMENT_MAX_LENGTH=s;exports.getApprovalStatusDisplay=A;exports.getApprovalWithdrawalCommentError=p;exports.getRecordApprovalWriteRestriction=E;exports.hasApprovalDefinition=c;exports.isApprovalSystemField=u;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=1024,l=t=>{if(typeof t!="string"||!t.trim())return"请填写撤回意见";if(t.trim().length>o)return`撤回意见不能超过 ${o} 个字符`},r="qfei_process_instance_status",n="qfei_is_locked",e={UNSUBMITTED:{label:"-",tone:"neutral"},IN_APPROVAL:{label:"审批中",tone:"processing"},RETURNING:{label:"审批中",tone:"processing"},APPROVED:{label:"已同意",tone:"success"},REJECTED:{label:"已拒绝",tone:"danger"},CANCELLED:{label:"已撤回",tone:"neutral"}},a=t=>t==null?e.UNSUBMITTED:typeof t=="string"&&Object.hasOwn(e,t)?e[t]:{label:"状态未知",tone:"unknown"},p=t=>!!t?.bpmDefinitionKey?.trim(),A=t=>t===r||t===n,i={locked:{reason:"locked",message:"该数据正在审批中,已被锁定,暂不可编辑"},"in-progress":{reason:"in-progress",message:"该数据正在审批中,暂不可编辑"}},c=(t,_={})=>{const s=t[r];return t[n]===!0?i.locked:s==="IN_APPROVAL"||s==="RETURNING"?i["in-progress"]:null};exports.APPROVAL_LOCK_FIELD=n;exports.APPROVAL_STATUS_FIELD=r;exports.APPROVAL_WITHDRAW_COMMENT_MAX_LENGTH=o;exports.getApprovalStatusDisplay=a;exports.getApprovalWithdrawalCommentError=l;exports.getRecordApprovalWriteRestriction=c;exports.hasApprovalDefinition=p;exports.isApprovalSystemField=A;
package/dist/index.d.ts CHANGED
@@ -15,7 +15,7 @@ export type ApprovalWriteRestriction = Readonly<{
15
15
  message: string;
16
16
  }>;
17
17
  /** Only guards ordinary record writes. Task actions and list membership are server-owned. */
18
- export declare const getRecordApprovalWriteRestriction: (record: Readonly<Record<string, unknown>>, options?: {
18
+ export declare const getRecordApprovalWriteRestriction: (record: Readonly<Record<string, unknown>>, _options?: {
19
19
  approvalEnabled?: boolean;
20
20
  }) => ApprovalWriteRestriction | null;
21
21
  export * from "./withdrawal.js";
package/dist/index.mjs CHANGED
@@ -1,30 +1,28 @@
1
- const a = 1024, A = (n) => {
2
- if (typeof n != "string" || !n.trim()) return "请填写撤回意见";
3
- if (n.trim().length > 1024)
1
+ const A = 1024, c = (t) => {
2
+ if (typeof t != "string" || !t.trim()) return "请填写撤回意见";
3
+ if (t.trim().length > 1024)
4
4
  return "撤回意见不能超过 1024 个字符";
5
- }, r = "qfei_process_instance_status", i = "qfei_is_locked", s = {
6
- UNSUBMITTED: { label: "—", tone: "neutral" },
5
+ }, o = "qfei_process_instance_status", r = "qfei_is_locked", e = {
6
+ UNSUBMITTED: { label: "-", tone: "neutral" },
7
7
  IN_APPROVAL: { label: "审批中", tone: "processing" },
8
8
  RETURNING: { label: "审批中", tone: "processing" },
9
9
  APPROVED: { label: "已同意", tone: "success" },
10
10
  REJECTED: { label: "已拒绝", tone: "danger" },
11
11
  CANCELLED: { label: "已撤回", tone: "neutral" }
12
- }, c = (n) => n == null ? s.UNSUBMITTED : typeof n == "string" && Object.hasOwn(s, n) ? s[n] : { label: "状态未知", tone: "unknown" }, p = (n) => !!n?.bpmDefinitionKey?.trim(), E = (n) => n === r || n === i, t = {
12
+ }, a = (t) => t == null ? e.UNSUBMITTED : typeof t == "string" && Object.hasOwn(e, t) ? e[t] : { label: "状态未知", tone: "unknown" }, _ = (t) => !!t?.bpmDefinitionKey?.trim(), p = (t) => t === o || t === r, s = {
13
13
  locked: { reason: "locked", message: "该数据正在审批中,已被锁定,暂不可编辑" },
14
- "in-progress": { reason: "in-progress", message: "该数据正在审批中,暂不可编辑" },
15
- ended: { reason: "ended", message: "流程已结束,原单只读" },
16
- unknown: { reason: "unknown", message: "审批状态暂不可确认,请刷新后重试" }
17
- }, u = (n, l = {}) => {
18
- const e = n[r], o = n[i];
19
- return o === !0 ? t.locked : e === "IN_APPROVAL" || e === "RETURNING" ? t["in-progress"] : e === "APPROVED" || e === "REJECTED" ? t.ended : o !== void 0 && typeof o != "boolean" ? t.unknown : e === "UNSUBMITTED" ? o === !1 ? null : t.unknown : e !== void 0 || l.approvalEnabled ? t.unknown : null;
14
+ "in-progress": { reason: "in-progress", message: "该数据正在审批中,暂不可编辑" }
15
+ }, E = (t, i = {}) => {
16
+ const n = t[o];
17
+ return t[r] === !0 ? s.locked : n === "IN_APPROVAL" || n === "RETURNING" ? s["in-progress"] : null;
20
18
  };
21
19
  export {
22
- i as APPROVAL_LOCK_FIELD,
23
- r as APPROVAL_STATUS_FIELD,
24
- a as APPROVAL_WITHDRAW_COMMENT_MAX_LENGTH,
25
- c as getApprovalStatusDisplay,
26
- A as getApprovalWithdrawalCommentError,
27
- u as getRecordApprovalWriteRestriction,
28
- p as hasApprovalDefinition,
29
- E as isApprovalSystemField
20
+ r as APPROVAL_LOCK_FIELD,
21
+ o as APPROVAL_STATUS_FIELD,
22
+ A as APPROVAL_WITHDRAW_COMMENT_MAX_LENGTH,
23
+ a as getApprovalStatusDisplay,
24
+ c as getApprovalWithdrawalCommentError,
25
+ E as getRecordApprovalWriteRestriction,
26
+ _ as hasApprovalDefinition,
27
+ p as isApprovalSystemField
30
28
  };
@@ -1,3 +1,3 @@
1
1
  export { MyApprovalsPage } from "./my-approvals-page.js";
2
- export type { ApprovalUIComponents, ApprovalInputProps, ApprovalSelectProps, ApprovalDatePickerProps, ApprovalButtonProps, ApprovalTabsProps, } from "./ui-components.js";
2
+ export type { ApprovalUIComponents, ApprovalInputProps, ApprovalSelectProps, ApprovalDateRangePickerProps, ApprovalButtonProps, ApprovalTabsProps, } from "./ui-components.js";
3
3
  export type { ApprovalListTab, ApprovalListItem, ApprovalListFilters, ApprovalListQuery, ApprovalPage, ApprovalListProvider, ApprovalStatusOption, ApprovalDiagnostic, ApprovalLogger, MyApprovalsPageProps, } from "./types.js";
@@ -12,8 +12,15 @@ export type ApprovalInputProps = Readonly<{
12
12
  export type ApprovalSelectProps = ApprovalInputProps & Readonly<{
13
13
  options: readonly ApprovalStatusOption[];
14
14
  }>;
15
- /** value / onChange 使用 YYYY-MM-DD;清空必须回传空字符串。 */
16
- export type ApprovalDatePickerProps = ApprovalInputProps;
15
+ /** 一个完整范围控件;日期为 YYYY-MM-DD,清空回传 ["", ""]。 */
16
+ export type ApprovalDateRangePickerProps = Readonly<{
17
+ id: string;
18
+ ariaLabel: string;
19
+ value: readonly [string, string];
20
+ onChange: (value: readonly [string, string]) => void;
21
+ placeholder?: readonly [string, string];
22
+ disabled?: boolean;
23
+ }>;
17
24
  export type ApprovalButtonProps = Readonly<{
18
25
  children: ReactNode;
19
26
  htmlType: "button" | "submit";
@@ -38,7 +45,7 @@ export type ApprovalTabsProps = Readonly<{
38
45
  export type ApprovalUIComponents = Readonly<{
39
46
  Input: ComponentType<ApprovalInputProps>;
40
47
  Select: ComponentType<ApprovalSelectProps>;
41
- DatePicker: ComponentType<ApprovalDatePickerProps>;
48
+ DateRangePicker: ComponentType<ApprovalDateRangePickerProps>;
42
49
  Button: ComponentType<ApprovalButtonProps>;
43
50
  Tabs: ComponentType<ApprovalTabsProps>;
44
51
  }>;
package/dist/list.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var ne=Object.create;var Z=Object.defineProperty;var le=Object.getOwnPropertyDescriptor;var ie=Object.getOwnPropertyNames;var ce=Object.getPrototypeOf,pe=Object.prototype.hasOwnProperty;var de=(e,t,n,I)=>{if(t&&typeof t=="object"||typeof t=="function")for(let T of ie(t))!pe.call(e,T)&&T!==n&&Z(e,T,{get:()=>t[T],enumerable:!(I=le(t,T))||I.enumerable});return e};var ue=(e,t,n)=>(n=e!=null?ne(ce(e)):{},de(t||!e||!e.__esModule?Z(n,"default",{value:e,enumerable:!0}):n,e));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("react/jsx-runtime"),i=require("react"),fe=require("./index.cjs"),te=e=>e==null||e==="UNSUBMITTED"?"状态未知":fe.getApprovalStatusDisplay(e).label,z=(e,t)=>{try{e?e(t):t.phase==="error"?console.warn("[make-app-approval]",t):console.info("[make-app-approval]",t)}catch{console.warn("[make-app-approval] diagnostic sink failed")}},ae=e=>typeof e=="object"&&e!==null,he=e=>ae(e)?["id","entityKey","recordId"].every(t=>typeof e[t]=="string"&&e[t].trim().length>0)&&["applicationNo","applicationType","applicantName","applicationTime"].every(t=>typeof e[t]=="string")&&["taskId","processInstanceId"].every(t=>e[t]===void 0||typeof e[t]=="string"&&e[t].trim().length>0)&&(typeof e.processStatus=="string"||e.processStatus===null):!1,Y=(e,t)=>{if(!ae(e)||!Array.isArray(e.rows)||!e.rows.every(he)||typeof e.total!="number"||!Number.isSafeInteger(e.total)||e.total<0||e.rows.length>t||e.rows.length>e.total||new Set(e.rows.map(n=>n.id)).size!==e.rows.length)throw new Error("Invalid approval page contract");return{rows:e.rows,total:e.total}},ge=({table:e,query:t,initialPage:n,loadPage:I,onChange:T,retryViewport:V,onReload:F,logger:P})=>{const g=new Map,C=new Set;let j=[];const p=new WeakMap,m=new Set;let w=n,f=n.total,d=0,k=!1,x=!1,y;const D=r=>Number.isSafeInteger(r)&&r>=0&&r*t.pageSize<f,b=(r,a,v)=>z(P,{operation:"list",phase:a,tab:t.tab,page:r+1,pageSize:t.pageSize,rowCount:v}),u=()=>{k||T({total:f,rows:j,failed:x||m.size>0,loading:g.size>0})},N=r=>{r.finished||(r.finished=!0,r.removeAbort(),g.get(r.page)===r&&g.delete(r.page),e.markPageLoadFailed(r.page,r.request),r.controller.abort(),b(r.page,"cancel"))},h=r=>{d=r;const a=e.getPagesInView()??[],v=new Set([d,...a].flatMap(l=>[l-1,l,l+1]).filter(D));for(const l of g.values())v.has(l.page)||N(l);for(const l of m)v.has(l)||m.delete(l)},E=(r,a,v)=>{const l=a.rows.map(s=>{const A={...s,statusLabel:te(s.processStatus)};return p.set(A,s),A});if(!e.setVirtualPageData(l,r,a.total,v)){b(r,"skip");return}f=a.total;try{j=Y({rows:e.getTableData()??[],total:f},Number.MAX_SAFE_INTEGER).rows.map(s=>p.get(s)??s)}catch(s){x=!0,j=[],w=void 0,y&&clearTimeout(y),y=void 0;for(const A of g.values())N(A);throw s}w&&w.total!==f&&(w=void 0),m.delete(r);for(const s of g.values())D(s.page)||N(s);!D(r)&&r>0&&e.scrollToRow(Math.max(0,f-1)),b(r,"success",a.rows.length)},M=()=>{if(k||y)return;const r=[...g.values()].filter(a=>!a.running&&!a.finished).sort((a,v)=>Math.abs(a.page-d)-Math.abs(v.page-d)||a.page-v.page);for(const a of r){if(C.size>=2)break;a.running=!0,C.add(a),b(a.page,"start"),(async()=>{try{const v=await I({...t,page:a.page+1},{signal:a.controller.signal});if(k||a.finished||a.controller.signal.aborted)return;const l=Y(v,t.pageSize);a.finished=!0,a.removeAbort(),g.delete(a.page),E(a.page,l,a.request)}catch{!k&&!a.controller.signal.aborted&&(m.add(a.page),b(a.page,"error"),e.markPageLoadFailed(a.page,a.request))}finally{a.finished||(a.finished=!0,a.removeAbort(),g.get(a.page)===a&&g.delete(a.page)),C.delete(a),u(),M()}})()}u()},O=()=>{y&&clearTimeout(y),y=setTimeout(()=>{y=void 0,M()},120)};return{load(r,a){if(a.useRequestIdentity(),k||x||a.signal.aborted||!D(r)){e.markPageLoadFailed(r,a),b(r,"skip");return}const v=g.get(r);if(v?.request===a&&!v.finished)return;if(v&&N(v),h(r),r===0&&w){const s=w;w=void 0;try{E(0,s,a)}catch{m.add(r),e.markPageLoadFailed(r,a),b(r,"error")}u();return}const l={page:r,request:a,controller:new AbortController,running:!1,finished:!1,removeAbort:()=>{}},c=()=>{N(l),u()};a.signal.addEventListener("abort",c,{once:!0}),l.removeAbort=()=>a.signal.removeEventListener("abort",c),g.set(r,l),O(),u()},viewportChanged(){if(k)return;const r=e.getPagesInView();r?.length&&(h(r[0]),g.size&&O(),u())},retry(){if(!k){if(x){F();return}m.size!==0&&(m.clear(),V(),u())}},dispose(){k=!0,y&&clearTimeout(y),y=void 0;for(const r of g.values())N(r);j=[],m.clear(),w=void 0}}},me=[{key:"applicationNo",title:"申请单编号",width:260},{key:"applicationType",title:"申请单类型",width:180},{key:"applicantName",title:"申请人",width:170},{key:"applicationTime",title:"申请时间",width:220},{key:"statusLabel",title:"流程状态",width:140}].map(e=>({...e,showEllipsis:!0,sortable:!1})),q=40,ve=e=>{const t=i.useRef(void 0);return e.initialPage?o.jsx(be,{...e,initialPage:e.initialPage,restoreScroll:t.current?.query===e.query?t.current.position:void 0,rememberScroll:n=>{t.current={query:e.query,position:n}}}):o.jsx("div",{className:"make-app-approval-canvas",role:"group","aria-label":"审批记录表格"})},be=({components:{Button:e},initialPage:t,query:n,provider:I,themeKey:T,onOpen:V,onReload:F,logger:P,restoreScroll:g,rememberScroll:C})=>{const j=i.useRef(null),p=i.useRef(null),m=i.useRef(null),[w,f]=i.useState(!1),[d,k]=i.useState(0),[x,y]=i.useState({...t,failed:!1,loading:!1}),D=i.useRef(t.rows),b=i.useRef(g),u=i.useRef({provider:I,onOpen:V,onReload:F,logger:P,rememberScroll:C});u.current={provider:I,onOpen:V,onReload:F,logger:P,rememberScroll:C};const N=i.useRef(()=>{});return i.useEffect(()=>{const h=j.current;if(!h)return;let E=!1,M,O,r,a=t.total;z(u.current.logger,{operation:"table",phase:"start"});const v=()=>{E||(f(!0),z(u.current.logger,{operation:"table",phase:"error"}))};return import("@qfei-design/canvas-table").then(l=>{if(E)return;const c=()=>{const A=p.current,$=A?.getScrollState();A&&$&&l.globalEventBus.emitWithNamespace("scroll:change",A.tableId,$)},s=()=>{if(!E)try{const{width:A,height:$}=h.getBoundingClientRect();if(A<=0||$<=0)return;const L=getComputedStyle(h),G={rowHeight:q,headerHeight:40,fontSize:14,fontFamily:L.fontFamily||"Arial, sans-serif",...L.color?{textColor:L.color}:{},...L.getPropertyValue("--make-approval-bg").trim()?{rowBgColor:L.getPropertyValue("--make-approval-bg").trim()}:{},...L.getPropertyValue("--make-approval-header").trim()?{headerBgColor:L.getPropertyValue("--make-approval-header").trim()}:{},...L.getPropertyValue("--make-approval-border").trim()?{borderColor:L.getPropertyValue("--make-approval-border").trim()}:{}},re=Math.max(Math.ceil(300/n.pageSize),Math.ceil($*3/q/n.pageSize)+2)*n.pageSize,J={enabled:!0,totalRowCount:a,pageSize:n.pageSize,maxCachedRows:re};if(p.current){p.current.updateProps({canvasWidth:A,canvasHeight:$,style:G,virtualOptions:J}),p.current.updateCanvasSize(A,$,!0),c();return}p.current=new l.CanvasTableComponent(h,{columns:me,canvasWidth:A,canvasHeight:$,style:G,rowKey:"id",showSN:{enabled:!0,width:64,formatter:(S,R)=>R+1},virtualOptions:J,selectable:{enabled:!1},rowSortable:{enabled:!1},editApplyMode:"controlled",customEdit:S=>{S.cancel()},emptyStateOptions:{text:"暂无审批记录"},bodyRowHeadSuffixOptions:{enabled:!0,width:20,render:({group:S,x:R,height:_,rowData:U})=>{const oe=U?.id,W=new l.RectShape({name:"approval-open",x:R,y:_/2-10,cursor:"pointer",tooltip:"查看审批详情",attrs:{width:20,height:20,fill:"transparent",stroke:"transparent"}});W.addChild(new l.ImgShape({name:"approval-open-icon",x:3,y:3,attrs:{width:14,height:14,img:"data:image/svg+xml;charset=utf-8,"+encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="${L.color||"#64748b"}" stroke-width="1.8"><path d="M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5"/></svg>`)}})),W.on("mousedown",X=>X.stopPropagation?.()),W.on("click",X=>{X.stopPropagation?.();const Q=D.current.find(se=>se.id===oe);Q&&u.current.onOpen(Q)}),S.addChild(W)}}});const B=p.current,K=ge({table:B,query:n,initialPage:t,retryViewport:c,onReload:()=>u.current.onReload(),loadPage:(S,R)=>u.current.provider.loadPage(S,R),logger:S=>z(u.current.logger,S),onChange:S=>{E||(a=S.total,D.current=S.rows,y(R=>R.total===S.total&&R.failed===S.failed&&R.loading===S.loading&&R.rows.length===S.rows.length&&R.rows.every((_,U)=>_===S.rows[U])?R:S))}});m.current=K,O=l.globalEventBus.onWithNamespace("data:load",B.tableId,(S,R)=>K.load(S,R)),r=l.globalEventBus.onWithNamespace("scroll:change",B.tableId,()=>K.viewportChanged()),b.current&&B.scrollTo(b.current.scrollX,b.current.scrollY),f(!1),z(u.current.logger,{operation:"table",phase:"success"})}catch{v()}};N.current=s,M=new ResizeObserver(s),M.observe(h),s()}).catch(v),()=>{E=!0,M?.disconnect(),N.current=()=>{},p.current&&(b.current=p.current.getScrollState(),b.current&&u.current.rememberScroll(b.current)),O?.(),r?.(),m.current?.dispose(),m.current=null,p.current?.destroy(),p.current=null,z(u.current.logger,{operation:"table",phase:"destroy"})}},[t,n,d]),i.useEffect(()=>{N.current()},[T]),o.jsxs("div",{className:"make-app-approval-table-frame","aria-busy":x.loading,children:[o.jsx("div",{ref:j,className:"make-app-approval-canvas",role:"group","aria-label":"审批记录表格"}),w&&o.jsxs("div",{className:"make-app-approval-overlay",role:"alert",children:["表格加载失败 ",o.jsx(e,{htmlType:"button",variant:"default",onClick:()=>{f(!1),k(h=>h+1)},children:"重新加载表格"})]}),!w&&x.failed&&o.jsxs("div",{className:"make-app-approval-table-feedback",role:"alert",children:["部分审批记录加载失败 ",o.jsx(e,{htmlType:"button",variant:"default",onClick:()=>m.current?.retry(),children:"重试加载"})]}),!w&&!x.failed&&x.loading&&o.jsxs("div",{className:"make-app-approval-table-loading",role:"status","aria-label":"正在加载审批记录","aria-live":"polite",children:[o.jsx("span",{className:"make-app-approval-loading-spinner","aria-hidden":"true"}),o.jsx("span",{children:"加载中"})]}),o.jsxs("ul",{className:"make-app-approval-accessible-rows","aria-label":"审批记录文字视图",children:[x.total===0&&o.jsx("li",{children:"暂无审批记录"}),x.rows.map(h=>o.jsxs("li",{children:[o.jsxs("span",{children:[h.applicationNo,",",h.applicationType,",",h.applicantName,",",h.applicationTime,",",te(h.processStatus),"。"]}),o.jsxs(e,{htmlType:"button",variant:"text",onClick:()=>V(h),children:["打开 ",h.applicationNo||h.applicationType," 的审批详情"]})]},h.id))]})]})},we=[],ye=(e,t,n,I)=>{const[T,V]=i.useState(0),[F,P]=i.useState(),g=i.useRef({provider:e,logger:I});g.current={provider:e,logger:I};const C=i.useMemo(()=>({}),[t,n,T]);i.useEffect(()=>{const p=new AbortController,{provider:m,logger:w}=g.current,f={operation:"list",tab:t.tab,page:t.page,pageSize:t.pageSize};z(w,{...f,phase:"start"});let d=!1;return(async()=>{try{const k=await m.loadPage(t,{signal:p.signal});if(p.signal.aborted)return;const x=Y(k,t.pageSize);d=!0,P({...x,token:C,status:"success"}),z(w,{...f,phase:"success",rowCount:x.rows.length})}catch{if(p.signal.aborted)return;d=!0,P({token:C,status:"error",rows:we,total:0}),z(w,{...f,phase:"error"})}})(),()=>{p.abort(),d||z(w,{...f,phase:"cancel"})}},[t,C]);const j=F?.token===C?F:void 0;return{initialPage:j?.status==="success"?j:void 0,total:j?.total??0,status:j?.status??"loading",reload:()=>V(p=>p+1)}},H={keyword:"",processStatus:"",startDate:"",endDate:""},Se=[{value:"pending",label:"待我处理"},{value:"approved",label:"已审批"}],ee=e=>{if(e==="")return!0;if(!/^\d{4}-\d{2}-\d{2}$/.test(e))return!1;const t=new Date(`${e}T00:00:00Z`);return Number.isFinite(t.getTime())&&t.toISOString().slice(0,10)===e},ke=({components:e,provider:t,onOpenApproval:n,statusOptions:I=[],pageSize:T=20,refreshKey:V,themeKey:F,logger:P,className:g})=>{const{Input:C,Select:j,DatePicker:p,Button:m,Tabs:w}=e,f=i.useId(),[d,k]=i.useState(H),[x,y]=i.useState(""),[D,b]=i.useState(""),[u,N]=i.useState({...H,tab:"pending",page:1,pageSize:T}),{initialPage:h,status:E,reload:M}=ye(t,u,V,P),O=i.useRef(!1),r=i.useRef(!0);i.useEffect(()=>(r.current=!0,()=>{r.current=!1}),[]);const a=E==="loading",v=async c=>{if(!O.current){O.current=!0,b(""),z(P,{operation:"open",phase:"start"});try{await n(c),z(P,{operation:"open",phase:"success"})}catch{r.current&&b("审批详情打开失败,请重试"),z(P,{operation:"open",phase:"error"})}finally{O.current=!1}}},l=c=>{c!==u.tab&&(y(""),b(""),N(s=>({...s,tab:c,page:1})))};return o.jsx("section",{className:["make-app-approval",g].filter(Boolean).join(" "),"aria-label":"我的审批",children:o.jsxs(w,{items:Se,value:u.tab,onChange:l,ariaLabel:"审批分类",children:[o.jsxs("form",{className:"make-app-approval-filters",onSubmit:c=>{if(c.preventDefault(),!ee(d.startDate)||!ee(d.endDate)){y("请选择有效日期");return}if(d.startDate&&d.endDate&&d.startDate>d.endDate){y("开始日期不能晚于结束日期");return}y(""),b(""),N(s=>({...s,...d,keyword:d.keyword.trim(),page:1}))},children:[o.jsxs("div",{className:"make-app-approval-filter-field",children:[o.jsx("label",{htmlFor:`${f}-keyword`,children:"申请单号或申请人"}),o.jsx(C,{id:`${f}-keyword`,ariaLabel:"申请单号或申请人",placeholder:"搜索申请单号、申请人",value:d.keyword,onChange:c=>k(s=>({...s,keyword:c}))})]}),o.jsxs("div",{className:"make-app-approval-filter-field",children:[o.jsx("label",{htmlFor:`${f}-status`,children:"流程状态"}),o.jsx(j,{id:`${f}-status`,ariaLabel:"流程状态",value:d.processStatus,options:[{value:"",label:"全部流程状态"},...I],onChange:c=>k(s=>({...s,processStatus:c}))})]}),o.jsxs("fieldset",{className:"make-app-approval-date-field",children:[o.jsx("legend",{children:"申请时间"}),o.jsxs("div",{className:"make-app-approval-date-range",children:[o.jsx("div",{className:"make-app-approval-date-control",children:o.jsx(p,{id:`${f}-start`,ariaLabel:"申请开始日期",placeholder:"开始日期",value:d.startDate,onChange:c=>k(s=>({...s,startDate:c}))})}),o.jsx("span",{children:"至"}),o.jsx("div",{className:"make-app-approval-date-control",children:o.jsx(p,{id:`${f}-end`,ariaLabel:"申请结束日期",placeholder:"结束日期",value:d.endDate,onChange:c=>k(s=>({...s,endDate:c}))})})]})]}),o.jsxs("div",{className:"make-app-approval-filter-actions",children:[o.jsx(m,{htmlType:"submit",variant:"primary",children:"查询"}),o.jsx(m,{htmlType:"button",variant:"default",onClick:()=>{k(H),y(""),b(""),N(c=>({...c,...H,page:1}))},children:"重置"}),o.jsx(m,{htmlType:"button",variant:"default",disabled:a,loading:a,onClick:M,children:"刷新"})]})]}),x&&o.jsx("p",{role:"alert",className:"make-app-approval-error",children:x}),D&&o.jsx("p",{role:"alert",className:"make-app-approval-error",children:D}),o.jsxs("div",{className:"make-app-approval-results","aria-busy":a,children:[o.jsx(ve,{components:e,initialPage:h,query:u,provider:t,onOpen:c=>{v(c)},onReload:M,themeKey:F,logger:P}),a&&o.jsx("div",{className:"make-app-approval-overlay",role:"status",children:"正在加载审批记录…"}),E==="error"&&o.jsxs("div",{className:"make-app-approval-overlay",role:"alert",children:[o.jsx("span",{children:"审批列表加载失败,请重试"}),o.jsx(m,{htmlType:"button",variant:"default",onClick:M,children:"重试"})]})]})]})})},xe=e=>{const t=e.pageSize??20;if(!e.scopeKey.trim())throw new Error("MyApprovalsPage requires scopeKey");for(const n of["Input","Select","DatePicker","Button","Tabs"])if(!e.components?.[n])throw new Error(`MyApprovalsPage requires components.${n}`);if(!Number.isSafeInteger(t)||t<1||t>200)throw new Error("pageSize must be an integer from 1 to 200");return o.jsx(ke,{...e,pageSize:t},JSON.stringify([e.scopeKey,t]))};exports.MyApprovalsPage=xe;
1
+ "use strict";var ne=Object.create;var Z=Object.defineProperty;var le=Object.getOwnPropertyDescriptor;var ie=Object.getOwnPropertyNames;var ce=Object.getPrototypeOf,pe=Object.prototype.hasOwnProperty;var de=(e,t,i,I)=>{if(t&&typeof t=="object"||typeof t=="function")for(let P of ie(t))!pe.call(e,P)&&P!==i&&Z(e,P,{get:()=>t[P],enumerable:!(I=le(t,P))||I.enumerable});return e};var ue=(e,t,i)=>(i=e!=null?ne(ce(e)):{},de(t||!e||!e.__esModule?Z(i,"default",{value:e,enumerable:!0}):i,e));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("react/jsx-runtime"),l=require("react"),fe=require("./index.cjs"),te=e=>e==null||e==="UNSUBMITTED"?"状态未知":fe.getApprovalStatusDisplay(e).label,z=(e,t)=>{try{e?e(t):t.phase==="error"?console.warn("[make-app-approval]",t):console.info("[make-app-approval]",t)}catch{console.warn("[make-app-approval] diagnostic sink failed")}},ae=e=>typeof e=="object"&&e!==null,ge=e=>ae(e)?["id","entityKey","recordId"].every(t=>typeof e[t]=="string"&&e[t].trim().length>0)&&["applicationNo","applicationType","applicantName","applicationTime"].every(t=>typeof e[t]=="string")&&["taskId","processInstanceId"].every(t=>e[t]===void 0||typeof e[t]=="string"&&e[t].trim().length>0)&&(typeof e.processStatus=="string"||e.processStatus===null):!1,Y=(e,t)=>{if(!ae(e)||!Array.isArray(e.rows)||!e.rows.every(ge)||typeof e.total!="number"||!Number.isSafeInteger(e.total)||e.total<0||e.rows.length>t||e.rows.length>e.total||new Set(e.rows.map(i=>i.id)).size!==e.rows.length)throw new Error("Invalid approval page contract");return{rows:e.rows,total:e.total}},he=({table:e,query:t,initialPage:i,loadPage:I,onChange:P,retryViewport:L,onReload:V,logger:C})=>{const f=new Map,A=new Set;let N=[];const u=new WeakMap,g=new Set;let b=i,h=i.total,p=0,k=!1,x=!1,R;const M=r=>Number.isSafeInteger(r)&&r>=0&&r*t.pageSize<h,v=(r,a,d)=>z(C,{operation:"list",phase:a,tab:t.tab,page:r+1,pageSize:t.pageSize,rowCount:d}),m=()=>{k||P({total:h,rows:N,failed:x||g.size>0,loading:f.size>0})},j=r=>{r.finished||(r.finished=!0,r.removeAbort(),f.get(r.page)===r&&f.delete(r.page),e.markPageLoadFailed(r.page,r.request),r.controller.abort(),v(r.page,"cancel"))},c=r=>{p=r;const a=e.getPagesInView()??[],d=new Set([p,...a].flatMap(n=>[n-1,n,n+1]).filter(M));for(const n of f.values())d.has(n.page)||j(n);for(const n of g)d.has(n)||g.delete(n)},D=(r,a,d)=>{const n=a.rows.map(w=>{const s={...w,statusLabel:te(w.processStatus)};return u.set(s,w),s});if(!e.setVirtualPageData(n,r,a.total,d)){v(r,"skip");return}h=a.total;try{N=Y({rows:e.getTableData()??[],total:h},Number.MAX_SAFE_INTEGER).rows.map(w=>u.get(w)??w)}catch(w){x=!0,N=[],b=void 0,R&&clearTimeout(R),R=void 0;for(const s of f.values())j(s);throw w}b&&b.total!==h&&(b=void 0),g.delete(r);for(const w of f.values())M(w.page)||j(w);!M(r)&&r>0&&e.scrollToRow(Math.max(0,h-1)),v(r,"success",a.rows.length)},F=()=>{if(k||R)return;const r=[...f.values()].filter(a=>!a.running&&!a.finished).sort((a,d)=>Math.abs(a.page-p)-Math.abs(d.page-p)||a.page-d.page);for(const a of r){if(A.size>=2)break;a.running=!0,A.add(a),v(a.page,"start"),(async()=>{try{const d=await I({...t,page:a.page+1},{signal:a.controller.signal});if(k||a.finished||a.controller.signal.aborted)return;const n=Y(d,t.pageSize);a.finished=!0,a.removeAbort(),f.delete(a.page),D(a.page,n,a.request)}catch{!k&&!a.controller.signal.aborted&&(g.add(a.page),v(a.page,"error"),e.markPageLoadFailed(a.page,a.request))}finally{a.finished||(a.finished=!0,a.removeAbort(),f.get(a.page)===a&&f.delete(a.page)),A.delete(a),m(),F()}})()}m()},O=()=>{R&&clearTimeout(R),R=setTimeout(()=>{R=void 0,F()},120)};return{load(r,a){if(a.useRequestIdentity(),k||x||a.signal.aborted||!M(r)){e.markPageLoadFailed(r,a),v(r,"skip");return}const d=f.get(r);if(d?.request===a&&!d.finished)return;if(d&&j(d),c(r),r===0&&b){const w=b;b=void 0;try{D(0,w,a)}catch{g.add(r),e.markPageLoadFailed(r,a),v(r,"error")}m();return}const n={page:r,request:a,controller:new AbortController,running:!1,finished:!1,removeAbort:()=>{}},B=()=>{j(n),m()};a.signal.addEventListener("abort",B,{once:!0}),n.removeAbort=()=>a.signal.removeEventListener("abort",B),f.set(r,n),O(),m()},viewportChanged(){if(k)return;const r=e.getPagesInView();r?.length&&(c(r[0]),f.size&&O(),m())},retry(){if(!k){if(x){V();return}g.size!==0&&(g.clear(),L(),m())}},dispose(){k=!0,R&&clearTimeout(R),R=void 0;for(const r of f.values())j(r);N=[],g.clear(),b=void 0}}},me=[{key:"applicationNo",title:"申请单编号",width:260},{key:"applicationType",title:"申请单类型",width:180},{key:"applicantName",title:"申请人",width:170},{key:"applicationTime",title:"申请时间",width:220},{key:"statusLabel",title:"流程状态",width:140}].map(e=>({...e,showEllipsis:!0,sortable:!1})),q=40,ve=e=>{const t=l.useRef(void 0);return e.initialPage?o.jsx(be,{...e,initialPage:e.initialPage,restoreScroll:t.current?.query===e.query?t.current.position:void 0,rememberScroll:i=>{t.current={query:e.query,position:i}}}):o.jsx("div",{className:"make-app-approval-canvas",role:"group","aria-label":"审批记录表格"})},be=({components:{Button:e},initialPage:t,query:i,provider:I,themeKey:P,onOpen:L,onReload:V,logger:C,restoreScroll:f,rememberScroll:A})=>{const N=l.useRef(null),u=l.useRef(null),g=l.useRef(null),[b,h]=l.useState(!1),[p,k]=l.useState(0),[x,R]=l.useState({...t,failed:!1,loading:!1}),M=l.useRef(t.rows),v=l.useRef(f),m=l.useRef({provider:I,onOpen:L,onReload:V,logger:C,rememberScroll:A});m.current={provider:I,onOpen:L,onReload:V,logger:C,rememberScroll:A};const j=l.useRef(()=>{});return l.useEffect(()=>{const c=N.current;if(!c)return;let D=!1,F,O,r,a=t.total;z(m.current.logger,{operation:"table",phase:"start"});const d=()=>{D||(h(!0),z(m.current.logger,{operation:"table",phase:"error"}))};return import("@qfei-design/canvas-table").then(n=>{if(D)return;const B=()=>{const s=u.current,S=s?.getScrollState();s&&S&&n.globalEventBus.emitWithNamespace("scroll:change",s.tableId,S)},w=()=>{if(!D)try{const{width:s,height:S}=c.getBoundingClientRect();if(s<=0||S<=0)return;const E=getComputedStyle(c),G={rowHeight:q,headerHeight:40,fontSize:14,fontFamily:E.fontFamily||"Arial, sans-serif",...E.color?{textColor:E.color}:{},...E.getPropertyValue("--make-approval-bg").trim()?{rowBgColor:E.getPropertyValue("--make-approval-bg").trim()}:{},...E.getPropertyValue("--make-approval-header").trim()?{headerBgColor:E.getPropertyValue("--make-approval-header").trim()}:{},...E.getPropertyValue("--make-approval-border").trim()?{borderColor:E.getPropertyValue("--make-approval-border").trim()}:{}},re=Math.max(Math.ceil(300/i.pageSize),Math.ceil(S*3/q/i.pageSize)+2)*i.pageSize,J={enabled:!0,totalRowCount:a,pageSize:i.pageSize,maxCachedRows:re};if(u.current){u.current.updateProps({canvasWidth:s,canvasHeight:S,style:G,virtualOptions:J}),u.current.updateCanvasSize(s,S,!0),B();return}u.current=new n.CanvasTableComponent(c,{columns:me,canvasWidth:s,canvasHeight:S,style:G,rowKey:"id",showSN:{enabled:!0,width:64,formatter:(y,T)=>T+1},virtualOptions:J,selectable:{enabled:!1},rowSortable:{enabled:!1},editApplyMode:"controlled",customEdit:y=>{y.cancel()},emptyStateOptions:{text:"暂无审批记录"},bodyRowHeadSuffixOptions:{enabled:!0,width:20,render:({group:y,x:T,height:_,rowData:U})=>{const oe=U?.id,K=new n.RectShape({name:"approval-open",x:T,y:_/2-10,cursor:"pointer",tooltip:"查看审批详情",attrs:{width:20,height:20,fill:"transparent",stroke:"transparent"}});K.addChild(new n.ImgShape({name:"approval-open-icon",x:3,y:3,attrs:{width:14,height:14,img:"data:image/svg+xml;charset=utf-8,"+encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="${E.color||"#64748b"}" stroke-width="1.8"><path d="M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5"/></svg>`)}})),K.on("mousedown",X=>X.stopPropagation?.()),K.on("click",X=>{X.stopPropagation?.();const Q=M.current.find(se=>se.id===oe);Q&&m.current.onOpen(Q)}),y.addChild(K)}}});const $=u.current,H=he({table:$,query:i,initialPage:t,retryViewport:B,onReload:()=>m.current.onReload(),loadPage:(y,T)=>m.current.provider.loadPage(y,T),logger:y=>z(m.current.logger,y),onChange:y=>{D||(a=y.total,M.current=y.rows,R(T=>T.total===y.total&&T.failed===y.failed&&T.loading===y.loading&&T.rows.length===y.rows.length&&T.rows.every((_,U)=>_===y.rows[U])?T:y))}});g.current=H,O=n.globalEventBus.onWithNamespace("data:load",$.tableId,(y,T)=>H.load(y,T)),r=n.globalEventBus.onWithNamespace("scroll:change",$.tableId,()=>H.viewportChanged()),v.current&&$.scrollTo(v.current.scrollX,v.current.scrollY),h(!1),z(m.current.logger,{operation:"table",phase:"success"})}catch{d()}};j.current=w,F=new ResizeObserver(w),F.observe(c),w()}).catch(d),()=>{D=!0,F?.disconnect(),j.current=()=>{},u.current&&(v.current=u.current.getScrollState(),v.current&&m.current.rememberScroll(v.current)),O?.(),r?.(),g.current?.dispose(),g.current=null,u.current?.destroy(),u.current=null,z(m.current.logger,{operation:"table",phase:"destroy"})}},[t,i,p]),l.useEffect(()=>{j.current()},[P]),o.jsxs("div",{className:"make-app-approval-table-frame","aria-busy":x.loading,children:[o.jsx("div",{ref:N,className:"make-app-approval-canvas",role:"group","aria-label":"审批记录表格"}),b&&o.jsxs("div",{className:"make-app-approval-overlay",role:"alert",children:["表格加载失败 ",o.jsx(e,{htmlType:"button",variant:"default",onClick:()=>{h(!1),k(c=>c+1)},children:"重新加载表格"})]}),!b&&x.failed&&o.jsxs("div",{className:"make-app-approval-table-feedback",role:"alert",children:["部分审批记录加载失败 ",o.jsx(e,{htmlType:"button",variant:"default",onClick:()=>g.current?.retry(),children:"重试加载"})]}),!b&&!x.failed&&x.loading&&o.jsxs("div",{className:"make-app-approval-table-loading",role:"status","aria-label":"正在加载审批记录","aria-live":"polite",children:[o.jsx("span",{className:"make-app-approval-loading-spinner","aria-hidden":"true"}),o.jsx("span",{children:"加载中"})]}),o.jsxs("ul",{className:"make-app-approval-accessible-rows","aria-label":"审批记录文字视图",children:[x.total===0&&o.jsx("li",{children:"暂无审批记录"}),x.rows.map(c=>o.jsxs("li",{children:[o.jsxs("span",{children:[c.applicationNo,",",c.applicationType,",",c.applicantName,",",c.applicationTime,",",te(c.processStatus),"。"]}),o.jsxs(e,{htmlType:"button",variant:"text",onClick:()=>L(c),children:["打开 ",c.applicationNo||c.applicationType," 的审批详情"]})]},c.id))]})]})},we=[],ye=(e,t,i,I)=>{const[P,L]=l.useState(0),[V,C]=l.useState(),f=l.useRef({provider:e,logger:I});f.current={provider:e,logger:I};const A=l.useMemo(()=>({}),[t,i,P]);l.useEffect(()=>{const u=new AbortController,{provider:g,logger:b}=f.current,h={operation:"list",tab:t.tab,page:t.page,pageSize:t.pageSize};z(b,{...h,phase:"start"});let p=!1;return(async()=>{try{const k=await g.loadPage(t,{signal:u.signal});if(u.signal.aborted)return;const x=Y(k,t.pageSize);p=!0,C({...x,token:A,status:"success"}),z(b,{...h,phase:"success",rowCount:x.rows.length})}catch{if(u.signal.aborted)return;p=!0,C({token:A,status:"error",rows:we,total:0}),z(b,{...h,phase:"error"})}})(),()=>{u.abort(),p||z(b,{...h,phase:"cancel"})}},[t,A]);const N=V?.token===A?V:void 0;return{initialPage:N?.status==="success"?N:void 0,total:N?.total??0,status:N?.status??"loading",reload:()=>L(u=>u+1)}},W={keyword:"",processStatus:"",startDate:"",endDate:""},Se=[{value:"pending",label:"待我处理"},{value:"approved",label:"已审批"}],ee=e=>{if(e==="")return!0;if(!/^\d{4}-\d{2}-\d{2}$/.test(e))return!1;const t=new Date(`${e}T00:00:00Z`);return Number.isFinite(t.getTime())&&t.toISOString().slice(0,10)===e},ke=({components:e,provider:t,onOpenApproval:i,statusOptions:I=[],pageSize:P=20,refreshKey:L,themeKey:V,logger:C,className:f})=>{const{Input:A,Select:N,DateRangePicker:u,Button:g,Tabs:b}=e,h=l.useId(),[p,k]=l.useState(W),[x,R]=l.useState(0),[M,v]=l.useState(""),[m,j]=l.useState(""),[c,D]=l.useState({...W,tab:"pending",page:1,pageSize:P}),{initialPage:F,status:O,reload:r}=ye(t,c,L,C),a=l.useRef(!1),d=l.useRef(!0);l.useEffect(()=>(d.current=!0,()=>{d.current=!1}),[]);const n=O==="loading",B=async s=>{if(!a.current){a.current=!0,j(""),z(C,{operation:"open",phase:"start"});try{await i(s),z(C,{operation:"open",phase:"success"})}catch{d.current&&j("审批详情打开失败,请重试"),z(C,{operation:"open",phase:"error"})}finally{a.current=!1}}},w=s=>{s!==c.tab&&(v(""),j(""),D(S=>({...S,tab:s,page:1})))};return o.jsx("section",{className:["make-app-approval",f].filter(Boolean).join(" "),"aria-label":"我的审批",children:o.jsxs(b,{items:Se,value:c.tab,onChange:w,ariaLabel:"审批分类",children:[o.jsxs("form",{className:"make-app-approval-filters",onSubmit:s=>{if(s.preventDefault(),!ee(p.startDate)||!ee(p.endDate)){v("请选择有效日期");return}if(p.startDate&&p.endDate&&p.startDate>p.endDate){v("开始日期不能晚于结束日期");return}v(""),j(""),D(S=>({...S,...p,keyword:p.keyword.trim(),page:1}))},children:[o.jsxs("div",{className:"make-app-approval-filter-field",children:[o.jsx("label",{htmlFor:`${h}-keyword`,children:"申请单号或申请人"}),o.jsx(A,{id:`${h}-keyword`,ariaLabel:"申请单号或申请人",placeholder:"搜索申请单号、申请人",value:p.keyword,onChange:s=>k(S=>({...S,keyword:s}))})]}),o.jsxs("div",{className:"make-app-approval-filter-field",children:[o.jsx("label",{htmlFor:`${h}-status`,children:"流程状态"}),o.jsx(N,{id:`${h}-status`,ariaLabel:"流程状态",value:p.processStatus,options:[{value:"",label:"全部流程状态"},...I],onChange:s=>k(S=>({...S,processStatus:s}))})]}),o.jsxs("fieldset",{className:"make-app-approval-date-field",children:[o.jsx("legend",{children:"申请时间"}),o.jsx("div",{className:"make-app-approval-date-control",children:o.jsx(u,{id:`${h}-period`,ariaLabel:"申请时间",placeholder:["开始日期","结束日期"],value:[p.startDate,p.endDate],onChange:([s,S])=>k(E=>({...E,startDate:s,endDate:S}))},x)})]}),o.jsxs("div",{className:"make-app-approval-filter-actions",children:[o.jsx(g,{htmlType:"submit",variant:"primary",children:"查询"}),o.jsx(g,{htmlType:"button",variant:"default",onClick:()=>{k(W),R(s=>s+1),v(""),j(""),D(s=>({...s,...W,page:1}))},children:"重置"}),o.jsx(g,{htmlType:"button",variant:"default",disabled:n,loading:n,onClick:r,children:"刷新"})]})]}),M&&o.jsx("p",{role:"alert",className:"make-app-approval-error",children:M}),m&&o.jsx("p",{role:"alert",className:"make-app-approval-error",children:m}),o.jsxs("div",{className:"make-app-approval-results","aria-busy":n,children:[o.jsx(ve,{components:e,initialPage:F,query:c,provider:t,onOpen:s=>{B(s)},onReload:r,themeKey:V,logger:C}),n&&o.jsx("div",{className:"make-app-approval-overlay",role:"status",children:"正在加载审批记录…"}),O==="error"&&o.jsxs("div",{className:"make-app-approval-overlay",role:"alert",children:[o.jsx("span",{children:"审批列表加载失败,请重试"}),o.jsx(g,{htmlType:"button",variant:"default",onClick:r,children:"重试"})]})]})]})})},xe=e=>{const t=e.pageSize??20;if(!e.scopeKey.trim())throw new Error("MyApprovalsPage requires scopeKey");for(const i of["Input","Select","DateRangePicker","Button","Tabs"])if(!e.components?.[i])throw new Error(`MyApprovalsPage requires components.${i}`);if(!Number.isSafeInteger(t)||t<1||t>200)throw new Error("pageSize must be an integer from 1 to 200");return o.jsx(ke,{...e,pageSize:t},JSON.stringify([e.scopeKey,t]))};exports.MyApprovalsPage=xe;
package/dist/list.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { jsx as n, jsxs as N } from "react/jsx-runtime";
2
- import { useRef as E, useState as x, useEffect as j, useMemo as ie, useId as ce } from "react";
1
+ import { jsx as s, jsxs as T } from "react/jsx-runtime";
2
+ import { useRef as M, useState as V, useEffect as j, useMemo as ie, useId as ce } from "react";
3
3
  import { getApprovalStatusDisplay as pe } from "./index.mjs";
4
4
  const re = (e) => e == null || e === "UNSUBMITTED" ? "状态未知" : pe(e).label, I = (e, a) => {
5
5
  try {
@@ -8,110 +8,110 @@ const re = (e) => e == null || e === "UNSUBMITTED" ? "状态未知" : pe(e).labe
8
8
  console.warn("[make-app-approval] diagnostic sink failed");
9
9
  }
10
10
  }, oe = (e) => typeof e == "object" && e !== null, de = (e) => oe(e) ? ["id", "entityKey", "recordId"].every((a) => typeof e[a] == "string" && e[a].trim().length > 0) && ["applicationNo", "applicationType", "applicantName", "applicationTime"].every((a) => typeof e[a] == "string") && ["taskId", "processInstanceId"].every((a) => e[a] === void 0 || typeof e[a] == "string" && e[a].trim().length > 0) && (typeof e.processStatus == "string" || e.processStatus === null) : !1, Q = (e, a) => {
11
- if (!oe(e) || !Array.isArray(e.rows) || !e.rows.every(de) || typeof e.total != "number" || !Number.isSafeInteger(e.total) || e.total < 0 || e.rows.length > a || e.rows.length > e.total || new Set(e.rows.map((m) => m.id)).size !== e.rows.length)
11
+ if (!oe(e) || !Array.isArray(e.rows) || !e.rows.every(de) || typeof e.total != "number" || !Number.isSafeInteger(e.total) || e.total < 0 || e.rows.length > a || e.rows.length > e.total || new Set(e.rows.map((h) => h.id)).size !== e.rows.length)
12
12
  throw new Error("Invalid approval page contract");
13
13
  return { rows: e.rows, total: e.total };
14
- }, ue = ({ table: e, query: a, initialPage: m, loadPage: F, onChange: H, retryViewport: $, onReload: B, logger: R }) => {
15
- const f = /* @__PURE__ */ new Map(), z = /* @__PURE__ */ new Set();
16
- let T = [];
17
- const i = /* @__PURE__ */ new WeakMap(), h = /* @__PURE__ */ new Set();
18
- let b = m, d = m.total, c = 0, k = !1, S = !1, w;
19
- const M = (r) => Number.isSafeInteger(r) && r >= 0 && r * a.pageSize < d, v = (r, t, g) => I(R, { operation: "list", phase: t, tab: a.tab, page: r + 1, pageSize: a.pageSize, rowCount: g }), p = () => {
20
- k || H({ total: d, rows: T, failed: S || h.size > 0, loading: f.size > 0 });
21
- }, C = (r) => {
22
- r.finished || (r.finished = !0, r.removeAbort(), f.get(r.page) === r && f.delete(r.page), e.markPageLoadFailed(r.page, r.request), r.controller.abort(), v(r.page, "cancel"));
23
- }, u = (r) => {
24
- c = r;
25
- const t = e.getPagesInView() ?? [], g = new Set([c, ...t].flatMap((s) => [s - 1, s, s + 1]).filter(M));
26
- for (const s of f.values()) g.has(s.page) || C(s);
27
- for (const s of h) g.has(s) || h.delete(s);
28
- }, D = (r, t, g) => {
29
- const s = t.rows.map((o) => {
30
- const A = { ...o, statusLabel: re(o.processStatus) };
31
- return i.set(A, o), A;
14
+ }, ue = ({ table: e, query: a, initialPage: h, loadPage: F, onChange: W, retryViewport: B, onReload: O, logger: z }) => {
15
+ const d = /* @__PURE__ */ new Map(), A = /* @__PURE__ */ new Set();
16
+ let C = [];
17
+ const p = /* @__PURE__ */ new WeakMap(), u = /* @__PURE__ */ new Set();
18
+ let v = h, f = h.total, i = 0, k = !1, S = !1, R;
19
+ const L = (r) => Number.isSafeInteger(r) && r >= 0 && r * a.pageSize < f, m = (r, t, c) => I(z, { operation: "list", phase: t, tab: a.tab, page: r + 1, pageSize: a.pageSize, rowCount: c }), g = () => {
20
+ k || W({ total: f, rows: C, failed: S || u.size > 0, loading: d.size > 0 });
21
+ }, N = (r) => {
22
+ r.finished || (r.finished = !0, r.removeAbort(), d.get(r.page) === r && d.delete(r.page), e.markPageLoadFailed(r.page, r.request), r.controller.abort(), m(r.page, "cancel"));
23
+ }, l = (r) => {
24
+ i = r;
25
+ const t = e.getPagesInView() ?? [], c = new Set([i, ...t].flatMap((n) => [n - 1, n, n + 1]).filter(L));
26
+ for (const n of d.values()) c.has(n.page) || N(n);
27
+ for (const n of u) c.has(n) || u.delete(n);
28
+ }, D = (r, t, c) => {
29
+ const n = t.rows.map((b) => {
30
+ const o = { ...b, statusLabel: re(b.processStatus) };
31
+ return p.set(o, b), o;
32
32
  });
33
- if (!e.setVirtualPageData(s, r, t.total, g)) {
34
- v(r, "skip");
33
+ if (!e.setVirtualPageData(n, r, t.total, c)) {
34
+ m(r, "skip");
35
35
  return;
36
36
  }
37
- d = t.total;
37
+ f = t.total;
38
38
  try {
39
- T = Q({ rows: e.getTableData() ?? [], total: d }, Number.MAX_SAFE_INTEGER).rows.map((o) => i.get(o) ?? o);
40
- } catch (o) {
41
- S = !0, T = [], b = void 0, w && clearTimeout(w), w = void 0;
42
- for (const A of f.values()) C(A);
43
- throw o;
39
+ C = Q({ rows: e.getTableData() ?? [], total: f }, Number.MAX_SAFE_INTEGER).rows.map((b) => p.get(b) ?? b);
40
+ } catch (b) {
41
+ S = !0, C = [], v = void 0, R && clearTimeout(R), R = void 0;
42
+ for (const o of d.values()) N(o);
43
+ throw b;
44
44
  }
45
- b && b.total !== d && (b = void 0), h.delete(r);
46
- for (const o of f.values()) M(o.page) || C(o);
47
- !M(r) && r > 0 && e.scrollToRow(Math.max(0, d - 1)), v(r, "success", t.rows.length);
48
- }, L = () => {
49
- if (k || w) return;
50
- const r = [...f.values()].filter((t) => !t.running && !t.finished).sort((t, g) => Math.abs(t.page - c) - Math.abs(g.page - c) || t.page - g.page);
45
+ v && v.total !== f && (v = void 0), u.delete(r);
46
+ for (const b of d.values()) L(b.page) || N(b);
47
+ !L(r) && r > 0 && e.scrollToRow(Math.max(0, f - 1)), m(r, "success", t.rows.length);
48
+ }, $ = () => {
49
+ if (k || R) return;
50
+ const r = [...d.values()].filter((t) => !t.running && !t.finished).sort((t, c) => Math.abs(t.page - i) - Math.abs(c.page - i) || t.page - c.page);
51
51
  for (const t of r) {
52
- if (z.size >= 2) break;
53
- t.running = !0, z.add(t), v(t.page, "start"), (async () => {
52
+ if (A.size >= 2) break;
53
+ t.running = !0, A.add(t), m(t.page, "start"), (async () => {
54
54
  try {
55
- const g = await F({ ...a, page: t.page + 1 }, { signal: t.controller.signal });
55
+ const c = await F({ ...a, page: t.page + 1 }, { signal: t.controller.signal });
56
56
  if (k || t.finished || t.controller.signal.aborted) return;
57
- const s = Q(g, a.pageSize);
58
- t.finished = !0, t.removeAbort(), f.delete(t.page), D(t.page, s, t.request);
57
+ const n = Q(c, a.pageSize);
58
+ t.finished = !0, t.removeAbort(), d.delete(t.page), D(t.page, n, t.request);
59
59
  } catch {
60
- !k && !t.controller.signal.aborted && (h.add(t.page), v(t.page, "error"), e.markPageLoadFailed(t.page, t.request));
60
+ !k && !t.controller.signal.aborted && (u.add(t.page), m(t.page, "error"), e.markPageLoadFailed(t.page, t.request));
61
61
  } finally {
62
- t.finished || (t.finished = !0, t.removeAbort(), f.get(t.page) === t && f.delete(t.page)), z.delete(t), p(), L();
62
+ t.finished || (t.finished = !0, t.removeAbort(), d.get(t.page) === t && d.delete(t.page)), A.delete(t), g(), $();
63
63
  }
64
64
  })();
65
65
  }
66
- p();
67
- }, O = () => {
68
- w && clearTimeout(w), w = setTimeout(() => {
69
- w = void 0, L();
66
+ g();
67
+ }, x = () => {
68
+ R && clearTimeout(R), R = setTimeout(() => {
69
+ R = void 0, $();
70
70
  }, 120);
71
71
  };
72
72
  return {
73
73
  load(r, t) {
74
- if (t.useRequestIdentity(), k || S || t.signal.aborted || !M(r)) {
75
- e.markPageLoadFailed(r, t), v(r, "skip");
74
+ if (t.useRequestIdentity(), k || S || t.signal.aborted || !L(r)) {
75
+ e.markPageLoadFailed(r, t), m(r, "skip");
76
76
  return;
77
77
  }
78
- const g = f.get(r);
79
- if (g?.request === t && !g.finished) return;
80
- if (g && C(g), u(r), r === 0 && b) {
81
- const o = b;
82
- b = void 0;
78
+ const c = d.get(r);
79
+ if (c?.request === t && !c.finished) return;
80
+ if (c && N(c), l(r), r === 0 && v) {
81
+ const b = v;
82
+ v = void 0;
83
83
  try {
84
- D(0, o, t);
84
+ D(0, b, t);
85
85
  } catch {
86
- h.add(r), e.markPageLoadFailed(r, t), v(r, "error");
86
+ u.add(r), e.markPageLoadFailed(r, t), m(r, "error");
87
87
  }
88
- p();
88
+ g();
89
89
  return;
90
90
  }
91
- const s = { page: r, request: t, controller: new AbortController(), running: !1, finished: !1, removeAbort: () => {
92
- } }, l = () => {
93
- C(s), p();
91
+ const n = { page: r, request: t, controller: new AbortController(), running: !1, finished: !1, removeAbort: () => {
92
+ } }, K = () => {
93
+ N(n), g();
94
94
  };
95
- t.signal.addEventListener("abort", l, { once: !0 }), s.removeAbort = () => t.signal.removeEventListener("abort", l), f.set(r, s), O(), p();
95
+ t.signal.addEventListener("abort", K, { once: !0 }), n.removeAbort = () => t.signal.removeEventListener("abort", K), d.set(r, n), x(), g();
96
96
  },
97
97
  viewportChanged() {
98
98
  if (k) return;
99
99
  const r = e.getPagesInView();
100
- r?.length && (u(r[0]), f.size && O(), p());
100
+ r?.length && (l(r[0]), d.size && x(), g());
101
101
  },
102
102
  retry() {
103
103
  if (!k) {
104
104
  if (S) {
105
- B();
105
+ O();
106
106
  return;
107
107
  }
108
- h.size !== 0 && (h.clear(), $(), p());
108
+ u.size !== 0 && (u.clear(), B(), g());
109
109
  }
110
110
  },
111
111
  dispose() {
112
- k = !0, w && clearTimeout(w), w = void 0;
113
- for (const r of f.values()) C(r);
114
- T = [], h.clear(), b = void 0;
112
+ k = !0, R && clearTimeout(R), R = void 0;
113
+ for (const r of d.values()) N(r);
114
+ C = [], u.clear(), v = void 0;
115
115
  }
116
116
  };
117
117
  }, fe = [
@@ -120,261 +120,267 @@ const re = (e) => e == null || e === "UNSUBMITTED" ? "状态未知" : pe(e).labe
120
120
  { key: "applicantName", title: "申请人", width: 170 },
121
121
  { key: "applicationTime", title: "申请时间", width: 220 },
122
122
  { key: "statusLabel", title: "流程状态", width: 140 }
123
- ].map((e) => ({ ...e, showEllipsis: !0, sortable: !1 })), te = 40, he = (e) => {
124
- const a = E(void 0);
125
- return e.initialPage ? /* @__PURE__ */ n(
126
- ge,
123
+ ].map((e) => ({ ...e, showEllipsis: !0, sortable: !1 })), te = 40, ge = (e) => {
124
+ const a = M(void 0);
125
+ return e.initialPage ? /* @__PURE__ */ s(
126
+ he,
127
127
  {
128
128
  ...e,
129
129
  initialPage: e.initialPage,
130
130
  restoreScroll: a.current?.query === e.query ? a.current.position : void 0,
131
- rememberScroll: (m) => {
132
- a.current = { query: e.query, position: m };
131
+ rememberScroll: (h) => {
132
+ a.current = { query: e.query, position: h };
133
133
  }
134
134
  }
135
- ) : /* @__PURE__ */ n("div", { className: "make-app-approval-canvas", role: "group", "aria-label": "审批记录表格" });
136
- }, ge = ({ components: { Button: e }, initialPage: a, query: m, provider: F, themeKey: H, onOpen: $, onReload: B, logger: R, restoreScroll: f, rememberScroll: z }) => {
137
- const T = E(null), i = E(null), h = E(null), [b, d] = x(!1), [c, k] = x(0), [S, w] = x({ ...a, failed: !1, loading: !1 }), M = E(a.rows), v = E(f), p = E({ provider: F, onOpen: $, onReload: B, logger: R, rememberScroll: z });
138
- p.current = { provider: F, onOpen: $, onReload: B, logger: R, rememberScroll: z };
139
- const C = E(() => {
135
+ ) : /* @__PURE__ */ s("div", { className: "make-app-approval-canvas", role: "group", "aria-label": "审批记录表格" });
136
+ }, he = ({ components: { Button: e }, initialPage: a, query: h, provider: F, themeKey: W, onOpen: B, onReload: O, logger: z, restoreScroll: d, rememberScroll: A }) => {
137
+ const C = M(null), p = M(null), u = M(null), [v, f] = V(!1), [i, k] = V(0), [S, R] = V({ ...a, failed: !1, loading: !1 }), L = M(a.rows), m = M(d), g = M({ provider: F, onOpen: B, onReload: O, logger: z, rememberScroll: A });
138
+ g.current = { provider: F, onOpen: B, onReload: O, logger: z, rememberScroll: A };
139
+ const N = M(() => {
140
140
  });
141
141
  return j(() => {
142
- const u = T.current;
143
- if (!u) return;
144
- let D = !1, L, O, r, t = a.total;
145
- I(p.current.logger, { operation: "table", phase: "start" });
146
- const g = () => {
147
- D || (d(!0), I(p.current.logger, { operation: "table", phase: "error" }));
142
+ const l = C.current;
143
+ if (!l) return;
144
+ let D = !1, $, x, r, t = a.total;
145
+ I(g.current.logger, { operation: "table", phase: "start" });
146
+ const c = () => {
147
+ D || (f(!0), I(g.current.logger, { operation: "table", phase: "error" }));
148
148
  };
149
- return import("@qfei-design/canvas-table").then((s) => {
149
+ return import("@qfei-design/canvas-table").then((n) => {
150
150
  if (D) return;
151
- const l = () => {
152
- const A = i.current, W = A?.getScrollState();
153
- A && W && s.globalEventBus.emitWithNamespace("scroll:change", A.tableId, W);
154
- }, o = () => {
151
+ const K = () => {
152
+ const o = p.current, y = o?.getScrollState();
153
+ o && y && n.globalEventBus.emitWithNamespace("scroll:change", o.tableId, y);
154
+ }, b = () => {
155
155
  if (!D)
156
156
  try {
157
- const { width: A, height: W } = u.getBoundingClientRect();
158
- if (A <= 0 || W <= 0) return;
159
- const V = getComputedStyle(u), Z = {
157
+ const { width: o, height: y } = l.getBoundingClientRect();
158
+ if (o <= 0 || y <= 0) return;
159
+ const E = getComputedStyle(l), Z = {
160
160
  rowHeight: te,
161
161
  headerHeight: 40,
162
162
  fontSize: 14,
163
- fontFamily: V.fontFamily || "Arial, sans-serif",
164
- ...V.color ? { textColor: V.color } : {},
165
- ...V.getPropertyValue("--make-approval-bg").trim() ? { rowBgColor: V.getPropertyValue("--make-approval-bg").trim() } : {},
166
- ...V.getPropertyValue("--make-approval-header").trim() ? { headerBgColor: V.getPropertyValue("--make-approval-header").trim() } : {},
167
- ...V.getPropertyValue("--make-approval-border").trim() ? { borderColor: V.getPropertyValue("--make-approval-border").trim() } : {}
168
- }, ne = Math.max(Math.ceil(300 / m.pageSize), Math.ceil(W * 3 / te / m.pageSize) + 2) * m.pageSize, q = { enabled: !0, totalRowCount: t, pageSize: m.pageSize, maxCachedRows: ne };
169
- if (i.current) {
170
- i.current.updateProps({ canvasWidth: A, canvasHeight: W, style: Z, virtualOptions: q }), i.current.updateCanvasSize(A, W, !0), l();
163
+ fontFamily: E.fontFamily || "Arial, sans-serif",
164
+ ...E.color ? { textColor: E.color } : {},
165
+ ...E.getPropertyValue("--make-approval-bg").trim() ? { rowBgColor: E.getPropertyValue("--make-approval-bg").trim() } : {},
166
+ ...E.getPropertyValue("--make-approval-header").trim() ? { headerBgColor: E.getPropertyValue("--make-approval-header").trim() } : {},
167
+ ...E.getPropertyValue("--make-approval-border").trim() ? { borderColor: E.getPropertyValue("--make-approval-border").trim() } : {}
168
+ }, ne = Math.max(Math.ceil(300 / h.pageSize), Math.ceil(y * 3 / te / h.pageSize) + 2) * h.pageSize, q = { enabled: !0, totalRowCount: t, pageSize: h.pageSize, maxCachedRows: ne };
169
+ if (p.current) {
170
+ p.current.updateProps({ canvasWidth: o, canvasHeight: y, style: Z, virtualOptions: q }), p.current.updateCanvasSize(o, y, !0), K();
171
171
  return;
172
172
  }
173
- i.current = new s.CanvasTableComponent(u, {
173
+ p.current = new n.CanvasTableComponent(l, {
174
174
  columns: fe,
175
- canvasWidth: A,
176
- canvasHeight: W,
175
+ canvasWidth: o,
176
+ canvasHeight: y,
177
177
  style: Z,
178
178
  rowKey: "id",
179
- showSN: { enabled: !0, width: 64, formatter: (y, P) => P + 1 },
179
+ showSN: { enabled: !0, width: 64, formatter: (w, P) => P + 1 },
180
180
  virtualOptions: q,
181
181
  selectable: { enabled: !1 },
182
182
  rowSortable: { enabled: !1 },
183
183
  editApplyMode: "controlled",
184
- customEdit: (y) => {
185
- y.cancel();
184
+ customEdit: (w) => {
185
+ w.cancel();
186
186
  },
187
187
  emptyStateOptions: { text: "暂无审批记录" },
188
188
  bodyRowHeadSuffixOptions: {
189
189
  enabled: !0,
190
190
  width: 20,
191
- render: ({ group: y, x: P, height: Y, rowData: G }) => {
192
- const se = G?.id, _ = new s.RectShape({ name: "approval-open", x: P, y: Y / 2 - 10, cursor: "pointer", tooltip: "查看审批详情", attrs: { width: 20, height: 20, fill: "transparent", stroke: "transparent" } });
193
- _.addChild(new s.ImgShape({ name: "approval-open-icon", x: 3, y: 3, attrs: {
191
+ render: ({ group: w, x: P, height: Y, rowData: G }) => {
192
+ const se = G?.id, _ = new n.RectShape({ name: "approval-open", x: P, y: Y / 2 - 10, cursor: "pointer", tooltip: "查看审批详情", attrs: { width: 20, height: 20, fill: "transparent", stroke: "transparent" } });
193
+ _.addChild(new n.ImgShape({ name: "approval-open-icon", x: 3, y: 3, attrs: {
194
194
  width: 14,
195
195
  height: 14,
196
- img: "data:image/svg+xml;charset=utf-8," + encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="${V.color || "#64748b"}" stroke-width="1.8"><path d="M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5"/></svg>`)
196
+ img: "data:image/svg+xml;charset=utf-8," + encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="${E.color || "#64748b"}" stroke-width="1.8"><path d="M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5"/></svg>`)
197
197
  } })), _.on("mousedown", (J) => J.stopPropagation?.()), _.on("click", (J) => {
198
198
  J.stopPropagation?.();
199
- const ee = M.current.find((le) => le.id === se);
200
- ee && p.current.onOpen(ee);
201
- }), y.addChild(_);
199
+ const ee = L.current.find((le) => le.id === se);
200
+ ee && g.current.onOpen(ee);
201
+ }), w.addChild(_);
202
202
  }
203
203
  }
204
204
  });
205
- const K = i.current, X = ue({
206
- table: K,
207
- query: m,
205
+ const H = p.current, X = ue({
206
+ table: H,
207
+ query: h,
208
208
  initialPage: a,
209
209
  // 公开滚动同步事件会重新检测缺页,不移动视口,也不复用失败身份。
210
- retryViewport: l,
211
- onReload: () => p.current.onReload(),
212
- loadPage: (y, P) => p.current.provider.loadPage(y, P),
213
- logger: (y) => I(p.current.logger, y),
214
- onChange: (y) => {
215
- D || (t = y.total, M.current = y.rows, w((P) => P.total === y.total && P.failed === y.failed && P.loading === y.loading && P.rows.length === y.rows.length && P.rows.every((Y, G) => Y === y.rows[G]) ? P : y));
210
+ retryViewport: K,
211
+ onReload: () => g.current.onReload(),
212
+ loadPage: (w, P) => g.current.provider.loadPage(w, P),
213
+ logger: (w) => I(g.current.logger, w),
214
+ onChange: (w) => {
215
+ D || (t = w.total, L.current = w.rows, R((P) => P.total === w.total && P.failed === w.failed && P.loading === w.loading && P.rows.length === w.rows.length && P.rows.every((Y, G) => Y === w.rows[G]) ? P : w));
216
216
  }
217
217
  });
218
- h.current = X, O = s.globalEventBus.onWithNamespace("data:load", K.tableId, (y, P) => X.load(y, P)), r = s.globalEventBus.onWithNamespace("scroll:change", K.tableId, () => X.viewportChanged()), v.current && K.scrollTo(v.current.scrollX, v.current.scrollY), d(!1), I(p.current.logger, { operation: "table", phase: "success" });
218
+ u.current = X, x = n.globalEventBus.onWithNamespace("data:load", H.tableId, (w, P) => X.load(w, P)), r = n.globalEventBus.onWithNamespace("scroll:change", H.tableId, () => X.viewportChanged()), m.current && H.scrollTo(m.current.scrollX, m.current.scrollY), f(!1), I(g.current.logger, { operation: "table", phase: "success" });
219
219
  } catch {
220
- g();
220
+ c();
221
221
  }
222
222
  };
223
- C.current = o, L = new ResizeObserver(o), L.observe(u), o();
224
- }).catch(g), () => {
225
- D = !0, L?.disconnect(), C.current = () => {
226
- }, i.current && (v.current = i.current.getScrollState(), v.current && p.current.rememberScroll(v.current)), O?.(), r?.(), h.current?.dispose(), h.current = null, i.current?.destroy(), i.current = null, I(p.current.logger, { operation: "table", phase: "destroy" });
223
+ N.current = b, $ = new ResizeObserver(b), $.observe(l), b();
224
+ }).catch(c), () => {
225
+ D = !0, $?.disconnect(), N.current = () => {
226
+ }, p.current && (m.current = p.current.getScrollState(), m.current && g.current.rememberScroll(m.current)), x?.(), r?.(), u.current?.dispose(), u.current = null, p.current?.destroy(), p.current = null, I(g.current.logger, { operation: "table", phase: "destroy" });
227
227
  };
228
- }, [a, m, c]), j(() => {
229
- C.current();
230
- }, [H]), /* @__PURE__ */ N("div", { className: "make-app-approval-table-frame", "aria-busy": S.loading, children: [
231
- /* @__PURE__ */ n("div", { ref: T, className: "make-app-approval-canvas", role: "group", "aria-label": "审批记录表格" }),
232
- b && /* @__PURE__ */ N("div", { className: "make-app-approval-overlay", role: "alert", children: [
228
+ }, [a, h, i]), j(() => {
229
+ N.current();
230
+ }, [W]), /* @__PURE__ */ T("div", { className: "make-app-approval-table-frame", "aria-busy": S.loading, children: [
231
+ /* @__PURE__ */ s("div", { ref: C, className: "make-app-approval-canvas", role: "group", "aria-label": "审批记录表格" }),
232
+ v && /* @__PURE__ */ T("div", { className: "make-app-approval-overlay", role: "alert", children: [
233
233
  "表格加载失败 ",
234
- /* @__PURE__ */ n(e, { htmlType: "button", variant: "default", onClick: () => {
235
- d(!1), k((u) => u + 1);
234
+ /* @__PURE__ */ s(e, { htmlType: "button", variant: "default", onClick: () => {
235
+ f(!1), k((l) => l + 1);
236
236
  }, children: "重新加载表格" })
237
237
  ] }),
238
- !b && S.failed && /* @__PURE__ */ N("div", { className: "make-app-approval-table-feedback", role: "alert", children: [
238
+ !v && S.failed && /* @__PURE__ */ T("div", { className: "make-app-approval-table-feedback", role: "alert", children: [
239
239
  "部分审批记录加载失败 ",
240
- /* @__PURE__ */ n(e, { htmlType: "button", variant: "default", onClick: () => h.current?.retry(), children: "重试加载" })
240
+ /* @__PURE__ */ s(e, { htmlType: "button", variant: "default", onClick: () => u.current?.retry(), children: "重试加载" })
241
241
  ] }),
242
- !b && !S.failed && S.loading && /* @__PURE__ */ N("div", { className: "make-app-approval-table-loading", role: "status", "aria-label": "正在加载审批记录", "aria-live": "polite", children: [
243
- /* @__PURE__ */ n("span", { className: "make-app-approval-loading-spinner", "aria-hidden": "true" }),
244
- /* @__PURE__ */ n("span", { children: "加载中" })
242
+ !v && !S.failed && S.loading && /* @__PURE__ */ T("div", { className: "make-app-approval-table-loading", role: "status", "aria-label": "正在加载审批记录", "aria-live": "polite", children: [
243
+ /* @__PURE__ */ s("span", { className: "make-app-approval-loading-spinner", "aria-hidden": "true" }),
244
+ /* @__PURE__ */ s("span", { children: "加载中" })
245
245
  ] }),
246
- /* @__PURE__ */ N("ul", { className: "make-app-approval-accessible-rows", "aria-label": "审批记录文字视图", children: [
247
- S.total === 0 && /* @__PURE__ */ n("li", { children: "暂无审批记录" }),
248
- S.rows.map((u) => /* @__PURE__ */ N("li", { children: [
249
- /* @__PURE__ */ N("span", { children: [
250
- u.applicationNo,
246
+ /* @__PURE__ */ T("ul", { className: "make-app-approval-accessible-rows", "aria-label": "审批记录文字视图", children: [
247
+ S.total === 0 && /* @__PURE__ */ s("li", { children: "暂无审批记录" }),
248
+ S.rows.map((l) => /* @__PURE__ */ T("li", { children: [
249
+ /* @__PURE__ */ T("span", { children: [
250
+ l.applicationNo,
251
251
  ",",
252
- u.applicationType,
252
+ l.applicationType,
253
253
  ",",
254
- u.applicantName,
254
+ l.applicantName,
255
255
  ",",
256
- u.applicationTime,
256
+ l.applicationTime,
257
257
  ",",
258
- re(u.processStatus),
258
+ re(l.processStatus),
259
259
  "。"
260
260
  ] }),
261
- /* @__PURE__ */ N(e, { htmlType: "button", variant: "text", onClick: () => $(u), children: [
261
+ /* @__PURE__ */ T(e, { htmlType: "button", variant: "text", onClick: () => B(l), children: [
262
262
  "打开 ",
263
- u.applicationNo || u.applicationType,
263
+ l.applicationNo || l.applicationType,
264
264
  " 的审批详情"
265
265
  ] })
266
- ] }, u.id))
266
+ ] }, l.id))
267
267
  ] })
268
268
  ] });
269
- }, me = [], ve = (e, a, m, F) => {
270
- const [H, $] = x(0), [B, R] = x(), f = E({ provider: e, logger: F });
271
- f.current = { provider: e, logger: F };
272
- const z = ie(() => ({}), [a, m, H]);
269
+ }, me = [], ve = (e, a, h, F) => {
270
+ const [W, B] = V(0), [O, z] = V(), d = M({ provider: e, logger: F });
271
+ d.current = { provider: e, logger: F };
272
+ const A = ie(() => ({}), [a, h, W]);
273
273
  j(() => {
274
- const i = new AbortController(), { provider: h, logger: b } = f.current, d = { operation: "list", tab: a.tab, page: a.page, pageSize: a.pageSize };
275
- I(b, { ...d, phase: "start" });
276
- let c = !1;
274
+ const p = new AbortController(), { provider: u, logger: v } = d.current, f = { operation: "list", tab: a.tab, page: a.page, pageSize: a.pageSize };
275
+ I(v, { ...f, phase: "start" });
276
+ let i = !1;
277
277
  return (async () => {
278
278
  try {
279
- const k = await h.loadPage(a, { signal: i.signal });
280
- if (i.signal.aborted) return;
279
+ const k = await u.loadPage(a, { signal: p.signal });
280
+ if (p.signal.aborted) return;
281
281
  const S = Q(k, a.pageSize);
282
- c = !0, R({ ...S, token: z, status: "success" }), I(b, { ...d, phase: "success", rowCount: S.rows.length });
282
+ i = !0, z({ ...S, token: A, status: "success" }), I(v, { ...f, phase: "success", rowCount: S.rows.length });
283
283
  } catch {
284
- if (i.signal.aborted) return;
285
- c = !0, R({ token: z, status: "error", rows: me, total: 0 }), I(b, { ...d, phase: "error" });
284
+ if (p.signal.aborted) return;
285
+ i = !0, z({ token: A, status: "error", rows: me, total: 0 }), I(v, { ...f, phase: "error" });
286
286
  }
287
287
  })(), () => {
288
- i.abort(), c || I(b, { ...d, phase: "cancel" });
288
+ p.abort(), i || I(v, { ...f, phase: "cancel" });
289
289
  };
290
- }, [a, z]);
291
- const T = B?.token === z ? B : void 0;
290
+ }, [a, A]);
291
+ const C = O?.token === A ? O : void 0;
292
292
  return {
293
- initialPage: T?.status === "success" ? T : void 0,
294
- total: T?.total ?? 0,
295
- status: T?.status ?? "loading",
296
- reload: () => $((i) => i + 1)
293
+ initialPage: C?.status === "success" ? C : void 0,
294
+ total: C?.total ?? 0,
295
+ status: C?.status ?? "loading",
296
+ reload: () => B((p) => p + 1)
297
297
  };
298
298
  }, U = { keyword: "", processStatus: "", startDate: "", endDate: "" }, be = [{ value: "pending", label: "待我处理" }, { value: "approved", label: "已审批" }], ae = (e) => {
299
299
  if (e === "") return !0;
300
300
  if (!/^\d{4}-\d{2}-\d{2}$/.test(e)) return !1;
301
301
  const a = /* @__PURE__ */ new Date(`${e}T00:00:00Z`);
302
302
  return Number.isFinite(a.getTime()) && a.toISOString().slice(0, 10) === e;
303
- }, we = ({ components: e, provider: a, onOpenApproval: m, statusOptions: F = [], pageSize: H = 20, refreshKey: $, themeKey: B, logger: R, className: f }) => {
304
- const { Input: z, Select: T, DatePicker: i, Button: h, Tabs: b } = e, d = ce(), [c, k] = x(U), [S, w] = x(""), [M, v] = x(""), [p, C] = x({ ...U, tab: "pending", page: 1, pageSize: H }), { initialPage: u, status: D, reload: L } = ve(a, p, $, R), O = E(!1), r = E(!0);
305
- j(() => (r.current = !0, () => {
306
- r.current = !1;
303
+ }, we = ({ components: e, provider: a, onOpenApproval: h, statusOptions: F = [], pageSize: W = 20, refreshKey: B, themeKey: O, logger: z, className: d }) => {
304
+ const { Input: A, Select: C, DateRangePicker: p, Button: u, Tabs: v } = e, f = ce(), [i, k] = V(U), [S, R] = V(0), [L, m] = V(""), [g, N] = V(""), [l, D] = V({ ...U, tab: "pending", page: 1, pageSize: W }), { initialPage: $, status: x, reload: r } = ve(a, l, B, z), t = M(!1), c = M(!0);
305
+ j(() => (c.current = !0, () => {
306
+ c.current = !1;
307
307
  }), []);
308
- const t = D === "loading", g = async (l) => {
309
- if (!O.current) {
310
- O.current = !0, v(""), I(R, { operation: "open", phase: "start" });
308
+ const n = x === "loading", K = async (o) => {
309
+ if (!t.current) {
310
+ t.current = !0, N(""), I(z, { operation: "open", phase: "start" });
311
311
  try {
312
- await m(l), I(R, { operation: "open", phase: "success" });
312
+ await h(o), I(z, { operation: "open", phase: "success" });
313
313
  } catch {
314
- r.current && v("审批详情打开失败,请重试"), I(R, { operation: "open", phase: "error" });
314
+ c.current && N("审批详情打开失败,请重试"), I(z, { operation: "open", phase: "error" });
315
315
  } finally {
316
- O.current = !1;
316
+ t.current = !1;
317
317
  }
318
318
  }
319
- }, s = (l) => {
320
- l !== p.tab && (w(""), v(""), C((o) => ({ ...o, tab: l, page: 1 })));
319
+ }, b = (o) => {
320
+ o !== l.tab && (m(""), N(""), D((y) => ({ ...y, tab: o, page: 1 })));
321
321
  };
322
- return /* @__PURE__ */ n("section", { className: ["make-app-approval", f].filter(Boolean).join(" "), "aria-label": "我的审批", children: /* @__PURE__ */ N(b, { items: be, value: p.tab, onChange: s, ariaLabel: "审批分类", children: [
323
- /* @__PURE__ */ N("form", { className: "make-app-approval-filters", onSubmit: (l) => {
324
- if (l.preventDefault(), !ae(c.startDate) || !ae(c.endDate)) {
325
- w("请选择有效日期");
322
+ return /* @__PURE__ */ s("section", { className: ["make-app-approval", d].filter(Boolean).join(" "), "aria-label": "我的审批", children: /* @__PURE__ */ T(v, { items: be, value: l.tab, onChange: b, ariaLabel: "审批分类", children: [
323
+ /* @__PURE__ */ T("form", { className: "make-app-approval-filters", onSubmit: (o) => {
324
+ if (o.preventDefault(), !ae(i.startDate) || !ae(i.endDate)) {
325
+ m("请选择有效日期");
326
326
  return;
327
327
  }
328
- if (c.startDate && c.endDate && c.startDate > c.endDate) {
329
- w("开始日期不能晚于结束日期");
328
+ if (i.startDate && i.endDate && i.startDate > i.endDate) {
329
+ m("开始日期不能晚于结束日期");
330
330
  return;
331
331
  }
332
- w(""), v(""), C((o) => ({ ...o, ...c, keyword: c.keyword.trim(), page: 1 }));
332
+ m(""), N(""), D((y) => ({ ...y, ...i, keyword: i.keyword.trim(), page: 1 }));
333
333
  }, children: [
334
- /* @__PURE__ */ N("div", { className: "make-app-approval-filter-field", children: [
335
- /* @__PURE__ */ n("label", { htmlFor: `${d}-keyword`, children: "申请单号或申请人" }),
336
- /* @__PURE__ */ n(z, { id: `${d}-keyword`, ariaLabel: "申请单号或申请人", placeholder: "搜索申请单号、申请人", value: c.keyword, onChange: (l) => k((o) => ({ ...o, keyword: l })) })
334
+ /* @__PURE__ */ T("div", { className: "make-app-approval-filter-field", children: [
335
+ /* @__PURE__ */ s("label", { htmlFor: `${f}-keyword`, children: "申请单号或申请人" }),
336
+ /* @__PURE__ */ s(A, { id: `${f}-keyword`, ariaLabel: "申请单号或申请人", placeholder: "搜索申请单号、申请人", value: i.keyword, onChange: (o) => k((y) => ({ ...y, keyword: o })) })
337
337
  ] }),
338
- /* @__PURE__ */ N("div", { className: "make-app-approval-filter-field", children: [
339
- /* @__PURE__ */ n("label", { htmlFor: `${d}-status`, children: "流程状态" }),
340
- /* @__PURE__ */ n(T, { id: `${d}-status`, ariaLabel: "流程状态", value: c.processStatus, options: [{ value: "", label: "全部流程状态" }, ...F], onChange: (l) => k((o) => ({ ...o, processStatus: l })) })
338
+ /* @__PURE__ */ T("div", { className: "make-app-approval-filter-field", children: [
339
+ /* @__PURE__ */ s("label", { htmlFor: `${f}-status`, children: "流程状态" }),
340
+ /* @__PURE__ */ s(C, { id: `${f}-status`, ariaLabel: "流程状态", value: i.processStatus, options: [{ value: "", label: "全部流程状态" }, ...F], onChange: (o) => k((y) => ({ ...y, processStatus: o })) })
341
341
  ] }),
342
- /* @__PURE__ */ N("fieldset", { className: "make-app-approval-date-field", children: [
343
- /* @__PURE__ */ n("legend", { children: "申请时间" }),
344
- /* @__PURE__ */ N("div", { className: "make-app-approval-date-range", children: [
345
- /* @__PURE__ */ n("div", { className: "make-app-approval-date-control", children: /* @__PURE__ */ n(i, { id: `${d}-start`, ariaLabel: "申请开始日期", placeholder: "开始日期", value: c.startDate, onChange: (l) => k((o) => ({ ...o, startDate: l })) }) }),
346
- /* @__PURE__ */ n("span", { children: "至" }),
347
- /* @__PURE__ */ n("div", { className: "make-app-approval-date-control", children: /* @__PURE__ */ n(i, { id: `${d}-end`, ariaLabel: "申请结束日期", placeholder: "结束日期", value: c.endDate, onChange: (l) => k((o) => ({ ...o, endDate: l })) }) })
348
- ] })
342
+ /* @__PURE__ */ T("fieldset", { className: "make-app-approval-date-field", children: [
343
+ /* @__PURE__ */ s("legend", { children: "申请时间" }),
344
+ /* @__PURE__ */ s("div", { className: "make-app-approval-date-control", children: /* @__PURE__ */ s(
345
+ p,
346
+ {
347
+ id: `${f}-period`,
348
+ ariaLabel: "申请时间",
349
+ placeholder: ["开始日期", "结束日期"],
350
+ value: [i.startDate, i.endDate],
351
+ onChange: ([o, y]) => k((E) => ({ ...E, startDate: o, endDate: y }))
352
+ },
353
+ S
354
+ ) })
349
355
  ] }),
350
- /* @__PURE__ */ N("div", { className: "make-app-approval-filter-actions", children: [
351
- /* @__PURE__ */ n(h, { htmlType: "submit", variant: "primary", children: "查询" }),
352
- /* @__PURE__ */ n(h, { htmlType: "button", variant: "default", onClick: () => {
353
- k(U), w(""), v(""), C((l) => ({ ...l, ...U, page: 1 }));
356
+ /* @__PURE__ */ T("div", { className: "make-app-approval-filter-actions", children: [
357
+ /* @__PURE__ */ s(u, { htmlType: "submit", variant: "primary", children: "查询" }),
358
+ /* @__PURE__ */ s(u, { htmlType: "button", variant: "default", onClick: () => {
359
+ k(U), R((o) => o + 1), m(""), N(""), D((o) => ({ ...o, ...U, page: 1 }));
354
360
  }, children: "重置" }),
355
- /* @__PURE__ */ n(h, { htmlType: "button", variant: "default", disabled: t, loading: t, onClick: L, children: "刷新" })
361
+ /* @__PURE__ */ s(u, { htmlType: "button", variant: "default", disabled: n, loading: n, onClick: r, children: "刷新" })
356
362
  ] })
357
363
  ] }),
358
- S && /* @__PURE__ */ n("p", { role: "alert", className: "make-app-approval-error", children: S }),
359
- M && /* @__PURE__ */ n("p", { role: "alert", className: "make-app-approval-error", children: M }),
360
- /* @__PURE__ */ N("div", { className: "make-app-approval-results", "aria-busy": t, children: [
361
- /* @__PURE__ */ n(he, { components: e, initialPage: u, query: p, provider: a, onOpen: (l) => {
362
- g(l);
363
- }, onReload: L, themeKey: B, logger: R }),
364
- t && /* @__PURE__ */ n("div", { className: "make-app-approval-overlay", role: "status", children: "正在加载审批记录…" }),
365
- D === "error" && /* @__PURE__ */ N("div", { className: "make-app-approval-overlay", role: "alert", children: [
366
- /* @__PURE__ */ n("span", { children: "审批列表加载失败,请重试" }),
367
- /* @__PURE__ */ n(h, { htmlType: "button", variant: "default", onClick: L, children: "重试" })
364
+ L && /* @__PURE__ */ s("p", { role: "alert", className: "make-app-approval-error", children: L }),
365
+ g && /* @__PURE__ */ s("p", { role: "alert", className: "make-app-approval-error", children: g }),
366
+ /* @__PURE__ */ T("div", { className: "make-app-approval-results", "aria-busy": n, children: [
367
+ /* @__PURE__ */ s(ge, { components: e, initialPage: $, query: l, provider: a, onOpen: (o) => {
368
+ K(o);
369
+ }, onReload: r, themeKey: O, logger: z }),
370
+ n && /* @__PURE__ */ s("div", { className: "make-app-approval-overlay", role: "status", children: "正在加载审批记录…" }),
371
+ x === "error" && /* @__PURE__ */ T("div", { className: "make-app-approval-overlay", role: "alert", children: [
372
+ /* @__PURE__ */ s("span", { children: "审批列表加载失败,请重试" }),
373
+ /* @__PURE__ */ s(u, { htmlType: "button", variant: "default", onClick: r, children: "重试" })
368
374
  ] })
369
375
  ] })
370
376
  ] }) });
371
377
  }, Ne = (e) => {
372
378
  const a = e.pageSize ?? 20;
373
379
  if (!e.scopeKey.trim()) throw new Error("MyApprovalsPage requires scopeKey");
374
- for (const m of ["Input", "Select", "DatePicker", "Button", "Tabs"])
375
- if (!e.components?.[m]) throw new Error(`MyApprovalsPage requires components.${m}`);
380
+ for (const h of ["Input", "Select", "DateRangePicker", "Button", "Tabs"])
381
+ if (!e.components?.[h]) throw new Error(`MyApprovalsPage requires components.${h}`);
376
382
  if (!Number.isSafeInteger(a) || a < 1 || a > 200) throw new Error("pageSize must be an integer from 1 to 200");
377
- return /* @__PURE__ */ n(we, { ...e, pageSize: a }, JSON.stringify([e.scopeKey, a]));
383
+ return /* @__PURE__ */ s(we, { ...e, pageSize: a }, JSON.stringify([e.scopeKey, a]));
378
384
  };
379
385
  export {
380
386
  Ne as MyApprovalsPage
package/dist/styles.css CHANGED
@@ -31,8 +31,6 @@
31
31
  --make-approval-radius: var(--radius, 6px);
32
32
  color: var(--make-approval-foreground);
33
33
  background: var(--make-approval-bg);
34
- border: 1px solid var(--make-approval-border);
35
- border-radius: var(--make-approval-radius);
36
34
  font-family: inherit;
37
35
  font-size: 14px;
38
36
  line-height: 1.5;
@@ -40,15 +38,28 @@
40
38
  min-width: 0;
41
39
  box-sizing: border-box;
42
40
  }
43
- .make-app-approval-filters { display: flex; align-items: end; flex-wrap: wrap; gap: 14px; padding: 20px; }
41
+ /* 为宿主控件的聚焦外框预留空间,避免被列表的 overflow 裁切。 */
42
+ .make-app-approval-filters { display: flex; flex: none; align-items: end; flex-wrap: wrap; gap: 14px; padding: 0 4px 12px; }
44
43
  .make-app-approval-filter-field { display: flex; flex-direction: column; gap: 6px; width: 200px; min-width: 0; }
45
44
  .make-app-approval-filter-field > label { color: var(--make-approval-muted); font-size: 12px; }
46
45
  .make-app-approval-date-field { border: 0; padding: 0; margin: 0; min-width: 0; }
47
46
  .make-app-approval-date-field > legend { padding: 0; margin-bottom: 6px; color: var(--make-approval-muted); font-size: 12px; }
48
- .make-app-approval-date-range, .make-app-approval-filter-actions { display: flex; align-items: center; gap: 8px; }
49
- .make-app-approval-date-control { min-width: 0; width: 145px; }
47
+ .make-app-approval-filter-actions { display: flex; align-items: center; gap: 8px; }
48
+ .make-app-approval-date-control { min-width: 0; width: 200px; max-width: 100%; }
50
49
  .make-app-approval-results, .make-app-approval-table-frame { position: relative; }
51
50
  .make-app-approval-canvas { width: 100%; height: var(--make-approval-table-height, 440px); min-height: 200px; overflow: hidden; color: var(--make-approval-foreground); }
51
+ /* 宿主提供确定的可用高度,并让 Tabs 根节点/活动面板传递 flex 剩余高度。 */
52
+ .make-app-approval--fill,
53
+ .make-app-approval--fill .make-app-approval-results,
54
+ .make-app-approval--fill .make-app-approval-table-frame {
55
+ display: flex;
56
+ flex: 1;
57
+ flex-direction: column;
58
+ min-width: 0;
59
+ min-height: 0;
60
+ overflow: hidden;
61
+ }
62
+ .make-app-approval--fill .make-app-approval-canvas { flex: 1; height: auto; min-height: 0; }
52
63
  .make-app-approval-overlay { position: absolute; inset: 40px 0 0; display: flex; align-items: center; justify-content: center; gap: 12px; background: var(--make-approval-bg); color: var(--make-approval-muted); }
53
64
  .make-app-approval-table-feedback { position: absolute; top: 48px; right: 12px; left: 12px; display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px; padding: 8px 12px; color: var(--make-approval-muted); background: var(--make-approval-bg); border: 1px solid var(--make-approval-border); border-radius: var(--make-approval-radius); }
54
65
  .make-app-approval-table-loading { position: absolute; right: 16px; bottom: 16px; display: inline-flex; align-items: center; gap: 8px; padding: 6px 10px; color: var(--make-approval-muted); background: var(--make-approval-bg); border: 1px solid var(--make-approval-border); border-radius: var(--make-approval-radius); font-size: 12px; line-height: 20px; white-space: nowrap; pointer-events: none; }
@@ -61,9 +72,6 @@
61
72
  .make-app-approval-accessible-rows { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
62
73
  .make-app-approval-accessible-rows:focus-within { position: relative; width: auto; height: auto; margin: 0; padding: 12px 20px; clip-path: none; white-space: normal; background: var(--make-approval-bg); list-style: none; }
63
74
  .make-app-approval-accessible-rows li { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
64
- @media (max-width: 680px) {
65
- .make-app-approval-filters { padding: 12px; }
66
- }
67
75
 
68
76
  .make-app-approval-detail { color: var(--make-approval-foreground, inherit); font-family: inherit; min-width: 0; display: flex; flex-direction: column; min-height: 100%; }
69
77
  .make-app-approval-detail-header { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 16px; padding: 24px 28px; border-bottom: 1px solid var(--make-approval-border, #e2e8f0); }
@@ -2,6 +2,14 @@
2
2
 
3
3
  本文是 `@qfei-design/make-app-approval` 的接入依据。先读当前能力,再接入对应入口;不要把“后续计划”当作已经存在的组件。
4
4
 
5
+ ## 列表铺满宿主内容区
6
+
7
+ 当前源码统一移除审批页外框,筛选区保留底部 12px、左右各 4px,以容纳宿主控件的聚焦外框。宿主通过 `className="make-app-approval--fill"` 开启剩余高度布局,外层工作区提供确定高度;Tabs 适配器的根节点、中间容器和活动面板须传递纵向 flex 高度(`flex: 1; min-height: 0; min-width: 0`),页签栏不收缩。包只负责自己拥有的结果区、表格框架和 Canvas,不能修改宿主组件库的内部样式。
8
+
9
+ 筛选换行后由布局自动重新分配高度,包已有 ResizeObserver 更新画布;宿主无需固定减去头部像素或重复监听尺寸。未开启撑满模式的嵌入式场景仍默认使用 440px 表格高度。
10
+
11
+ 这些样式由 0.2.0 提供。使用公网 0.1.0 的宿主升级后可删除包侧兼容规则,但仍须保留自己 Tabs 的高度链。完整合同见 [公开 API](../PUBLIC_API.md#列表布局与剩余高度)。
12
+
5
13
  ## 当前交付范围
6
14
 
7
15
  目前已提供桌面/平板“我的审批”列表 `MyApprovalsPage`、流程状态 `ApprovalStatus`、普通记录编辑限制工具。列表使用 `@qfei-design/canvas-table@^1.3.1`,本项目以 `1.3.1` 验证。
@@ -17,7 +25,7 @@
17
25
  | 能力 | 包侧提供 | 宿主 UI / App Service 提供 | 后端提供 | 当前情况 |
18
26
  | --- | --- | --- | --- | --- |
19
27
  | 审批列表页面 | 两个页签、筛选布局、CanvasTable 列、虚拟滚动和状态反馈 | 安装组件、导入 CSS、挂载到路由 | 无 | 已实现 |
20
- | 基础控件 | 固定页签/筛选功能、受控值、日期校验、查询/重置/刷新/重试逻辑 | 必传 `components`:Input、Select、DatePicker、Button、Tabs;适配事件并继承宿主组件库/主题 | 无 | 已实现,无原生控件兜底 |
28
+ | 基础控件 | 固定页签/筛选功能、受控值、日期校验、查询/重置/刷新/重试逻辑 | 必传 `components`:Input、Select、DateRangePicker、Button、Tabs;适配事件并继承宿主组件库/主题 | 无 | 已实现,无原生控件兜底 |
21
29
  | 列表查询 | 调用 `provider.loadPage`,管理滚动事件、数据页请求、取消、缓存、防旧结果覆盖与重试 | UI 复用统一请求层访问 App Service;Service 转换接口参数和结果 | 当前用户范围、页签归类、筛选、排序、分页和总数 | 包接口已实现,真实接口待确认 |
22
30
  | 应用与身份隔离 | `scopeKey` 改变时取消旧请求,重置数据/页签/筛选 | 提供包含应用、身份会话、环境的稳定上下文标识;退出时卸载 | 验证实际身份和 App 访问权限 | 已实现 |
23
31
  | 流程状态 | 已确认状态码的统一文字;未知码显示“状态未知” | 提供原始流程状态;只传后端支持的状态筛选项 | 返回状态码,不以个人任务状态替代流程状态 | 已实现;CANCELLED 显示已撤回 |
@@ -71,7 +79,7 @@ const provider: ApprovalListProvider = {
71
79
 
72
80
  控件合同见 [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
81
 
74
- - 输入、状态和日期都接收包提供的受控值,并通过 `onChange(value)` 回传;不要把 DOM Event、Dayjs 或组件库的选项对象直接回传给包。Antd Input 取 `event.target.value`,DatePicker 取 `date.format("YYYY-MM-DD")`,清空均回传空字符串。
82
+ - 输入、状态和日期都接收包提供的受控值,并通过 `onChange(value)` 回传;不要把 DOM Event、Dayjs 或组件库的选项对象直接回传给包。Antd Input 取 `event.target.value`;DateRangePicker 使用 `DatePicker.RangePicker`,两个日期各转为 `date.format("YYYY-MM-DD")`,清空回传 `["", ""]`。包只挂载一个范围组件,不拼装两个独立单日期控件;其他组件库使用各自的范围控件。
75
83
  - 包负责筛选草稿与已查询条件、查询/重置、日期合法性及起止校验、刷新/重试和虚拟滚动;宿主控件不得另起请求或根据流程状态归类记录。
76
84
  - 所有按钮包括查询、重置、刷新、首屏/后续页/表格重试和键盘详情入口都使用注入的 Button。查询映射为 `htmlType="submit"`,其他按钮为 `button`,保证键盘提交与操作不会混淆。
77
85
  - Tabs 只渲染一次活动内容;不得在每个面板同时挂载 children,否则会创建多个表格。键盘、焦点、tab 与 panel 关联沿用宿主组件库。
@@ -125,7 +133,7 @@ const provider: ApprovalListProvider = {
125
133
 
126
134
  - “待我处理”只包含后端返回的当前用户真实待办。
127
135
  - 本人同意而下一审批人尚未处理时,这条先离开待办;整个流程结束后才进入已审批。包不会用 `processStatus` 或 `taskId` 计算所属页签。
128
- - 列表不合并、不去重、不按状态过滤返回行。“我的审批”不显示“—”,此前原型中的短横杠已由用户确认为图片错误;业务对象未配置审批时仍可显示“—”。
136
+ - 列表不合并、不去重、不按状态过滤返回行。“我的审批”不显示“—”,此前原型中的短横杠已由用户确认为图片错误;业务对象流程状态缺失或为 UNSUBMITTED 时复用宿主字段空值样式,显示灰色 `-`。
129
137
  - 新建表单保存必须检查必填,失败时标红并阻止保存。当前用户是发起人且后端流程状态为审批中时显示撤回,其他状态隐藏;无需额外的允许撤回标识。撤回接口已补充:bpm-process + UpdateResource,必填 bpmKey、recordId、comment,成功状态 CANCELLED;宿主撤回入口等待发起人和流程 key 来源确认,最终身份、状态及并发校验由后端负责。
130
138
  - 筛选选项来自 `statusOptions`,只传后端支持的真实状态码。该属性只定义筛选项,不覆盖包的状态显示规则;新增流程状态应在确认后统一更新公共包。
131
139
  - `bpmDefinitionKey` 有值即有效且启用;无值使用普通业务规则。审批新建字段源是 `bpm-start-config.fields`,不可再与普通 IAM 字段取交集;对象/操作/行级权限仍保留。
@@ -147,7 +155,7 @@ const provider: ApprovalListProvider = {
147
155
  ## 验收清单
148
156
 
149
157
  - 普通 Make App 提供 components、provider、路由、详情回调和样式导入后即可显示统一列表,无需宿主私有列配置。
150
- - 宿主注入的 Input/Select/DatePicker/Button/Tabs 实际生效;主题、禁用、焦点与弹层样式跟随宿主,输入/清空/Enter 查询/日期倒置/重置行为保持一致。
158
+ - 宿主注入的 Input/Select/DateRangePicker/Button/Tabs 实际生效;主题、禁用、焦点与弹层样式跟随宿主,输入/清空/Enter 查询/日期倒置/重置行为保持一致。
151
159
  - 待我处理、已审批请求参数正确,前端显示后端原始顺序和总数;滚动加载后续批次,查询/重置回到顶部。
152
160
  - 切换页签、应用、账号、环境及卸载时没有旧数据回流;详情开关不重复加载,办理成功才刷新。
153
161
  - 状态/日期/关键字由 Service 透传正确;空数据保留表头;查询失败能重试,401/403 不能包装成成功空列表。
@@ -215,3 +223,20 @@ const provider: ApprovalListProvider = {
215
223
  ### 2026-10-16 撤回接口更新
216
224
 
217
225
  Workflow 已提供 `POST /make/workflow/v1/bpm-process`,target `MakeService.UpdateResource`,请求 `appKey/entityKey/bpmKey/recordId/comment`;comment trim 后必填且不超过 1024 字。成功返回 `CANCELLED`,业务记录状态和锁由后端 consumer 异步更新。公共包已补状态与意见校验;宿主 Service 保留原始状态、Content-Type 和正文,不代替后端判断能否撤回。不要将请求成功直接等同于业务记录已解锁,也不要自动重发失败的撤回请求。
226
+
227
+ ## Make App 统一保存标准(2026-10-17)
228
+
229
+ 所有宿主的业务新建及普通编辑表单(含移动端)使用“保存”。仅新建且 Schema 存在 `bpmDefinitionKey` 时,保存后自动发起审批;普通编辑只更新原记录,沿用普通 IAM,不读取启动字段或自动发起。有效与启用状态由后端判断。已有记录需要发起或恢复发起时,使用独立入口和原 recordID。
230
+
231
+ 接入步骤由宿主完成:
232
+
233
+ 1. 仅新建且有 key 时读取启动配置,沿用节点可见、可编辑及必填规则;普通编辑或无 key 时沿用普通字段权限。
234
+ 2. 点击保存先校验;校验失败标红,不写记录、不发起。附件和关系复用宿主保存能力。
235
+ 3. 新建保存成功取得 recordID 后,有 key 时调用发起接口,使用返回的真实流程状态刷新列表并关闭表单;普通编辑或没有 key 时不调用发起接口。
236
+ 4. 防止重复点击;记录已保存但发起失败时保留同一 ID,Toast 保留原错误和追踪信息,禁止再次创建。保存结果不明时也先核查,不能盲目重试。
237
+
238
+ 公共包提供统一的审批列表、办理详情、状态映射及普通禁编能力;业务表单控件、保存/发起编排、网络接口与凭据由宿主负责。业务对象空状态使用 `-`,列表中缺失/异常流程状态仍显示“状态未知”,不能混用这两种展示。
239
+
240
+ ## 业务详情标题的状态 Tag(2026-10-16)
241
+
242
+ 宿主将记录的 `qfei_process_instance_status` 传给 `/react` 入口的 `ApprovalStatus`,放在对象/记录标题之后。null、undefined、UNSUBMITTED 对应业务空值 `-`,宿主不挂载 Tag;其他值由包统一显示文案和主题,未知值保持“状态未知”。该规则依据记录本身,不依赖对象目前是否仍启用审批。关联详情每层使用自己的记录状态,移动端 App 自有页头沿用同一规则;包不负责宿主抽屉布局和移动容器页头注册。
@@ -19,7 +19,7 @@
19
19
 
20
20
  ## 基础控件接入已实现
21
21
 
22
- 宿主必须传入 `components`(Input、Select、DatePicker、Button、Tabs)。包固定筛选/校验/查询/重置/刷新/重试与 CanvasTable 滚动功能,宿主仅适配组件值和事件、提供主题。没有原生控件兜底;Antd 示例随包交付,其他组件库按相同合同对接。此前列表接入方式需补传此属性,根入口工具不受影响。
22
+ 宿主必须传入 `components`(Input、Select、DateRangePicker、Button、Tabs)。包固定筛选/校验/查询/重置/刷新/重试与 CanvasTable 滚动功能,宿主仅适配组件值和事件、提供主题。没有原生控件兜底;Antd 示例随包交付,其他组件库按相同合同对接。此前列表接入方式需补传此属性,根入口工具不受影响。
23
23
 
24
24
  ## 已确认的后端责任
25
25
 
@@ -25,7 +25,7 @@ always-auth=true
25
25
  ```
26
26
 
27
27
  安装源设置不改变发布目标。公共发布工具为每个目标显式指定包作用域的 registry,
28
- 因此上述 npmjs 安装配置不会将内部发布重定向到 npmjs。当前审批包版本为 `0.1.0`,
28
+ 因此上述 npmjs 安装配置不会将内部发布重定向到 npmjs。本次审批包发布版本为 `0.2.0`,
29
29
  CanvasTable 依赖版本保持 `1.3.1`。
30
30
 
31
31
  ## 分支、验证与通道
@@ -12,9 +12,11 @@ export const antdApprovalComponents: ApprovalUIComponents = {
12
12
  id={id} aria-label={ariaLabel} value={value} disabled={disabled}
13
13
  options={options.map(option => ({ ...option }))} onChange={value => onChange(value ?? "")} style={{ width: "100%" }}
14
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%" }}
15
+ DateRangePicker: ({ id, ariaLabel, value, onChange, placeholder, disabled }) => <DatePicker.RangePicker
16
+ id={{ start: id, end: `${id}-end` }} aria-label={ariaLabel}
17
+ value={[value[0] ? dayjs(value[0]) : null, value[1] ? dayjs(value[1]) : null]}
18
+ placeholder={placeholder ? [...placeholder] : undefined} disabled={disabled}
19
+ format="YYYY-MM-DD" allowClear onChange={dates => onChange([dates?.[0]?.format("YYYY-MM-DD") ?? "", dates?.[1]?.format("YYYY-MM-DD") ?? ""])} style={{ width: "100%" }}
18
20
  />,
19
21
  Button: ({ children, htmlType, variant, onClick, disabled, loading, ariaLabel }) => <Button
20
22
  type={variant} htmlType={htmlType} onClick={onClick} disabled={disabled} loading={loading} aria-label={ariaLabel}
package/package.ai.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "packageName": "@qfei-design/make-app-approval",
3
- "minimumRuntimeVersion": "0.1.0",
4
- "published": false,
3
+ "minimumRuntimeVersion": "0.2.0",
4
+ "published": true,
5
5
  "readOrder": [
6
6
  "package.ai.json",
7
7
  "README.md",
@@ -49,7 +49,7 @@
49
49
  ],
50
50
  "hostResponsibilities": [
51
51
  "导航、路由、登录、应用/身份/环境上下文及 scopeKey",
52
- "必传 components:Input / Select / DatePicker / Button / Tabs,适配组件库事件并继承宿主主题",
52
+ "必传 components:Input / Select / DateRangePicker / Button / Tabs,适配组件库事件并继承宿主主题",
53
53
  "提供 provider.loadPage,复用 UI -> App Service 请求层并透传 AbortSignal",
54
54
  "将最终后端接口转换成 ApprovalListQuery / ApprovalPage;不在前端归类或拼接分页",
55
55
  "提供 onOpenApproval;办理成功更新 refreshKey 并刷新关联业务页面",
@@ -62,7 +62,7 @@
62
62
  "页签归类、筛选和分页由后端完成,不在包内判断本人已办是否结束",
63
63
  "普通入口禁编不等同于任务节点操作授权",
64
64
  "不保存 Token,不直接调用 Make 后端,不复制宿主私有组件",
65
- "当前版本尚未发布,先构建打包文件进行宿主验证",
65
+ "通过 GitLab 标签任务发布;消费前确认 npmjs 中存在目标版本,不能仅凭本地版本号判断",
66
66
  "MyApprovalsPage 已实现,只从 /list 使用;详情与节点授权动作从 /detail 使用;历史/移动仍未实现",
67
67
  "CanvasTable 是可选 peer:列表 UI 必须安装,纯 Service 无需安装",
68
68
  "宿主接入前先读 docs/host-integration.md 和可类型检查示例",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qfei-design/make-app-approval",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Make App 审批公共组件与普通记录编辑限制。",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
package/recipes.json CHANGED
@@ -10,7 +10,7 @@
10
10
  ],
11
11
  "hostProvides": [
12
12
  "CanvasTable ^1.3.1",
13
- "components: ApprovalUIComponents(Input、Select、DatePicker、Button、Tabs)",
13
+ "components: ApprovalUIComponents(Input、Select、DateRangePicker、Button、Tabs)",
14
14
  "scopeKey",
15
15
  "provider.loadPage(query, { signal }) -> { rows, total }",
16
16
  "onOpenApproval(row)",