adspecs 0.1.40 → 0.1.41

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.
@@ -0,0 +1,262 @@
1
+ # 页面开发规则
2
+
3
+ ## 默认页面形态
4
+
5
+ 新增业务页面优先使用:
6
+
7
+ - Vue 3 `script setup lang="ts"`。
8
+ - Element Plus 布局组件。
9
+ - `@gushen/gushen-common-components` 的表格、查询栏、上传等组件。
10
+ - `src/api/<module>/index.ts` 管理请求。
11
+ - `src/lang/modules/<module>/{zh-cn,en-us,zh-tw}.ts` 管理文案。
12
+
13
+ 维护已有旧页面时,可以保留 Options API;新增页面不要主动复制旧页面里的 `console.log`、`@ts-nocheck`、硬编码文案和隐式 any。
14
+
15
+ ## 列表页
16
+
17
+ 推荐结构:
18
+
19
+ ```text
20
+ src/views/<module>/<page>/
21
+ ├── index.vue
22
+ ├── components/
23
+ │ └── <可选子组件>.vue
24
+ └── config/
25
+ └── gridConfig.ts
26
+ ```
27
+
28
+ 列表页默认使用 `GsCustomizeTable` / `<gs-table>`:
29
+
30
+ - 查询按钮放在表格 `#btn` 或页面顶部查询区。
31
+ - 列配置单独抽到 `config/gridConfig.ts`,复杂列通过 `slotArr` 开启插槽。
32
+ - 列表请求函数放在 `src/api/<module>/<page>/index.ts`。
33
+ - 成功判断用 `reqIsSucceed`,不要写死单一成功码,除非维护旧接口已有固定协议。
34
+
35
+ ## 表单页
36
+
37
+ 推荐结构:
38
+
39
+ ```text
40
+ src/views/<module>/<page>/
41
+ ├── index.vue
42
+ └── components/
43
+ └── BasicForm.vue
44
+ ```
45
+
46
+ 表单规则:
47
+
48
+ - 使用 `el-form`、`el-row`、`el-col` 组织字段。
49
+ - 新增/编辑共用表单组件,通过 `disabled` 或 `readonly` 控制只读。
50
+ - 保存入参按后端要求包裹 `{ params: formData }`。
51
+ - 表单初始化、回显映射、保存映射拆成小函数。
52
+ - 复杂弹窗或选择器优先封装为独立子组件,不要在主页面堆大量弹窗逻辑。
53
+
54
+ ## 流程页面
55
+
56
+ 流程表单模板统一从 `page-template-index.md` 选择,不再以当前工程旧页面作为模板入口。
57
+
58
+ 流程页面需要保留方法契约:
59
+
60
+ - `get_process_variables()`:返回流程变量。
61
+ - `ap_save()`:保存草稿,返回 Promise。
62
+ - 包装页通过 props 接收 `isSubmit`、`editStatus`、`bpm_businessKey`、`taskServiceParams`、`curTaskName`。
63
+
64
+ ## 动态表单页
65
+
66
+ 参考:`src/views/dynamic-form/index.vue`。
67
+
68
+ - 当前页面只挂载 `<FormRender ref="formRenderRef" />`。
69
+ - 使用 IntersectionObserver 在页面可见时向事件总线发送当前路由信息。
70
+ - 使用前确认 `@gushen/gushen-form-render` 插件是否已在 `src/core/initMain.ts` 注册。
71
+
72
+ ## 路由与菜单
73
+
74
+ - 普通业务页默认由后台菜单动态加载,只需确保页面路径存在。
75
+ - 菜单 `menuUrl` / `originalAddr` 会在 `src/permission.ts` 中映射到 `@/views${componentPath}/index.vue`。
76
+ - 例如后台菜单路径 `/test-menu/demo1` 对应 `src/views/test-menu/demo1/index.vue`。
77
+ - 静态壳内页面才补 `src/router/index.ts`。
78
+ - 新增路由 meta 至少包含 `title`、`enTitle`、`noCache`、`hideInMenu`、`isBootstrap`。
79
+ - 动态表单菜单通过 `parameter.menu_type === 'DYNAMIC_FORM'` 进入固定 `FormRender` 容器,一般不新建手写业务页。
80
+
81
+ ## 样式
82
+
83
+ - 页面根节点使用唯一 class,例如 `<section class="xxx-page">`。
84
+ - 页面级样式使用 `<style scoped lang="scss">`。
85
+ - 不要把布局高度写死到固定 px;优先使用 flex 和 `min-height: 0`。
86
+
87
+ ## 生成后自检
88
+
89
+ - 页面能被路由加载。
90
+ - API 路径、请求方法、参数包装与现有接口一致。
91
+ - 表格远程刷新、分页、勾选、操作列能闭环。
92
+ - 用户可见文案进入语言包。
93
+ - 无新增 TypeScript / Vue 诊断。
94
+
95
+ ## 常见问题与修复指南
96
+
97
+ 以下是开发过程中已验证的高频问题及修复方案,生成页面时务必逐项检查。
98
+
99
+ ### 1. gs-table 操作列不显示
100
+
101
+ **现象**:表格渲染正常,但操作列(`prop: 'action'`)和自定义内容列(如状态 Tag)显示空白。
102
+
103
+ **根因**:`GsTable` 组件的 `#cell` 插槽仅在列的 `editable: true` 时才渲染。源码逻辑:
104
+
105
+ ```ts
106
+ // GsTable/index.vue
107
+ if (type === "cell") {
108
+ const { editable } = column || {};
109
+ return editable === true;
110
+ }
111
+ ```
112
+
113
+ **修复**:需要自定义内容渲染的列必须加 `editable: true`:
114
+
115
+ ```ts
116
+ { prop: 'forecastStatus', label: '状态', editable: true },
117
+ { prop: 'action', label: '操作', searchable: false, editable: true },
118
+ ```
119
+
120
+ ### 2. gs-table 数据请求方式选择
121
+
122
+ **原则**:优先使用项目中已验证的 `:columns` + `:options` + `:remote` 模式(参照 `mdmCustomer`),而非 `GsCustomizeTable` 的 `:grid-config` + `:query-params` 模式。
123
+
124
+ **原因**:两种组件的数据响应格式不同:
125
+ - `GsCustomizeTable` 内部期望 `res.data.data` 是行数组 + `res.data.count` 是总数
126
+ - `:remote` 模式由开发者自行处理响应格式转换,兼容性更好
127
+
128
+ ```html
129
+ <gs-table
130
+ ref="tableRef"
131
+ :columns="columns"
132
+ :options="tableOptions"
133
+ :remote="remoteData"
134
+ >
135
+ <template #header>
136
+ <el-button type="primary" @click="openAddDialog">新增</el-button>
137
+ </template>
138
+ <template #cell="{ column, row }">
139
+ <template v-if="column.prop === 'action'">
140
+ <!-- 操作按钮 -->
141
+ </template>
142
+ </template>
143
+ </gs-table>
144
+ ```
145
+
146
+ `remoteData` 函数必须返回 `{ data: rows[], total: number, number: pageIndex, size: pageRows }` 格式:
147
+
148
+ ```ts
149
+ async function remoteData(otherParams: any = {}) {
150
+ const { number, size, params } = otherParams || {}
151
+ const res = await findPagination({ pageIndex: number, pageRows: size, params })
152
+ if (res.code === 800) {
153
+ const d = res.data || {}
154
+ return { data: d.rows || [], number, size, total: Number(d.total) || 0 }
155
+ }
156
+ return { data: [], number, size, total: 0 }
157
+ }
158
+ ```
159
+
160
+ ### 3. Mock 模式下 GET 请求的查询参数丢失
161
+
162
+ **现象**:详情页打开后空白,控制台报 `Cannot read properties of undefined (reading 'data')`。
163
+
164
+ **根因**:axios 的 `params` 配置项在所有请求拦截器执行**之后**才拼接到 URL。Mock 拦截器运行时 URL 上没有查询参数,导致 mock router 提取不到 `id` 等参数。
165
+
166
+ ```ts
167
+ // ❌ 错误:params 在拦截器之后拼接,mock 拿不到 id
168
+ export function getById(id: number) {
169
+ return request({ url: '/api/forecast/get-id', method: 'get', params: { id } })
170
+ }
171
+
172
+ // ✅ 正确:参数直接拼在 URL 上
173
+ export function getById(id: number) {
174
+ return request({ url: `/api/forecast/get-id?id=${id}`, method: 'get' })
175
+ }
176
+ ```
177
+
178
+ **适用范围**:所有 GET 请求的 mock API 都必须将参数直接拼入 URL 字符串,不能依赖 axios 的 `params` 配置。
179
+
180
+ ### 4. 语言包 key 结构与 i18n 引用不匹配
181
+
182
+ **现象**:页面上显示原始 key 名(如 `forecast-order.listTitle`)而非翻译文案。
183
+
184
+ **根因**:语言包模块通过 `import.meta.glob` 自动加载,文件夹名作为模块 key(如 `forecast-order`)。`$t('forecast-order.listTitle')` 按**嵌套对象路径**解析,而非扁平 key。语言包必须使用嵌套对象结构:
185
+
186
+ ```ts
187
+ // ✅ 正确:嵌套对象,$t('forecast-order.status.DRAFT') 可以解析
188
+ export default {
189
+ status: { DRAFT: '草稿', SUBMITTED: '已提交' },
190
+ msg: { saveSuccess: '保存成功' },
191
+ }
192
+
193
+ // ❌ 错误:扁平 key,$t('forecast-order.status.DRAFT') 无法解析为 'forecast-order'['status']['DRAFT']
194
+ export default {
195
+ 'status.DRAFT': '草稿',
196
+ 'msg.saveSuccess': '保存成功',
197
+ }
198
+ ```
199
+
200
+ **自检清单**:
201
+ - 所有 `$t('forecast-order.xxx')` 引用能在语言包中找到对应嵌套路径
202
+ - 特别注意模板字符串中的动态 key:`` $t(`forecast-order.status.${val}`) ``
203
+ - 语言包有 `{ immediate: true }` 的 watch 不能遗漏语言初始化时机
204
+
205
+ ### 5. mock 菜单路由出现双斜杠 `//`
206
+
207
+ **现象**:点击左侧菜单跳转报错,URL 变成 `//forecast-order`。
208
+
209
+ **根因**:`permission.ts` 的 `generatePath` 函数拼接路由时 `parentPath=''`,结果为 `'' + '/' + '/forecast-order'` = `'//forecast-order'`。去双斜杠逻辑需要 `!item.originalAddr` 为 `true` 才生效。
210
+
211
+ **修复**:mock 菜单中 `originalAddr` 设为 `null`,不需要该字段:
212
+
213
+ ```ts
214
+ export const mockMenus = [
215
+ {
216
+ menuId: 1001,
217
+ menuCode: 'forecast-order',
218
+ menuUrl: '/forecast-order',
219
+ menuName: '销售预测',
220
+ originalAddr: null, // ← 关键:不能设为非空值
221
+ children: [],
222
+ // ...
223
+ },
224
+ ]
225
+ ```
226
+
227
+ ### 6. drawer/dialog 组件 v-model 循环同步
228
+
229
+ **现象**:弹窗打开后空白,或者关闭后无法再打开。
230
+
231
+ **根因**:手动用 `ref` + `watch` 同步 `props.visible ↔ internal visible` 加上 `el-drawer v-model` 内部也有自己的响应式逻辑,造成循环触发或事件丢失。
232
+
233
+ **修复**:直接用 `:model-value` + `@update:model-value` 模式,不创建内部 visible ref:
234
+
235
+ ```html
236
+ <el-drawer
237
+ :model-value="props.visible"
238
+ @update:model-value="onVisibleChange"
239
+ >
240
+ ```
241
+
242
+ ```ts
243
+ function onVisibleChange(val: boolean) {
244
+ if (!val) { /* 清理状态 */ }
245
+ emit('update:visible', val)
246
+ }
247
+ ```
248
+
249
+ ### 7. 硬编码文案遗漏
250
+
251
+ **现象**:表单校验错误提示、ElMessage 弹窗消息、el-drawer 标题等始终显示中文,不随语言切换变化。
252
+
253
+ **检查范围**:
254
+ - `el-form` 的 `:rules` 中 `message` 字段
255
+ - `ElMessage.success/warning/error()` 调用
256
+ - `ElMessageBox.confirm()` 的 `title`、`message`
257
+ - 弹窗/抽屉的 `:title` 属性
258
+ - 表格列配置中 `placeholder`、`searchOptions.loop` 的 `label`
259
+ - `'是'/'否'`、`'确定'/'取消'` 等通用词
260
+
261
+ **修复**:以上所有位置统一使用 `t('module.key')` 或 `$t('module.key')`。需要先在语言包中定义对应 key。
262
+
@@ -0,0 +1,374 @@
1
+ # 页面模板索引
2
+
3
+ 本文件是页面生成的唯一模板入口。模板均从丰富参考工程中提炼为“模式”,只描述可迁移结构、组件组合和生成要点;不要在生成代码或规则中引用参考工程的绝对路径。
4
+
5
+ ## 使用原则
6
+
7
+ - 生成页面前先在本文件选择模板模式,再按 `page-development-rules.md` 落到目标工程路径。
8
+ - 目标工程页面通常放在 `src/views/<menuUrl>/index.vue`,由后台菜单动态加载。
9
+ - API 放在 `src/api/<module>/<page>/index.ts`,语言包放在 `src/lang/modules/<module>/`。
10
+ - 不直接复制旧页面,不迁移参考工程的接口前缀、业务编码、绝对路径和专属本地组件。
11
+ - 目标工程没有的依赖或组件必须替换为已有组件,或先明确补依赖。
12
+
13
+ ## 1. 查询栏 + 高级表格 + 弹窗表单
14
+
15
+ 适用场景:普通 CRUD 列表、配置列表、调度任务、消息模板、基础资料维护。
16
+
17
+ 推荐结构:
18
+
19
+ ```text
20
+ src/views/<module>/<page>/
21
+ ├── index.vue
22
+ ├── searchConfig.ts
23
+ ├── gridConfig.ts
24
+ └── components/
25
+ ├── AddForm.vue
26
+ ├── EditForm.vue
27
+ └── DetailDrawer.vue
28
+ ```
29
+
30
+ 生成要点:
31
+
32
+ - 顶部使用 `GsSearchBar`,查询项放在 `searchConfig`。
33
+ - 主表使用 `GsCustomizeTable`,列配置放在 `gridConfig`。
34
+ - 外部查询更新 `queryParams.params.params` 后调用 `tableRef.value?.outSearch(false)`。
35
+ - 新增、编辑、调试、详情等动作放入独立弹窗/抽屉组件。
36
+ - 操作列使用 `slotArr: ['action']`,批量按钮放在 `#btn`。
37
+
38
+ ## 2. 单表 CRUD + 导入导出
39
+
40
+ 适用场景:字典类型、消息模板、系统参数、基础资料维护、带批量导入导出的业务列表。
41
+
42
+ 推荐结构:
43
+
44
+ ```text
45
+ src/views/<module>/<page>/
46
+ ├── index.vue
47
+ ├── gridConfig.ts
48
+ └── components/
49
+ ├── AddDialog.vue
50
+ ├── EditDialog.vue
51
+ └── BatchUpload.vue
52
+ ```
53
+
54
+ 生成要点:
55
+
56
+ - 表格开启 `selectable`、`rowKey`、唯一 `localstorageName`。
57
+ - 工具栏包含新增、导入、导出;导出以选中行 ID 组装请求。
58
+ - 导入成功后刷新表格。
59
+ - 复杂字段用字段插槽渲染,普通字典字段优先使用 `dictCode`。
60
+ - 保存、删除、导入、导出都要有 loading 或禁用态防重复点击。
61
+
62
+ ## 3. 左树右详情 Tab
63
+
64
+ 适用场景:菜单管理、组织机构、分类目录、资源配置、主从层级维护。
65
+
66
+ 推荐结构:
67
+
68
+ ```text
69
+ src/views/<module>/<page>/
70
+ ├── index.vue
71
+ └── components/
72
+ ├── TreePanel.vue
73
+ ├── BasicInfoTab.vue
74
+ ├── ResourceTab.vue
75
+ └── PermissionTab.vue
76
+ ```
77
+
78
+ 生成要点:
79
+
80
+ - 左侧固定宽度树,包含系统筛选、关键字过滤、新增按钮。
81
+ - 右侧根据当前节点显示 `el-tabs`,每个 Tab 独立组件。
82
+ - 当前节点通过 props 下发,子组件通过事件通知父组件刷新树。
83
+ - Tab 是否显示由按钮权限、节点类型和租户/所有者条件共同决定。
84
+ - 保留 `currentNode`、`defaultExpandedKeys`、`filterText`、`loading` 等状态。
85
+
86
+ ## 4. 左列表右内联编辑
87
+
88
+ 适用场景:模板中心、导入导出模板、打印模板、文档模板、需要边选边编辑的配置中心。
89
+
90
+ 推荐结构:
91
+
92
+ ```text
93
+ src/views/<module>/<page>/
94
+ ├── index.vue
95
+ └── components/
96
+ ├── TemplateListPanel.vue
97
+ ├── TemplateSearchBar.vue
98
+ ├── InlineEditor.vue
99
+ └── UseHistoryDialog.vue
100
+ ```
101
+
102
+ 生成要点:
103
+
104
+ - 左侧为可分页模板列表,右侧为详情/编辑区。
105
+ - `selectedItem` 控制右侧显示,`isEditing` 控制只读/编辑态。
106
+ - 顶部工具栏在查看态显示编辑/删除,在编辑态显示保存/取消。
107
+ - 右侧编辑区可用 `el-scrollbar` 包裹,避免页面整体滚动。
108
+ - 新增时构造临时选中项,保存成功后刷新左侧列表并选中最新项。
109
+
110
+ ## 5. 权限控制多 Tab 中心
111
+
112
+ 适用场景:权限中心、配置中心、模板中心、模块化后台页面。
113
+
114
+ 推荐结构:
115
+
116
+ ```text
117
+ src/views/<module>/<page>/
118
+ ├── index.vue
119
+ └── tabs/
120
+ ├── FirstTab.vue
121
+ ├── SecondTab.vue
122
+ └── ThirdTab.vue
123
+ ```
124
+
125
+ 生成要点:
126
+
127
+ - 父组件只负责 Tab 容器、权限判断和公共数据加载。
128
+ - `authTabs` 由 `isHaveBtnAuth(code)` 过滤生成,默认激活第一个可见 Tab。
129
+ - 公共数据通过 `provide` 下发给 Tab 子组件。
130
+ - 每个 Tab 自己负责列表、表单、弹窗和 API。
131
+ - 对首次切换才需要初始化的 Tab,在 `tab-change` 中调用子组件方法。
132
+
133
+ ## 6. 动态组件配置中心
134
+
135
+ 适用场景:系统配置中心、按权限展示多个配置模块、每个配置模块相互独立的页面。
136
+
137
+ 推荐结构:
138
+
139
+ ```text
140
+ src/views/<module>/<page>/
141
+ ├── index.vue
142
+ └── components/
143
+ ├── ConfigA.vue
144
+ ├── ConfigB.vue
145
+ └── ConfigC.vue
146
+ ```
147
+
148
+ 生成要点:
149
+
150
+ - `tabList` 由权限和运行配置计算得到。
151
+ - 使用 `<component :is="item.key" />` 渲染配置组件。
152
+ - 无可见 Tab 时展示 `el-empty`。
153
+ - 适合每个配置模块彼此独立、只共享少量全局配置的场景。
154
+
155
+ ## 7. 分段抽屉表单
156
+
157
+ 适用场景:任务配置、消息渠道、第三方接入、复杂基础资料。
158
+
159
+ 推荐结构:
160
+
161
+ ```text
162
+ src/views/<module>/<page>/components/
163
+ └── FormDrawer.vue
164
+ ```
165
+
166
+ 生成要点:
167
+
168
+ - 使用抽屉或弹窗承载大表单,表单内用分段标题分组。
169
+ - 每段用 `el-divider` 或统一分段标题组件。
170
+ - 字段按 12/24 栅格组织,复杂字段条件显示。
171
+ - 保存按钮带 `saveLoading`,保存成功后关闭并 emit `refresh`。
172
+ - 复杂子选择器、Cron、代码编辑器等放入二级弹窗组件。
173
+
174
+ ## 8. 调度任务与 Cron 配置
175
+
176
+ 适用场景:定时任务、计划任务、推送任务、需要启动/停止/执行一次的任务管理。
177
+
178
+ 推荐结构:
179
+
180
+ ```text
181
+ src/views/<module>/<page>/
182
+ ├── index.vue
183
+ ├── gridConfig.ts
184
+ ├── searchConfig.ts
185
+ └── components/
186
+ ├── TaskForm.vue
187
+ ├── TriggerDialog.vue
188
+ └── CodeEditorDialog.vue
189
+ ```
190
+
191
+ 生成要点:
192
+
193
+ - 列表操作列包含编辑、复制、删除、执行一次、查看日志、启动/停止。
194
+ - 表单按“基础配置 / 调度配置 / 执行配置 / 高级配置”分段。
195
+ - Cron 字段使用只读输入框触发 Cron 弹窗。
196
+ - `scheduleType` 改变时联动清空或校验 `scheduleConf`。
197
+ - 启停、执行一次等动作需要二次确认或 loading。
198
+
199
+ ## 9. 日志列表 + 详情抽屉 + 分析面板
200
+
201
+ 适用场景:接口日志、登录日志、审计日志、用户行为日志、调用统计。
202
+
203
+ 推荐结构:
204
+
205
+ ```text
206
+ src/views/<module>/<page>/
207
+ ├── index.vue
208
+ ├── config/
209
+ │ └── gridConfig.ts
210
+ └── components/
211
+ ├── ParamsDrawer.vue
212
+ ├── LinkDrawer.vue
213
+ └── DataViewDrawer.vue
214
+ ```
215
+
216
+ 生成要点:
217
+
218
+ - 列表 Tab 使用 `GsCustomizeTable`,可自定义 `#pager` 加首页/末页按钮。
219
+ - 查询条件可放在 `#btn-r` 或独立筛选栏。
220
+ - 大字段如 request/response/exception 只显示“详情”按钮,点击打开抽屉。
221
+ - 分析 Tab 展示统计卡片、趋势图、Top 列表。
222
+ - 图表依赖不存在时,先降级为统计卡片 + 表格,不要强行引入新依赖。
223
+
224
+ ## 10. 开放 API 列表 + 编辑页切换
225
+
226
+ 适用场景:接口管理、API 编排、服务接口定义、请求/响应参数维护。
227
+
228
+ 推荐结构:
229
+
230
+ ```text
231
+ src/views/<module>/<page>/
232
+ ├── index.vue
233
+ └── components/
234
+ ├── ParamTable.vue
235
+ ├── DebugDialog.vue
236
+ └── ApiImportDialog.vue
237
+ ```
238
+
239
+ 生成要点:
240
+
241
+ - 页面用 `isEditing` / `mode` 在列表区和编辑区之间切换。
242
+ - 列表区使用表格,支持增量/全量导入、导出、调试。
243
+ - 编辑区包含基础表单 + 请求参数表 + 响应参数表。
244
+ - 返回列表时刷新数据并重置表单。
245
+ - 参数表作为子组件维护增删改,父组件保存时统一组装 payload。
246
+
247
+ ## 11. 权限矩阵 / 授权选择器
248
+
249
+ 适用场景:字段权限、数据权限、接口权限、人员授权、组织授权。
250
+
251
+ 推荐结构:
252
+
253
+ ```text
254
+ src/views/<module>/<page>/
255
+ ├── index.vue
256
+ └── components/
257
+ ├── OrgTreePanel.vue
258
+ ├── PermissionListPanel.vue
259
+ ├── GrantDialog.vue
260
+ └── ConditionTable.vue
261
+ ```
262
+
263
+ 生成要点:
264
+
265
+ - 左侧组织/角色/用户树,右侧权限列表或矩阵。
266
+ - 权限数据通常需要系统、菜单、角色、组织等固定参数。
267
+ - 授权弹窗要支持已选回显和确认后局部刷新。
268
+ - 条件配置可用表格维护,复杂表达式再引入编辑器。
269
+
270
+ ## 12. 规则设计 / 多模式编辑器
271
+
272
+ 适用场景:规则引擎、表达式配置、低代码规则编排、条件动作设计。
273
+
274
+ 推荐结构:
275
+
276
+ ```text
277
+ src/views/<module>/<page>/
278
+ ├── index.vue
279
+ └── components/
280
+ ├── VisualDesigner.vue
281
+ ├── ExpressionEditor.vue
282
+ ├── CodeEditor.vue
283
+ └── ExecutionDrawer.vue
284
+ ```
285
+
286
+ 生成要点:
287
+
288
+ - 根据路由 query 或页面状态选择设计模式。
289
+ - 父组件统一加载规则对象、保存、执行。
290
+ - 视觉设计、表达式、代码编辑拆成互斥子组件。
291
+ - 执行前先保存或校验当前编辑内容。
292
+ - 若目标工程缺少编辑器依赖,先生成普通文本域或只读 JSON 预览,不直接引用外部编辑器组件。
293
+
294
+ ## 13. 基础资料表单
295
+
296
+ 适用场景:个人信息、密码修改、简单基础资料、只读/编辑切换的小表单。
297
+
298
+ 推荐结构:
299
+
300
+ ```text
301
+ src/views/<module>/<page>/
302
+ ├── index.vue
303
+ └── components/
304
+ └── BasicForm.vue
305
+ ```
306
+
307
+ 生成要点:
308
+
309
+ - 使用 `el-form` + `el-row` + `el-col`。
310
+ - 简单页面可以直接在 `index.vue` 放表单;字段超过一屏时拆 `BasicForm.vue`。
311
+ - 头像、附件、图片类字段优先使用目标工程已有上传组件。
312
+ - 保存前校验表单,保存成功后提示并按需刷新用户信息或返回列表。
313
+ - 密码类表单必须包含确认字段校验和 loading 防重复提交。
314
+
315
+ ## 14. 流程表单包装页
316
+
317
+ 适用场景:流程引擎嵌入业务表单、审批页内保存草稿、发起流程。
318
+
319
+ 推荐结构:
320
+
321
+ ```text
322
+ src/views/<module>/<page>/
323
+ ├── index.vue
324
+ └── components/
325
+ └── BusinessForm.vue
326
+ ```
327
+
328
+ 生成要点:
329
+
330
+ - 包装页接收 `isSubmit`、`editStatus`、`bpm_businessKey`、`taskServiceParams`、`curTaskName`。
331
+ - 业务表单实现 `get_process_variables()` 和 `ap_save()`。
332
+ - `editStatus` 和路由参数共同决定字段是否可编辑。
333
+ - 保存草稿返回 Promise,供流程引擎调用。
334
+ - 流程标识、系统类型、租户字段不要硬编码,优先从配置或 props 传入。
335
+
336
+ ## 15. 动态表单入口
337
+
338
+ 适用场景:后台表单设计器配置出的动态表单页面。
339
+
340
+ 推荐结构:
341
+
342
+ ```text
343
+ src/views/<module>/<page>/index.vue
344
+ ```
345
+
346
+ 生成要点:
347
+
348
+ - 使用 `FormRender` 前确认 `@gushen/gushen-form-render` 插件是否已注册。
349
+ - 动态表单页面通常不写业务字段,只负责挂载渲染器和同步路由标题。
350
+ - 如果菜单参数已经声明动态表单,优先走 `permission.ts` 的动态表单路由处理,不新建手写 CRUD。
351
+
352
+ ## API 模板模式
353
+
354
+ 新增 API 时按业务复杂度选择:
355
+
356
+ - 标准 CRUD:`findPagination`、`save`、`update`、`getById`、`delete`。
357
+ - 导入导出:列表查询、模板下载、导入上传、导出下载、历史记录。
358
+ - 树结构:树查询、节点新增、节点更新、节点删除、拖拽排序。
359
+ - 授权类:主体查询、权限查询、授权保存、授权删除、回显详情。
360
+ - 日志类:分页查询、详情查询、统计总数、趋势、TopN。
361
+
362
+ API 生成约束:
363
+
364
+ - 不复用参考工程接口前缀;先参考目标工程同模块 API 风格。
365
+ - POST 保存/更新通常使用 `{ params }` 包装。
366
+ - 文件下载使用 Blob 时要释放 URL。
367
+ - 用户可见错误和成功提示走 i18n。
368
+
369
+ ## 不建议直接迁移的模式
370
+
371
+ - 依赖专有设计器、Monaco、打印设计器、复杂工作流 SDK 的页面:目标工程未必具备依赖,应先确认包和注册方式。
372
+ - 深度绑定平台权限模型、租户模型、接口导入协议的页面:只迁移结构,不迁移字段和接口。
373
+ - 大量 Options API mixin 的旧页面:新页面优先用 `script setup` 重写结构。
374
+ - 使用参考工程专属本地组件的页面:必须替换为目标工程已有组件或先补组件。
@@ -0,0 +1,48 @@
1
+ # 项目结构与入口规则
2
+
3
+ ## 技术栈
4
+
5
+ - Vue 3 + TypeScript + Vite。
6
+ - Element Plus 全量注册,Element Plus 图标在 `src/core/initMain.ts` 中全局注册。
7
+ - `@gushen/gushen-common-components` 通过 `instance.use(GsComponents)` 全局注册,并引入组件库 CSS。
8
+ - `@gushen/gushen-form-render` 依赖存在,但当前 `src/core/initMain.ts` 中插件注册被注释;使用 `FormRender` 前先确认是否需要启用插件注册。
9
+ - 当前 `package.json` 未安装 `@gushen/gushen-work-flow`;流程相关页面主要作为壳层/包装层存在,若要直接使用工作流组件,先补依赖并确认注册方式。
10
+
11
+ ## 关键入口
12
+
13
+ - 应用入口:`src/main.ts`
14
+ - 插件、全局属性、全局组件:`src/core/initMain.ts`
15
+ - 本地全局组件:`src/components/global-register.ts`
16
+ - 路由:`src/router/index.ts`
17
+ - 动态菜单与路由守卫:`src/permission.ts`
18
+ - 页面:`src/views`
19
+ - API:`src/api`
20
+ - 语言包:`src/lang`、`src/lang/modules`
21
+
22
+ ## 代码风格
23
+
24
+ - 新增 Vue 页面优先使用 Vue 3 `script setup lang="ts"`;维护旧页面时可沿用 Options API。
25
+ - 新增业务代码不要使用 `// @ts-nocheck`。
26
+ - `ref([])`、`reactive({})`、回调参数要显式类型,避免 `vue-tsc` 隐式 `any`。
27
+ - 类型导入使用 `import type`。
28
+ - 不要把 `isc-web-ui` 中依赖本地组件的规则照搬到本工程,除非本工程已有同名组件或先补齐组件。
29
+
30
+ ## 路由规则
31
+
32
+ - 静态路由集中在 `src/router/index.ts` 的 `routes`。
33
+ - 主布局子页面放在根路由 `/` 的 `children` 中,常用 meta:`title`、`enTitle`、`icon`、`noCache`、`hideInMenu`、`isBootstrap`。
34
+ - 普通业务页优先走后台菜单动态路由:`src/permission.ts` 的 `formatRoutes` 会按 `menuUrl` / `originalAddr` 动态加载 `@/views${componentPath}/index.vue`。
35
+ - 新建业务页通常放在 `src/views/{菜单路径}/index.vue`,菜单平台配置 `menuUrl` 后即可加载;不要默认修改 `src/router/index.ts`。
36
+ - 只有登录页、首页、消息中心、个人中心等壳内固定页才补 `src/router/index.ts`。
37
+ - HTTP 菜单会走 `src/views/third-view.vue` iframe;动态表单菜单通过 `handleDynamicFormRoute` 映射到固定动态表单入口。
38
+
39
+ ## 全局属性
40
+
41
+ `src/core/initMain.ts` 中注入:
42
+
43
+ - `this.$http` / `window.$request`:请求工具。
44
+ - `this.reqIsSucceed`:统一成功判断。
45
+ - `this.$bus`:事件总线。
46
+ - `window._config`:运行配置。
47
+
48
+ 新增 Composition API 页面需要使用这些能力时,优先直接 import 对应工具函数;只有维护旧 Options API 页面时才使用 `this.xxx`。