@epxing/cli 0.5.0-beta.11 → 0.5.0-beta.13
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/API.md +2 -2
- package/README.md +5 -1
- package/dist/cli.mjs +1 -1
- package/dist/skills/epxing-frontend-vue3-migrate/SKILL.md +9 -5
- package/dist/skills/epxing-frontend-vue3-migrate/pitfalls.md +808 -743
- package/dist/skills/epxing-frontend-vue3-migrate/references/upgrade-review.md +89 -0
- package/dist/skills/epxing-frontend-vue3-migrate/scripts/check-draggable-slots.cjs +36 -0
- package/dist/skills/epxing-frontend-vue3-migrate/scripts/check-icon-migration.cjs +56 -0
- package/dist/skills/epxing-frontend-vue3-migrate/scripts/check-list-slots.cjs +95 -0
- package/package.json +1 -1
|
@@ -1,743 +1,808 @@
|
|
|
1
|
-
# 踩坑清单(产品升级实证)
|
|
2
|
-
|
|
3
|
-
按"什么时候咬人"分组。每条给**判据**(怎么确认是它)和**修法**。
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## A. 迁移工具阶段(`epx migrate`)
|
|
8
|
-
|
|
9
|
-
### A1. gogocode 会改坏二进制文件
|
|
10
|
-
|
|
11
|
-
**现象**:字体/图片体积莫名变化,构建仍然绿。实测 `FontAwesome.otf` 134808→258854 B、
|
|
12
|
-
`fontawesome-webfont.eot` 165742→212524 B。
|
|
13
|
-
|
|
14
|
-
**根因**:整目录 `cpSync` 把 gogocode 处理过的文件原样拷回,二进制被按文本处理过。
|
|
15
|
-
|
|
16
|
-
**判据**:`git status` 看 `src/assets/` 有没有莫名其妙的改动;`git diff --stat` 看体积。
|
|
17
|
-
|
|
18
|
-
**修法**:拷回只走扩展名白名单(`.vue/.js/.jsx/.ts/.tsx`)。已修在
|
|
19
|
-
`tools/epx-migrate/copy-back-sources.mjs`。踩到就 `git checkout -- src/assets/`。
|
|
20
|
-
|
|
21
|
-
### A2. `install(Vue, options)` 的正则漏匹配
|
|
22
|
-
|
|
23
|
-
**现象**:运行时 install 报 TypeError,构建期完全正常。
|
|
24
|
-
|
|
25
|
-
**根因**:老正则只认 `install = function (Vue)`,产品写成 `(Vue, options)` 就不匹配 →
|
|
26
|
-
gogocode 按它自己的约定把函数体改成 `window.$vueApp.*`,而**平台从不设置这个全局**。
|
|
27
|
-
|
|
28
|
-
**判据**:`grep -n 'window.\$vueApp' src/` 有命中就是它。
|
|
29
|
-
|
|
30
|
-
**修法**:`tools/epx-migrate/codemod-lib-entry.mjs` 覆盖 fn/method 两种形态与额外形参,
|
|
31
|
-
且必须排在 gogocode(stage ③)**之前**。
|
|
32
|
-
|
|
33
|
-
### A3. 具名导入收敛丢掉正文还在用的名字
|
|
34
|
-
|
|
35
|
-
**现象**:运行时 `ReferenceError: store is not defined`,webpack 构建是绿的。
|
|
36
|
-
|
|
37
|
-
**修法**:import 收敛必须放在**所有正文改写之后**,且保留仍被引用的名字。
|
|
38
|
-
|
|
39
|
-
### A4. eslint 全家被删,工具链先坏
|
|
40
|
-
|
|
41
|
-
**现象**:`npx eslint --fix <file>` 报 `couldn't find the config "@vue/standard"`。
|
|
42
|
-
|
|
43
|
-
**根因**:依赖迁移删 vue-cli 生态时连带删了它。
|
|
44
|
-
|
|
45
|
-
**修法**(eslint 8 的正确组合):`@vue/eslint-config-standard@8` +
|
|
46
|
-
`eslint-config-standard@17`(16 要 eslint@7)+ `@babel/eslint-parser`;
|
|
47
|
-
`.eslintrc.js` 切 `plugin:vue/vue3-essential`、`parser: '@babel/eslint-parser'`。
|
|
48
|
-
注意 standard 17 起 `eslint-plugin-node` 改名 `eslint-plugin-n`。
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
### A5. 独立包升级后仍显示欢迎页或平台入口空白
|
|
53
|
-
|
|
54
|
-
**区分根组件与系统视图**:core 的 createApp 提供路由根组件;createFrameRouter 提供平台路由和守卫;Business 注册的系统 App 依赖已加载的应用配置,再选择 FIRST_PAGE / MOBILE_FIRST_PAGE。只挂计数欢迎页不会进入系统;直接把系统 App 当根组件则绕过配置加载链。
|
|
55
|
-
|
|
56
|
-
**修复边界**:新版 upgrade 只自动修复 main.js 和 App.vue 均为原始计数模板的工程。自定义页面和入口保留,按用途核对路由及插件,不全量覆盖为平台模板。scan 的 R4 能提示原始欢迎页;构建、静态检查和模拟 DOM 通过都不能替代真实后端的登录、角色、菜单、首页及产品组件验收。
|
|
57
|
-
|
|
58
|
-
**远程交付**:产品库入口保留组件安装/导出和 runtime 标识,不把本地 main.js 的 createApp/mount 打入远程库。组件校验优先读取产物;无产物时按入口目录列源码候选,动态收集规则先构建再校验,不把空集合误判为缺失全部业务组件。
|
|
59
|
-
|
|
60
|
-
## B. Vite 化阶段
|
|
61
|
-
|
|
62
|
-
### B1. `recorder-core` 引擎与核心之间**没有模块图边**
|
|
63
|
-
|
|
64
|
-
**现象**:dev 壳白屏,`TypeError: Cannot read properties of undefined (reading 'i18n')`
|
|
65
|
-
指向 `.vite/deps/chunk-XXXX.js`。
|
|
66
|
-
|
|
67
|
-
**根因**:引擎(`src/engine/wav.js`)对核心是**零 import**,执行时从 `window.Recorder` 取,
|
|
68
|
-
第一行就是 `var rec = win.Recorder, ni = rec.i18n`。模块图里没有 wav→core 这条边,
|
|
69
|
-
**没有任何打包器有义务先执行核心**。源码里两条相邻 import 只是"声明序",
|
|
70
|
-
esbuild 依赖预打包一拆 chunk 就没了。
|
|
71
|
-
|
|
72
|
-
**注意**:炸的是 **epx-frame 自己**(`lib/epx-frame.esm.mjs` 第 1、2 行),产品侧收口模块管不着。
|
|
73
|
-
|
|
74
|
-
**修法**:补边 shim + **dev 专用** alias(正则 `$` 锚定,shim 内部引带 `.js` 后缀的说明符
|
|
75
|
-
避免 alias 回自己)。
|
|
76
|
-
|
|
77
|
-
**别用 `optimizeDeps.exclude`**:recorder-core 只有 CJS 入口,排除预打包就没有 interop,
|
|
78
|
-
换来 `SyntaxError: ... does not provide an export named 'default'`。
|
|
79
|
-
|
|
80
|
-
### B2. `import * as ns` 挡住 shim 生成
|
|
81
|
-
|
|
82
|
-
**现象**:按需构建报死,说某个 external 无法生成 shim。
|
|
83
|
-
|
|
84
|
-
**根因**:shim 无法静态枚举命名空间导入会访问哪些名字。
|
|
85
|
-
|
|
86
|
-
**修法**:改具名导入。这是**构建期报死**的设计(不静默),改掉即可。
|
|
87
|
-
|
|
88
|
-
### B3. 未映射的 external 全局名
|
|
89
|
-
|
|
90
|
-
**现象**:构建期报 `external 'xxx' 没有对应的运行时全局映射`。
|
|
91
|
-
|
|
92
|
-
**修法**:产品自己知道全局名的(如 `recorder-core` → `Recorder`),
|
|
93
|
-
在 `vite.config.mjs` 的 `output.globals` 里配;平台提供的去 `build/lazy-lib/gen-shims.mjs` 补。
|
|
94
|
-
|
|
95
|
-
### B4. `url(~@)` 在 Vite 下静默 404
|
|
96
|
-
|
|
97
|
-
webpack 遗留写法,Vite 不认 `~`。改成 `@/` 或相对路径。
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
## C. 按需打包特有(**dev 壳一个都验不出来**)
|
|
102
|
-
|
|
103
|
-
### C1. `extractName` 抽不到 `name` → 注册名退成路径兜底
|
|
104
|
-
|
|
105
|
-
**现象**:页面按 `epx-hr-search` 找组件找不到,控制台一声不吭。
|
|
106
|
-
|
|
107
|
-
**根因**(已修):老实现用 `export default` 之后 **400 字符的窗口**找 `name`。
|
|
108
|
-
`Search.vue` 里 `export default {` 在 264 行、`name` 在 303 行,隔了 39 行远超窗口 →
|
|
109
|
-
退到路径兜底名 `hrcomponentsSearch`。而 dev 壳读**运行时** `comp.name`,两边不一致。
|
|
110
|
-
|
|
111
|
-
**判据**:`index.manifest.json` 里有不含 `-` 的键,就是路径兜底名。
|
|
112
|
-
|
|
113
|
-
```bash
|
|
114
|
-
node -e "const m=require('./lib/index.manifest.json');console.log(Object.keys(m.components).filter(k=>!k.includes('-')))"
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
**修法**:babelParse 真解析、只认顶层属性。**别把窗口调大**——`name` 写在 methods
|
|
118
|
-
后面又会漏。
|
|
119
|
-
|
|
120
|
-
### C2. 平台占位抢注
|
|
121
|
-
|
|
122
|
-
**现象**:`[loadComponent] system.component 里没有 epx-xxx`。
|
|
123
|
-
|
|
124
|
-
**根因**:平台 `loadComponent` 与产品 `install` 都是「没人占我才占」
|
|
125
|
-
(都是 `if (!app.component(name))`),谁先跑谁赢。页面配置引用组件时
|
|
126
|
-
`Page.vue` 先按名字调平台工厂抢注了占位,产品 install 随后跳过。
|
|
127
|
-
而占位的解析链里**只有平台组件 + dashboard + designer,没有产品库**。
|
|
128
|
-
|
|
129
|
-
**判据**:控制台查槽位里是谁——产品的懒加载 wrapper 有 `.name`,平台占位没有。
|
|
130
|
-
|
|
131
|
-
```js
|
|
132
|
-
const c = document.querySelector('#app').__vue_app__._context.components['epx-hr-search']
|
|
133
|
-
c?.name ? 'product wrapper' : 'platform stub'
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
**修法**(已修):解析链在平台组件之后、可选能力组之前查 `window.componentLibrary`。
|
|
137
|
-
排在可选能力组之前很关键,否则一个产品组件会白拉 dashboard + 体积很大的 designer。
|
|
138
|
-
|
|
139
|
-
### C3. 记账写在 install 之前 → 产品幂等守卫空转
|
|
140
|
-
|
|
141
|
-
**现象**:`window.__epxInstalledLibs__` 里有 `hr@3.0.1`,但 app 里 **0 个**产品组件。完全静默。
|
|
142
|
-
|
|
143
|
-
**根因**:平台 `util.loadJs` 先记账再调 install,而产品 install 开头是
|
|
144
|
-
`if (marks[sig]) return`(PROJECT-GUIDE 约定的幂等写法)——**一进门就返回**。
|
|
145
|
-
两侧去重分两次提交时各自都对,撞一起才炸。
|
|
146
|
-
|
|
147
|
-
**判据**:
|
|
148
|
-
|
|
149
|
-
```js
|
|
150
|
-
JSON.stringify({
|
|
151
|
-
libs: Object.keys(window.__epxInstalledLibs__ || {}),
|
|
152
|
-
count: Object.keys(document.querySelector('#app').__vue_app__._context.components)
|
|
153
|
-
.filter(n => /^epx-/.test(n)).length
|
|
154
|
-
})
|
|
155
|
-
```
|
|
156
|
-
表里有记录、count 却是 0,就是它。
|
|
157
|
-
|
|
158
|
-
**修法**(已修):记账挪到 `useVuePlugin` **之后**。
|
|
159
|
-
|
|
160
|
-
### C4. 嵌套构建没继承 `assetsInlineLimit`
|
|
161
|
-
|
|
162
|
-
**现象**:图片变成 `assets/*.png` 文件,产物里引用为**绝对路径** `/assets/xxx.png`。
|
|
163
|
-
远程库从文件服务子路径加载,这个 URL 解析到平台页面的 origin 根目录 → **运行时 404,构建期无声**。
|
|
164
|
-
|
|
165
|
-
**判据**:整体单体形态**不产生** `assets/` 目录(全内联),按需形态却有 → 就是没继承。
|
|
166
|
-
|
|
167
|
-
**修法**(已修):`inheritedBuildOptions()` 把 `assetsInlineLimit`/`assetsDir`/`target`/
|
|
168
|
-
`cssTarget`/`sourcemap`/`minify` 一并转发。`target` 同样要紧:壳由外层构建、chunk 由嵌套
|
|
169
|
-
构建,两边不一致会产出语法级别不同的代码。
|
|
170
|
-
|
|
171
|
-
### C5. 没有读者的 `.deps.json`
|
|
172
|
-
|
|
173
|
-
第①趟给每个 chunk、第③趟给 `index.full.js` 都会产出一份,**都没人读**
|
|
174
|
-
(chunk 走 `import()` 不过 ensure;`index.full.js` 只经降级裸 script 加载)。
|
|
175
|
-
实测 hr:49 个文件 49 KB 死重量。
|
|
176
|
-
|
|
177
|
-
**修法**(已修):`rmStrayDepsJson` 事后删,**不能**把 `epx-frame-deps` 插件从嵌套构建滤掉——
|
|
178
|
-
组件源码只在第①趟被 transform,插件靠同一实例跨趟累积,滤掉会让壳的清单变成空数组。
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
## D. 本地联调环境
|
|
183
|
-
|
|
184
|
-
### D1. junction 链接平台必须配 `resolve.dedupe`
|
|
185
|
-
|
|
186
|
-
Vite 默认 `preserveSymlinks:false`,按真实路径解析 → vue 会有两份
|
|
187
|
-
(实测 frame 3.5.39 / 产品 3.5.42),provide/inject 断链、组件注册表分裂。
|
|
188
|
-
|
|
189
|
-
```js
|
|
190
|
-
resolve: { dedupe: ['vue', 'vuex', 'vue-router'] }
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
### D2. Git Bash 不跟随 junction
|
|
194
|
-
|
|
195
|
-
`ls/stat/grep` 会报文件不存在。用 PowerShell 或 node 验。Vite/Node 走 Windows 原生 API 正常。
|
|
196
|
-
|
|
197
|
-
### D3. 本地顶替远程库时,CSS 会走线上旧构建
|
|
198
|
-
|
|
199
|
-
后端 `listWebpackUrl` 返回的**一相对一绝对**:
|
|
200
|
-
|
|
201
|
-
```json
|
|
202
|
-
{"code":"0","data":{"entitys":[{"path":[
|
|
203
|
-
"/file-server/npm/test/web_hr/index.js", // 相对 → 拼 window.IP → 本地
|
|
204
|
-
"https://dev.epxing.com:88/file-server/npm/test/web_hr/index.css" // 绝对 → 原样用 → 线上
|
|
205
|
-
]}]}}
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
`loadLibrary` 只给相对路径拼 `window.IP`。于是组件是本地新 chunk、样式是线上旧 CSS,
|
|
209
|
-
scoped 的 `data-v-<hash>` 两边对不上 → **组件渲染出来但完全没样式**。
|
|
210
|
-
|
|
211
|
-
**判据**:`[...document.styleSheets].map(s=>s.href).filter(h=>h&&h.includes('<pkg>'))`
|
|
212
|
-
看 host 是不是本地。
|
|
213
|
-
|
|
214
|
-
**修法**:本地反代里拦 `listWebpackUrl` 响应,把绝对地址改写成相对。
|
|
215
|
-
注意响应外面**包了一层 `data`**(`{code,msg,data:{entitys},...}`),
|
|
216
|
-
直接取 `json.entitys` 会拿到 undefined 并静默改写 0 条。
|
|
217
|
-
|
|
218
|
-
### D4. `epx-frame` 未发布时 npm 装不了任何东西
|
|
219
|
-
|
|
220
|
-
产品 package.json 声明 `epx-frame@^5.3.0`,未发布前 npm **解析根清单就失败**(ETARGET),
|
|
221
|
-
连加一个 devDep 都装不了。绕法:别处建空目录装好,再逐包拷进产品的 `node_modules`
|
|
222
|
-
(所有 `@scope` 要下沉到子包粒度判存在,按整个 scope 目录判会漏);
|
|
223
|
-
版本冲突的包放**嵌套** `node_modules`。
|
|
224
|
-
|
|
225
|
-
---
|
|
226
|
-
|
|
227
|
-
## E. 主题/样式
|
|
228
|
-
|
|
229
|
-
### E1. 换肤 mixin 按主题展开,既臃肿又漏主题
|
|
230
|
-
|
|
231
|
-
**现象**:`index.css` 里同一条规则有 N 份 `[data-theme=x]` 拷贝;
|
|
232
|
-
用户选了某些主题色,产品组件不跟着变。
|
|
233
|
-
|
|
234
|
-
**根因**:产品自带的 `mixins.scss` 用 `@each` 遍历**写死的主题列表**。
|
|
235
|
-
实测 hr 写死 8 套,而平台有 **11 套预设 + custom**(运行时注入)——**4 套完全不生效**。
|
|
236
|
-
|
|
237
|
-
**判据**:
|
|
238
|
-
|
|
239
|
-
```bash
|
|
240
|
-
node -e "const c=require('fs').readFileSync('lib/index.css','utf8');console.log(c.split('}').filter(r=>/\[data-theme=/.test(r.split('{')[0])).length)"
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
**修法**:统一走 `rgba(var(--color-primary), $opacity)`。平台在 `[data-theme=x]` 上设
|
|
244
|
-
`--color-primary: r,g,b`,custom 由运行时 JS 注入,所有主题自动生效。
|
|
245
|
-
`$color` 参数改作 `var()` 的兜底值。实测 hr:130.4 → 85.0 KB(−35%),
|
|
246
|
-
`[data-theme=*]` 规则 600 → 0。
|
|
247
|
-
|
|
248
|
-
**注意 gzip 收益很小**(21.3 → 17.4 KB)——重复内容压得好,**省的是解析与选择器匹配成本**。
|
|
249
|
-
|
|
250
|
-
---
|
|
251
|
-
|
|
252
|
-
### E2. 产品/项目样式必须跟随平台主题轴
|
|
253
|
-
|
|
254
|
-
平台主题包已经由宿主加载。产品只消费变量,不再次导入主题入口,也不在 `:root`、`html` 或 `body` 写默认主题变量或固定主题属性;否则懒加载 CSS 到达后可能重置用户当前主题。不要因此改回 Vue2 垫片或删除 scoped。
|
|
255
|
-
|
|
256
|
-
**升级时执行修复**:`epxing migrate ... --apply` 自动迁移常见静态值;已升级工程用 `epxing theme <工程目录>` 预览,再加 `--apply`。覆盖 src 中 Vue style、template/JSX/render 内联样式及 CSS/SCSS/Less。原值作为变量 fallback,默认外观保持一致;重复执行幂等。返回码 2 表示仍有动态或特殊样式需复核,自动部分可能已经写入。`epxing scan` 的 R3 检查静态残留。
|
|
257
|
-
|
|
258
|
-
| 样式语义 | 平台变量 | 默认值 |
|
|
259
|
-
|---|---|---|
|
|
260
|
-
| 小/正文/大/标题字号 | `--epx-font-size-s/base/l/xl`(分别使用完整变量名) | 12/14/16/20px |
|
|
261
|
-
| 小/基础/大圆角 | `--epx-radius-s/base/l`(分别使用完整变量名) | 2/4/8px |
|
|
262
|
-
| 页面底色/卡片底色 | `--epx-bg` / `--epx-bg-card` | #f5f7fa / #fff |
|
|
263
|
-
| 主要/次要/辅助文字 | `--epx-text-primary` / `--epx-text-secondary` / `--epx-text-assistant` | #303133 / #606266 / #909399(亮色);`--epx-text` 为主要文字兼容别名 |
|
|
264
|
-
| 基础边框 | `--epx-border` | #dcdfe6(亮色) |
|
|
265
|
-
| 与密度一起变化的页面留白 | `--epx-margin` | 10px |
|
|
266
|
-
| 品牌主色 | `rgba(var(--color-primary), 透明度)` | RGB 元组,参照 E1 |
|
|
267
|
-
|
|
268
|
-
例如 `font-size: var(--epx-font-size-base, 14px); border-radius: var(--epx-radius-base, 4px); background-color: var(--epx-bg-card, #fff);`。Element Plus 组件的字号、圆角由平台 `--el-*` 映射管理,不在产品全局重写 `.el-*`。`--epx-space-*` 是固定间距级别,不等于密度轴;只有明确属于密度留白的布局才使用 `--epx-margin`。
|
|
269
|
-
|
|
270
|
-
**自动修复的边界与后续处理**:
|
|
271
|
-
|
|
272
|
-
1. 追踪工具报告的 style 变量、函数、对象展开、Sass/Less 变量及 mixin。区分实际 DOM 样式与 ECharts/canvas/导出数据等业务配置,确认消费位置后完成主题适配。CSS 变量或 calc 已存在也需确认它最终指向主题轴,不能仅凭使用 var 就验收。
|
|
273
|
-
2. 13/15/18px 字号、12px 等非标准圆角不盲目改成最近令牌。先按设计角色选令牌;若必须保持默认尺寸且随平台字号变化,可使用 `calc(var(--epx-font-size-base, 14px) + 4px)`。记录理由,不能把所有尺寸机械加减。固定宽高、图标/图表、圆形头像/胶囊、字体零值布局、打印/导出、动画和品牌/状态颜色按实际设计保留;有疑问先验证用途。
|
|
274
|
-
3. 缩进式 Sass/Stylus、外部 src 样式、脚本动态值、font 简写以及旧主题展开 mixin 需继续修复源文件。工具只处理 src 范围,不回写依赖和构建产物。检查最终编译 CSS 是否仍有阻断主题的覆盖;新建组件同样使用主题变量。
|
|
275
|
-
4. 主题迁移保持原有作用域隔离。继承与插槽链依 F15 验证,用专属容器局部 `:deep()` 处理实际失配,禁止为了主题同步制造全局泄漏。
|
|
276
|
-
|
|
277
|
-
#### 全量组件检查:每个组件都要有结论
|
|
278
|
-
|
|
279
|
-
此要求适用于所有产品升级路径,包括已使用 Vue3 和独立包、只更新框架版本的工程。来源:用户明确要求产品升级接入主题和全局 CSS 变量,逐组件检查。
|
|
280
|
-
|
|
281
|
-
1. **先建清单再修改**:以产品真实组件收集入口为主,合并源码中的内部子组件、独立弹窗/浮层、动态加载、移动端及可视化组件。复用现有 `epxing verify` 结果和源码索引;无产物时源码清单只算候选,构建后再与实际清单对账。入口未注册但被组件引用的子组件不能漏掉;同一实现的别名列明对应关系,不重复计为独立实现。
|
|
282
|
-
2. **逐个检查实际样式来源**:读取组件及其继承、共享样式、内联 style、JSX/render、动态函数、Sass/Less 变量、mixin、外部样式 src。检查背景、三档文字、边框颜色/宽度/类型、圆角、间距、阴影、模糊以及控件、表格、浮层适配。仅搜索 `.vue` 的 style 块或仅看 CLI 告警不算完成。
|
|
283
|
-
3. **逐个按用途接入**:先判断外层与内部各区域的角色,再修实际消费节点。继承组件也要检查是否存在覆写、硬编码内层底色或作用域失配;无样式组件记录其继承/消费路径,不强行为它添加 CSS。
|
|
284
|
-
4. **逐个关闭问题**:可确认的适配问题继续修复并验证,不把“人工复核”作为完成结论。业务语义不明、组件无法打开或目标平台能力不足时,逐条记录依据、缺少的信息与下一步;不能靠填充默认值或删除组件让清单变绿。
|
|
285
|
-
|
|
286
|
-
逐组件结果写入升级任务的主题验收台账,并从 `MIGRATION-REPORT.md` 链接;也可直接附在该报告中。至少包含下列字段:
|
|
287
|
-
|
|
288
|
-
| 组件文件/名称及别名 | 页面/触发入口 | 角色及区域 | 样式来源与变量接入/改动 | 静态结论 | 最终产物与主题切换证据 | 状态/例外/下一步 |
|
|
289
|
-
| --- | --- | --- | --- | --- | --- | --- |
|
|
290
|
-
| 实际路径及注册名 | 菜单、嵌入、弹窗或移动分支 | 外层 layout,内部 surface 等 | 继承、共享、内联、动态来源 | 逐项核查结果 | 页面、模式、计算样式、截图等 | 已通过 / 已改待验证 / 待处理 / 阻塞 |
|
|
291
|
-
|
|
292
|
-
报告列明组件总数及各状态数量,数量必须对齐清单,不能只列改动文件。打印、品牌图片、状态色、固定结构尺寸等例外记录到具体组件和属性,写明保留理由及运行证据;“例外已确认”不能替代该组件其它区域的主题检查。不可达或未运行的组件不得记为已通过。
|
|
293
|
-
|
|
294
|
-
#### 按用途消费全局变量
|
|
295
|
-
|
|
296
|
-
下面是用途映射,不是将每个变量强塞给每个组件。目标版本的变量全集和接入契约以平台文档为准;本平台仓入口为 `frontend/frame/docs/revision/theme/guide.md` 与 `component-style.md`。在独立产品中按当前安装版本的文档核对,不假设技能复制后仍包含平台仓目录。
|
|
297
|
-
|
|
298
|
-
| 区域/角色 | 接入要求 |
|
|
299
|
-
| --- | --- |
|
|
300
|
-
| 面板、卡片、列表区块 | 组件选项与 name 同级声明 `appearance: 'surface'`,通过平台公共混入或包装器接入;消费 `--epx-component-bg`、`--epx-component-border-color/width/style`、`--epx-component-gap`、`--epx-component-radius`、`--epx-component-shadow`、`--epx-component-backdrop-filter`。斜线分组需展开为独立变量名 |
|
|
301
|
-
| 原子控件、表单字段 | 使用 `control` 角色或既有控件映射;消费 `--epx-control-bg`、`--epx-control-border-color`、`--epx-control-radius`,不套面板背景/阴影;检查禁用、焦点、占位及错误状态 |
|
|
302
|
-
| 布局、栅格、容器、文字、图标 | 分别使用 `layout` / `plain` 或既有无装饰行为,不强加面板外观;内部面板独立接入。文字按主要/次要/辅助语义使用对应文字变量,图标保留 currentColor 或明确状态/品牌色 |
|
|
303
|
-
| 顶部、侧边导航 | 使用独立 `--epx-top-nav-*` / `--epx-side-nav-*` 背景、文字/选中、阴影及模糊变量,不受面板装饰统一覆盖;自写导航须核对目标版本支持的每个用途变量 |
|
|
304
|
-
| 页面内容 | 使用 `--epx-page-bg` 等页面背景契约;AppIndex 外壳不强行铺页面背景;组件内部不可用基础页面色替代所有用途背景 |
|
|
305
|
-
| 表格 | 接入 `--epx-table-header-bg`、`--epx-table-hover-bg`,检查固定列、合并表头与编辑态;面板内数据区按平台语义透出面板背景,不能残留硬编码白底遮住渐变 |
|
|
306
|
-
| 浮层、弹窗、抽屉 | 接入 `--epx-overlay-bg` / `--epx-overlay-shadow` 及基础文字、圆角;检查 Teleport 到 body 后变量和局部样式是否仍生效 |
|
|
307
|
-
|
|
308
|
-
普通 HTML 或直接挂载的 Vue 子组件不能仅靠声明 `appearance` 假定生效:普通面板使用 `epx-surface`,原生控件用 `epx-control`,浮层用 `epx-overlay`;flex/grid 父布局可用 `epx-layout-gap`。这些类由宿主提供,不另写一套全局类;使用 el-card 时检查自带内边距,避免面板类的 padding 叠加。
|
|
309
|
-
|
|
310
|
-
**兼容优先级**:旧 `configs.componentSurface` 布尔值高于 `configs.appearance`,后者高于组件定义及 extends 的角色。检查遗留退出配置和显式 style,保留有意覆盖;新增面板未知于平台默认列表时必须主动接入。根节点保持 class/style 透传,不重复外观类名提高权重,不批量加 `!important`。
|
|
311
|
-
|
|
312
|
-
**透明度、渐变与玻璃效果**:面板和页面/导航使用 `background` 消费完整背景变量,不能用 `background-color` 接收渐变。透明度放在颜色 alpha,不用根节点 opacity;内层不透明背景需按用途处理。需要纯色的控件/浮层不直接继承渐变。运行时配置可生成对应 `-color` 派生变量,手写 CSS 渐变时一并提供。直接消费模糊变量时使用完整 `none` / `blur(20px)`;不能把面板配置界面的数字输入规则误当成 CSS 滤镜语法。默认面板无阴影,导航独立阴影不能被面板主题误关。
|
|
313
|
-
|
|
314
|
-
CLI 当前静态转换只覆盖其支持的常见值,**不自动完成角色判定、34 个用途配置字段覆盖或逐组件运行验收**。不为通过扫描机械替换品牌/业务状态颜色、打印纸张、图片和图表数据;canvas、iframe 及图表色板单独核对,不能把外壳变量变化记成内部绘制已适配。
|
|
315
|
-
|
|
316
|
-
#### 逐组件运行验收
|
|
317
|
-
|
|
318
|
-
对清单中每个组件使用最终产物,在实际平台入口或保持同等包装/插槽/浮层链路的验收页中验证:
|
|
319
|
-
|
|
320
|
-
- 默认亮色、暗色和自定义品牌色;三档文字层次及禁用/状态对比。固定颜色的保留项单独说明,不宣称会自动随暗色切换。
|
|
321
|
-
- 至少两套差异明显的完整主题,包含透明渐变面板;调整组件边框、圆角、间距、阴影及模糊,检查适用区域确实变化,不适用区域保持正确。默认/关闭值和 0 值必须有效。
|
|
322
|
-
- 圆角、字号、密度及控件尺寸的不同档位;检查布局、标签、表格、按钮没有裁切或溢出。无对应属性的区域标注理由,不凭空添加装饰。
|
|
323
|
-
- 弹窗、悬停、选中、编辑等实际分支;产品有移动/可视化版本时同样纳入,不只验 PC 主视图。
|
|
324
|
-
- **先切主题再首次懒加载**,再切另一主题、离开页面进入其它页面;记录目标节点的计算样式及必要尺寸,确认产品 CSS 不重置全局主题,组件外同名元素不受污染。
|
|
325
|
-
|
|
326
|
-
多个组件可在同一验收页批量采证,但必须能定位到每个组件的节点、适用变量及结论;代表页抽测、仅根变量变了、代码中出现 var、静态告警为零或 build 通过均不能代替逐组件验收。普通 Vue 子组件至少在真实父组件调用链中核验,不能因没有单独菜单而漏检。主题继承与配置中心读取另外验证应用默认、个人选择、跟随默认及刷新恢复,不能用直接修改 DOM 变量代替完整主题选择链路。
|
|
327
|
-
|
|
328
|
-
**发布验收**:以最终完整包或懒加载包,在实际平台页面切换默认/大/小字号、默认/直角/圆润圆角、明暗主题和密度;核对卡片、表单、按钮、标题及弹窗同步且不裁切/溢出。主题切换后再首次加载产品组件,确认不回到默认主题;离开页面后检查平台与外部同名元素不受影响。保留打印等设计例外,并记录测试主题、源文件、动态项处理、截图及待验证项。lint/build 或隔离 fixture 通过不能代替实际产品验收。
|
|
329
|
-
|
|
330
|
-
---
|
|
331
|
-
|
|
332
|
-
## F. gogocode 转换残留 & 第三方库(product/recruit 145 组件实证)
|
|
333
|
-
|
|
334
|
-
> 这一组是「gogocode 没转干净 + 项目自带 Vue2 依赖」,多数在 `build:lib:lazy` 才暴露。
|
|
335
|
-
> **能机械化的已进 CLI 自动做**(下面标「已 CLI」),**结构性/换库的靠本节手动**。
|
|
336
|
-
|
|
337
|
-
### F1. gogocode 漏转「元素上的」旧具名插槽 `slot="x"`
|
|
338
|
-
|
|
339
|
-
**现象**:build 报 `[vite:vue] Cannot read properties of undefined (reading 'type')`(**无行号**),
|
|
340
|
-
或 `slot="x" is deprecated`。gogocode-plugin-vue 转了 `<template slot>`,却漏了**直接写在元素上**的。
|
|
341
|
-
|
|
342
|
-
**判据**:`grep -rn 'slot="' src --include=*.vue | grep -v v-slot`(排掉注释行)。
|
|
343
|
-
|
|
344
|
-
**修法**(结构变化,逐个手改;CLI 不做——配对闭合标签的正则不可靠):
|
|
345
|
-
```
|
|
346
|
-
<el-dropdown-menu slot="dropdown">…</el-dropdown-menu>
|
|
347
|
-
→ <template #dropdown><el-dropdown-menu>…</el-dropdown-menu></template>
|
|
348
|
-
|
|
349
|
-
<span slot="footer" class="dialog-footer">…</span> (el-dialog 的 footer)
|
|
350
|
-
→ <template #footer><span class="dialog-footer">…</span></template>
|
|
351
|
-
```
|
|
352
|
-
recruit 实测 7 处需改(另 3 处在注释里,忽略)。定位无行号的 `reading 'type'`:从报错文件里逐个 `slot="` 查起。
|
|
353
|
-
|
|
354
|
-
### F2. gogocode 漏转 `slot-scope`
|
|
355
|
-
`slot-scope="scope"` → `v-slot="scope"`(或 `#default="scope"`)。recruit 1 处,手改。
|
|
356
|
-
|
|
357
|
-
### F3.(已 CLI)`:prop.sync` → `v-model:prop`
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
|
374
|
-
|
|
375
|
-
| `vue-
|
|
376
|
-
| `
|
|
377
|
-
| `
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
`
|
|
402
|
-
|
|
403
|
-
`
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
CLI
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
-
|
|
503
|
-
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
```
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
-
|
|
741
|
-
-
|
|
742
|
-
|
|
743
|
-
|
|
1
|
+
# 踩坑清单(产品升级实证)
|
|
2
|
+
|
|
3
|
+
按"什么时候咬人"分组。每条给**判据**(怎么确认是它)和**修法**。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## A. 迁移工具阶段(`epx migrate`)
|
|
8
|
+
|
|
9
|
+
### A1. gogocode 会改坏二进制文件
|
|
10
|
+
|
|
11
|
+
**现象**:字体/图片体积莫名变化,构建仍然绿。实测 `FontAwesome.otf` 134808→258854 B、
|
|
12
|
+
`fontawesome-webfont.eot` 165742→212524 B。
|
|
13
|
+
|
|
14
|
+
**根因**:整目录 `cpSync` 把 gogocode 处理过的文件原样拷回,二进制被按文本处理过。
|
|
15
|
+
|
|
16
|
+
**判据**:`git status` 看 `src/assets/` 有没有莫名其妙的改动;`git diff --stat` 看体积。
|
|
17
|
+
|
|
18
|
+
**修法**:拷回只走扩展名白名单(`.vue/.js/.jsx/.ts/.tsx`)。已修在
|
|
19
|
+
`tools/epx-migrate/copy-back-sources.mjs`。踩到就 `git checkout -- src/assets/`。
|
|
20
|
+
|
|
21
|
+
### A2. `install(Vue, options)` 的正则漏匹配
|
|
22
|
+
|
|
23
|
+
**现象**:运行时 install 报 TypeError,构建期完全正常。
|
|
24
|
+
|
|
25
|
+
**根因**:老正则只认 `install = function (Vue)`,产品写成 `(Vue, options)` 就不匹配 →
|
|
26
|
+
gogocode 按它自己的约定把函数体改成 `window.$vueApp.*`,而**平台从不设置这个全局**。
|
|
27
|
+
|
|
28
|
+
**判据**:`grep -n 'window.\$vueApp' src/` 有命中就是它。
|
|
29
|
+
|
|
30
|
+
**修法**:`tools/epx-migrate/codemod-lib-entry.mjs` 覆盖 fn/method 两种形态与额外形参,
|
|
31
|
+
且必须排在 gogocode(stage ③)**之前**。
|
|
32
|
+
|
|
33
|
+
### A3. 具名导入收敛丢掉正文还在用的名字
|
|
34
|
+
|
|
35
|
+
**现象**:运行时 `ReferenceError: store is not defined`,webpack 构建是绿的。
|
|
36
|
+
|
|
37
|
+
**修法**:import 收敛必须放在**所有正文改写之后**,且保留仍被引用的名字。
|
|
38
|
+
|
|
39
|
+
### A4. eslint 全家被删,工具链先坏
|
|
40
|
+
|
|
41
|
+
**现象**:`npx eslint --fix <file>` 报 `couldn't find the config "@vue/standard"`。
|
|
42
|
+
|
|
43
|
+
**根因**:依赖迁移删 vue-cli 生态时连带删了它。
|
|
44
|
+
|
|
45
|
+
**修法**(eslint 8 的正确组合):`@vue/eslint-config-standard@8` +
|
|
46
|
+
`eslint-config-standard@17`(16 要 eslint@7)+ `@babel/eslint-parser`;
|
|
47
|
+
`.eslintrc.js` 切 `plugin:vue/vue3-essential`、`parser: '@babel/eslint-parser'`。
|
|
48
|
+
注意 standard 17 起 `eslint-plugin-node` 改名 `eslint-plugin-n`。
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
### A5. 独立包升级后仍显示欢迎页或平台入口空白
|
|
53
|
+
|
|
54
|
+
**区分根组件与系统视图**:core 的 createApp 提供路由根组件;createFrameRouter 提供平台路由和守卫;Business 注册的系统 App 依赖已加载的应用配置,再选择 FIRST_PAGE / MOBILE_FIRST_PAGE。只挂计数欢迎页不会进入系统;直接把系统 App 当根组件则绕过配置加载链。
|
|
55
|
+
|
|
56
|
+
**修复边界**:新版 upgrade 只自动修复 main.js 和 App.vue 均为原始计数模板的工程。自定义页面和入口保留,按用途核对路由及插件,不全量覆盖为平台模板。scan 的 R4 能提示原始欢迎页;构建、静态检查和模拟 DOM 通过都不能替代真实后端的登录、角色、菜单、首页及产品组件验收。
|
|
57
|
+
|
|
58
|
+
**远程交付**:产品库入口保留组件安装/导出和 runtime 标识,不把本地 main.js 的 createApp/mount 打入远程库。组件校验优先读取产物;无产物时按入口目录列源码候选,动态收集规则先构建再校验,不把空集合误判为缺失全部业务组件。
|
|
59
|
+
|
|
60
|
+
## B. Vite 化阶段
|
|
61
|
+
|
|
62
|
+
### B1. `recorder-core` 引擎与核心之间**没有模块图边**
|
|
63
|
+
|
|
64
|
+
**现象**:dev 壳白屏,`TypeError: Cannot read properties of undefined (reading 'i18n')`
|
|
65
|
+
指向 `.vite/deps/chunk-XXXX.js`。
|
|
66
|
+
|
|
67
|
+
**根因**:引擎(`src/engine/wav.js`)对核心是**零 import**,执行时从 `window.Recorder` 取,
|
|
68
|
+
第一行就是 `var rec = win.Recorder, ni = rec.i18n`。模块图里没有 wav→core 这条边,
|
|
69
|
+
**没有任何打包器有义务先执行核心**。源码里两条相邻 import 只是"声明序",
|
|
70
|
+
esbuild 依赖预打包一拆 chunk 就没了。
|
|
71
|
+
|
|
72
|
+
**注意**:炸的是 **epx-frame 自己**(`lib/epx-frame.esm.mjs` 第 1、2 行),产品侧收口模块管不着。
|
|
73
|
+
|
|
74
|
+
**修法**:补边 shim + **dev 专用** alias(正则 `$` 锚定,shim 内部引带 `.js` 后缀的说明符
|
|
75
|
+
避免 alias 回自己)。
|
|
76
|
+
|
|
77
|
+
**别用 `optimizeDeps.exclude`**:recorder-core 只有 CJS 入口,排除预打包就没有 interop,
|
|
78
|
+
换来 `SyntaxError: ... does not provide an export named 'default'`。
|
|
79
|
+
|
|
80
|
+
### B2. `import * as ns` 挡住 shim 生成
|
|
81
|
+
|
|
82
|
+
**现象**:按需构建报死,说某个 external 无法生成 shim。
|
|
83
|
+
|
|
84
|
+
**根因**:shim 无法静态枚举命名空间导入会访问哪些名字。
|
|
85
|
+
|
|
86
|
+
**修法**:改具名导入。这是**构建期报死**的设计(不静默),改掉即可。
|
|
87
|
+
|
|
88
|
+
### B3. 未映射的 external 全局名
|
|
89
|
+
|
|
90
|
+
**现象**:构建期报 `external 'xxx' 没有对应的运行时全局映射`。
|
|
91
|
+
|
|
92
|
+
**修法**:产品自己知道全局名的(如 `recorder-core` → `Recorder`),
|
|
93
|
+
在 `vite.config.mjs` 的 `output.globals` 里配;平台提供的去 `build/lazy-lib/gen-shims.mjs` 补。
|
|
94
|
+
|
|
95
|
+
### B4. `url(~@)` 在 Vite 下静默 404
|
|
96
|
+
|
|
97
|
+
webpack 遗留写法,Vite 不认 `~`。改成 `@/` 或相对路径。
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## C. 按需打包特有(**dev 壳一个都验不出来**)
|
|
102
|
+
|
|
103
|
+
### C1. `extractName` 抽不到 `name` → 注册名退成路径兜底
|
|
104
|
+
|
|
105
|
+
**现象**:页面按 `epx-hr-search` 找组件找不到,控制台一声不吭。
|
|
106
|
+
|
|
107
|
+
**根因**(已修):老实现用 `export default` 之后 **400 字符的窗口**找 `name`。
|
|
108
|
+
`Search.vue` 里 `export default {` 在 264 行、`name` 在 303 行,隔了 39 行远超窗口 →
|
|
109
|
+
退到路径兜底名 `hrcomponentsSearch`。而 dev 壳读**运行时** `comp.name`,两边不一致。
|
|
110
|
+
|
|
111
|
+
**判据**:`index.manifest.json` 里有不含 `-` 的键,就是路径兜底名。
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
node -e "const m=require('./lib/index.manifest.json');console.log(Object.keys(m.components).filter(k=>!k.includes('-')))"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**修法**:babelParse 真解析、只认顶层属性。**别把窗口调大**——`name` 写在 methods
|
|
118
|
+
后面又会漏。
|
|
119
|
+
|
|
120
|
+
### C2. 平台占位抢注
|
|
121
|
+
|
|
122
|
+
**现象**:`[loadComponent] system.component 里没有 epx-xxx`。
|
|
123
|
+
|
|
124
|
+
**根因**:平台 `loadComponent` 与产品 `install` 都是「没人占我才占」
|
|
125
|
+
(都是 `if (!app.component(name))`),谁先跑谁赢。页面配置引用组件时
|
|
126
|
+
`Page.vue` 先按名字调平台工厂抢注了占位,产品 install 随后跳过。
|
|
127
|
+
而占位的解析链里**只有平台组件 + dashboard + designer,没有产品库**。
|
|
128
|
+
|
|
129
|
+
**判据**:控制台查槽位里是谁——产品的懒加载 wrapper 有 `.name`,平台占位没有。
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
const c = document.querySelector('#app').__vue_app__._context.components['epx-hr-search']
|
|
133
|
+
c?.name ? 'product wrapper' : 'platform stub'
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**修法**(已修):解析链在平台组件之后、可选能力组之前查 `window.componentLibrary`。
|
|
137
|
+
排在可选能力组之前很关键,否则一个产品组件会白拉 dashboard + 体积很大的 designer。
|
|
138
|
+
|
|
139
|
+
### C3. 记账写在 install 之前 → 产品幂等守卫空转
|
|
140
|
+
|
|
141
|
+
**现象**:`window.__epxInstalledLibs__` 里有 `hr@3.0.1`,但 app 里 **0 个**产品组件。完全静默。
|
|
142
|
+
|
|
143
|
+
**根因**:平台 `util.loadJs` 先记账再调 install,而产品 install 开头是
|
|
144
|
+
`if (marks[sig]) return`(PROJECT-GUIDE 约定的幂等写法)——**一进门就返回**。
|
|
145
|
+
两侧去重分两次提交时各自都对,撞一起才炸。
|
|
146
|
+
|
|
147
|
+
**判据**:
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
JSON.stringify({
|
|
151
|
+
libs: Object.keys(window.__epxInstalledLibs__ || {}),
|
|
152
|
+
count: Object.keys(document.querySelector('#app').__vue_app__._context.components)
|
|
153
|
+
.filter(n => /^epx-/.test(n)).length
|
|
154
|
+
})
|
|
155
|
+
```
|
|
156
|
+
表里有记录、count 却是 0,就是它。
|
|
157
|
+
|
|
158
|
+
**修法**(已修):记账挪到 `useVuePlugin` **之后**。
|
|
159
|
+
|
|
160
|
+
### C4. 嵌套构建没继承 `assetsInlineLimit`
|
|
161
|
+
|
|
162
|
+
**现象**:图片变成 `assets/*.png` 文件,产物里引用为**绝对路径** `/assets/xxx.png`。
|
|
163
|
+
远程库从文件服务子路径加载,这个 URL 解析到平台页面的 origin 根目录 → **运行时 404,构建期无声**。
|
|
164
|
+
|
|
165
|
+
**判据**:整体单体形态**不产生** `assets/` 目录(全内联),按需形态却有 → 就是没继承。
|
|
166
|
+
|
|
167
|
+
**修法**(已修):`inheritedBuildOptions()` 把 `assetsInlineLimit`/`assetsDir`/`target`/
|
|
168
|
+
`cssTarget`/`sourcemap`/`minify` 一并转发。`target` 同样要紧:壳由外层构建、chunk 由嵌套
|
|
169
|
+
构建,两边不一致会产出语法级别不同的代码。
|
|
170
|
+
|
|
171
|
+
### C5. 没有读者的 `.deps.json`
|
|
172
|
+
|
|
173
|
+
第①趟给每个 chunk、第③趟给 `index.full.js` 都会产出一份,**都没人读**
|
|
174
|
+
(chunk 走 `import()` 不过 ensure;`index.full.js` 只经降级裸 script 加载)。
|
|
175
|
+
实测 hr:49 个文件 49 KB 死重量。
|
|
176
|
+
|
|
177
|
+
**修法**(已修):`rmStrayDepsJson` 事后删,**不能**把 `epx-frame-deps` 插件从嵌套构建滤掉——
|
|
178
|
+
组件源码只在第①趟被 transform,插件靠同一实例跨趟累积,滤掉会让壳的清单变成空数组。
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## D. 本地联调环境
|
|
183
|
+
|
|
184
|
+
### D1. junction 链接平台必须配 `resolve.dedupe`
|
|
185
|
+
|
|
186
|
+
Vite 默认 `preserveSymlinks:false`,按真实路径解析 → vue 会有两份
|
|
187
|
+
(实测 frame 3.5.39 / 产品 3.5.42),provide/inject 断链、组件注册表分裂。
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
resolve: { dedupe: ['vue', 'vuex', 'vue-router'] }
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### D2. Git Bash 不跟随 junction
|
|
194
|
+
|
|
195
|
+
`ls/stat/grep` 会报文件不存在。用 PowerShell 或 node 验。Vite/Node 走 Windows 原生 API 正常。
|
|
196
|
+
|
|
197
|
+
### D3. 本地顶替远程库时,CSS 会走线上旧构建
|
|
198
|
+
|
|
199
|
+
后端 `listWebpackUrl` 返回的**一相对一绝对**:
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{"code":"0","data":{"entitys":[{"path":[
|
|
203
|
+
"/file-server/npm/test/web_hr/index.js", // 相对 → 拼 window.IP → 本地
|
|
204
|
+
"https://dev.epxing.com:88/file-server/npm/test/web_hr/index.css" // 绝对 → 原样用 → 线上
|
|
205
|
+
]}]}}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`loadLibrary` 只给相对路径拼 `window.IP`。于是组件是本地新 chunk、样式是线上旧 CSS,
|
|
209
|
+
scoped 的 `data-v-<hash>` 两边对不上 → **组件渲染出来但完全没样式**。
|
|
210
|
+
|
|
211
|
+
**判据**:`[...document.styleSheets].map(s=>s.href).filter(h=>h&&h.includes('<pkg>'))`
|
|
212
|
+
看 host 是不是本地。
|
|
213
|
+
|
|
214
|
+
**修法**:本地反代里拦 `listWebpackUrl` 响应,把绝对地址改写成相对。
|
|
215
|
+
注意响应外面**包了一层 `data`**(`{code,msg,data:{entitys},...}`),
|
|
216
|
+
直接取 `json.entitys` 会拿到 undefined 并静默改写 0 条。
|
|
217
|
+
|
|
218
|
+
### D4. `epx-frame` 未发布时 npm 装不了任何东西
|
|
219
|
+
|
|
220
|
+
产品 package.json 声明 `epx-frame@^5.3.0`,未发布前 npm **解析根清单就失败**(ETARGET),
|
|
221
|
+
连加一个 devDep 都装不了。绕法:别处建空目录装好,再逐包拷进产品的 `node_modules`
|
|
222
|
+
(所有 `@scope` 要下沉到子包粒度判存在,按整个 scope 目录判会漏);
|
|
223
|
+
版本冲突的包放**嵌套** `node_modules`。
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## E. 主题/样式
|
|
228
|
+
|
|
229
|
+
### E1. 换肤 mixin 按主题展开,既臃肿又漏主题
|
|
230
|
+
|
|
231
|
+
**现象**:`index.css` 里同一条规则有 N 份 `[data-theme=x]` 拷贝;
|
|
232
|
+
用户选了某些主题色,产品组件不跟着变。
|
|
233
|
+
|
|
234
|
+
**根因**:产品自带的 `mixins.scss` 用 `@each` 遍历**写死的主题列表**。
|
|
235
|
+
实测 hr 写死 8 套,而平台有 **11 套预设 + custom**(运行时注入)——**4 套完全不生效**。
|
|
236
|
+
|
|
237
|
+
**判据**:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
node -e "const c=require('fs').readFileSync('lib/index.css','utf8');console.log(c.split('}').filter(r=>/\[data-theme=/.test(r.split('{')[0])).length)"
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**修法**:统一走 `rgba(var(--color-primary), $opacity)`。平台在 `[data-theme=x]` 上设
|
|
244
|
+
`--color-primary: r,g,b`,custom 由运行时 JS 注入,所有主题自动生效。
|
|
245
|
+
`$color` 参数改作 `var()` 的兜底值。实测 hr:130.4 → 85.0 KB(−35%),
|
|
246
|
+
`[data-theme=*]` 规则 600 → 0。
|
|
247
|
+
|
|
248
|
+
**注意 gzip 收益很小**(21.3 → 17.4 KB)——重复内容压得好,**省的是解析与选择器匹配成本**。
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
### E2. 产品/项目样式必须跟随平台主题轴
|
|
253
|
+
|
|
254
|
+
平台主题包已经由宿主加载。产品只消费变量,不再次导入主题入口,也不在 `:root`、`html` 或 `body` 写默认主题变量或固定主题属性;否则懒加载 CSS 到达后可能重置用户当前主题。不要因此改回 Vue2 垫片或删除 scoped。
|
|
255
|
+
|
|
256
|
+
**升级时执行修复**:`epxing migrate ... --apply` 自动迁移常见静态值;已升级工程用 `epxing theme <工程目录>` 预览,再加 `--apply`。覆盖 src 中 Vue style、template/JSX/render 内联样式及 CSS/SCSS/Less。原值作为变量 fallback,默认外观保持一致;重复执行幂等。返回码 2 表示仍有动态或特殊样式需复核,自动部分可能已经写入。`epxing scan` 的 R3 检查静态残留。
|
|
257
|
+
|
|
258
|
+
| 样式语义 | 平台变量 | 默认值 |
|
|
259
|
+
|---|---|---|
|
|
260
|
+
| 小/正文/大/标题字号 | `--epx-font-size-s/base/l/xl`(分别使用完整变量名) | 12/14/16/20px |
|
|
261
|
+
| 小/基础/大圆角 | `--epx-radius-s/base/l`(分别使用完整变量名) | 2/4/8px |
|
|
262
|
+
| 页面底色/卡片底色 | `--epx-bg` / `--epx-bg-card` | #f5f7fa / #fff |
|
|
263
|
+
| 主要/次要/辅助文字 | `--epx-text-primary` / `--epx-text-secondary` / `--epx-text-assistant` | #303133 / #606266 / #909399(亮色);`--epx-text` 为主要文字兼容别名 |
|
|
264
|
+
| 基础边框 | `--epx-border` | #dcdfe6(亮色) |
|
|
265
|
+
| 与密度一起变化的页面留白 | `--epx-margin` | 10px |
|
|
266
|
+
| 品牌主色 | `rgba(var(--color-primary), 透明度)` | RGB 元组,参照 E1 |
|
|
267
|
+
|
|
268
|
+
例如 `font-size: var(--epx-font-size-base, 14px); border-radius: var(--epx-radius-base, 4px); background-color: var(--epx-bg-card, #fff);`。Element Plus 组件的字号、圆角由平台 `--el-*` 映射管理,不在产品全局重写 `.el-*`。`--epx-space-*` 是固定间距级别,不等于密度轴;只有明确属于密度留白的布局才使用 `--epx-margin`。
|
|
269
|
+
|
|
270
|
+
**自动修复的边界与后续处理**:
|
|
271
|
+
|
|
272
|
+
1. 追踪工具报告的 style 变量、函数、对象展开、Sass/Less 变量及 mixin。区分实际 DOM 样式与 ECharts/canvas/导出数据等业务配置,确认消费位置后完成主题适配。CSS 变量或 calc 已存在也需确认它最终指向主题轴,不能仅凭使用 var 就验收。
|
|
273
|
+
2. 13/15/18px 字号、12px 等非标准圆角不盲目改成最近令牌。先按设计角色选令牌;若必须保持默认尺寸且随平台字号变化,可使用 `calc(var(--epx-font-size-base, 14px) + 4px)`。记录理由,不能把所有尺寸机械加减。固定宽高、图标/图表、圆形头像/胶囊、字体零值布局、打印/导出、动画和品牌/状态颜色按实际设计保留;有疑问先验证用途。
|
|
274
|
+
3. 缩进式 Sass/Stylus、外部 src 样式、脚本动态值、font 简写以及旧主题展开 mixin 需继续修复源文件。工具只处理 src 范围,不回写依赖和构建产物。检查最终编译 CSS 是否仍有阻断主题的覆盖;新建组件同样使用主题变量。
|
|
275
|
+
4. 主题迁移保持原有作用域隔离。继承与插槽链依 F15 验证,用专属容器局部 `:deep()` 处理实际失配,禁止为了主题同步制造全局泄漏。
|
|
276
|
+
|
|
277
|
+
#### 全量组件检查:每个组件都要有结论
|
|
278
|
+
|
|
279
|
+
此要求适用于所有产品升级路径,包括已使用 Vue3 和独立包、只更新框架版本的工程。来源:用户明确要求产品升级接入主题和全局 CSS 变量,逐组件检查。
|
|
280
|
+
|
|
281
|
+
1. **先建清单再修改**:以产品真实组件收集入口为主,合并源码中的内部子组件、独立弹窗/浮层、动态加载、移动端及可视化组件。复用现有 `epxing verify` 结果和源码索引;无产物时源码清单只算候选,构建后再与实际清单对账。入口未注册但被组件引用的子组件不能漏掉;同一实现的别名列明对应关系,不重复计为独立实现。
|
|
282
|
+
2. **逐个检查实际样式来源**:读取组件及其继承、共享样式、内联 style、JSX/render、动态函数、Sass/Less 变量、mixin、外部样式 src。检查背景、三档文字、边框颜色/宽度/类型、圆角、间距、阴影、模糊以及控件、表格、浮层适配。仅搜索 `.vue` 的 style 块或仅看 CLI 告警不算完成。
|
|
283
|
+
3. **逐个按用途接入**:先判断外层与内部各区域的角色,再修实际消费节点。继承组件也要检查是否存在覆写、硬编码内层底色或作用域失配;无样式组件记录其继承/消费路径,不强行为它添加 CSS。
|
|
284
|
+
4. **逐个关闭问题**:可确认的适配问题继续修复并验证,不把“人工复核”作为完成结论。业务语义不明、组件无法打开或目标平台能力不足时,逐条记录依据、缺少的信息与下一步;不能靠填充默认值或删除组件让清单变绿。
|
|
285
|
+
|
|
286
|
+
逐组件结果写入升级任务的主题验收台账,并从 `MIGRATION-REPORT.md` 链接;也可直接附在该报告中。至少包含下列字段:
|
|
287
|
+
|
|
288
|
+
| 组件文件/名称及别名 | 页面/触发入口 | 角色及区域 | 样式来源与变量接入/改动 | 静态结论 | 最终产物与主题切换证据 | 状态/例外/下一步 |
|
|
289
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
290
|
+
| 实际路径及注册名 | 菜单、嵌入、弹窗或移动分支 | 外层 layout,内部 surface 等 | 继承、共享、内联、动态来源 | 逐项核查结果 | 页面、模式、计算样式、截图等 | 已通过 / 已改待验证 / 待处理 / 阻塞 |
|
|
291
|
+
|
|
292
|
+
报告列明组件总数及各状态数量,数量必须对齐清单,不能只列改动文件。打印、品牌图片、状态色、固定结构尺寸等例外记录到具体组件和属性,写明保留理由及运行证据;“例外已确认”不能替代该组件其它区域的主题检查。不可达或未运行的组件不得记为已通过。
|
|
293
|
+
|
|
294
|
+
#### 按用途消费全局变量
|
|
295
|
+
|
|
296
|
+
下面是用途映射,不是将每个变量强塞给每个组件。目标版本的变量全集和接入契约以平台文档为准;本平台仓入口为 `frontend/frame/docs/revision/theme/guide.md` 与 `component-style.md`。在独立产品中按当前安装版本的文档核对,不假设技能复制后仍包含平台仓目录。
|
|
297
|
+
|
|
298
|
+
| 区域/角色 | 接入要求 |
|
|
299
|
+
| --- | --- |
|
|
300
|
+
| 面板、卡片、列表区块 | 组件选项与 name 同级声明 `appearance: 'surface'`,通过平台公共混入或包装器接入;消费 `--epx-component-bg`、`--epx-component-border-color/width/style`、`--epx-component-gap`、`--epx-component-radius`、`--epx-component-shadow`、`--epx-component-backdrop-filter`。斜线分组需展开为独立变量名 |
|
|
301
|
+
| 原子控件、表单字段 | 使用 `control` 角色或既有控件映射;消费 `--epx-control-bg`、`--epx-control-border-color`、`--epx-control-radius`,不套面板背景/阴影;检查禁用、焦点、占位及错误状态 |
|
|
302
|
+
| 布局、栅格、容器、文字、图标 | 分别使用 `layout` / `plain` 或既有无装饰行为,不强加面板外观;内部面板独立接入。文字按主要/次要/辅助语义使用对应文字变量,图标保留 currentColor 或明确状态/品牌色 |
|
|
303
|
+
| 顶部、侧边导航 | 使用独立 `--epx-top-nav-*` / `--epx-side-nav-*` 背景、文字/选中、阴影及模糊变量,不受面板装饰统一覆盖;自写导航须核对目标版本支持的每个用途变量 |
|
|
304
|
+
| 页面内容 | 使用 `--epx-page-bg` 等页面背景契约;AppIndex 外壳不强行铺页面背景;组件内部不可用基础页面色替代所有用途背景 |
|
|
305
|
+
| 表格 | 接入 `--epx-table-header-bg`、`--epx-table-hover-bg`,检查固定列、合并表头与编辑态;面板内数据区按平台语义透出面板背景,不能残留硬编码白底遮住渐变 |
|
|
306
|
+
| 浮层、弹窗、抽屉 | 接入 `--epx-overlay-bg` / `--epx-overlay-shadow` 及基础文字、圆角;检查 Teleport 到 body 后变量和局部样式是否仍生效 |
|
|
307
|
+
|
|
308
|
+
普通 HTML 或直接挂载的 Vue 子组件不能仅靠声明 `appearance` 假定生效:普通面板使用 `epx-surface`,原生控件用 `epx-control`,浮层用 `epx-overlay`;flex/grid 父布局可用 `epx-layout-gap`。这些类由宿主提供,不另写一套全局类;使用 el-card 时检查自带内边距,避免面板类的 padding 叠加。
|
|
309
|
+
|
|
310
|
+
**兼容优先级**:旧 `configs.componentSurface` 布尔值高于 `configs.appearance`,后者高于组件定义及 extends 的角色。检查遗留退出配置和显式 style,保留有意覆盖;新增面板未知于平台默认列表时必须主动接入。根节点保持 class/style 透传,不重复外观类名提高权重,不批量加 `!important`。
|
|
311
|
+
|
|
312
|
+
**透明度、渐变与玻璃效果**:面板和页面/导航使用 `background` 消费完整背景变量,不能用 `background-color` 接收渐变。透明度放在颜色 alpha,不用根节点 opacity;内层不透明背景需按用途处理。需要纯色的控件/浮层不直接继承渐变。运行时配置可生成对应 `-color` 派生变量,手写 CSS 渐变时一并提供。直接消费模糊变量时使用完整 `none` / `blur(20px)`;不能把面板配置界面的数字输入规则误当成 CSS 滤镜语法。默认面板无阴影,导航独立阴影不能被面板主题误关。
|
|
313
|
+
|
|
314
|
+
CLI 当前静态转换只覆盖其支持的常见值,**不自动完成角色判定、34 个用途配置字段覆盖或逐组件运行验收**。不为通过扫描机械替换品牌/业务状态颜色、打印纸张、图片和图表数据;canvas、iframe 及图表色板单独核对,不能把外壳变量变化记成内部绘制已适配。
|
|
315
|
+
|
|
316
|
+
#### 逐组件运行验收
|
|
317
|
+
|
|
318
|
+
对清单中每个组件使用最终产物,在实际平台入口或保持同等包装/插槽/浮层链路的验收页中验证:
|
|
319
|
+
|
|
320
|
+
- 默认亮色、暗色和自定义品牌色;三档文字层次及禁用/状态对比。固定颜色的保留项单独说明,不宣称会自动随暗色切换。
|
|
321
|
+
- 至少两套差异明显的完整主题,包含透明渐变面板;调整组件边框、圆角、间距、阴影及模糊,检查适用区域确实变化,不适用区域保持正确。默认/关闭值和 0 值必须有效。
|
|
322
|
+
- 圆角、字号、密度及控件尺寸的不同档位;检查布局、标签、表格、按钮没有裁切或溢出。无对应属性的区域标注理由,不凭空添加装饰。
|
|
323
|
+
- 弹窗、悬停、选中、编辑等实际分支;产品有移动/可视化版本时同样纳入,不只验 PC 主视图。
|
|
324
|
+
- **先切主题再首次懒加载**,再切另一主题、离开页面进入其它页面;记录目标节点的计算样式及必要尺寸,确认产品 CSS 不重置全局主题,组件外同名元素不受污染。
|
|
325
|
+
|
|
326
|
+
多个组件可在同一验收页批量采证,但必须能定位到每个组件的节点、适用变量及结论;代表页抽测、仅根变量变了、代码中出现 var、静态告警为零或 build 通过均不能代替逐组件验收。普通 Vue 子组件至少在真实父组件调用链中核验,不能因没有单独菜单而漏检。主题继承与配置中心读取另外验证应用默认、个人选择、跟随默认及刷新恢复,不能用直接修改 DOM 变量代替完整主题选择链路。
|
|
327
|
+
|
|
328
|
+
**发布验收**:以最终完整包或懒加载包,在实际平台页面切换默认/大/小字号、默认/直角/圆润圆角、明暗主题和密度;核对卡片、表单、按钮、标题及弹窗同步且不裁切/溢出。主题切换后再首次加载产品组件,确认不回到默认主题;离开页面后检查平台与外部同名元素不受影响。保留打印等设计例外,并记录测试主题、源文件、动态项处理、截图及待验证项。lint/build 或隔离 fixture 通过不能代替实际产品验收。
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## F. gogocode 转换残留 & 第三方库(product/recruit 145 组件实证)
|
|
333
|
+
|
|
334
|
+
> 这一组是「gogocode 没转干净 + 项目自带 Vue2 依赖」,多数在 `build:lib:lazy` 才暴露。
|
|
335
|
+
> **能机械化的已进 CLI 自动做**(下面标「已 CLI」),**结构性/换库的靠本节手动**。
|
|
336
|
+
|
|
337
|
+
### F1. gogocode 漏转「元素上的」旧具名插槽 `slot="x"`
|
|
338
|
+
|
|
339
|
+
**现象**:build 报 `[vite:vue] Cannot read properties of undefined (reading 'type')`(**无行号**),
|
|
340
|
+
或 `slot="x" is deprecated`。gogocode-plugin-vue 转了 `<template slot>`,却漏了**直接写在元素上**的。
|
|
341
|
+
|
|
342
|
+
**判据**:`grep -rn 'slot="' src --include=*.vue | grep -v v-slot`(排掉注释行)。
|
|
343
|
+
|
|
344
|
+
**修法**(结构变化,逐个手改;CLI 不做——配对闭合标签的正则不可靠):
|
|
345
|
+
```
|
|
346
|
+
<el-dropdown-menu slot="dropdown">…</el-dropdown-menu>
|
|
347
|
+
→ <template #dropdown><el-dropdown-menu>…</el-dropdown-menu></template>
|
|
348
|
+
|
|
349
|
+
<span slot="footer" class="dialog-footer">…</span> (el-dialog 的 footer)
|
|
350
|
+
→ <template #footer><span class="dialog-footer">…</span></template>
|
|
351
|
+
```
|
|
352
|
+
recruit 实测 7 处需改(另 3 处在注释里,忽略)。定位无行号的 `reading 'type'`:从报错文件里逐个 `slot="` 查起。
|
|
353
|
+
|
|
354
|
+
### F2. gogocode 漏转 `slot-scope`
|
|
355
|
+
`slot-scope="scope"` → `v-slot="scope"`(或 `#default="scope"`)。recruit 1 处,手改。
|
|
356
|
+
|
|
357
|
+
### F3.(已 CLI)`:prop.sync` → `v-model:prop`
|
|
358
|
+
|
|
359
|
+
机械替换只完成语法转换,不能证明模型协议正确;例如旧 visible.sync 转成 v-model:visible 后,目标控件可能已改用默认模型。按 F24 核实目标版本的 props/emits 和包装链,保留仍公开的命名模型,不把“已 CLI”当作该项验收通过。
|
|
360
|
+
Vue3 移除 `.sync`。gogocode 漏,现由 `fix-inline-handlers.mjs` 的 `fixSyncModifier` 在 migrate ③ 自动转,不用手改。
|
|
361
|
+
|
|
362
|
+
### F4. epxing 字段名里的 `|` 把 gogocode filters 规则搞崩(良性,忽略)
|
|
363
|
+
**现象**:③ 转换刷 `文件转换异常,规则:filters … expressionStatementAst.attr is not a function`。
|
|
364
|
+
**根因**:epxing 字段名带竖线(`info['SCHEME|NOTICE_FILE#']`),gogocode 过滤器规则把它当 Vue2 过滤器管道解析 AST,崩。
|
|
365
|
+
**良性**:不是真过滤器,文件其它转换正常、内容没坏。dry-run 报的「过滤器 N 文件」也因此**虚高**(把字段名 `|` 误算成过滤器)。
|
|
366
|
+
**判据**:`grep -rnE "\{\{[^}]*\|\s*[a-z_]+\s*\}\}" src` 把 `|大写字段名` 排掉后基本为 0 → 确认无真过滤器,忽略刷屏即可。
|
|
367
|
+
|
|
368
|
+
### F5.(已 CLI)内联事件多语句缺分号
|
|
369
|
+
Vue2 容忍换行分隔、Vue3 编译器报 `Unexpected token, expected ","`。现由 `fixInlineHandlers` 在 migrate ③ 自动补分号。
|
|
370
|
+
|
|
371
|
+
### F6. 换掉没有 Vue3 版的第三方库(⑥ 会告警,换库是人工决策)
|
|
372
|
+
|
|
373
|
+
| 库 | Vue3 替代 | 改动 |
|
|
374
|
+
|---|---|---|
|
|
375
|
+
| `vue-quill-editor@3` | `@vueup/vue-quill` | 换 import + 组件用法(v-model/options 略变) |
|
|
376
|
+
| `vuedraggable@2` | Vue3 兼容版本(如 v4,按工程契约核实) | API 需迁移:item 插槽、稳定 item-key、footer 与排序回写一起验证,见 F29 |
|
|
377
|
+
| `vue-orgchart-direction` | 无——改 `bpmn-js` 直用 / SVG 自实现 | 组织架构图重写(hr T2 同款) |
|
|
378
|
+
| `element-ui` | Element Plus/当前平台公开组件 | 移除旧插件安装,核对宿主注册与产品导入;直接导入控件或图标时保留对应构建依赖,不假定所有工程都全局提供 |
|
|
379
|
+
| `vant@2` | `vant@4`(移动端) | 用法有差异,逐个核 |
|
|
380
|
+
|
|
381
|
+
**先查有几个文件真 import 再决定工作量**:`grep -rl <lib> src --include=*.vue --include=*.js`。
|
|
382
|
+
recruit 实测:element-ui 1 / vue-quill-editor 1 / vuedraggable 2 / vant 2 —— 量小,逐个换即可。
|
|
383
|
+
(依赖在 package.json 但源码 0 引用只列为清理候选;先核对插件注册、动态加载、样式和构建入口,再决定是否移除。)
|
|
384
|
+
|
|
385
|
+
### F7.(已 CLI)`node-sass` / `fibers` 残留
|
|
386
|
+
老 scss 工具链(node-gyp 原生编译),新 node 上 `npm i` 必挂、还骗过 exit code(管道吞 code)。
|
|
387
|
+
已加进 to-vite 的 `WEBPACK_ONLY_DEPS` 自动删,Vite 用 dart-sass(`sass`)。**判 npm i 真成败别信 exit code**——
|
|
388
|
+
`grep -iE "gyp ERR|Build failed" 安装日志`。
|
|
389
|
+
|
|
390
|
+
### F8. 垃圾副本文件(`Xxx copy.vue`)在按需形态撞名
|
|
391
|
+
**现象**:build 报 `[lazy-lib] 组件注册名冲突:epx-xxx 同时来自 A copy.vue / A.vue`。
|
|
392
|
+
单体形态静默覆盖(线上不暴露)、按需形态 `assertUniqueKeys` 拦下(替项目揪出垃圾副本)。
|
|
393
|
+
**判据**:`find src -iname "*copy*"`。**修法**:删副本(或改其 name)。
|
|
394
|
+
|
|
395
|
+
### F9.(已 CLI)无扩展名 import .vue
|
|
396
|
+
老项目 webpack `resolve.extensions` 含 `.vue`,`import X from './X'` 遍地;Vite 默认不含。
|
|
397
|
+
create 模板的 vite.config 已带 `resolve.extensions: ['.mjs','.js','.json','.vue']`,`--to-vite` 生成的配置自带,不用手加。
|
|
398
|
+
|
|
399
|
+
### F10.(已 CLI)`import * as Vue/Vuex/VueRouter` 命名空间导入 → 具名
|
|
400
|
+
gogocode 把 Vue2 默认导入转 Vue3 时,对 vue/vuex/vue-router 常产**命名空间导入**:
|
|
401
|
+
`import * as Vue from 'vue'` + `Vue.h(...)`、`import * as Vuex from 'vuex'` + `Vuex.createStore(...)`、
|
|
402
|
+
`import * as VueRouter from 'vue-router'` + `VueRouter.createRouter/createWebHashHistory(...)`(router/store 是 .js,gogocode `-s ./src` 也转了它们)。
|
|
403
|
+
`lazyLib` 为 external `'vue'` 生成 shim 时**不支持 `import * as ns`**(见 B2,recruit 实测 build 挂:`无法为 external 'vue' 生成 shim`)。
|
|
404
|
+
CLI ③ 修补⑥(`fix-namespace-import.mjs`)收敛为具名:收集 `NS.member` 用到的成员改 `import { m1, m2 } from 'lib'`,
|
|
405
|
+
`Vue.h`→`h` 等;整文件没用到就删这行。只认这三个框架库(白名单),业务库的命名空间导入不碰。
|
|
406
|
+
|
|
407
|
+
### F11.(已 CLI)`require('@/assets/*.png')` → 静态 import + `assetsInlineLimit`
|
|
408
|
+
webpack 支持 `require('图片')` 当 URL 用(三元/字符串拼接里),Vite 不支持 → 运行时 `require is not defined`。
|
|
409
|
+
CLI V 阶段(`fix-vite-source.mjs`)提为顶部静态 import(别名 `@` 支持)、`require(...)` 换成变量。
|
|
410
|
+
**只在 `<script>`/JS 里改**:模板表达式里的 `require` 换成模块顶层变量后 Options API 的 this 取不到,那种报人工(手工挂 data/computed,见 F11 配套的 recruit Center.vue/Guide.vue 改法)。
|
|
411
|
+
配套:create 模板的 `build.assetsInlineLimit` 设 `50*1024`,让这些小图标内联成 data URI——否则超 4096 的(recruit 的 `fileHover.png` 4115B,只超一点点)会出独立文件、远程库形态引用 `/assets/xxx` 绝对路径运行时 404(构建期看不出;C4 管 lazy 嵌套构建继承这个值,前提是主 build 设了)。
|
|
412
|
+
|
|
413
|
+
### F12.(已 CLI)`require.context` 非入口(store 收集 vuex modules)→ `import.meta.glob`
|
|
414
|
+
入口的 `require.context('@/X', true, ...)` `--to-vite` 一直会转;**漏网的是 store/index.js** 的
|
|
415
|
+
`require.context('./modules', false, ...)`(收集 vuex modules)。CLI V 阶段现遍历全 src 通用改写:
|
|
416
|
+
recursive 决定 `**/` 还是单层、正则决定扩展名(`.vue`/`.js`)、dir 前缀(`@/` 或 `./`)原样保留;
|
|
417
|
+
配套 `VAR.keys()`→`Object.keys(VAR)`、`VAR(k)`→`VAR[k]`(变量名不一定叫 files,store 里叫 context,故按声明抓变量名)。
|
|
418
|
+
|
|
419
|
+
### F13. `::v-deep` 弃用警告(**不进 CLI;容器块形态手工 / postcss**)
|
|
420
|
+
Vue3 弃用 `::v-deep` 作 combinator,建议 `:deep(<inner>)`。recruit 实测 **141 处是容器块 `::v-deep{ ... }`**(combinator 仅 10、`>>>` 仅 5)。
|
|
421
|
+
容器块转 `:deep()` 要拆 SCSS 嵌套块(花括号配对、多层嵌套),141 处正则拆分**极易改坏 style,风险远大于收益**——
|
|
422
|
+
而它只是**弃用警告:build 通过、运行正常**。故**不进 CLI**(同 gogocode 的 style 快照保护:不用正则动 style 块)。
|
|
423
|
+
真要消警告:写 postcss 插件按 AST 转、或按文件手工,别正则批量。
|
|
424
|
+
|
|
425
|
+
### F14.(部分已 CLI)`<script>` 里的 Vue2 JSX 残留 → Vue3(gogocode 只转 template,不碰 JSX)
|
|
426
|
+
gogocode 只转 `<template>`,**不碰 `<script>` 里的 JSX / 手写 render**——Vue2 JSX 前缀原样留到 Vue3,
|
|
427
|
+
`@vue/babel-plugin-jsx` 不识别、当普通属性丢弃 → **build 过、console 不报错、运行时静默失效**(最隐蔽的一类)。
|
|
428
|
+
recruit 实测(145 组件):表单 note 不显示、折叠操作点击无效、子表格自定义列/操作列不渲染。
|
|
429
|
+
③ 本地简历审核详情「流程图」tab 空白也是同款——平台 `ProcessChart.vue` 用 `domPropsInnerHTML` 贴 saveSVG 的图,
|
|
430
|
+
实证显示容器 `innerHTML.length=0`(图在隐藏容器画好了、没贴出来);平台侧 Vue2 JSX 残留共 **11 处 / 8 文件**(`domPropsInnerHTML` 9 + `nativeOnClick` 2:`ProcessChart` / `EntityList`×2 / `Mobile`×2 / `Field` / `AddFormItem` / `NoInstallSystemList` / `AppNav` / `Login`),本会话已用**同一个 CLI `fix-jsx-vue2`** 批量修完(平台组件同样适用)、`build:pre` 通过,`ProcessChart` 流程图肉眼验证 `innerHTML 0→30753`。
|
|
431
|
+
- **已 CLI**(③ 修补⑦ `fix-jsx-vue2.mjs`,只换 JSX 属性名、不动值/结构,安全):
|
|
432
|
+
`domPropsInnerHTML=`→`innerHTML=`、`domPropsValue=`→`value=`、`nativeOn<E>=`→`on<E>=`(nativeOnClick→onClick,Vue3 组件事件默认透传根元素)、`scopedSlots={{`→`v-slots={{`。
|
|
433
|
+
- **人工**(结构性重排,CLI 不碰):手写 render 的 data 对象 `return { scopedSlots:{...} }` / `h(tag,{on,props,attrs})`——
|
|
434
|
+
Vue2 createElement data 到 Vue3 差异大,正则不保险。recruit 实测 InnerEntitySubList 3 处(方法返回 `{scopedSlots:{default}}` +
|
|
435
|
+
调用点 `<el-table-column {...this.renderX()}>` spread),最小改法:调用点 `{...this.renderX()}` → `v-slots={this.renderX().scopedSlots}`(方法内数据属性名不动)。改后实测子表格字段/操作列/彩色标签正常渲染。
|
|
436
|
+
- 排查:`grep -rE "domProps|nativeOn[A-Z]|scopedSlots" src`;`scopedSlots=`(等号)=JSX 属性可自动,`scopedSlots:`(冒号)=render 对象需人工。
|
|
437
|
+
- **第 5 类 `slot="xxx"`(postAppoint 补)**:`<script>`/JSX 区的具名插槽(`slot="reference"`/`slot="footer"`/`slot="title"`)同样被 Vue3 JSX 丢弃 → el-popover 触发钮不渲染(「更多」按钮消失)/ el-dialog 底部按钮·标题不显示。`fix-jsx-vue2` 现**报告**(列人工清单,不自动改——改 `v-slots` 要重组 JSX 结构,正则做不了)。⚠ **不能一律判失效**:实测 el-dropdown 的 `slot="dropdown"` 靠组件运行时兼容仍正常(`$slots` 含 dropdown),el-popover `reference` / el-dialog `footer`·`title` 才真失效——逐个 `$slots` 或 DOM 实证。postAppoint `MainEntityList..vue` 的 `slot="reference"`(卡片「更多」)实证失效,改 `v-slots={{ reference: () => (...) }}`(commit `d6386c3`)。
|
|
438
|
+
|
|
439
|
+
### F15. JSX/render 与 scoped 样式:先验证匹配,再做最小修复并验收隔离
|
|
440
|
+
|
|
441
|
+
**默认保留 scoped**。JSX/render、无 `<template>` 或 `extends` 只能作为排查线索,不能推断“深层元素一定没有 scopeId”或“scoped 整体失效”。实际结果与项目的编译插件、组件继承、插槽和渲染路径有关;当前项目中能正确携带作用域并匹配规则的组件保持原样。不得依据历史案例或静态告警批量删除 scoped,也不得把所有选择器机械改为 `:deep()`。
|
|
442
|
+
|
|
443
|
+
**先证明具体问题**:使用最终构建产物(按需发布时使用最终 lazy/chunks 产物),确认实际加载的是本次 JS 与 CSS。对一个确实显示异常的元素,记录:
|
|
444
|
+
|
|
445
|
+
- 所属组件、页面、预期样式与实际 `getComputedStyle`/尺寸;
|
|
446
|
+
- 产物中的具体 CSS 选择器、样式是否已加载,以及目标/祖先 DOM 上实际的 class、`data-v-*`;用开发者工具匹配规则或 `element.matches(实际选择器)` 确认是否命中;
|
|
447
|
+
- 失效究竟来自 CSS 未加载、优先级覆盖、根命名空间缺失、作用域不匹配,还是其他布局原因。作用域存在且规则命中时,不按“去 scoped”处理。
|
|
448
|
+
|
|
449
|
+
#### 继承组件经过插槽渲染后,局部样式可能失效
|
|
450
|
+
|
|
451
|
+
**卡片列表的分发入口也要检查**:产品重写 `renderTable` 并不代表卡片模式会调用它。先按当前基类的 `renderList` 分发契约检查 `renderTable`/`renderCardList`;旧的 `renderVerticalItem` 可能被平台卡片子组件作为回调调用,生成的卡片后代没有产品的 scopeId。薪酬发放页已出现根节点有 scopeId、卡片容器却无 scopeId,最终卡片 padding 为 0、display 为 block,标题继承复选框组的 font-size: 0。产品有自己的卡片主体时,应接入 `renderCardList` 保留原结构;对经过 UI 插槽的后代,在有效的产品容器内局部使用 `:deep(.card-item...)`。同时检查空数据、分页、卡片方向和操作按钮,不能只补一个标题字号。
|
|
452
|
+
|
|
453
|
+
不要仅凭 JSX、`extends` 或无 template 判定 scoped 失效,也不要仅凭隔离测试正常就排除问题。必须检查实际平台页面的完整渲染链:继承组件的内容可能经过平台包装组件、Element Plus 等组件的插槽。实际 DOM 中,组件根节点带有 `data-v-*`,插槽内的图片、标题等后代却可能没有对应属性,导致 `.projectTop[data-v-xxx]` 等产物规则无法匹配。根节点有作用域属性不等于所有后代都有;问题应定位到具体元素和选择器,不能扩大成整个组件的 scoped 都无效。
|
|
454
|
+
|
|
455
|
+
**排查步骤**:
|
|
456
|
+
|
|
457
|
+
1. 确认 CSS 已加载,且与 JS/chunks 来自同一次构建;先排除旧缓存或不同批产物造成的 scopeId 失配。
|
|
458
|
+
2. 检查失效元素及祖先的 class、`data-v-*`,核对最终产物选择器是否实际匹配;不要只检查组件根节点。
|
|
459
|
+
3. 使用 `getComputedStyle()` 和实际尺寸检查图片宽高、`object-fit`、字号、内边距等,同时追查继承值与规则来源。例如卡片标题规则失配后,可能继承复选框组的 `font-size: 0`,文本仍在 DOM 中却不可见。
|
|
460
|
+
4. 在实际页面验证“继承组件 → 平台包装组件 → UI组件插槽 → 目标DOM”的完整链路。简化基类、直接挂载子组件或替换 UI 组件的隔离测试可辅助定位,但可能改变作用域传递路径,不能替代实际页面验收。
|
|
461
|
+
|
|
462
|
+
**局部渲染回调的识别**:重点追踪 `renderVerticalItem`、`renderField`、`renderInputContent` 等被继承组件或 UI 插槽调用的方法;方法写在产品 SFC 内,并不保证返回的所有 DOM 都获得该 SFC 的作用域属性。卡片背景、间距、装饰图和字段布局同时消失,文字却仍正常显示时,要检查原局部规则是否失配,导致字号等属性继承自复选框组或其它祖先;不能只检查字体文件或用全局 CSS 补外观。记录目标元素和祖先的作用域属性、匹配规则以及修复前后的字号、padding、display、背景与尺寸。
|
|
463
|
+
|
|
464
|
+
**推荐修法**:确认作用域失配后,保留 scoped,在带有对应作用域属性的专属容器内,对必要的后代使用 `:deep()`。例如:
|
|
465
|
+
|
|
466
|
+
```vue
|
|
467
|
+
<template>
|
|
468
|
+
<div class="epx-training-project-list">
|
|
469
|
+
<!-- 包装组件及插槽内容 -->
|
|
470
|
+
</div>
|
|
471
|
+
</template>
|
|
472
|
+
|
|
473
|
+
<style scoped>
|
|
474
|
+
.epx-training-project-list :deep(.projectTop) {
|
|
475
|
+
width: 100%;
|
|
476
|
+
height: 160px;
|
|
477
|
+
object-fit: cover;
|
|
478
|
+
}
|
|
479
|
+
</style>
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
示例尺寸按目标页面设计调整;标题等其他失效规则也逐条处理,不把整块样式机械改为 deep。检查最终 CSS 仍类似 `.epx-training-project-list[data-v-xxx] .projectTop`,并确认专属容器在实际渲染分支中存在且带有匹配的作用域属性,图片确实是其后代。若内容经 Teleport 等挂到容器之外,应单独定位其专属挂载范围,不能为命中目标而取消边界。
|
|
483
|
+
|
|
484
|
+
已有继承根满足上述条件时,可直接作为锚点,无需为了修样式新增 template 或改写整套 render。例如 `.epx-entity-list :deep(.product-card__field)` 应编译为 `.epx-entity-list[data-v-xxx] .product-card__field`,且该根在当前分支确实是目标节点的祖先。通用根类的隔离依赖对应作用域属性,不能只保留裸 `.epx-entity-list .product-card__field`。只修已确认失配的规则,仍正常匹配的工具栏、根节点和独立浮层规则分别判断。
|
|
485
|
+
|
|
486
|
+
**发布验收**:用最终完整包或 lazy/chunks 产物在实际页面确认插槽内图片、标题等样式恢复,同时组件外部同名元素不受影响;再检查离开页面后的其他页面。不要直接删除 scoped,让 `.projectTop`、`.avatar`、`.container` 等通用类变成全局规则。未能复现完整页面链路时记录为待验证,不以隔离测试通过宣布修复完成。
|
|
487
|
+
|
|
488
|
+
**同类组件检查**:确认一个失配案例后,检查继承它的子组件、复制了同套样式或卡片结构的组件,以及同样通过插槽调用局部渲染方法的组件,复用 E2 台账记录具体选择器和消费链路。子组件重复声明父样式不能保证修复,仍需核对它自己的作用域属性;父组件修好也不代表所有子组件已经通过。静态命中仅列为候选,逐项区分“运行确认失配”“已改待页面验证”“已运行通过”和“缺业务数据或入口、待验证”;说明缺少的条件,不把这些状态汇总为全部正常。后续验证按各组件实际业务入口进行,不靠批量删除 scoped 或替换所有选择器关闭清单。
|
|
489
|
+
|
|
490
|
+
**修复顺序**:先修正加载、渲染根、选择器或局部布局,尽量保留 scoped。确认为 scoped 无法匹配继承/子组件的目标 DOM 时,才针对具体规则评估 `:deep()`,并确认其锚定在本组件有效的根范围内;`:deep()` 本身不保证隔离,仍须验收。历史 recruit `CardFormItem` 头像缺少对应 `data-v-*`、`MainEntityList` 深层规则失配等案例,只证明当时那些元素的具体问题,不能推广到其他组件。
|
|
491
|
+
|
|
492
|
+
**去掉 scoped 是备选方案,隔离与修改必须同时完成**:
|
|
493
|
+
|
|
494
|
+
1. 为组件定义唯一命名空间(如 `.epx-product-resume-card`),确认其覆盖全部实际渲染入口:自有 render、继承根、多分支、动态页,以及弹窗/Teleport 等独立挂载的内容。不能假定给某一个根加 class 就覆盖全部 DOM,另见 F19。
|
|
495
|
+
2. 将该 style 块内所有规则限制在命名空间内,包括逗号分隔的每个选择器、SCSS 嵌套展开、媒体查询、深层选择器和 mixin 生成的规则。明确本就全局的主题规则另行审查。核对最终编译 CSS,不能只看源码外层有一层嵌套;不得留下裸 `.avatar`、`.container`、`img` 或平台通用类选择器。
|
|
496
|
+
3. 所需规则若作用于根本身,使用 `.epx-product-resume-card.container` 等同元素选择器;`.epx-product-resume-card .container` 只匹配后代。移除 scoped 后,对原 `:deep()` 等语法按最终 CSS 和目标 DOM 调整,不能留下无效或无边界的穿透规则。
|
|
497
|
+
4. BEM 名称也不能免除检查:只有最终 CSS 的每个选择器都实际限定在组件唯一前缀内、且组件外回归通过时,才可沿用现有 BEM 隔离;不要仅凭“使用了 BEM/mixin”就判安全。
|
|
498
|
+
5. 根范围和完整样式隔离尚未完成时,保留或恢复 scoped,将该项记为未解决;不要留下“先去 scoped,稍后补隔离”的提交或发布产物。逐个组件处理,避免整批转换扩大影响。
|
|
499
|
+
|
|
500
|
+
**验收必须同时证明“组件内生效”和“组件外不受影响”**:
|
|
501
|
+
|
|
502
|
+
- 记录组件内的关键宽高、布局、图片与弹窗样式,并核对所有渲染分支及产品支持的窄视口;仅改 `innerWidth` 不能代替真实移动端视口验收。
|
|
503
|
+
- 在组件范围外放置或选取真实的 `.avatar`、`.container` 等同名对照元素,记录组件加载前、懒加载 JS/CSS 后的计算样式和尺寸,确认未被本组件规则改变。若 CSS 在入口即加载,应与隔离变更前的产物基线对照;不能只比较样式早已污染后的两次快照。
|
|
504
|
+
- 从目标页面离开再进入其他页面,复核外部同名元素、平台布局和弹窗。懒加载 CSS 可能在离开页面后继续存在,关闭组件并不等于清除影响。
|
|
505
|
+
- 在迁移报告中逐个记录改动组件、失配证据、隔离方式、产物版本及上述检查结果。未具备环境或未执行的检查写为待验证;构建、lint 或静态扫描通过不代表隔离验收通过。
|
|
506
|
+
|
|
507
|
+
**静态扫描的边界**:迁移器的“无 template + scoped”列表和 `detect-vue3-risks.mjs` R1 都只提示人工复核,不证明运行时失效。扫描器不覆盖所有全局泄漏,`R1` 未命中(包括改用 `:deep()` 后)也不表示安全;不得为了让扫描结果变绿而破坏隔离。
|
|
508
|
+
|
|
509
|
+
**空状态与栅格嵌套**:列表正常不代表 `v-else` 正常。若空卡片文字逐字换行,先量父容器与卡片宽度,并核对 `flex-basis`、`max-width` 和栅格类。工资 `ViewdataCard` 的空分支将手写 `el-col-3` 放在自适应宽度的中间容器内,12.5% 规则使卡片实际仅 20px。此类问题应按布局意图去除多余嵌套及错误栅格类,给空状态独立、可收缩的宽度,保留有数据分支的栅格配置;不要全局覆盖 `.el-col-*` 或修改数据来隐藏空状态。扫描同类手写栅格后,分别验证空/非空分支与窄容器,记录文字换行、宽度和溢出;源码隔离验证与目标站发布验收分开记录。
|
|
510
|
+
|
|
511
|
+
**统计卡片挤压后续区域**:同时测量配置外层、UI 卡片、统计子项和后续兄弟的位置。KqShowCard 曾出现外层配置高度 180px、内部实际高度 238px,造成后续卡片与统计区域重叠;其 BEM mixin 生成的类名还与模板不一致,scoped 样式也未命中 UI 内部 header/body。此类内容驱动的概览容器应按实际用途随内容撑高,使用与模板一致的局部选择器,并仅对 UI 内部节点局部 deep;子项间距可由列 padding 承担,避免百分比高度再叠垂直 margin 造成溢出。不能全局取消所有配置高度。以修复后子项位于容器内、后续区域不重叠为验收依据,保留线上发布边界。
|
|
512
|
+
|
|
513
|
+
### F16. 数组 `splice` 同引用 + `deep:1` watcher 不触发(弹窗标题恒空,postAppoint 平台侧实证)
|
|
514
|
+
Vue3 起浅层 watcher 不响应 push/splice;`deep:1` 只监听「数组元素引用」这一层。若 `route.name=title; routes.splice(i,1,route)`——splice 放回**同一对象引用**、只改元素内部属性 `name`,`deep:1` 监听不到 → watcher 不触发 → `$emit('change')` 不重发 → 消费方(`Popup.routerChange`)拿到的 route.name 恒空 → **弹窗标题不显示**(build 过、无报错)。
|
|
515
|
+
- 修法:splice 放**浅拷贝新对象** `routes.splice(i,1,{...route,name:title})`(改元素引用才触发 `deep:1`);消费方再加空值兜底(异步 `$refs.component` 未 resolve 时用 `route.name`)。平台 `RouterView.pageChange` + `Popup.routerChange` 双改、build:pre 实测标题空→「新增公示」(commit `0061cb7c63`)。修前先运行时验证:手动 `rv.routes.splice(0,1,{...rv.routes[0]})` 看消费方值是否变,成立再改、免白 build。
|
|
516
|
+
- 通用:Vue3 靠 push/splice 增删的数组,watcher 要么 `deep`、要么 splice 换新对象引用;**改元素内部属性不触发浅层 watcher**(同类历史 commit `df29393`/`2161c04` 数组 watcher 补 deep)。
|
|
517
|
+
|
|
518
|
+
### F17. CSS code-split 下组件借用别组件的全局样式(图表容器塌陷,postAppoint 平台侧实证)
|
|
519
|
+
Vue2 webpack 把全部 CSS 合进主 bundle,任何组件的非 scoped 全局样式总能加载;Vue3 vite **CSS code-split** 按组件拆 CSS chunk。若「实际渲染的组件」自身无 style、借用**别组件**里定义的非 scoped 全局样式(如 `.epx-chart` 布局散在 `chart/Table.vue`、`report-chart/Chart.vue` 的 `@include b(chart)`,而实际渲染的 `Common.vue`(epx-chart-common) 无 style),只加载渲染组件 chunk 时**不带那些 CSS chunk** → 样式缺失。实证:`.epx-chart__main` 高度塌陷 0、echarts 画不出(JS chunk 已加载、`getReportData` 数据正常)。
|
|
520
|
+
- 修法:让渲染组件**自带**渲染必需的样式(Common.vue 补 scoped `<style>` 定义 `.epx-chart` 根 flex+min-height / `__main` flex / `__echarts` 铺满,随本组件 chunk 加载、有 template 故 scoped 生效、零污染,commit `0061cb7c63`);别关 `cssCodeSplit`(牺牲首屏)。
|
|
521
|
+
- 排查判据:`getComputedStyle(容器).height==='0px'` + `performance` 里该组件 CSS chunk **不在**已加载列表(`document.styleSheets` 里也搜不到那几条规则)。
|
|
522
|
+
|
|
523
|
+
### F18. `$emit` 不读实例 `__epxEvents`(程序化事件断链、内容区空白,postAppoint 平台侧实证)
|
|
524
|
+
Vue3 移除实例 `$on/$off`,平台改用实例级轻量事件 `$epxOn/$epxEmit`(handler 存实例 `__epxEvents`,见 core `mixins/methods-to-prototype.js`)。子组件(如 RouterView)用 `$parent.$epxOn('createPage',...)` 注册;而发射方仍是裸 `this.$emit('createPage')`——Vue3 `$emit` 只触发**父模板上**的监听器、**不读 `__epxEvents`** → 注册方永远收不到(emit 没人听的事件是合法 no-op、无报错)→ 内容区整块空白、点击无效。实证 `EntityTreeManage.vue`(机构管理右侧空白)。
|
|
525
|
+
- 修法:发射走「`$epxEmit` + `$emit`」**双发射**闸口(保留 `$emit` 以防模板上有 `@xxx`)。commit `543f85daf3`。
|
|
526
|
+
- 排查:全仓 grep `\.\$emit\('程序化事件名'`(createPage / backModule / push 等),逐个确认消费方是 `$epxOn` 注册还是模板绑定;全仓「没有任何模板 `@createPage`」即强信号。
|
|
527
|
+
|
|
528
|
+
### F19. 非 scoped 样式改为根 class 命名空间后,其他渲染入口漏加 class(服务大厅登录页实证)
|
|
529
|
+
|
|
530
|
+
仅在 F15 已证明需要调整隔离且选择根命名空间方案时,把 `<style scoped>` 改成 `<style>`,并将规则包进 `.epx-xxx { ... }`;所有实际渲染入口都必须在该命名空间内。检查 `extends` 子组件覆盖的 `render()`、父组件的自定义页面分支、动态 `<component>` 和多分支根节点;只给默认分支加 class 不够。此问题与 `data-v` hash 失配不同:CSS 已加载、DOM 也有原来的内部类,但根节点缺少 `.epx-xxx`,整组选择器匹配不到,构建和控制台通常正常。
|
|
531
|
+
|
|
532
|
+
- **实证**:`Login.vue` 样式改为 `.epx-login` 包裹后,默认登录分支有该类;`ClientLogin.vue` 覆盖的根节点及配置 `LOGIN_PAGE` 走的 `renderPage()` 没有。`#/hall/login/frame` 渲染出 `.PCcontainer` 和 `.loginCon`,却没有 `.epx-login` 祖先,登录页失去布局。给 `ClientLogin` 根和 `renderPage()` 的页面容器补 class 后,浏览器实测 `.loginCon` 恢复绝对定位、宽 357px,`.PCcontainer` 铺满 1440×900 视口。
|
|
533
|
+
- **升级检查**:凡将 scoped 样式改为根 class 命名空间,逐个打开使用该组件的实际页面/分支,检查 `el.closest('.epx-xxx')` 是否存在,并用 `getComputedStyle(el)` 验证宽高、定位等关键规则。若 CSS 已加载但命名空间不存在,在相应渲染根或共同祖先补 class;不要靠失效的 `__scopeId` 转写补丁。静态扫描只能提示风险,页面验收仍需运行时确认。
|
|
534
|
+
|
|
535
|
+
### F20. Element Plus 选择框占满工具栏,升级后按钮换行(HR 组织架构图)
|
|
536
|
+
|
|
537
|
+
**现象与判断**:旧产品在 Vue3 平台显示正常,升级为原生 Vue3 后右侧层级选择框与导出按钮分成两行。Element Plus 的 `.el-select` 使用 `width: var(--el-select-width, 100%)`;旧产品的 Vue2 render 兼容层曾给选择框补 `--el-select-width: 202px`,原生 Vue3 产品不再走该处理。若产品未明确工具栏选择框宽度,默认 100% 就会挤走相邻按钮。HR `OrgChart.vue` 中还存在无效的 `style="marginright: 20px"`,须改为有效 CSS 属性。
|
|
538
|
+
|
|
539
|
+
**修法与边界**:在产品拥有的工具栏内设置选择框宽度、按钮间距及对齐方式,加入迁移人工检查项;不要给全局 `.el-select` 统一定宽,也不要为了布局让 Vue3 产品重新走 Vue2 垫片。全局改宽度会影响表单、查询面板和弹窗中需要 100% 宽度的选择框。无法仅凭标签自动判断工具栏还是表单,因此不对所有 `el-select` 做机械替换。平台自有工具栏出现同类问题时,在对应平台组件修复。
|
|
540
|
+
|
|
541
|
+
工资 `BatchSearch.vue` 的“当前批次”也命中此模式:裸 `.container` 内的选择框没有局部宽度。改用组件专属根类、flex 对齐与间距,并给选择框独立宽度及最大宽度,桌面保持标题/选择框/链接同排,窄屏允许换行且不溢出。检查标题加选择框的紧凑查询组件,不能只扫按钮工具栏;无运行环境时明确区分编译/布局隔离验证与真实页面验收。
|
|
542
|
+
|
|
543
|
+
例如 HR 组织图右侧容器增加 `class="org-chart-actions"`,层级选择框增加 `class="org-chart-level"` 并删除无效的 `marginright`;在现有 scoped 样式中加入(宽度以原页面设计为准,202px 是旧垫片参考值):
|
|
544
|
+
|
|
545
|
+
```scss
|
|
546
|
+
.org-header > .org-chart-actions {
|
|
547
|
+
display: flex;
|
|
548
|
+
align-items: center;
|
|
549
|
+
gap: 20px;
|
|
550
|
+
}
|
|
551
|
+
.org-chart-actions > .org-chart-level {
|
|
552
|
+
width: 202px;
|
|
553
|
+
flex: 0 0 202px;
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
不要仅把父容器改成 `display:flex` 而让选择框继续以 100% 宽度参与收缩;也不要只修间距拼写。先确认实际根 class、CSS 已加载且选择器命中;无模板的 JSX/render scoped 问题另按 F15 处理。
|
|
558
|
+
|
|
559
|
+
**验收**:用实际 Vue3 产品产物加载页面,确认宽度约等于设定值,选择框和按钮纵向中心对齐;切换层级、打开/关闭导出弹窗,检查对话框中格式选择框仍按其自身布局显示。分别检查常用桌面宽度和产品支持的最窄视口,窄屏按产品设计允许整组换行或收缩,避免按钮挤出容器。记录实际页面截图与尺寸,构建成功不能代替布局验收。
|
|
560
|
+
|
|
561
|
+
### F21. Element Plus 日期格式 token 迁移遗漏(HR 历史组织机构图)
|
|
562
|
+
|
|
563
|
+
**已定位实例**:HR `OrgHistoryChart.vue` 日期控件仍写 `value-format="yyyy-MM-dd"`。本地已复现:绑定日期为 `2026-10-20`,输入框却显示 `2026-02-01`,下方数据时点仍正确;不是仅凭截图就能判定历史查询接口出错。产品已把该处改为 `YYYY-MM-DD` 并通过 lint,须重新构建、部署后验证浏览器。此前报告中的日期绑定/历史查询“待核”应按此根因更新,不把源码修复当成部署已生效。
|
|
564
|
+
|
|
565
|
+
**根因与边界**:Element UI 的 fecha 与 Element Plus 的 Day.js 格式 token 不同;`format` 控制显示,`value-format`(JS为`valueFormat`)控制绑定值,应一起检查。`yyyy→YYYY`、`yy→YY`、日 `dd→DD`/`d→D`;保留月 `MM`、分钟 `mm`、秒 `ss`,星期 `ddd/dddd` 和方括号转义文字不作日 token 替换。保留原来的年/月/日粒度、分隔符和时区约定。后端 Java、date-fns 等其它格式不能机械套用 Day.js 规则。参考 [Element Plus 日期格式](https://element-plus.org/en-US/component/date-picker.html#date-formats) 和 [Day.js 格式 token](https://day.js.org/docs/en/display/format)。
|
|
566
|
+
|
|
567
|
+
**技能执行要求:发现同类问题就修,不只加说明**。
|
|
568
|
+
|
|
569
|
+
1. 产品、项目升级及已迁移工程补修都扫描 `src`,覆盖 `.vue` template/script、JSX/TSX 和 JS/TS render。检查 `el-date-picker`、`ElDatePicker`、`el-time-picker`、`ElTimePicker` 的 `format`、`value-format/valueFormat`,包括导入别名、静态绑定、条件分支,以及 `h/createVNode` 的 props/attrs。
|
|
570
|
+
2. `migrate.mjs` 在常规 `--apply` 中自动改可静态确定的格式;不需要额外开启 gogocode。单独补修:`node tools/epx-migrate/fix-date-formats.mjs <目标工程> --apply`。只读预览去掉 `--apply`;修复幂等,保留 scoped、注释和其它代码。`detect-vue3-risks.mjs` 的 R2 在发布前检查残留。
|
|
571
|
+
3. 动态变量、`v-bind`、JSX展开、函数返回或共享配置需追踪来源:确认该值给 Element Plus 控件消费后,自动修复相应配置或在控件侧生成正确格式。若同一常量还服务后端/其它日期库,分别处理消费场景,不能直接改共享常量。工具不能安全确定的位置须由执行技能的代理继续分析并修复;只有信息不足才列未解决项。不要扫描或回写 `node_modules`、`lib`、`dist` 构建产物来代替源代码修复。
|
|
572
|
+
4. 原生 `runtime: vue3` 包不再通过旧 Vue2 render 垫片转换格式。不要去掉 runtime 标识、回挂旧垫片或给所有日期控件打全局运行时补丁;修复源组件并重新构建发布。
|
|
573
|
+
|
|
574
|
+
**验收**:至少检查 `2026-10-20` 初始回显不漂移;选择另一日期后输入框、绑定值、历史提示/数据时点及接口日期参数一致;月份/年份控件保留粒度,时间保持分钟与秒。检查实际完整包或懒加载产物与CSS同批部署。记录修复文件/行号、转换前后格式、动态来源处理结果和部署后截图。lint/build通过不等于日期逻辑已验收;没有部署权限时明确写“源码已修,待重新构建部署复测”。
|
|
575
|
+
|
|
576
|
+
### F22. 旧卡片从配置中的 propsData 读取组件标题(薪酬月度卡片)
|
|
577
|
+
|
|
578
|
+
**现象与判据**:Vue3 平台加载旧薪酬库时,`epx-wage-month-card` 在显示标题的分支抛出 `Cannot read properties of undefined (reading 'compTitle')`。已用已加载库的渲染函数与属性访问探针复现,读取链为 `$configs.propsData.compTitle`;不能仅凭属性名判断标题 prop 本身未传入。
|
|
579
|
+
|
|
580
|
+
**根因**:旧组件依赖配置对象中附带的 propsData 读取实例属性,这不是新版 `$configs` 的正式接口;组件标题应直接读 `compTitle`。平台 Vue2 垫片只为旧编译组件补充当前实例的 propsData 读取兼容,不污染公共 component mixin;兼容值不可枚举,不参与配置复制与持久化,显式配置仍优先。
|
|
581
|
+
|
|
582
|
+
**执行升级技能时必须处理源码,不只记录待办**:
|
|
583
|
+
|
|
584
|
+
1. 在产品 `src` 的 Vue template/script、JS/TS、JSX/TSX 中检索 `$configs.propsData` 与 `$options.propsData`,沿别名、解构和动态访问确认实际消费字段。排除 `node_modules`、`lib`、`dist` 等产物;不要把 `new Vue({ propsData })` 等其它用途作同样替换。
|
|
585
|
+
2. 确认字段来自本组件已声明的 prop 后,脚本或 render 中将 `this.$configs.propsData.compTitle` 改为 `this.compTitle`,模板中改为 `compTitle`;其它字段按实际 prop 名逐项适配。若 propsData 是明确配置的业务对象或读取其它组件的属性,保留业务语义并追踪来源,不能全仓机械删除 propsData 层。
|
|
586
|
+
3. CLI 当前没有该专用转换规则,由执行技能的 AI 继续定位和修复;迁移报告为空也不代表无此问题。原生 Vue3 产品应修源代码,不回挂旧垫片、删除 runtime 标识或在公共 `$configs` 中增加旧接口。
|
|
587
|
+
|
|
588
|
+
**验收**:验证显示标题与隐藏标题两条分支、多个卡片标题互不串用、父级标题更新,以及配置整体替换后的显示;保留明确配置的业务对象。使用最终产品产物复验原页面无该异常,区分源码修复、已发布与页面验证状态。旧库尚不能重建时,只使用平台 Vue2 垫片过渡,并明确该兼容不代表产品全部业务已完成迁移。
|
|
589
|
+
|
|
590
|
+
### F23. 列表已有字段名称却显示 ID/代码(旧单写插槽返回协议)
|
|
591
|
+
|
|
592
|
+
**已定位实例**:薪酬 `epx-wage-move-list` 继承平台 EntityList 并重写 renderField。接口和组件行数据均有 `GROUP#`、`CHANGE_TYPE#`、`TYPE#` 名称,但对应 ElTableColumn 的插槽只有 scopedSlots,没有 default,最终显示原始 GROUP、CHANGE_TYPE、TYPE。页面临时归一化返回结构后,这三列立即显示名称;不是后端未翻译或元数据未配置。
|
|
593
|
+
|
|
594
|
+
**根因**:旧方法返回 `{ scopedSlots: { default: ({ row }) => ... } }`,Vue3 平台继承的 renderTable 通过 `v-slots={this.renderField(...)}` 消费,需要 `{ default: ({ row }) => ... }`。旧作用域插槽被当成名为 scopedSlots 的插槽,默认渲染回退到原始字段;构建和控制台可能完全正常。renderColumnOperates 的旧返回结构也会使操作按钮不显示。
|
|
595
|
+
|
|
596
|
+
**另一端同样必查**:产品重写 `renderTable` 时,即使 `renderField` 已返回新版 `{ default }`,`<el-table-column {...this.renderField(field)}>` 仍会把函数当成普通属性,未传给 Vue3 插槽。可见列可能退回 ID/代码;操作列还可能因 `type="index"` 而显示序号。实例上 `attrs.default` 是函数而有效默认插槽缺失或仅为无内容的 JSX 子节点,是调用点未迁移的证据;仅确认 `$slots.default` 存在不足以判通过。
|
|
597
|
+
|
|
598
|
+
**执行升级技能时必须关闭此项**:
|
|
599
|
+
|
|
600
|
+
1. 扫描产品 src 中的 renderField、renderColumnOperates 及其引用的辅助函数、父类回退分支,检查返回值是否仍嵌套 scopedSlots;覆盖 Vue script、JS/TS、JSX/TSX。CLI 的 JSX `scopedSlots=` 属性转换不处理这种 `scopedSlots:` 对象返回,不能据 CLI 完成就跳过。
|
|
601
|
+
2. 确认调用方消费的是 Vue3 插槽对象后,去掉返回值外层 scopedSlots,保留内部 default/header 等插槽名、参数、字段名称优先级、标签样式和事件。例如脚本中返回 `{ default: ({ row }) => h('span', row[field.attriName + '#'] || row[field.attriName]) }`;父类回退直接保留新版父类的返回对象。不要把所有 vnode data 中的 scopedSlots 一律扁平化,也不要全仓替换 `scopedSlots:`。
|
|
602
|
+
同时检查调用点:JSX 叶子列使用 `v-slots={this.renderField(field, index)}`,操作列使用 `v-slots={this.renderColumnOperates()}`;`h` 渲染将插槽对象传入第三参。若同一 JSX 元素还通过 children 生成分组子列,应按用途选择默认插槽,例如 `v-slots={field.children?.length ? undefined : this.renderField(field, index)}`,保留父分组列的子列结构。只转换经确认返回插槽对象的方法调用;属性对象 spread 与直接返回 VNode 的方法保留。验证分组标题、叶子列数、表体列对齐与原有列顺序,不能以单元格修好为由丢失子列。
|
|
603
|
+
3. 核对同一条记录的请求列、接口响应、组件行数据与可见单元格:列表请求应包含相应 `字段#`,名称存在时实际显示名称,原值仍用于提交与操作。未返回名称的情况另查取数或标签解析,不用改字段类型或硬编码名称掩盖。
|
|
604
|
+
4. 用最终 Vue3 产品产物验证对象、代码项及关联字段列的显示、标签样式、名称更新、点击与操作列。关键列的 ElTableColumn 应有 default 插槽;只剩 scopedSlots 表示此项未完成。已有旧垫片能显示名称不能作为升级完成依据,须确认产品返回值已是 Vue3 结构且不依赖旧方法归一化;原生 Vue3 独立挂载回归也应通过。
|
|
605
|
+
|
|
606
|
+
**制品来源核验**:本地入口注册过组件,不代表页面正在使用该实现。后台远程库可在稍后覆盖注册或创建异步包装器;核对实际实例、远程脚本地址和本批制品。开发采证应明确区分源码热更新与远程库加载;需要替换时只在本地将本产品 JS、CSS、deps/manifest 和 chunks 统一指向同批本地制品,不改后台接入配置。未确认实际加载修复产物时,不能将刷新后仍显示 ID 归因于接口,也不能把临时改实例的显示结果记为交付通过。
|
|
607
|
+
|
|
608
|
+
**本地制品必须按原文件提供**:按需 UMD 入口虽然包含动态 import,仍由经典 script 加载;如果本地中间件把 lib 请求转发到 Vite `/@fs/` 等模块转换链,Vite 可能注入顶层 `import { injectQuery } ...`,触发 `Cannot use import statement outside a module`,导致组件库未加载完整。开发服务应直接返回制品原字节,JS/CSS/JSON 设置正确 MIME 和禁缓存,保留入口与分片各自的加载方式。验收入口、CSS、deps/manifest 和至少一个实际分片的 HTTP 响应与磁盘文件一致,经典入口可按其格式解析,再实页点击复核。源码预注册或旧缓存可能掩盖加载失败,不能只看首页正常就认定按需库可用。另将接口空列表与渲染失败分开:例如方案发放配置为空时概览无业务卡片,应查接口与配置来源,不为填满页面改写业务数据。
|
|
609
|
+
|
|
610
|
+
**兼容边界**:暂时不能重建的旧库由平台 Vue2 垫片在实例上包装 renderField、renderColumnOperates,提供 Vue3 可枚举插槽,并保留不可枚举的旧 scopedSlots 读取入口;不修改平台原生列表或共享方法定义。升级产品应修改源码,保留 runtime 标识,不以回挂垫片替代迁移。存在未修复分支或缺少目标环境时明确记录待验证,不能声称升级后已消除问题。
|
|
611
|
+
|
|
612
|
+
**手写列表扫描与回归补充**:运行 `node <本技能目录>/scripts/check-list-slots.cjs <前端工程根目录>`。脚本从目标工程解析依赖,只读扫描 src 中 Vue script 与 JS/TS/JSX/TSX,检查 render 方法内的旧 scopedSlots 和 el-table-column 上渲染函数的属性展开(包括条件表达式)。退出码 1 表示候选问题,须按实际返回契约复核;0 只表示这些语法模式未命中,不能替代模板、别名调用、h 第三参、元数据及真实页面验收。
|
|
613
|
+
|
|
614
|
+
迁移时连同 renderGroupItem、renderIndexField、renderLevelColumn 等辅助链一起检查。折叠分组 `...(!expandItems ? this.renderGroupItem(group) : {})` 应改为 `v-slots={!expandItems ? this.renderGroupItem(group) : undefined}`,展开时保留子列。回归应编译实际列表达式,验证叶子单元格、分组子列、操作按钮、开关初值与回写;显示字段名称时原始 ID 保持用于操作和提交。不要将接口名称缺失误判为同一插槽故障。
|
|
615
|
+
|
|
616
|
+
### F24. 控件初值不回显/弹窗打不开:模型属性和 JSX 事件仍是 Vue2 协议
|
|
617
|
+
|
|
618
|
+
**现象与根因**:`el-switch value={enabled}` 在 Element Plus 中不会设置模型初值;即使行数据为启用,开关也可能一直显示关闭。`el-dialog visible={flag}`、旧的 `{...{ on: { 'update:visible': fn } }}`、向默认模型已经改名的组件传 `v-model:value`,同样可能构建成功但不回显、不关闭或不回写。迁移工具处理过模板不能代表 JSX 和手写 VNode 已全部适配。
|
|
619
|
+
|
|
620
|
+
**检查与修复**:
|
|
621
|
+
|
|
622
|
+
1. 扫描所有产品组件的 template、JSX、JS/TS render 和包装组件,按目标版本实际 props/emits 核对协议。Element Plus 默认模型是 `modelValue`/`update:modelValue`,Vant 4 Dialog 显隐是 `show`/`update:show`。平台控件需逐项确认:TableForm、FormScope、ScopeDrawer 的值使用默认模型;ScopeDrawer 的显隐仍是 `visible`/`update:visible`,部分设计器控件仍公开 `value`,不能全仓替换所有 value/visible。
|
|
623
|
+
2. JSX 中双向状态可用 `v-model={this.dialogVisible}`;需要确认后才提交的开关保留受控方式,如 `<el-switch modelValue={enabled} onChange={this.confirmStatusChange} />`。不要为了改成 v-model 而提前改业务行状态,或改变取消后的值。
|
|
624
|
+
3. Vue3 VNode 属性与事件是平铺的:`{...{ on: { 'update:visible': fn } }}` 改为 `{...{ 'onUpdate:visible': fn }}`;`{...{ props: this.tableProps }}` 改为 `{...this.tableProps}`。目标事件改名时同步调整监听;保留业务自定义事件名与回调参数,不把 change 一律改成模型更新事件。
|
|
625
|
+
4. 核对包装组件的两端:子控件 `:model-value`/`@update:model-value` 与包装组件公开的 `formData`/`update:formData` 可以不同。原生 input 的 value/input、el-option 的 value、明确公开 value 的平台控件属于正常用法。涉及 UI 库代际升级时还要核对回调签名,例如 Vant 4 beforeClose 返回 boolean/Promise,不能继续调用旧 done 回调。
|
|
626
|
+
|
|
627
|
+
**验收**:使用最终产品制品检查启用、停用、空值初态;打开、取消、关闭后再次打开;父状态与子控件同步,回调不重复。真实写入需在授权测试场景进行;可用隔离的回调替身先验证渲染与事件,明确它不等于接口保存已验证。记录扫描范围、已修项、正常保留项及待验证项,不能以全量 lint/build 通过代替交互验证。
|
|
628
|
+
|
|
629
|
+
参考:[Vue3 v-model 迁移](https://v3-migration.vuejs.org/breaking-changes/v-model.html)、[Element Plus Switch](https://element-plus.org/en-US/component/switch)、[Dialog](https://element-plus.org/en-US/component/dialog)、[Vant Dialog](https://vant-ui.github.io/vant/#/zh-CN/dialog)。
|
|
630
|
+
|
|
631
|
+
**对象表单回显与必填联查(薪酬升级实证)**:目标 `epx-object` 公开协议为 `modelValue` / `update:modelValue` 时,模板的 `v-model:value` 会静默丢失已保存对象的回显,应改成默认 `v-model`。UI 保留 `{ id, name }`,业务 ID 字段在请求中仍发送标量 ID;不要将控件对象直接塞进 ID 字段。必填不能只判断对象 truthy:`{ id: '' }`、空数组、空白字符串均应拦截,0、false 和有效 ID 应按字段语义保留。联查读取、显示、选择、提交序列化与再次读取,空对象不得发出提交请求;未实际保存时只能记为模拟请求回归通过,不可推断后端已写入。此项与 F24 模型迁移一并检查。
|
|
632
|
+
|
|
633
|
+
**模板抽屉补查**:机械转换可能留下 `<el-drawer v-model:visible="flag">`。这会传 visible/update:visible,而 Element Plus 使用 modelValue/update:modelValue;事件已执行且 flag=true,抽屉仍不会打开。仅对确认的 Element Plus Dialog/Drawer 改为默认 v-model,保留平台自定义组件公开的命名模型。旧 Drawer 的 wrapperClosable 应按当前 close-on-click-modal 契约迁移,核对关闭、取消及销毁行为。若“添加”先创建草稿再开抽屉,用接口替身验证顺序、ID 和取消清理,不为复现弹层在真实库重复新增数据。回归覆盖添加、编辑、设置及关闭后再次打开,不能只查 JSX visible 属性。
|
|
634
|
+
|
|
635
|
+
**表格样式回调的返回类型**:Element Plus 的 cell-style、row-style、header-cell-style、header-row-style 回调按 CSSProperties 对象返回,例如 `return { color: levelColor }`;无样式返回 `{}` 或 undefined。不要返回 `color:red;` 等 CSS 字符串。目标宿主将字符串展开/合并为对象时,会产生 0、1 等索引属性,Vue 写入 CSSStyleDeclaration 可报 `Failed to set an indexed property [0]`,从而出现有总数/表头但表体空白。普通 DOM 的 style 字符串不属于此规则,不能全仓机械替换。
|
|
636
|
+
|
|
637
|
+
遇到该错误,沿实际表格绑定定位样式回调,同时检查其他表格的单元格与表头样式;保留等级颜色和提示语义。`scripts/check-list-slots.cjs` 现同时检查 el-table 上的旧 props 包装,以及 JSX/模板静态绑定到本组件方法的样式回调字符串返回;别名、导入函数与运行时返回值仍需人工核实。验证至少包括有颜色与无颜色的行、真实表格渲染、树形子行和实际编辑状态;只验 VNode 属性存在不足以发现表体样式写入错误。
|
|
638
|
+
|
|
639
|
+
**区分旧过滤器选项与业务字段名**:Vue2 的组件选项 `filters` 已移除,但 `data()` 中名为 filters 的筛选状态是普通业务数据,不能因此删除、拆成数组或移入 methods。迁移后检查模板所读状态是否在 data/computed/props 中声明,methods 中是否误放数组或对象,并验证异步筛选结果能触发更新。统计页已有接口响应不代表初次渲染与后续更新都正常;从空初值启动实际组件,点击行入口并确认 Drawer/Dialog 可见、关闭及换行重开,不能仅验证 showMember 等方法能把布尔值设为 true。
|
|
640
|
+
|
|
641
|
+
### F25. 页面有字段却查不到数据:参数占位符中的 eval 依赖局部变量
|
|
642
|
+
|
|
643
|
+
**已复现**:起薪办理页已收到 people、id、changePersonId;人员卡片及变动通知却为空。实际查询的 scopeParams.values 仍是 `$[people]`、`$[changePersonId]`。源码用 `eval` 拼接 `that.$query...` 读取参数,但最终产品制品中的 that 声明缺失,异常被 catch 后返回原占位符。把查询值换成页面已有 ID 后,同一接口立即返回人员和通知数据;不是模型绑定或接口缺数。
|
|
644
|
+
|
|
645
|
+
**自动执行流程(每次升级都执行,含已有 Vue3 产品)**:
|
|
646
|
+
|
|
647
|
+
目标工程安装的依赖须可解析 `@babel/parser`、`@babel/traverse`、`@vue/compiler-sfc`;缺少时报告工具链问题,不临时下载依赖或跳过后宣称已扫描。脚本使用平台公开的 `$_.get`,适用本技能范围内的 epx 产品。
|
|
648
|
+
|
|
649
|
+
```sh
|
|
650
|
+
node "<技能目录>/scripts/fix-eval-paths.cjs" "<前端工程>" --report ".cache/eval-paths-preview.json"
|
|
651
|
+
node "<技能目录>/scripts/fix-eval-paths.cjs" "<前端工程>" --apply --report ".cache/eval-paths-applied.json"
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
1. 预览覆盖 `src` 下 Vue 双脚本、JS/JSX、TS/TSX/MJS/CJS;跳过依赖、构建目录和符号链接。先核对目标与报告,再在已有升级授权内应用确定项,不因工作树已有修改而覆盖或清空它。
|
|
655
|
+
2. AST 会追踪动态 key 是否来自 `$[...]`、`#[...]`、`#{...}` 的替换回调或路径提取,确认组件 this/只读组件别名后才改为路径读取;保留优先级、回调和配置。有效别名显式引用,缺少的 that/_this 仅在已确认组件词法 this 时替换。普通回调的 this、被重新赋值的别名、模板内 eval、业务表达式和未知来源只报告。
|
|
656
|
+
3. 默认预览不改源码;`--report` 仅写指定报告。应用前完成全部源码解析并备份到目标工程 `.cache/eval-path-backups/`,只替换命中表达式,保留格式和已有修改。任一解析失败时不应用其它文件;报告保留路径、行号、前后表达式及备份位置。重复应用应无新增改动。
|
|
657
|
+
4. 退出码 `0` 表示本次无遗留工具项,`2` 表示预览有可改项或仍有人工复核项,`1` 表示依赖/解析等执行失败。`--apply` 返回 `2` 时确定项可能已应用,必须读报告;不要重复应用试图清掉合法业务表达式。AI 按下方检查步骤复核并记录保留依据、已修复或待验证状态,不能把所有非零结果当成语法失败。
|
|
658
|
+
5. 应用后重扫,确认自动项清零;运行目标工程 ESLint,重建产品库,用最终制品验证实际查询值和显示。维护此脚本时运行 `node "<技能目录>/scripts/fix-eval-paths.test.cjs" "<前端工程>"`;回归使用目标构建链的 esbuild 和 lodash-es,覆盖压缩后取值、回调边界、预览、备份、幂等及解析失败不写入。
|
|
659
|
+
|
|
660
|
+
**检查与修复**:
|
|
661
|
+
|
|
662
|
+
1. 追踪进入页面的参数 → 子组件 `$query` → 格式化后的 scopeParams/跳转参数 → 实际请求 → 响应 → 组件数据/DOM。页面有 ID 不代表子组件查询使用了 ID;核对请求中是否残留 `$[...]`、`#{...}`、`#[...]`,不要先改元数据或填默认值。
|
|
663
|
+
2. 扫描 template/script、JS/TS/JSX 内拼接路径的 eval,尤其是 ``eval(`that.$query.${key}`)``、``eval(`_this.${key}`)``。字符串内的变量引用不会被静态工具可靠识别,既可能掩盖源码未声明变量,也可能在清理/构建后失去声明;源码 lint 不报错不等于制品可用。
|
|
664
|
+
3. 确认只是属性路径读取后,改成直接下标访问或公开的路径读取工具,例如 `this.$_.get(this.$query, key)`、`this.$_.get(this, valueKey)`,保留原查找优先级和业务值语义。嵌套路径、可选上下文及含 `|` 的字段不能用简单拆字符串替代。无声明的 that/_this 应按真实词法上下文改用 this,不重新引入供 eval 使用的别名。
|
|
665
|
+
4. 按用途区分业务条件、公式计算及配置对象解析中的 eval;它们可能接受完整表达式,不能当路径读取批量替换成 get,也不能仅为消除构建警告删除。保留的表达式仍要检查上下文:若只通过字符串使用 that,构建后可能同样丢失变量,导致工资项/操作区渲染异常。确认当前方法及嵌套箭头函数的 this 均指向组件后,可将表达式前缀改为 this,保留表达式本身及其真值规则;不能在改变 this 的普通回调中机械套用。记录未处理类别与验证边界。
|
|
666
|
+
|
|
667
|
+
**验收**:用最终产品制品打开真实页面,确认请求值等于当前人员/记录 ID,响应、组件数据与可见内容一致;验证参数替换、嵌套路径、缺省上下文及原配置未被改写。源码单测通过后仍需制品实页复验,防止优化阶段再次产生差异。只读排障不执行发起、审批、保存等写操作;无法定位截图对应记录时说明使用的是同页可复现记录。
|
|
668
|
+
|
|
669
|
+
### F26. 旧共享入口有基类,独立包缺导出,继承组件初始化白屏
|
|
670
|
+
|
|
671
|
+
**已复现**:测试宿主中 `window.frame.Step`、`StepItem` 已有真身,`__EPX_PACKAGES__['@epxing/components']` 却没有这两个导出。产品按需分片从独立共享包同步取值,实际生成 `extends: undefined`,随后 created 读取 upPage.component、render 读取 $component.style 接连失败。本地安装完整独立包会掩盖差异;这类堆栈不能直接归因于业务数据或一律改成空对象兜底。
|
|
672
|
+
|
|
673
|
+
**检查**:在用户指定宿主的新会话中读取最终 deps 清单与共享 shim 的实际包映射,核对需要继承的导出是否存在、是否为同步组件真身;同时比较旧入口与独立共享包。不能只检查包对象存在、npm 版本或 app 的组件注册数。源码与分片使用同一哈希也不能证明宿主导出齐全。
|
|
674
|
+
|
|
675
|
+
**根因与修法**:平台按需预加载把真身写回旧入口,却漏了独立命名空间。统一在首次解析和缓存命中时原地发布真身,保留命名空间与组件对象身份;可选 designer 基类按包归属发布,解析失败不发布 undefined。同步检查其它产品依赖的懒导出,避免只补 Step 名字。该问题需平台修复与宿主发布,产品参数修复脚本不负责改宿主。
|
|
676
|
+
|
|
677
|
+
**验收与状态**:覆盖首次 ensure、缓存回填、未解析组件与可选包归属的回归;使用目标宿主验证继承上下文及详情渲染。ESM 分片可能已捕获 undefined,补齐注册表后须新会话重新加载。浏览器临时补映射只能证明根因,刷新撤销,单独记录;不能记为平台已发布或用户页面已永久恢复。发布后才对原入口关闭此项。
|
|
678
|
+
|
|
679
|
+
### F27. 当前角色 ID/code 统一使用平台角色上下文
|
|
680
|
+
|
|
681
|
+
**迁移契约**:项目组件获取当前角色 ID 使用 `this.$currentRoleId`,也可使用 `this.$currentRole?.id`;编号使用 `this.$currentRole?.code`。平台统一兼容旧地址及不带角色的新菜单地址,并从当前应用角色列表匹配 ID。产品不自行解析 URL,不读 localStorage/sessionStorage 等角色缓存,不自行请求角色列表做当前角色匹配,也不取账号角色列表第一项代替当前角色。
|
|
682
|
+
|
|
683
|
+
继承平台列表、表单等基类,或已经接入 `mixins.component`/`ComponentMixin` 的组件直接使用,避免重复混入导致初始化或请求重复。普通自定义 Options API 组件按公共接口接入:
|
|
684
|
+
|
|
685
|
+
```js
|
|
686
|
+
import { ComponentMixin } from '@epxing/core'
|
|
687
|
+
|
|
688
|
+
export default {
|
|
689
|
+
mixins: [ComponentMixin],
|
|
690
|
+
computed: {
|
|
691
|
+
roleId () {
|
|
692
|
+
return this.$currentRoleId
|
|
693
|
+
},
|
|
694
|
+
roleCode () {
|
|
695
|
+
return this.$currentRole?.code || ''
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
**执行升级时扫描并修复**:
|
|
702
|
+
|
|
703
|
+
1. 覆盖 template、script、JS/TS/JSX/TSX、共享方法和继承链,定位当前角色的来源与消费点:`$route.params` 的角色参数、path/hash 分段、当前角色存储项、按编号查询/遍历角色列表,以及 `roleId`、`roleCode`、`curRole` 等传参。命中后追踪用途,不能仅按变量名全仓替换。
|
|
704
|
+
2. 确认为当前角色后,ID 查询、角色 ID 范围筛选及页签参数使用平台 ID,编号接口及编号比较使用平台 code。保留 ID/code 原有业务区别,不把编号填进 ID 字段;同时检查跳转参数、工资方案筛选、字段权限及菜单后续请求,不能只改一个显示值。
|
|
705
|
+
3. 删除只为解析当前角色而存在的地址拆分、角色列表请求及缓存读取;若列表还用于角色选择、管理或业务展示则保留相应用途。角色管理表单中的被编辑角色、用户选择的目标角色、业务记录角色字段不属于当前角色,不能强制替换。
|
|
706
|
+
4. 在请求发生时读取上下文;需要随角色变化的派生值使用 computed,不在 data 中保存一次快照。按组件实际初始化和复用链路核对角色切换后的重新取数,避免重复加 watcher。上下文缺失时检查目标宿主 API/基类/混入及角色加载时序,不退回解析地址、读缓存或额外匹配角色,也不以静默跳过请求掩盖问题。
|
|
707
|
+
|
|
708
|
+
**验收**:最终制品覆盖旧含角色地址、新不含角色的菜单地址、直接进入/刷新、多应用及切换角色后的缓存组件复用;核对实际请求中的 roleId/roleCode、ID 范围筛选和编号权限判断。宿主未提供新 API 时明确记录依赖及待发布状态。源码检查或单个角色页面通过不能代替这些场景,也不能改写角色权限数据来让请求成功。
|
|
709
|
+
|
|
710
|
+
### F28. 审核详情空白、加载遮罩残留与懒加载页签计数
|
|
711
|
+
|
|
712
|
+
审核列表/卡片已返回数据但详情空白时,先捕获首条异常及实际接收组件。平台表单会下发 `form` 实例;未声明为 prop 的接收组件在 Vue3 自动继承 attrs 时可能将实例写到原生 DOM,报 `setAttribute: Cannot convert object to primitive value`,后续可能连带出现 null vnode/parentNode 异常。工资审核页实证接收端为平台 Card、EntityList(含产品派生列表)。修复接收端声明 `form: Object`,保留 class/style 继承及上下文;不要 stringify 实例、吞渲染异常或批量关闭 inheritAttrs。扫描同一传参链的其他接收端,以 null 原型对象或真实 Vue 实例验证 form 不落 DOM、外部样式仍落根;平台源码修复须另核对目标宿主发布版本,不能只发布产品包。
|
|
713
|
+
|
|
714
|
+
若加载变量已 false 但遮罩仍在,检查实际可见节点、VNode 分支及指令生命周期。JSX/render 在有/无数据分支复用同类型根 div,仅空分支带 v-loading 时可能留下旧遮罩;给互斥分支不同 key 或统一指令绑定,验证加载→内容、空结果及请求失败,不以手动删除遮罩验收。同时检查异步链空结果是否结束加载。模板 v-if 的编译器处理与手写 render 不同,静态命中不等于缺陷;按实际分支检查。
|
|
715
|
+
|
|
716
|
+
页签计数读取子项配置前,核对懒加载实例及 itemConfigs 是否就绪。配置尚未就绪时延后该项统计,不能用空配置发起无范围查询,也不能将计数异常认作后端无数据。已有模板/实例配置来源按当前平台契约保留。
|
|
717
|
+
|
|
718
|
+
**懒加载页签不可只验默认页**:同一 form 透传链必须逐个激活业务页签和嵌套页签,记录组件名、首条异常和实际内容。工资“办理→流程信息”实证还涉及 CheckHistory、ProcessChart,不能仅补 Card/EntityList 就认定共性扫描完成。流程信息应核对审核记录节点及流程图实际内容,工资明细应核对列表/嵌入页,空内容要区分接口空结果、权限和渲染失败。比较目标宿主运行时 props 与源码声明,旧制品仍缺声明和源码遗漏是两个独立结论;不以补默认页、临时透传实验或单个字段正常代替全部页签验收。
|
|
719
|
+
|
|
720
|
+
验收分别记录平台/产品源码、自动化与临时浏览器验证、正式发布状态;审批按钮进入详情只作只读验证,不执行同意、退回或提交来证明修复。
|
|
721
|
+
|
|
722
|
+
### F29. 拖拽组件升级后不渲染与步骤选中状态混淆
|
|
723
|
+
|
|
724
|
+
vuedraggable 等第三方库升级 major 后,核对模板与 JSX 的实际插槽 API,不能只更新依赖。Vue3 版本需要 item 插槽时,将每条业务数据放入 item 插槽并提供稳定 item-key;每次插槽调用返回一个对应项根节点,排序回写仍绑定业务数组。新增卡片、末尾按钮等固定入口放 footer 或组件外,不混入可排序项。检查导入别名、局部包装和 JSX 用法,覆盖空/单项/多项,实际验证拖动后顺序与新增/编辑入口。只读模板扫描使用 `scripts/check-draggable-slots.cjs`,JSX 的 review 结果继续编译和渲染,不当作已通过。
|
|
725
|
+
|
|
726
|
+
向导步骤的当前项、业务已完成数与 Element Plus active/finish-status 可能含义不同,分别追踪实际业务状态。当前选中由真实当前页驱动,可用 aria-current=step 和产品主题样式表示;不能把所有已完成项都当成当前项,也不能只按固定最后一步显示选中。验证回看已完成步骤、前进/后退、全部完成后重新选页。选用实心图标、标题颜色或指示条属于产品设计,不将本产品的视觉选择强制推广到所有产品。
|
|
727
|
+
|
|
728
|
+
### F30. 布局、分页及可选配置的静默回退
|
|
729
|
+
|
|
730
|
+
手写 render 根若未带 template scoped 样式所需的类名/作用域,侧栏与内容区域可能失去 flex、间距和滚动边界。先检查实际根 class、父子布局、min-width/min-height、配置高度与局部选择器是否匹配;在产品专属范围修复,按 F15 验证继承与插槽链、组件外同名元素和跨页面影响。工具栏选择框、导出字段行和设置面板按实际用途声明宽度/网格/换行,不能全局缩窄所有表单控件;同时检查重复 padding 和 UI 按钮默认相邻 margin。
|
|
731
|
+
|
|
732
|
+
分页在模板与 JSX 中的绑定形式不同:模板 @current-change/@size-change 属于正常组件事件,Vue3 JSX 使用 onCurrentChange/onSizeChange 等当前公开事件,旧 onCurrent-change/onSize-change 不等价。sizes 布局存在时按目标版本要求接收页大小变更;核对 currentPage/pageSize 的更新和查询参数,不仅删除警告。验证第一页、后续页、切换页大小及历史/懒加载列表入口。
|
|
733
|
+
|
|
734
|
+
可选图标、标题或自定义字段配置允许为空时,进入该分支前核对 null,不直接读取 iconField.dataType 等属性;缺省时保留完整内容和交互。页面 ID 等参数应保留字符串等公开类型,不用渲染函数当空值。复制的完成页、空状态与标题检查业务名称和按钮含义;这些缺陷通常不会阻止构建。
|
|
735
|
+
|
|
736
|
+
### F31. 触发按钮存在但菜单为空、设置状态改变却不显示
|
|
737
|
+
|
|
738
|
+
与 F14、F24 一起检查两个独立关口:弹层的具名插槽是否真正进入 `$slots`,以及弹层是否收到当前版本的模型属性/更新事件。按钮可见、点击事件执行或状态已经 true,都不能替代这两个检查。
|
|
739
|
+
|
|
740
|
+
- 原生 Vue3 JSX 中将 `<el-dropdown-menu slot="dropdown">` 移到父下拉的 `v-slots={{ dropdown: () => (...) }}`;reference、footer、title 等按接收组件的真实插槽名处理。保留默认触发按钮、作用域变量和原事件,不能只删除 slot 属性。旧宿主曾兼容 dropdown 的案例不代表目标宿主或原生产品制品仍兼容;实际 `$slots` 只有 default 就应按缺失插槽定位。
|
|
741
|
+
- Element Plus Dialog/Drawer 使用 modelValue/update:modelValue;不要把“展示状态改成 true”视为弹窗已经打开。命名模型属于平台自定义组件时保留其公开协议。独立布尔 checkbox 的选中值使用 modelValue;分组中的 value/label 有各自语义,不能一律替换。
|
|
742
|
+
- 调用 ref 方法前核对当前版本公开 exposes。Element Plus Dropdown 的关闭使用 handleClose;旧 hide 调用可能在状态赋值前抛异常。检查完整“关闭下拉→初始化草稿→打开弹窗”链路,不用可选调用吞掉不兼容,也不通过删除关闭逻辑留下菜单遮挡。
|
|
743
|
+
|
|
744
|
+
对所有实际导入和包装组件做 AST/编译后检查,分别记录旧 JSX 插槽与旧模型命中数。回归应验证命名插槽函数返回内容、默认触发按钮保留、公开关闭方法可调用、模型初值与更新回写;在目标宿主点击打开、关闭、再次打开确认可见内容。保存/导入/同步等写操作仅在授权测试范围执行;隔离 VNode 回归、临时浏览器诊断与正式发布分别记状态。
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
**图标迁移不能丢失交互契约**:将旧 `<i class="el-icon-*">` 替换为组件图标时,保留点击事件、stop/prevent 等修饰符、原参数、v-if/v-show、附加类名及样式。事件可放到外层真实 button(type="button"),不要仅保留图形。重复图标必须按各自的循环/分支上下文核对;同一个文件存在相同处理方法,不代表所有入口都绑定正确。曾出现等级编辑误传 TOTAL、对比人员入口误传当前方案、附件重传/删除与返回按钮事件丢失,以及详情/警告图标丢失显示条件。
|
|
748
|
+
|
|
749
|
+
有升级前 Git 基线时,运行 `node <本技能目录>/scripts/check-icon-migration.cjs <前端工程根目录> <升级前Git引用>`。脚本只读比较模板图标的顺序、指令和参数,接受事件移动到祖先按钮;无法对齐的结构变化列为人工项,不自动恢复旧代码。结果只覆盖模板静态图标,JSX、动态图标、普通属性与 CSS 仍需复核。按原始图标类和按钮状态检查最终选择器,避免图标换成 SVG 后 hover/隐藏样式失效。回归点击入口本身,验证实际打开、选中回显、关闭、原记录 ID、上下文参数及事件冒泡;仅测试方法存在不足以发现失接线。
|
|
750
|
+
|
|
751
|
+
### F32. 同层插槽条件访问尚未声明的局部参数
|
|
752
|
+
|
|
753
|
+
`<template #default="scope" v-if="scope.row.data">` 中,外层条件不能使用该插槽才声明的参数;`#cell="{ row: item }"` 配合 `v-if="item.data"` 同样需要检查,不能只搜索 scope。将依赖局部参数的判断放入插槽内部,保留父行/叶子行、编辑/查看及相邻 if/else-if/else 的完整语义。外层条件只引用组件状态时可以保留,不全局移动所有带插槽的条件。
|
|
754
|
+
|
|
755
|
+
运行 `scripts/check-list-slots.cjs` 检查模板 AST 中同层条件与绑定参数的引用关系。脚本只读,解析不确定项报告复核,不自动改写条件链。修复一处后扫描全部模板,沿同基类和复制组件扩查;验收应从真实表格验证父行、叶子行、空值、编辑模型与原记录 ID,不能加空 scope 或删除业务条件来隐藏错误。
|
|
756
|
+
|
|
757
|
+
命中同名引用仍是候选:若组件外层确有同名状态,先确认条件究竟读取它还是误用了插槽局部参数,不能机械移动后改变条件含义。
|
|
758
|
+
|
|
759
|
+
### F33. 响应式状态、树刷新与异步依赖未形成完整闭环
|
|
760
|
+
|
|
761
|
+
按 F24 区分组件旧 filters 选项和 data.filters 等业务状态;实例所读写的状态须在 data/props/computed 等公开响应式入口声明,不能将数组或对象误迁到 methods。模式切换后清理旧显示分支,按当前模式消费响应;请求可能交错返回时,验证旧请求不会覆盖新上下文,不把所有 watcher 扩大为无差别深度监听。
|
|
762
|
+
|
|
763
|
+
树数据整体刷新后,旧节点引用不再代表新数据。按稳定业务 ID 在新树中恢复有效选中;选中项消失或树为空时,清除关联列表、指标、详情和依赖旧节点的操作入口。核对 rowKey 的真实唯一性、展开/收起和选择回写,不用名称去重或字符串缩进冒充真实树关系。
|
|
764
|
+
|
|
765
|
+
对依赖多次异步初始化的状态查询,列清每个必需参数的来源及就绪条件,分别验证 A 先返回、B 先返回、空结果、失败与上下文切换。失败不能继续展示初始成功/已提交值,也不能强制设为未提交来显示按钮;按实际协议区分未加载、加载成功和加载失败,保留权限及业务筛选。具体业务接口的参数名不可推广成所有请求都必传。
|
|
766
|
+
|
|
767
|
+
### F34. 移动布局与报告结论必须按实际场景验收
|
|
768
|
+
|
|
769
|
+
按 F15/F30 检查继承及 render/插槽链的实际根和 scopeId;移动页尤其核对 flex 收缩、min-width/min-height、横纵滚动、底栏自然高度与安全区。抽屉内部避免用 100vw 假定其等于容器宽度;宽表、长名称与按钮换行应按真实容器验证。只在确认当前作用域失配后修复,保留专属命名空间和外部隔离,不套用其他产品的固定高度、像素或整块删除 scoped。
|
|
770
|
+
|
|
771
|
+
源码、组件/浏览器夹具、最终制品及目标宿主分别记结果。工具初始报告中的未勾选项须与后续修复和验证逐项对账,不能直接把历史清单当作当前缺陷,也不能因本地构建成功整体勾选。报告乱码、缺失请求或未复现入口不构成根因证据;待定位问题保留症状、已知证据、缺少条件与下一步,不能变成共性自动修法。
|
|
772
|
+
|
|
773
|
+
## G. 产品升级的跨会话缺陷回归
|
|
774
|
+
|
|
775
|
+
本节从近期产品故障的提交及交接记录提炼检查场景,补充 E2 的逐组件主题验收。历史记录证明曾出现过这些问题,不证明当前目标环境已发布修复;其中只有临时浏览器实验、单测或尚待现场验证的结论,不能提升为已部署通过。具体来源及证据层次保留在平台仓 `docs/handoff/tasks/` 的原任务中,技能只保留可复用检查方法。
|
|
776
|
+
|
|
777
|
+
### 适用判定与结果记录
|
|
778
|
+
|
|
779
|
+
以真实组件入口、继承/共享方法、内嵌子页和浮层为检查范围。在 E2 台账增加“G 节命中项/不适用依据/结果”列,逐组件判断以下场景;只检查实际支持的能力,不为凑清单新增移动版、设计器或业务动作。无适用能力时写明理由;未能运行写待验证,不能写不适用。跨组件的登录、导航及导出链路另建场景行,并关联参与组件。
|
|
780
|
+
|
|
781
|
+
| 编号 | 触发场景与近期故障类型 | 升级时检查 | 可观察的验收证据 |
|
|
782
|
+
| --- | --- | --- | --- |
|
|
783
|
+
| G01 | 表单、多选、字段配置:原生事件覆盖组件值,或数组原地变化不回写 | 核对声明事件、modelValue/update 协议、回调参数及数组监听深度;检查单选/多选/清空、初始回显和再次编辑。既有响应问题参见 F16/F18 | 修改后父表单值及提交参数一致;不会变成 DOM Event、误清空或重复触发;取消不保存 |
|
|
784
|
+
| G02 | 列表有数据却空白、名称显示 ID、卡片标题/样式丢失 | 按 F14/F15/F19/F22/F23 核对插槽、props 与实际样式归属;查旧代码对 vnode.data.style、组件内部字段等 Vue2 私有结构的读写,按 Vue3 公开方式适配并保留原事件 | 响应→行数据→实际 DOM 对得上;名称显示但操作仍用原 ID,多实例互不串值,完整产品包不靠旧垫片维持 |
|
|
785
|
+
| G03 | 弹窗打开报非法 DOM 属性、快捷菜单点击无效、勾选状态不回显 | 检查参数展开是否将 `$refesh`/`$refresh` 等组件回调传到原生 DOM;核对旧 hide/关闭方法及独立 checkbox 布尔契约,不能只改方法名或任意丢掉回调 | 弹窗可打开、确认、取消和再次打开;父页按契约刷新一次,菜单切换与勾选初值一致,DOM 无非法属性异常 |
|
|
786
|
+
| G04 | 数据/PDF 已到但遮罩不退,详情返回后异步初始化报错 | 分开检查 loading 状态、真实遮罩、指令卸载;覆盖加载分支复用根 DOM、异常/取消、元数据晚返回、nextTick、离开页面与 KeepAlive。不要只手删遮罩或屏蔽异常 | 正常和失败路径均退出加载,内容保留且可交互;卸载后不再初始化/写已清理配置,缓存组件按预期恢复 |
|
|
787
|
+
| G05 | 对象树/源字段选择弹层偏移、内容长高后出界 | 排查 Element UI 的 popperElm/updatePopper 等旧私有接口和手算 top/left;按目标版本 Popper/虚拟锚点契约处理异步内容、换行切换、滚动、resize 和销毁清理 | 实际输入框与浮层边缘及间隔正确,上下自动翻转;长树能滚到末项,点击外部可关闭,监听不重复、不泄漏 |
|
|
788
|
+
| G06 | 旧 van-popup 内容直接铺在表单中,遮罩/关闭失效 | 核对目标平台支持的移动弹层组件、显隐与状态回写、位置、尺寸及滚动锁;可重建产品应适配源码,旧库兼容仅作过渡,保留产品明确注册的实现 | 关闭时内容不外露,打开时在正确位置;关闭按钮/遮罩/程序关闭同步且只回写一次,页面滚动锁可释放 |
|
|
789
|
+
| G07 | 卡片增高、嵌套间距翻倍、宫格按内容收缩、关闭标题仍留空栏 | 核对默认栅格与显式 flex、height:100% 与 margin 的总高度、列宽及卡片插槽;默认布局不能因升级隐式改语义,主题检查见 E2 | 同后端/视口下卡片列数、边距、标题显隐正确;窄屏响应式正常;自定义宽度和显式 flex 仍有效 |
|
|
790
|
+
| G08 | 更多查询页签重叠、长卡片标题/分页/导入弹窗裁切 | 按 F20 验工具栏选择器宽度,再查 flex 最小尺寸、固定高度、换行、overflow 和 CSS 晚加载;避免为一个弹窗全局改所有确认框 | 窄宽和矮视口下分页及按钮完整可点,长标题不被截断;基础 CSS 先/后加载均正确,普通确认框不受影响 |
|
|
791
|
+
| G09 | 固定卡片内容不可滚、悬浮横条能显示却拖不动 | 检查内容区 min-height/overflow、真实滚动宿主及异步挂载后监听;显示、隐藏、重载与卸载不能重复绑定或错过首次绑定 | 最后一条内容可达;拖悬浮条能带动实际表体,表体滚动也同步滑块;标题区保持设计位置,离开后释放监听 |
|
|
792
|
+
| G10 | 角色切换仍进入旧菜单、刷新侧栏消失、跨应用“设置”打不开 | 分清角色入口与菜单直达地址,核对持久化顺序、旧 menuKey 清理、导航来源恢复及程序化跨应用跳转;透传实际所选记录参数,不以绕过权限补路由 | 换角色后菜单与角色一致,同角色选择不无故刷新;刷新、返回、跨应用保持正确记录;应用/角色/用户不串上下文 |
|
|
793
|
+
| G11 | 产品自定义登录后请求仍用旧账号,退出后凭证残留 | 使用平台目标版本 Token/存储接口,检查产品自己的 Storage/cookie 调用;区分部署路径、客户、local/session 及续期,禁止复制其它部署的历史凭证作为修复 | 请求使用当前凭证;同部署共享、不同部署隔离,刷新/续期/切账号/退出一致。只在实际执行的浏览器上记通过 |
|
|
794
|
+
| G12 | 移动端子集新增露出主记录字段,配置中心新增误只读 | 核对父记录绑定、隐藏字段、数量限制、目标页与刷新回调;使用平台统一系统/客户上下文,不假设 `$query.SYSTEM` 总存在,不将权限问题用可编辑样式掩盖 | 从真实菜单进入的新增/编辑参数正确;移动和桌面归属一致,角色/系统维护范围正确。只读权限未放宽,取消不写数据 |
|
|
795
|
+
| G13 | 统计合并表头盖住数值列、暗色报表纸张不可读 | 核对合并起点与固定列偏移;检查纸张区域和外围工具栏的不同语义,保留模板显式颜色,不给打印纸张盲目套暗色 | 合并标题与表体对应列边缘对齐,滚动/刷新后仍正确;默认纸张白底黑字及模板色保留,外围主题正常 |
|
|
796
|
+
| G14 | 设计器宫格预览成缩略图、背景保存后不回填、链接类型无默认选中 | 核对主组件/visual/容器预览分发、递归子项、默认配置与旧值兼容;检查读取→草稿→保存→重进链路及 0/false/空值,不能只验证运行态 | 实际设计器中容器、样式面板和运行态一致;显式配置优先,已有菜单/页面目标不变,取消不会改远端配置 |
|
|
797
|
+
| G15 | 单元格编辑/字段选择卡顿,拖拽报旧库错误或不能回写排序 | 按 F6/F14 查旧拖拽依赖与插槽;检查大列表是否全量挂载,优先已有虚拟列表;拖动时避免文本误选,浮层/拖影不被裁切 | 记录选项数/挂载节点数及可复现耗时;搜索、手输、清空、拖拽排序及回写正确。性能改善不能代替原有编辑语义验收 |
|
|
798
|
+
| G16 | 同步、查询报空指针,或字段权限误报登录过期 | 先取失败请求/响应/上下文,再沿实际 Action/BO 与元数据、关联路径、字段映射定位;有方案 ID 不代表内部关联仍有效,禁止仅加空判断、吞错或反复刷新 Token | 区分配置失效、后端缺陷、权限校验与前端漏参,保留明确证据。未复现原堆栈不冒称完整根因;未获授权不执行覆盖同步或修改权限 |
|
|
799
|
+
|
|
800
|
+
### 执行与证据边界
|
|
801
|
+
|
|
802
|
+
- **先核实目标制品**:记录平台版本、产品 JS/CSS/清单及页面/角色,确认来自同一批构建;平台修复与产品源码迁移分别列状态。先前某个会话提交或单测通过,不说明目标环境已加载修复。
|
|
803
|
+
- **区分产品、宿主与配置**:平台公共修复优先通过目标平台版本验证,升级产品不复制一份平台内部实现;可确认的产品源码问题按当前任务修复。涉及后端或跨记录配置调整时按任务授权边界处理,不能因为前端升级默认改业务口径或扩权。
|
|
804
|
+
- **复用现有测试**:先运行目标项目可用的相关回归,再在真实调用链验证。平台仓用例可用于定位模式;产品没有相同文件时不虚构命令,也不要求为每个低影响样式修改新增镜像单测。
|
|
805
|
+
- **安全采证**:打开、查询、取消与抓取请求可先完成;实际保存/导入/审核/同步等写操作需处于授权的测试范围。拦截请求只能证明待发送参数,临时注入 CSS/修正实例只能证明诊断假设,不能记作保存成功或部署生效。
|
|
806
|
+
- **状态真实**:将已验证、已改待发布、已发布待验证、失败/阻塞分别记录。未关闭的适用场景进入升级报告的未完成清单,不用“均无异常”覆盖缺环境、被权限阻断或只做静态检查的项。
|
|
807
|
+
|
|
808
|
+
已有专项修法优先使用 A–F 与 E2;G 节不要求看到历史模式就修改。静态命中需结合当前消费契约及运行结果,保留仍正确的产品实现,不盲目套用旧事故中的选择器、像素值、组件名或业务字段。
|