adspecs 0.1.33 → 0.1.34
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/.adspecs/paths.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.qoder-plugin/plugin.json +1 -1
- package/.workbuddy-plugin/plugin.json +1 -1
- package/CLAUDE.md +2 -2
- package/README.md +1 -1
- package/package.json +1 -1
- package/references/ant6-front-standard/02-/347/273/204/344/273/266/350/247/204/350/214/203.md +7 -0
- package/references/ant6-front-standard/03-/345/210/227/350/241/250/350/247/204/350/214/203.md +222 -147
- package/references/ant6-front-standard/04-/350/241/250/345/215/225/350/247/204/350/214/203.md +8 -0
- package/references/ant6-front-standard/09-/345/270/270/350/247/201/351/227/256/351/242/230/350/247/204/350/214/203.md +1 -1
- package/references/ant6-front-standard/index.md +100 -99
- package/references/yudaocloud-end-standard/01-Java/345/220/216/347/253/257/347/274/226/347/240/201/350/247/204/350/214/203.md +1166 -0
- package/references/yudaocloud-end-standard/02-/346/225/260/346/215/256/345/272/223/350/256/276/350/256/241/344/270/216/344/275/277/347/224/250/350/247/204/350/214/203.md +1023 -0
- package/references/yudaocloud-end-standard/03-/346/225/260/346/215/256/345/255/227/345/205/270/344/270/216/350/217/234/345/215/225/350/247/204/350/214/203.md +336 -0
- package/references/yudaocloud-end-standard/index.md +14 -3
- package/references/yudaocloud-end-standard/system_dict_type.sql +186 -0
- package/skills/adspecs-plan/SKILL.md +1 -1
- package/skills/adspecs-utest/SKILL.md +62 -40
- package/skills/project-init/SKILL.md +4 -4
- package/src/lib/paths-defaults.js +1 -1
- package/src/lib/readme-gen.js +1 -1
- package/references/ant6-front-standard/05-/345/211/215/347/253/257/347/274/226/347/240/201/350/247/204/350/214/203.md +0 -1443
package/.adspecs/paths.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"sow_dir": "docs/05-sow",
|
|
9
9
|
"prd_dir": "docs/20-prd",
|
|
10
10
|
"design_dir": "docs/30-system-design",
|
|
11
|
-
"test_report_dir": "docs/80-development",
|
|
11
|
+
"test_report_dir": "docs/80-development/802-test",
|
|
12
12
|
"export_dir": "docs/90-export",
|
|
13
13
|
"front_demo_dir": "front-demo",
|
|
14
14
|
"front_prototype_dir": "web-ui",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adspecs",
|
|
3
3
|
"displayName": "adspecs AI 工程规范驱动开发插件",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.34",
|
|
5
5
|
"description": "AI-first specification-driven development plugin for ECP platform full-lifecycle development",
|
|
6
6
|
"descriptionZh": "AI优先的工程规范驱动开发插件,覆盖需求分析、PRD、系统设计、任务拆解、TDD、代码评审、Wiki同步全流程",
|
|
7
7
|
"author": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adspecs",
|
|
3
3
|
"displayName": "adspecs AI 工程规范驱动开发插件",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.34",
|
|
5
5
|
"description": "AI-first specification-driven development plugin for ECP platform full-lifecycle development",
|
|
6
6
|
"descriptionZh": "AI优先的工程规范驱动开发插件,覆盖需求分析、PRD、系统设计、任务拆解、TDD、代码评审、Wiki同步全流程",
|
|
7
7
|
"author": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adspecs",
|
|
3
3
|
"displayName": "adspecs AI 工程规范驱动开发插件",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.34",
|
|
5
5
|
"description": "AI-first specification-driven development plugin for ECP platform full-lifecycle development",
|
|
6
6
|
"descriptionZh": "AI优先的工程规范驱动开发插件,覆盖需求分析、PRD、系统设计、任务拆解、TDD、代码评审、Wiki同步全流程",
|
|
7
7
|
"author": {
|
package/CLAUDE.md
CHANGED
|
@@ -138,7 +138,7 @@ Skills support configurable output directories via `.adspecs/paths.json` in the
|
|
|
138
138
|
"sow_dir": "docs/05-sow",
|
|
139
139
|
"prd_dir": "docs/20-prd",
|
|
140
140
|
"design_dir": "docs/30-system-design",
|
|
141
|
-
"test_report_dir": "docs/80-development",
|
|
141
|
+
"test_report_dir": "docs/80-development/802-test",
|
|
142
142
|
"session_log_dir": "docs/80-development/800-log",
|
|
143
143
|
"export_dir": "docs/90-export",
|
|
144
144
|
"wiki_dir": "wiki"
|
|
@@ -156,7 +156,7 @@ Skills support configurable output directories via `.adspecs/paths.json` in the
|
|
|
156
156
|
| `sow_dir` | `docs/05-sow` | adspecs-write-sow |
|
|
157
157
|
| `prd_dir` | `docs/20-prd` | adspecs-prd, adspecs-plan (input), adspecs-front-spec (input), wiki-update (source) |
|
|
158
158
|
| `design_dir` | `docs/30-system-design` | adspecs-plan (output), adspecs-tasks, adspecs-front-spec (output), adspecs-front-tasks, adspecs-utest (input), wiki-update (source) |
|
|
159
|
-
| `test_report_dir` | `docs/80-development`
|
|
159
|
+
| `test_report_dir` | `docs/80-development/802-test` | adspecs-utest (unit test reports, saved by module subdirectory) |
|
|
160
160
|
| `session_log_dir` | `docs/80-development/800-log` | session-logger (AI session logs, triggered by keyword / PreCompact / session end) |
|
|
161
161
|
| `export_dir` | `docs/90-export` | adspecs-export-word (Word 导出输出) |
|
|
162
162
|
| `wiki_dir` | `wiki` | wiki-update (output target) |
|
package/README.md
CHANGED
|
@@ -391,7 +391,7 @@ Skill 支持通过 `.adspecs/paths.json` 自定义输出目录:
|
|
|
391
391
|
"sow_dir": "docs/05-sow",
|
|
392
392
|
"prd_dir": "docs/20-prd",
|
|
393
393
|
"design_dir": "docs/30-system-design",
|
|
394
|
-
"test_report_dir": "docs/80-development",
|
|
394
|
+
"test_report_dir": "docs/80-development/802-test",
|
|
395
395
|
"export_dir": "docs/90-export",
|
|
396
396
|
"front_demo_dir": "front-demo",
|
|
397
397
|
"front_prototype_dir": "web-ui",
|
package/package.json
CHANGED
package/references/ant6-front-standard/02-/347/273/204/344/273/266/350/247/204/350/214/203.md
CHANGED
|
@@ -187,3 +187,10 @@ render: (_, record) => dateFormatter(record.createTime) || '-',
|
|
|
187
187
|
> **注意**:如果后端 `createTime` 可能是 number/Date,统一用 `new Date(value).getTime()` 包装再比较。
|
|
188
188
|
|
|
189
189
|
---
|
|
190
|
+
|
|
191
|
+
## 4、<Alert />组件使用规范
|
|
192
|
+
|
|
193
|
+
- Ant Design 6 中 `Alert` 的 `message` 属性已弃用,控制台报 `[antd: Alert] message is deprecated. Please use title instead.`
|
|
194
|
+
- **严禁**写 `<Alert message="..." />`,**必须**改为 `<Alert title="..." />`
|
|
195
|
+
- 全仓库排查命令:`grep -rn "Alert" web-ui/src --include="*.tsx" | grep "message="`
|
|
196
|
+
- 已清理范围:本体模块(detail 编辑锁提示条、BindingTab、GraphTab、CleanupModal、VersionDiffDrawer、YamlTab 等)——新增代码必须遵守,勿让该警告回潮
|
package/references/ant6-front-standard/03-/345/210/227/350/241/250/350/247/204/350/214/203.md
CHANGED
|
@@ -1,147 +1,222 @@
|
|
|
1
|
-
# 列表规范
|
|
2
|
-
|
|
3
|
-
> 适用范围: React 19 + TypeScript 6 + Ant Design 6 + ProComponents
|
|
4
|
-
> 版本: v3.
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## 1、列表页标准模式(ProTable)
|
|
9
|
-
|
|
10
|
-
> 所有「带筛选 + 分页」的列表都**必须**使用 `ProTable`,**不要再用** `Card + Form + Table`。
|
|
11
|
-
> 树形列表(dept/menu)例外,用本地 `dataSource` + `pagination={false}` + `expandable`。
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
1
|
+
# 列表规范
|
|
2
|
+
|
|
3
|
+
> 适用范围: React 19 + TypeScript 6 + Ant Design 6 + ProComponents
|
|
4
|
+
> 版本: v3.4
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1、列表页标准模式(ProTable)
|
|
9
|
+
|
|
10
|
+
> 所有「带筛选 + 分页」的列表都**必须**使用 `ProTable`,**不要再用** `Card + Form + Table`。
|
|
11
|
+
> 树形列表(dept/menu)例外,用本地 `dataSource` + `pagination={false}` + `expandable`。
|
|
12
|
+
|
|
13
|
+
**三条硬性要求**:
|
|
14
|
+
|
|
15
|
+
1. **高级筛选区**:`search={{ layout: 'vertical', span: <按筛选项数量分档> }}` —— 表单项 **vertical 布局**(label 在上),**span 按筛选项数量分档**(见 §1.1a)
|
|
16
|
+
2. **操作栏**:若表格有操作列,宽度统一 `width: ACTION_COL_WIDTH`(= **150**,以 `TableActions` 组件导出为准,`fixed: 'right'`,`search: false`)
|
|
17
|
+
3. **操作项展示规则**:操作项 **≤ 3 个平铺展示**;**> 3 个**时前 3 个平铺,其余收进 `...`,**鼠标悬停展开**。展示顺序按**后台菜单功能设置排序**(菜单树的按钮节点 `sort` 升序);具体显示的项**按用户权限与数据状态动态计算,不得写死**——统一使用通用组件 `<TableActions />`(见 §1.3)
|
|
18
|
+
|
|
19
|
+
### 1.1a 高级筛选区 span 分档
|
|
20
|
+
|
|
21
|
+
> vertical 布局下,span 决定每行可容纳的筛选项数量(24 栅格制)。**筛选项数量 N** = 未设 `search: false` 且带 `dataIndex`/`valueType` 的列数(含 `hideInTable: true` 的隐藏搜索列,如日期区间拆分列)。
|
|
22
|
+
|
|
23
|
+
| 筛选项数量 N | span | 每行项数 | 示例 |
|
|
24
|
+
| ------------ | ---- | -------- | ---- |
|
|
25
|
+
| N ≤ 2 | 8 | 3 | `search={{ layout: 'vertical', span: 8 }}` |
|
|
26
|
+
| N = 3 | 6 | 4 | `search={{ layout: 'vertical', span: 6 }}` |
|
|
27
|
+
| N = 4-6 | 4 | 6 | `search={{ layout: 'vertical', span: 4 }}` |
|
|
28
|
+
|
|
29
|
+
- **日期区间(dateRange)**:组件约占两栏宽度。当 N = 4-6 且含日期区间时,日期列通过 `order`(Form 排序,数值大者靠后)排到搜索表单**最后**,自然换行到第二行;N ≤ 3(span 8/6)时日期项宽度充足,无需处理
|
|
30
|
+
- **上限**:单页筛选项建议 ≤ 6 项;超过时靠高级筛选区默认「展开/收起」自折叠,不刻意堆满
|
|
31
|
+
|
|
32
|
+
### 1.1 标准模板
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { useRef, useState } from 'react';
|
|
36
|
+
import { App, Button, Space } from 'antd';
|
|
37
|
+
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons';
|
|
38
|
+
import { ProTable, type ProColumns, type ActionType } from '@ant-design/pro-components';
|
|
39
|
+
import { getXxxPage, deleteXxx, deleteXxxList, type XxxVO } from '@/api/xxx';
|
|
40
|
+
import Access from '@/components/Access';
|
|
41
|
+
import TableActions, { ACTION_COL_WIDTH, type TableActionItem } from '@/components/TableActions';
|
|
42
|
+
import DictTag from '@/components/DictTag';
|
|
43
|
+
import { dateFormatter } from '@/utils/formatTime';
|
|
44
|
+
import { useDictStore } from '@/stores/useDictStore';
|
|
45
|
+
import XxxModal from './components/XxxModal';
|
|
46
|
+
|
|
47
|
+
const XxxPage: React.FC = () => {
|
|
48
|
+
const { message, modal } = App.useApp();
|
|
49
|
+
const actionRef = useRef<ActionType>(undefined);
|
|
50
|
+
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
|
51
|
+
const [modalOpen, setModalOpen] = useState(false);
|
|
52
|
+
const [editId, setEditId] = useState<number | null>(null);
|
|
53
|
+
const getDictOptions = useDictStore((s) => s.getDictOptions);
|
|
54
|
+
|
|
55
|
+
const reload = () => actionRef.current?.reload();
|
|
56
|
+
|
|
57
|
+
const handleDelete = (record: XxxVO) => {
|
|
58
|
+
modal.confirm({
|
|
59
|
+
title: '确认删除?', okType: 'danger',
|
|
60
|
+
content: `确认删除「${record.name}」吗?`,
|
|
61
|
+
onOk: async () => {
|
|
62
|
+
await deleteXxx(record.id!);
|
|
63
|
+
message.success('删除成功'); reload();
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const handleDeleteBatch = () => {
|
|
69
|
+
modal.confirm({
|
|
70
|
+
title: '确认批量删除?', okType: 'danger',
|
|
71
|
+
content: `确认删除选中的 ${selectedRowKeys.length} 项?`,
|
|
72
|
+
onOk: async () => {
|
|
73
|
+
await deleteXxxList(selectedRowKeys.map(Number));
|
|
74
|
+
message.success('删除成功');
|
|
75
|
+
setSelectedRowKeys([]);
|
|
76
|
+
reload();
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 操作项定义(顺序即默认展示顺序;权限与数据状态驱动显隐,不写死):
|
|
83
|
+
* - permission:按钮权限码(菜单树 type=3 按钮),未授权自动隐藏
|
|
84
|
+
* - show:数据状态判定(布尔或 (record) => boolean),如「草稿才显示编辑」
|
|
85
|
+
* - 最终展示顺序由 TableActions 按当前菜单按钮 sort 重排
|
|
86
|
+
*/
|
|
87
|
+
const buildActionItems = (record: XxxVO): TableActionItem<XxxVO>[] => {
|
|
88
|
+
const items: TableActionItem<XxxVO>[] = [];
|
|
89
|
+
const push = (key: string, show: boolean, permission: string, label: string, danger?: boolean) => {
|
|
90
|
+
if (show) items.push({ key, permission, label, danger, onClick: () => setEditId(record.id!) && setModalOpen(true) });
|
|
91
|
+
};
|
|
92
|
+
push('edit', true, 'xxx:update', '编辑');
|
|
93
|
+
push('delete', record.status !== 'PUBLISHED', 'xxx:delete', '删除', true);
|
|
94
|
+
return items;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const columns: ProColumns<XxxVO>[] = [
|
|
98
|
+
{ title: '名称', dataIndex: 'name', width: 200 },
|
|
99
|
+
{
|
|
100
|
+
title: '状态', dataIndex: 'status', width: 100,
|
|
101
|
+
valueType: 'select',
|
|
102
|
+
valueEnum: Object.fromEntries(
|
|
103
|
+
getDictOptions('common_status').map((d) => [d.value, { text: d.label }]),
|
|
104
|
+
),
|
|
105
|
+
render: (_, r) => <DictTag type="common_status" value={r.status} />,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
title: '创建时间', dataIndex: 'createTime', width: 180,
|
|
109
|
+
valueType: 'dateRange',
|
|
110
|
+
search: {
|
|
111
|
+
transform: (value) => {
|
|
112
|
+
if (!Array.isArray(value)) return {};
|
|
113
|
+
return { createTimeStart: value[0], createTimeEnd: value[1] };
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
render: (_, r) => dateFormatter(r.createTime) || '-',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
title: '操作', key: 'action', width: ACTION_COL_WIDTH, fixed: 'right', search: false,
|
|
120
|
+
render: (_, record) => <TableActions record={record} items={buildActionItems(record)} />,
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<>
|
|
126
|
+
<ProTable<XxxVO>
|
|
127
|
+
columns={columns}
|
|
128
|
+
actionRef={actionRef}
|
|
129
|
+
rowKey="id"
|
|
130
|
+
scroll={{ x: 1200 }}
|
|
131
|
+
// 高级筛选区:表单项 vertical 布局,span 按筛选项数量分档(本示例 3 项 → span 6,见 §1.1a)
|
|
132
|
+
search={{ layout: 'vertical', span: 6 }}
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
134
|
+
request={async (params: any) => {
|
|
135
|
+
const { current, pageSize, ...rest } = params;
|
|
136
|
+
const { data } = await getXxxPage({ pageNo: current ?? 1, pageSize: pageSize ?? 10, ...rest });
|
|
137
|
+
return { data: data.list, total: data.total, success: true };
|
|
138
|
+
}}
|
|
139
|
+
pagination={{ defaultPageSize: 10, showSizeChanger: true }}
|
|
140
|
+
rowSelection={{ selectedRowKeys, onChange: setSelectedRowKeys }}
|
|
141
|
+
toolBarRender={() => [
|
|
142
|
+
<Access code="xxx:create" key="add">
|
|
143
|
+
<Button type="primary" icon={<PlusOutlined />}
|
|
144
|
+
onClick={() => { setEditId(null); setModalOpen(true); }}>新增</Button>
|
|
145
|
+
</Access>,
|
|
146
|
+
<Access code="xxx:delete" key="batch">
|
|
147
|
+
<Button danger icon={<DeleteOutlined />}
|
|
148
|
+
disabled={selectedRowKeys.length === 0}
|
|
149
|
+
onClick={handleDeleteBatch}>批量删除</Button>
|
|
150
|
+
</Access>,
|
|
151
|
+
]}
|
|
152
|
+
/>
|
|
153
|
+
<XxxModal open={modalOpen} id={editId}
|
|
154
|
+
onSuccess={() => { setModalOpen(false); reload(); }}
|
|
155
|
+
onCancel={() => setModalOpen(false)} />
|
|
156
|
+
</>
|
|
157
|
+
);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export default XxxPage;
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 1.2 ProTable 列定义关键规则
|
|
164
|
+
|
|
165
|
+
| 场景 | 写法 |
|
|
166
|
+
| ------------- | ------------------------------------------------------------------------------------------ |
|
|
167
|
+
| 隐藏搜索 | `search: false`(**不是** `hideInSearch`) |
|
|
168
|
+
| 下拉搜索 | `valueType: 'select'` + `valueEnum: Object.fromEntries(getDictOptions(...).map(...))` |
|
|
169
|
+
| 日期区间 | `valueType: 'dateRange'` + `search.transform` 拆 `xxxStart/xxxEnd` |
|
|
170
|
+
| 日期区间渲染 | **必须用 `render`,不要用 `renderText`** |
|
|
171
|
+
| 长 label 搜索 | `search={{ labelWidth: 'auto' }}`(在 ProTable 上) |
|
|
172
|
+
| 高级筛选区 | `search={{ layout: 'vertical', span: <分档> }}`(表单项垂直,span 按筛选项数量分档,见 §1.1a) |
|
|
173
|
+
| 操作列 | `width: ACTION_COL_WIDTH`(150)+ `fixed: 'right'` + `search: false` + `<TableActions />`;操作项文字较多时可自定义加宽(如 180) |
|
|
174
|
+
| request 参数 | 固定加 `// eslint-disable-next-line @typescript-eslint/no-explicit-any` 允许 `params: any` |
|
|
175
|
+
| 树形表 | 去掉 `request`,改用 `dataSource={tree}` + `pagination={false}` + `expandable` |
|
|
176
|
+
|
|
177
|
+
### 1.3 通用操作组件 TableActions
|
|
178
|
+
|
|
179
|
+
> 路径:`web-ui/src/components/TableActions/index.tsx`
|
|
180
|
+
|
|
181
|
+
**为什么做成通用组件**:操作项显隐 = 权限 × 数据状态、展示顺序 = 后台菜单 sort、折叠规则 = ≤3 平铺 />3 `...` 悬停展开——这些是所有列表页共通的展示逻辑,若每页手写(如早期 agent-factory 的 renderActions)必然重复且易写死。抽成组件后,页面只需声明「有哪些操作项」及其权限码/状态判定/回调,**展示规则由组件统一保证**。
|
|
182
|
+
|
|
183
|
+
**Props**:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
export interface TableActionItem<T = unknown> {
|
|
187
|
+
key: string;
|
|
188
|
+
label?: React.ReactNode; // 按钮文案(显式传优先;否则取菜单按钮 name)
|
|
189
|
+
permission?: string; // 权限码(菜单树 type=3 按钮 permission;不传=常显)
|
|
190
|
+
show?: boolean | ((record: T) => boolean); // 数据状态显隐
|
|
191
|
+
danger?: boolean; // 危险操作(红色)
|
|
192
|
+
color?: string; // 自定义文字颜色(如橙色警告操作;danger 优先)
|
|
193
|
+
icon?: React.ReactNode;
|
|
194
|
+
onClick?: (record: T) => void;
|
|
195
|
+
}
|
|
196
|
+
export interface TableActionsProps<T = unknown> {
|
|
197
|
+
record: T;
|
|
198
|
+
items: TableActionItem<T>[];
|
|
199
|
+
max?: number; // 平铺上限,默认 3
|
|
200
|
+
sortByMenu?: boolean; // 是否按当前菜单按钮 sort 排序,默认 true
|
|
201
|
+
}
|
|
202
|
+
export const ACTION_COL_WIDTH = 150; // 操作栏列宽
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**机制**:
|
|
206
|
+
|
|
207
|
+
1. **过滤**:`permission` 非空且未授权 → 隐藏;`show` 为函数时以 `record` 计算,为 `false` 隐藏
|
|
208
|
+
2. **排序**:`sortByMenu`(默认开)从 `useAuthStore.menus` 取当前路由(`useLocation().pathname` 最长前缀匹配)对应菜单节点的 `type===3` 按钮子节点,按 `sort` 升序得到权限顺序,`items` 按 `permission ?? key` 对齐重排;**菜单树未匹配或无按钮节点时退化为 items 传入顺序**(不报错)
|
|
209
|
+
3. **折叠**:过滤排序后 ≤ `max`(默认 3)平铺;> `max` 前 `max` 个平铺,其余进 `Dropdown trigger={['hover']}`(`...` 悬停展开)
|
|
210
|
+
4. **样式**:`<a>` 链接形式,`danger` 红色(`#ff4d4f`)、`color` 自定义色(danger 优先)
|
|
211
|
+
|
|
212
|
+
**接入示例**(见 §1.1 模板 `buildActionItems`):页面定义操作项数组(permission + show + onClick),操作列渲染 `<TableActions record={record} items={buildActionItems(record)} />` 即可。
|
|
213
|
+
|
|
214
|
+
**注意事项**:
|
|
215
|
+
|
|
216
|
+
- 排序依赖**后台菜单管理已配置按钮节点及 sort**(系统管理 → 菜单管理,type=按钮);未配置时按 items 传入顺序展示,不影响功能
|
|
217
|
+
- 操作列宽度默认 `ACTION_COL_WIDTH`(150);操作项文字较多导致换行时,页面可自定义加宽(如 180),不必拘泥默认值
|
|
218
|
+
- 操作项的「数据状态判定」在页面内实现(如 `getAgentRowActions(record)`),组件不感知业务
|
|
219
|
+
- 危险操作(删除/驳回/废弃等)传 `danger: true`,无需再手动加色
|
|
220
|
+
- 批量操作(toolBarRender 的新增/批量删除)不属于行操作项,不走 TableActions
|
|
221
|
+
|
|
222
|
+
---
|
package/references/ant6-front-standard/04-/350/241/250/345/215/225/350/247/204/350/214/203.md
CHANGED
|
@@ -13,6 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
2、表单较复杂(字段 ≥ 4 个)或业务流程需要连续操作时,推荐使用右侧 Drawer 替代 Modal。
|
|
15
15
|
|
|
16
|
+
3、在使用drawer抽屉后的表单有需要弹窗选择的弹窗,推荐使用 Modal
|
|
17
|
+
|
|
18
|
+
4、使用抽屉drawer组件时,必须使用 `closable={{ placement: 'end' }}`和`mask={{ closable: false }}`,其宽度必须使用`size`不能使用`width`
|
|
19
|
+
|
|
20
|
+
5、使用抽屉drawer组件时,内容区块必须使用<Card></Card>组建包裹
|
|
21
|
+
|
|
22
|
+
|
|
16
23
|
> 根据表单字段数量分级选择布局和 Drawer 宽度。
|
|
17
24
|
|
|
18
25
|
| 字段数量 | 布局 | Drawer 宽度 | Form layout | 说明 |
|
|
@@ -307,6 +314,7 @@ const XxxDrawer: React.FC<Props> = ({ open, id, onSuccess, onClose }) => {
|
|
|
307
314
|
- `originData = useRef()` 保留服务端字段,提交时合并:`{ ...originData.current, ...values, id }`
|
|
308
315
|
- 编辑模式下隐藏字段用 `hidden={!!id}` 而非条件渲染(条件渲染会让 `validateFields` 跳过该字段)
|
|
309
316
|
- 用 `destroyOnHidden` 自动清理状态,**不要**手动 reset
|
|
317
|
+
- Drawer 宽度必须使用`size`不能使用`width`
|
|
310
318
|
|
|
311
319
|
---
|
|
312
320
|
|