@suzhou-lab/page-components 1.0.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.
@@ -0,0 +1,402 @@
1
+ ---
2
+ name: list-page-refactor
3
+ description: 将列表页面从旧 queryCondition + tableContent 模式改造为 <list-page> + <filter-form> 组件。适用 Vue 2 + Element UI + qiankun。严格单页改造,改造前备份,改造后构建验证。
4
+ principles:
5
+ - 不改原项目全局 CSS / SCSS 变量,覆盖样式只在本组件 scoped 内通过 ::v-deep 完成
6
+ - list-page / filter-form 组件必须自包含(不 @import 项目 _var / _theme)
7
+ - 一次只改一个页面,改完须经构建验证和用户确认,绝不允许批量改造
8
+ - 改造前必须备份原文件,绝不跳过
9
+ triggers:
10
+ - 改造列表页面
11
+ - 改造页面
12
+ - 列表页改造
13
+ - 重构列表
14
+ - list-page
15
+ - filter-form
16
+ - 继续改造
17
+ - 下一个页面
18
+ ---
19
+
20
+ # 列表页面改造
21
+
22
+ 将微应用中所有"上方查询表单 + 下方表格列表"的页面,统一改造为 `<list-page>` + `<filter-form>` 共享组件。
23
+
24
+ ---
25
+
26
+ ## 执行流程(必须严格按顺序执行,不可跳过任何一步)
27
+
28
+ ### 第 0 步:前置检查(技能激活后立即执行)
29
+
30
+ 以下两项检查**必须在改造任何页面前完成**,缺一不可。
31
+
32
+ #### 0.1 检查 .container 满高样式
33
+
34
+ 检查 `src/views/common/Home.vue` 中 `<router-view>` 是否有 `class="container"`:
35
+
36
+ ```bash
37
+ grep -n 'router-view.*class.*container' src/views/common/Home.vue
38
+ ```
39
+
40
+ 如果没有,在 `<router-view>` 上添加 `class="container"`,并确保存在无 scoped 的样式块:
41
+
42
+ ```html
43
+ <router-view class="container" />
44
+ ```
45
+
46
+ 此项检查必须完成并告知用户结果。
47
+
48
+ #### 0.2 检查 page-components 包
49
+
50
+ 检查 `@suzhou-lab/page-components` 是否已安装并注册:
51
+
52
+ ```bash
53
+ # 是否在 node_modules 中
54
+ ls node_modules/@suzhou-lab/page-components/index.js 2>/dev/null && echo "✅ 已安装" || echo "❌ 未安装"
55
+ ```
56
+
57
+ ```bash
58
+ # 是否在 main.js 中注册
59
+ grep -r "page-components\|@/components/page" src/main.js src/utils/component.js 2>/dev/null
60
+ ```
61
+
62
+ 未安装则提示用户执行:
63
+ ```bash
64
+ npm install @suzhou-lab/page-components
65
+ ```
66
+
67
+ 未注册则在 `src/main.js` 中添加:
68
+ ```js
69
+ import PageComponents from '@suzhou-lab/page-components'
70
+ Vue.use(PageComponents)
71
+ ```
72
+
73
+ 此项检查必须完成并告知用户结果。
74
+
75
+ ---
76
+
77
+ ### 第 1 步:检测待改造页面
78
+
79
+ 扫描项目中所有 `.vue` 文件,找出符合以下特征的页面:
80
+ - 包含 `queryCondition` 数据字段(旧筛选模式)
81
+ - 或包含 `tableContent` class(旧表格容器)
82
+ - 或包含 `isshowbutton` / `isfolid` 等旧折叠字段
83
+
84
+ 将结果整理为清单,格式如下:
85
+
86
+ ```
87
+ 发现 N 个待改造页面:
88
+
89
+ [ ] 1. src/views/modules/establish/info-center/declare-list.vue
90
+ 模式:queryCondition + tableContent | 复杂度:中 | 列数:8
91
+ [ ] 2. src/views/modules/establish/acceptance-center/acceptance-list.vue
92
+ 模式:queryCondition + tableContent | 复杂度:高 | 列数:13
93
+ ...
94
+ ```
95
+
96
+ ---
97
+
98
+ ### 第 2 步:等待用户确认
99
+
100
+ **列出清单后必须停下来**,询问用户:
101
+
102
+ > "共发现 N 个可改造页面。请选择:输入编号(如 `1` 改造第一个,或 `1,3,5` 指定多个) / 输入 `全部` / 输入其他页面路径"
103
+
104
+ **用户回复后只改造用户指定的页面,不要自作主张。**
105
+
106
+ ---
107
+
108
+ ### 第 3 步:创建备份目录并备份
109
+
110
+ 改造**每个页面之前**,必须:
111
+
112
+ ```bash
113
+ mkdir -p .backups
114
+ # 按原路径结构备份
115
+ cp src/views/xxx/xxx.vue ".backups/src/views/xxx/xxx.vue.bak"
116
+ ```
117
+
118
+ 若目标 `.backups` 路径不存在父目录,先 `mkdir -p` 创建。**备份完成后才能开始改原文件。**
119
+
120
+ ---
121
+
122
+ ### 第 4 步:改造单个页面(一次只改一个)
123
+
124
+ 按照下方"改造规范"进行改造。改完后:
125
+
126
+ ```bash
127
+ npm run build 2>&1 | tail -30
128
+ ```
129
+
130
+ 若构建失败,根据错误信息修复,**不得跳过构建验证直接交付**。
131
+
132
+ 构建通过后生成简短的改造日志:
133
+
134
+ ```
135
+ ✅ 改造完成:src/views/xxx/xxx.vue
136
+ 备份:.backups/src/views/xxx/xxx.vue.bak
137
+ 变更:模板(queryCondition → filter-form)、脚本(数据拆分、inject 注入)、样式(移除 isshowbutton 等)
138
+ 构建:通过 ✓
139
+ ```
140
+
141
+ ---
142
+
143
+ ### 第 5 步:交付并等待确认
144
+
145
+ 展示改造日志,询问用户:
146
+
147
+ > "改造完成,构建通过。是否继续下一个页面?"
148
+
149
+ **用户确认后才能继续下一个。绝不允许在用户未确认的情况下连续改造多个页面。**
150
+
151
+ ---
152
+
153
+ ## 组件位置
154
+
155
+ 组件通过 npm 安装,位于 `node_modules/@suzhou-lab/page-components/`:
156
+
157
+ | 组件 | 来源 |
158
+ |------|------|
159
+ | `list-page` | `@suzhou-lab/page-components` |
160
+ | `filter-form` | `@suzhou-lab/page-components` |
161
+
162
+ 注册方式:
163
+ ```js
164
+ import PageComponents from '@suzhou-lab/page-components'
165
+ Vue.use(PageComponents)
166
+ ```
167
+
168
+ ---
169
+
170
+ ## 改造规范
171
+
172
+ ### inject & tableHeight(每个页面必须)
173
+
174
+ ```js
175
+ inject: {
176
+ listPage: { from: 'listPage', default() { return { tableHeight: 300 } } }
177
+ },
178
+ computed: {
179
+ tableHeight() { return this.listPage.tableHeight },
180
+ }
181
+ ```
182
+
183
+ 表格统一绑定:`:height="tableHeight"`
184
+
185
+ ### filters 数据(每个页面必须)
186
+
187
+ ```js
188
+ data() {
189
+ return {
190
+ filters: {
191
+ name: '',
192
+ status: '',
193
+ year: ''
194
+ },
195
+ pageParam: {
196
+ page: 1,
197
+ pageSize: 10
198
+ },
199
+ }
200
+ }
201
+ ```
202
+
203
+ ### filterItems 写法(放在 computed 中)
204
+
205
+ ```js
206
+ filterItems() {
207
+ return [
208
+ { label: '名称', prop: 'name', type: 'input', span: 8 },
209
+ { label: '状态', prop: 'status', type: 'select', span: 8,
210
+ options: [
211
+ { value: '1', label: '待审核' },
212
+ { value: '2', label: '已通过' }
213
+ ]
214
+ },
215
+ { label: '年度', prop: 'year', type: 'yearpicker', span: 8 },
216
+ // 条件显隐
217
+ { label: '专家姓名', prop: 'expertName', type: 'input', span: 8,
218
+ visible: this.groupInfo.reviewWay == '1'
219
+ },
220
+ ]
221
+ }
222
+ ```
223
+
224
+ 要点:
225
+ - `span: 8` → 一行 3 个(24/8),`span: 6` → 一行 4 个(24/6)
226
+ - options 必须 map 为 `[{ value, label }]`
227
+ - 函数式 options:`options: (model) => this.dynList.map(...)`
228
+ - `visible` 可以是布尔或 `(model) => bool`
229
+
230
+ ### onSearch / onReset(每个页面必须)
231
+
232
+ ```js
233
+ methods: {
234
+ onSearch(model) {
235
+ this.pageParam = { ...this.pageParam, ...model, page: 1 };
236
+ this.fetchList();
237
+ },
238
+ onReset(model) {
239
+ this.pageParam = { page: 1, pageSize: 10, ...model };
240
+ this.belongPlanDataAll = [];
241
+ this.fetchList();
242
+ },
243
+ }
244
+ ```
245
+
246
+ ### 联动方法中读 filters 而非 pageParam
247
+
248
+ ```js
249
+ refreshBelongPlan() {
250
+ if (this.filters.projectStage && this.filters.reviewGroupYear) {
251
+ api.queryPlanList(this.filters.projectStage, this.filters.reviewGroupYear)
252
+ .then(data => { this.belongPlanDataAll = data; this.filters.belongPlan = ''; });
253
+ }
254
+ }
255
+ ```
256
+
257
+ ### 移除的旧代码
258
+
259
+ | 旧 data 字段 | 旧方法 |
260
+ |-------------|--------|
261
+ | `isfolid`, `isfolid1` | `unfoldandfold()` |
262
+ | `isshowbutton` | `unfoldandfold1()` |
263
+ | `screenHeight` | `tableheight()` / `getTableHeight()` |
264
+ | `tableHeight` | `resetQuery()` / `resetBusinessQuery()` |
265
+
266
+ ## 组件 API
267
+
268
+ ### `<list-page>`
269
+
270
+ | Slot | 用途 |
271
+ |------|------|
272
+ | `#aside` | 左侧/右侧面板 |
273
+ | `#filter` | 筛选区,放 `<filter-form>` |
274
+ | `#toolbar` | 工具栏按钮 |
275
+ | default | `<el-table>` |
276
+ | `#pagination` | `<el-pagination>` |
277
+
278
+ Props: `asideWidth`(默认 `'240px'`)
279
+
280
+ ### `<filter-form>`
281
+
282
+ Props: `items`, `v-model`, `collapsed`(默认 true), `defaultRowCount`(默认 1), `loading`, `labelWidth`
283
+
284
+ Events: `@search(model)`, `@reset(model)`
285
+
286
+ Item 字段: `label`, `prop`, `type`, `span`, `options`, `visible`, `multiple`, `onChange`, `placeholder`, `clearable`, `filterable`
287
+
288
+ ## 改造流程
289
+
290
+ ### 1. 模板改造(标准模式)
291
+
292
+ ```html
293
+ <template>
294
+ <div class="pageName">
295
+ <list-page>
296
+ <template #filter>
297
+ <filter-form :items="filterItems" v-model="filters" @search="onSearch" @reset="onReset" />
298
+ </template>
299
+ <template #toolbar>
300
+ <el-button type="primary" icon="el-icon-plus" @click="handleAdd">新增</el-button>
301
+ </template>
302
+ <el-table
303
+ ref="table"
304
+ :data="list"
305
+ :height="tableHeight"
306
+ v-loading="loading"
307
+ border stripe
308
+ >
309
+ <el-table-column type="selection" width="55" />
310
+ <el-table-column type="index" label="序号" width="60" />
311
+ </el-table>
312
+ <template #pagination>
313
+ <el-pagination
314
+ background
315
+ :total="total"
316
+ :current-page="pageParam.page"
317
+ :page-size="pageParam.pageSize"
318
+ layout="total, prev, pager, next, sizes"
319
+ @current-change="page => { pageParam.page = page; fetchList() }"
320
+ @size-change="size => { pageParam.pageSize = size; pageParam.page = 1; fetchList() }"
321
+ />
322
+ </template>
323
+ </list-page>
324
+ <el-dialog>...</el-dialog>
325
+ </div>
326
+ </template>
327
+ ```
328
+
329
+ ### 2. 脚本结构
330
+
331
+ ```js
332
+ export default {
333
+ inject: {
334
+ listPage: { from: 'listPage', default() { return { tableHeight: 300 } } }
335
+ },
336
+ data() {
337
+ return {
338
+ filters: { /* key 与 filterItems prop 一致 */ },
339
+ pageParam: { page: 1, pageSize: 10 },
340
+ }
341
+ },
342
+ computed: {
343
+ tableHeight() { return this.listPage.tableHeight },
344
+ filterItems() { return [ /* ... */ ] },
345
+ },
346
+ mounted() {
347
+ this.fetchList()
348
+ },
349
+ methods: {
350
+ onSearch(model) { /* ... */ },
351
+ onReset(model) { /* ... */ },
352
+ fetchList() { /* API 调用 */ },
353
+ }
354
+ }
355
+ ```
356
+
357
+ ### 3. 构建验证
358
+
359
+ ```bash
360
+ npm run build 2>&1 | tail -20
361
+ ```
362
+
363
+ ## 常见错误
364
+
365
+ | 错误 | 解决 |
366
+ |------|------|
367
+ | template "no matching end tag" | 确保 `<list-page>` 和 `<el-dialog>` 被同一父 `<div>` 包裹 |
368
+ | 可选链 `?.` 编译失败 | 改为 `if (x) x.method()` |
369
+ | filterItems 中 options 不显示 | 检查是否 map 为了 `[{ value, label }]` 格式 |
370
+ | 联动下拉不刷新 | 联动回调中是否读了 `this.pageParam.xxx` 而非 `this.filters.xxx` |
371
+ | 列表高度不对 | 确认 inject 和 `:height="tableHeight"` 已加 |
372
+
373
+ ## 扩展功能
374
+
375
+ ### 表格列筛选(Element UI 内置)
376
+
377
+ ```html
378
+ <el-table-column
379
+ label="年度"
380
+ prop="projectYear"
381
+ :filters="columnFilterOptions.projectYear"
382
+ :filter-method="(value, row) => String(row.projectYear) === value"
383
+ filter-placement="bottom-start"
384
+ column-key="projectYear"
385
+ />
386
+ ```
387
+
388
+ ```js
389
+ computed: {
390
+ columnFilterOptions() {
391
+ const data = this.tableData || []
392
+ return {
393
+ projectYear: [...new Set(data.map(r => String(r.projectYear)).filter(Boolean))]
394
+ .map(v => ({ text: v, value: v })),
395
+ }
396
+ },
397
+ }
398
+ ```
399
+
400
+ ### 表格列宽拖拽
401
+
402
+ 设置 `border` 属性后自动支持列宽拖拽调整,无需额外依赖。
@@ -0,0 +1,131 @@
1
+ ---
2
+ name: micro-app-setup
3
+ description: 微应用项目的环境检测、依赖安装、qiankun 配置等标准化安装流程,确保项目能正常启动和接入主应用。
4
+ principles:
5
+ - 不改原项目文件,问题通过配置和共享组件解决
6
+ - 每一步后验证结果,不过度安装非必需依赖
7
+ triggers:
8
+ - 安装项目
9
+ - 启动项目
10
+ - 配置qiankun
11
+ - 子应用配置
12
+ - 跑起来
13
+ - npm install
14
+ - 环境检测
15
+ - 端口冲突
16
+ ---
17
+
18
+ # 微应用项目安装配置指南
19
+
20
+ ## 核心原则
21
+
22
+ **不改原项目文件,通过共享组件覆盖。** 全局 CSS、SCSS 变量、工具类等属于各项目自身资产,不得修改。`list-page` / `filter-form` 等共享组件必须**自包含**,通过 scoped `::v-deep` 覆盖 Element UI 样式,不依赖项目 `_var.scss` / `_theme.scss`。
23
+
24
+ ---
25
+
26
+ ## 1. 环境检测(每次必做)
27
+
28
+ ### 1.1 检测项目基础信息
29
+
30
+ ```bash
31
+ # 读取项目名和依赖
32
+ node -p "require('./package.json').name"
33
+ node -p "require('./package.json').engines"
34
+ # 检查 Vue/CLI 版本
35
+ node -p "require('./package.json').devDependencies['@vue/cli-service']"
36
+ # 检查 Sass 实现
37
+ node -p "require('./package.json').devDependencies.sass || require('./package.json').devDependencies['node-sass'] || '无'"
38
+ ```
39
+
40
+ ### 1.2 检测端口是否冲突
41
+
42
+ ```bash
43
+ # 读取当前项目端口配置
44
+ grep -A5 "devServer" vue.config.js | grep "port"
45
+ # 检查已占用端口
46
+ lsof -i :9600 -i :9601 -i :9602 2>/dev/null || echo "端口空闲"
47
+ ```
48
+
49
+ ### 1.3 检测 .npmrc
50
+
51
+ ```bash
52
+ cat .npmrc 2>/dev/null || echo "无项目级 .npmrc,使用全局 registry"
53
+ ```
54
+
55
+ ---
56
+
57
+ ## 2. 依赖安装
58
+
59
+ ```bash
60
+ rm -f package-lock.json
61
+ rm -rf node_modules
62
+ npm install --legacy-peer-deps
63
+ ```
64
+
65
+ 常见错误处理:
66
+ - `ENOTFOUND repo.inspur.com` → 删除 `package-lock.json` 重装
67
+ - `ERESOLVE peer dependency` → 加 `--legacy-peer-deps`
68
+ - `node-sass` 编译失败 → 换 `sass` (Dart Sass),无需编译原生模块
69
+
70
+ ---
71
+
72
+ ## 3. qiankun 子应用必配项(按需检查)
73
+
74
+ 以下配置仅当通过 umc-main 主应用加载时需要,独立运行不受影响。
75
+
76
+ ### 3.1 CORS 跨域头
77
+
78
+ 检查 `vue.config.js` 的 `devServer` 中是否有:
79
+ ```js
80
+ headers: { 'Access-Control-Allow-Origin': '*' }
81
+ ```
82
+ 没有则添加。
83
+
84
+ ### 3.2 布局隐藏
85
+
86
+ 检查 `src/main.js` 中是否有:
87
+ ```js
88
+ if (window.__POWERED_BY_QIANKUN__ || process.env.NODE_ENV != "development") {
89
+ generalConfig.hasLayout = false
90
+ }
91
+ ```
92
+ 只有 `process.env.NODE_ENV != "development"` 则需补充 `__POWERED_BY_QIANKUN__` 判断。
93
+
94
+ ### 3.3 .container 满高样式
95
+
96
+ 检查 `src/views/common/Home.vue` 中 `<router-view>` 是否有 `class="container"`,且存在无 scoped 的样式:
97
+ ```html
98
+ <style lang="scss">
99
+ .container { width: 100%; height: 100%; }
100
+ </style>
101
+ ```
102
+
103
+ ### 3.4 umc-main devEntries 注册
104
+
105
+ 在 `umc-main/src/libs/util.qiankun.js` 的 `devEntries` 中添加当前子应用条目。应用名称从当前项目 `package.json` 的 `name` 字段获取。
106
+
107
+ ---
108
+
109
+ ## 4. zoom 缩放问题检测
110
+
111
+ 部分项目通过 `document.body.style.zoom` 做页面缩放。list-page 使用 `ResizeObserver` 计算表格高度,body 缩放会导致容器尺寸与可视区域不一致,**表格高度塌陷**。此外 `zoom` 是 Chrome 私有属性,Firefox 不支持,Element UI popover 也会定位偏移。
112
+
113
+ **检测:**
114
+ ```bash
115
+ grep -rn "document\.body\.style\.zoom" src/ --include="*.vue" --include="*.js"
116
+ grep -rn "\.style\.zoom" src/ --include="*.vue" --include="*.js" | grep -v "document\.body"
117
+ grep -rn "zoom:\s*1" src/ --include="*.css" --include="*.scss"
118
+ ```
119
+
120
+ **修复:** 注释 `document.body.style.zoom = scale`。组件内的反缩放代码(`element.style.zoom = 1/zz`)在 body 缩放移除后 zoom 值恒为 1,不影响功能,可逐步清理。
121
+
122
+ ---
123
+
124
+ ## 5. 启动验证
125
+
126
+ ```bash
127
+ npm run dev
128
+ # 验证:curl -s -o /dev/null -w "%{http_code}" http://localhost:<端口>
129
+ ```
130
+
131
+ 通过 umc-main 访问确认 qiankun 模式正常,无 CORS 报错、无布局异常。