@epxing/cli 0.5.0-beta.5 → 0.5.0-beta.6
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.
|
@@ -1,506 +1,624 @@
|
|
|
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
|
-
|
|
|
264
|
-
|
|
|
265
|
-
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
Vue3 移除 `.sync`。gogocode 漏,现由 `fix-inline-handlers.mjs` 的 `fixSyncModifier` 在 migrate ③ 自动转,不用手改。
|
|
359
|
+
|
|
360
|
+
### F4. epxing 字段名里的 `|` 把 gogocode filters 规则搞崩(良性,忽略)
|
|
361
|
+
**现象**:③ 转换刷 `文件转换异常,规则:filters … expressionStatementAst.attr is not a function`。
|
|
362
|
+
**根因**:epxing 字段名带竖线(`info['SCHEME|NOTICE_FILE#']`),gogocode 过滤器规则把它当 Vue2 过滤器管道解析 AST,崩。
|
|
363
|
+
**良性**:不是真过滤器,文件其它转换正常、内容没坏。dry-run 报的「过滤器 N 文件」也因此**虚高**(把字段名 `|` 误算成过滤器)。
|
|
364
|
+
**判据**:`grep -rnE "\{\{[^}]*\|\s*[a-z_]+\s*\}\}" src` 把 `|大写字段名` 排掉后基本为 0 → 确认无真过滤器,忽略刷屏即可。
|
|
365
|
+
|
|
366
|
+
### F5.(已 CLI)内联事件多语句缺分号
|
|
367
|
+
Vue2 容忍换行分隔、Vue3 编译器报 `Unexpected token, expected ","`。现由 `fixInlineHandlers` 在 migrate ③ 自动补分号。
|
|
368
|
+
|
|
369
|
+
### F6. 换掉没有 Vue3 版的第三方库(⑥ 会告警,换库是人工决策)
|
|
370
|
+
|
|
371
|
+
| 库 | Vue3 替代 | 改动 |
|
|
372
|
+
|---|---|---|
|
|
373
|
+
| `vue-quill-editor@3` | `@vueup/vue-quill` | 换 import + 组件用法(v-model/options 略变) |
|
|
374
|
+
| `vuedraggable@2` | `vuedraggable@4`(同名升 major) | 用法基本兼容,重装升版本 |
|
|
375
|
+
| `vue-orgchart-direction` | 无——改 `bpmn-js` 直用 / SVG 自实现 | 组织架构图重写(hr T2 同款) |
|
|
376
|
+
| `element-ui` | 平台运行时已提供 element-plus | **直接删 import**(el-* 全局可用),不装 element-plus |
|
|
377
|
+
| `vant@2` | `vant@4`(移动端) | 用法有差异,逐个核 |
|
|
378
|
+
|
|
379
|
+
**先查有几个文件真 import 再决定工作量**:`grep -rl <lib> src --include=*.vue --include=*.js`。
|
|
380
|
+
recruit 实测:element-ui 1 / vue-quill-editor 1 / vuedraggable 2 / vant 2 —— 量小,逐个换即可。
|
|
381
|
+
(依赖在 package.json 但源码 0 引用的是**遗留依赖**,如 recruit 的 bpmn-js/vue-bpmn,直接从 deps 删。)
|
|
382
|
+
|
|
383
|
+
### F7.(已 CLI)`node-sass` / `fibers` 残留
|
|
384
|
+
老 scss 工具链(node-gyp 原生编译),新 node 上 `npm i` 必挂、还骗过 exit code(管道吞 code)。
|
|
385
|
+
已加进 to-vite 的 `WEBPACK_ONLY_DEPS` 自动删,Vite 用 dart-sass(`sass`)。**判 npm i 真成败别信 exit code**——
|
|
386
|
+
`grep -iE "gyp ERR|Build failed" 安装日志`。
|
|
387
|
+
|
|
388
|
+
### F8. 垃圾副本文件(`Xxx copy.vue`)在按需形态撞名
|
|
389
|
+
**现象**:build 报 `[lazy-lib] 组件注册名冲突:epx-xxx 同时来自 A copy.vue / A.vue`。
|
|
390
|
+
单体形态静默覆盖(线上不暴露)、按需形态 `assertUniqueKeys` 拦下(替项目揪出垃圾副本)。
|
|
391
|
+
**判据**:`find src -iname "*copy*"`。**修法**:删副本(或改其 name)。
|
|
392
|
+
|
|
393
|
+
### F9.(已 CLI)无扩展名 import .vue
|
|
394
|
+
老项目 webpack `resolve.extensions` 含 `.vue`,`import X from './X'` 遍地;Vite 默认不含。
|
|
395
|
+
create 模板的 vite.config 已带 `resolve.extensions: ['.mjs','.js','.json','.vue']`,`--to-vite` 生成的配置自带,不用手加。
|
|
396
|
+
|
|
397
|
+
### F10.(已 CLI)`import * as Vue/Vuex/VueRouter` 命名空间导入 → 具名
|
|
398
|
+
gogocode 把 Vue2 默认导入转 Vue3 时,对 vue/vuex/vue-router 常产**命名空间导入**:
|
|
399
|
+
`import * as Vue from 'vue'` + `Vue.h(...)`、`import * as Vuex from 'vuex'` + `Vuex.createStore(...)`、
|
|
400
|
+
`import * as VueRouter from 'vue-router'` + `VueRouter.createRouter/createWebHashHistory(...)`(router/store 是 .js,gogocode `-s ./src` 也转了它们)。
|
|
401
|
+
`lazyLib` 为 external `'vue'` 生成 shim 时**不支持 `import * as ns`**(见 B2,recruit 实测 build 挂:`无法为 external 'vue' 生成 shim`)。
|
|
402
|
+
CLI ③ 修补⑥(`fix-namespace-import.mjs`)收敛为具名:收集 `NS.member` 用到的成员改 `import { m1, m2 } from 'lib'`,
|
|
403
|
+
`Vue.h`→`h` 等;整文件没用到就删这行。只认这三个框架库(白名单),业务库的命名空间导入不碰。
|
|
404
|
+
|
|
405
|
+
### F11.(已 CLI)`require('@/assets/*.png')` → 静态 import + `assetsInlineLimit`
|
|
406
|
+
webpack 支持 `require('图片')` 当 URL 用(三元/字符串拼接里),Vite 不支持 → 运行时 `require is not defined`。
|
|
407
|
+
CLI V 阶段(`fix-vite-source.mjs`)提为顶部静态 import(别名 `@` 支持)、`require(...)` 换成变量。
|
|
408
|
+
**只在 `<script>`/JS 里改**:模板表达式里的 `require` 换成模块顶层变量后 Options API 的 this 取不到,那种报人工(手工挂 data/computed,见 F11 配套的 recruit Center.vue/Guide.vue 改法)。
|
|
409
|
+
配套:create 模板的 `build.assetsInlineLimit` 设 `50*1024`,让这些小图标内联成 data URI——否则超 4096 的(recruit 的 `fileHover.png` 4115B,只超一点点)会出独立文件、远程库形态引用 `/assets/xxx` 绝对路径运行时 404(构建期看不出;C4 管 lazy 嵌套构建继承这个值,前提是主 build 设了)。
|
|
410
|
+
|
|
411
|
+
### F12.(已 CLI)`require.context` 非入口(store 收集 vuex modules)→ `import.meta.glob`
|
|
412
|
+
入口的 `require.context('@/X', true, ...)` `--to-vite` 一直会转;**漏网的是 store/index.js** 的
|
|
413
|
+
`require.context('./modules', false, ...)`(收集 vuex modules)。CLI V 阶段现遍历全 src 通用改写:
|
|
414
|
+
recursive 决定 `**/` 还是单层、正则决定扩展名(`.vue`/`.js`)、dir 前缀(`@/` 或 `./`)原样保留;
|
|
415
|
+
配套 `VAR.keys()`→`Object.keys(VAR)`、`VAR(k)`→`VAR[k]`(变量名不一定叫 files,store 里叫 context,故按声明抓变量名)。
|
|
416
|
+
|
|
417
|
+
### F13. `::v-deep` 弃用警告(**不进 CLI;容器块形态手工 / postcss**)
|
|
418
|
+
Vue3 弃用 `::v-deep` 作 combinator,建议 `:deep(<inner>)`。recruit 实测 **141 处是容器块 `::v-deep{ ... }`**(combinator 仅 10、`>>>` 仅 5)。
|
|
419
|
+
容器块转 `:deep()` 要拆 SCSS 嵌套块(花括号配对、多层嵌套),141 处正则拆分**极易改坏 style,风险远大于收益**——
|
|
420
|
+
而它只是**弃用警告:build 通过、运行正常**。故**不进 CLI**(同 gogocode 的 style 快照保护:不用正则动 style 块)。
|
|
421
|
+
真要消警告:写 postcss 插件按 AST 转、或按文件手工,别正则批量。
|
|
422
|
+
|
|
423
|
+
### F14.(部分已 CLI)`<script>` 里的 Vue2 JSX 残留 → Vue3(gogocode 只转 template,不碰 JSX)
|
|
424
|
+
gogocode 只转 `<template>`,**不碰 `<script>` 里的 JSX / 手写 render**——Vue2 JSX 前缀原样留到 Vue3,
|
|
425
|
+
`@vue/babel-plugin-jsx` 不识别、当普通属性丢弃 → **build 过、console 不报错、运行时静默失效**(最隐蔽的一类)。
|
|
426
|
+
recruit 实测(145 组件):表单 note 不显示、折叠操作点击无效、子表格自定义列/操作列不渲染。
|
|
427
|
+
③ 本地简历审核详情「流程图」tab 空白也是同款——平台 `ProcessChart.vue` 用 `domPropsInnerHTML` 贴 saveSVG 的图,
|
|
428
|
+
实证显示容器 `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`。
|
|
429
|
+
- **已 CLI**(③ 修补⑦ `fix-jsx-vue2.mjs`,只换 JSX 属性名、不动值/结构,安全):
|
|
430
|
+
`domPropsInnerHTML=`→`innerHTML=`、`domPropsValue=`→`value=`、`nativeOn<E>=`→`on<E>=`(nativeOnClick→onClick,Vue3 组件事件默认透传根元素)、`scopedSlots={{`→`v-slots={{`。
|
|
431
|
+
- **人工**(结构性重排,CLI 不碰):手写 render 的 data 对象 `return { scopedSlots:{...} }` / `h(tag,{on,props,attrs})`——
|
|
432
|
+
Vue2 createElement data 到 Vue3 差异大,正则不保险。recruit 实测 InnerEntitySubList 3 处(方法返回 `{scopedSlots:{default}}` +
|
|
433
|
+
调用点 `<el-table-column {...this.renderX()}>` spread),最小改法:调用点 `{...this.renderX()}` → `v-slots={this.renderX().scopedSlots}`(方法内数据属性名不动)。改后实测子表格字段/操作列/彩色标签正常渲染。
|
|
434
|
+
- 排查:`grep -rE "domProps|nativeOn[A-Z]|scopedSlots" src`;`scopedSlots=`(等号)=JSX 属性可自动,`scopedSlots:`(冒号)=render 对象需人工。
|
|
435
|
+
- **第 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`)。
|
|
436
|
+
|
|
437
|
+
### F15. JSX/render 与 scoped 样式:先验证匹配,再做最小修复并验收隔离
|
|
438
|
+
|
|
439
|
+
**默认保留 scoped**。JSX/render、无 `<template>` 或 `extends` 只能作为排查线索,不能推断“深层元素一定没有 scopeId”或“scoped 整体失效”。实际结果与项目的编译插件、组件继承、插槽和渲染路径有关;当前项目中能正确携带作用域并匹配规则的组件保持原样。不得依据历史案例或静态告警批量删除 scoped,也不得把所有选择器机械改为 `:deep()`。
|
|
440
|
+
|
|
441
|
+
**先证明具体问题**:使用最终构建产物(按需发布时使用最终 lazy/chunks 产物),确认实际加载的是本次 JS 与 CSS。对一个确实显示异常的元素,记录:
|
|
442
|
+
|
|
443
|
+
- 所属组件、页面、预期样式与实际 `getComputedStyle`/尺寸;
|
|
444
|
+
- 产物中的具体 CSS 选择器、样式是否已加载,以及目标/祖先 DOM 上实际的 class、`data-v-*`;用开发者工具匹配规则或 `element.matches(实际选择器)` 确认是否命中;
|
|
445
|
+
- 失效究竟来自 CSS 未加载、优先级覆盖、根命名空间缺失、作用域不匹配,还是其他布局原因。作用域存在且规则命中时,不按“去 scoped”处理。
|
|
446
|
+
|
|
447
|
+
#### 继承组件经过插槽渲染后,局部样式可能失效
|
|
448
|
+
|
|
449
|
+
不要仅凭 JSX、`extends` 或无 template 判定 scoped 失效,也不要仅凭隔离测试正常就排除问题。必须检查实际平台页面的完整渲染链:继承组件的内容可能经过平台包装组件、Element Plus 等组件的插槽。实际 DOM 中,组件根节点带有 `data-v-*`,插槽内的图片、标题等后代却可能没有对应属性,导致 `.projectTop[data-v-xxx]` 等产物规则无法匹配。根节点有作用域属性不等于所有后代都有;问题应定位到具体元素和选择器,不能扩大成整个组件的 scoped 都无效。
|
|
450
|
+
|
|
451
|
+
**排查步骤**:
|
|
452
|
+
|
|
453
|
+
1. 确认 CSS 已加载,且与 JS/chunks 来自同一次构建;先排除旧缓存或不同批产物造成的 scopeId 失配。
|
|
454
|
+
2. 检查失效元素及祖先的 class、`data-v-*`,核对最终产物选择器是否实际匹配;不要只检查组件根节点。
|
|
455
|
+
3. 使用 `getComputedStyle()` 和实际尺寸检查图片宽高、`object-fit`、字号、内边距等,同时追查继承值与规则来源。例如卡片标题规则失配后,可能继承复选框组的 `font-size: 0`,文本仍在 DOM 中却不可见。
|
|
456
|
+
4. 在实际页面验证“继承组件 → 平台包装组件 → UI组件插槽 → 目标DOM”的完整链路。简化基类、直接挂载子组件或替换 UI 组件的隔离测试可辅助定位,但可能改变作用域传递路径,不能替代实际页面验收。
|
|
457
|
+
|
|
458
|
+
**推荐修法**:确认作用域失配后,保留 scoped,在带有对应作用域属性的专属容器内,对必要的后代使用 `:deep()`。例如:
|
|
459
|
+
|
|
460
|
+
```vue
|
|
461
|
+
<template>
|
|
462
|
+
<div class="epx-training-project-list">
|
|
463
|
+
<!-- 包装组件及插槽内容 -->
|
|
464
|
+
</div>
|
|
465
|
+
</template>
|
|
466
|
+
|
|
467
|
+
<style scoped>
|
|
468
|
+
.epx-training-project-list :deep(.projectTop) {
|
|
469
|
+
width: 100%;
|
|
470
|
+
height: 160px;
|
|
471
|
+
object-fit: cover;
|
|
472
|
+
}
|
|
473
|
+
</style>
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
示例尺寸按目标页面设计调整;标题等其他失效规则也逐条处理,不把整块样式机械改为 deep。检查最终 CSS 仍类似 `.epx-training-project-list[data-v-xxx] .projectTop`,并确认专属容器在实际渲染分支中存在且带有匹配的作用域属性,图片确实是其后代。若内容经 Teleport 等挂到容器之外,应单独定位其专属挂载范围,不能为命中目标而取消边界。
|
|
477
|
+
|
|
478
|
+
**发布验收**:用最终完整包或 lazy/chunks 产物在实际页面确认插槽内图片、标题等样式恢复,同时组件外部同名元素不受影响;再检查离开页面后的其他页面。不要直接删除 scoped,让 `.projectTop`、`.avatar`、`.container` 等通用类变成全局规则。未能复现完整页面链路时记录为待验证,不以隔离测试通过宣布修复完成。
|
|
479
|
+
|
|
480
|
+
**修复顺序**:先修正加载、渲染根、选择器或局部布局,尽量保留 scoped。确认为 scoped 无法匹配继承/子组件的目标 DOM 时,才针对具体规则评估 `:deep()`,并确认其锚定在本组件有效的根范围内;`:deep()` 本身不保证隔离,仍须验收。历史 recruit `CardFormItem` 头像缺少对应 `data-v-*`、`MainEntityList` 深层规则失配等案例,只证明当时那些元素的具体问题,不能推广到其他组件。
|
|
481
|
+
|
|
482
|
+
**去掉 scoped 是备选方案,隔离与修改必须同时完成**:
|
|
483
|
+
|
|
484
|
+
1. 为组件定义唯一命名空间(如 `.epx-product-resume-card`),确认其覆盖全部实际渲染入口:自有 render、继承根、多分支、动态页,以及弹窗/Teleport 等独立挂载的内容。不能假定给某一个根加 class 就覆盖全部 DOM,另见 F19。
|
|
485
|
+
2. 将该 style 块内所有规则限制在命名空间内,包括逗号分隔的每个选择器、SCSS 嵌套展开、媒体查询、深层选择器和 mixin 生成的规则。明确本就全局的主题规则另行审查。核对最终编译 CSS,不能只看源码外层有一层嵌套;不得留下裸 `.avatar`、`.container`、`img` 或平台通用类选择器。
|
|
486
|
+
3. 所需规则若作用于根本身,使用 `.epx-product-resume-card.container` 等同元素选择器;`.epx-product-resume-card .container` 只匹配后代。移除 scoped 后,对原 `:deep()` 等语法按最终 CSS 和目标 DOM 调整,不能留下无效或无边界的穿透规则。
|
|
487
|
+
4. BEM 名称也不能免除检查:只有最终 CSS 的每个选择器都实际限定在组件唯一前缀内、且组件外回归通过时,才可沿用现有 BEM 隔离;不要仅凭“使用了 BEM/mixin”就判安全。
|
|
488
|
+
5. 根范围和完整样式隔离尚未完成时,保留或恢复 scoped,将该项记为未解决;不要留下“先去 scoped,稍后补隔离”的提交或发布产物。逐个组件处理,避免整批转换扩大影响。
|
|
489
|
+
|
|
490
|
+
**验收必须同时证明“组件内生效”和“组件外不受影响”**:
|
|
491
|
+
|
|
492
|
+
- 记录组件内的关键宽高、布局、图片与弹窗样式,并核对所有渲染分支及产品支持的窄视口;仅改 `innerWidth` 不能代替真实移动端视口验收。
|
|
493
|
+
- 在组件范围外放置或选取真实的 `.avatar`、`.container` 等同名对照元素,记录组件加载前、懒加载 JS/CSS 后的计算样式和尺寸,确认未被本组件规则改变。若 CSS 在入口即加载,应与隔离变更前的产物基线对照;不能只比较样式早已污染后的两次快照。
|
|
494
|
+
- 从目标页面离开再进入其他页面,复核外部同名元素、平台布局和弹窗。懒加载 CSS 可能在离开页面后继续存在,关闭组件并不等于清除影响。
|
|
495
|
+
- 在迁移报告中逐个记录改动组件、失配证据、隔离方式、产物版本及上述检查结果。未具备环境或未执行的检查写为待验证;构建、lint 或静态扫描通过不代表隔离验收通过。
|
|
496
|
+
|
|
497
|
+
**静态扫描的边界**:迁移器的“无 template + scoped”列表和 `detect-vue3-risks.mjs` R1 都只提示人工复核,不证明运行时失效。扫描器不覆盖所有全局泄漏,`R1` 未命中(包括改用 `:deep()` 后)也不表示安全;不得为了让扫描结果变绿而破坏隔离。
|
|
498
|
+
|
|
499
|
+
### F16. 数组 `splice` 同引用 + `deep:1` watcher 不触发(弹窗标题恒空,postAppoint 平台侧实证)
|
|
500
|
+
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 过、无报错)。
|
|
501
|
+
- 修法: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。
|
|
502
|
+
- 通用:Vue3 靠 push/splice 增删的数组,watcher 要么 `deep`、要么 splice 换新对象引用;**改元素内部属性不触发浅层 watcher**(同类历史 commit `df29393`/`2161c04` 数组 watcher 补 deep)。
|
|
503
|
+
|
|
504
|
+
### F17. CSS code-split 下组件借用别组件的全局样式(图表容器塌陷,postAppoint 平台侧实证)
|
|
505
|
+
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` 数据正常)。
|
|
506
|
+
- 修法:让渲染组件**自带**渲染必需的样式(Common.vue 补 scoped `<style>` 定义 `.epx-chart` 根 flex+min-height / `__main` flex / `__echarts` 铺满,随本组件 chunk 加载、有 template 故 scoped 生效、零污染,commit `0061cb7c63`);别关 `cssCodeSplit`(牺牲首屏)。
|
|
507
|
+
- 排查判据:`getComputedStyle(容器).height==='0px'` + `performance` 里该组件 CSS chunk **不在**已加载列表(`document.styleSheets` 里也搜不到那几条规则)。
|
|
508
|
+
|
|
509
|
+
### F18. `$emit` 不读实例 `__epxEvents`(程序化事件断链、内容区空白,postAppoint 平台侧实证)
|
|
510
|
+
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`(机构管理右侧空白)。
|
|
511
|
+
- 修法:发射走「`$epxEmit` + `$emit`」**双发射**闸口(保留 `$emit` 以防模板上有 `@xxx`)。commit `543f85daf3`。
|
|
512
|
+
- 排查:全仓 grep `\.\$emit\('程序化事件名'`(createPage / backModule / push 等),逐个确认消费方是 `$epxOn` 注册还是模板绑定;全仓「没有任何模板 `@createPage`」即强信号。
|
|
513
|
+
|
|
514
|
+
### F19. 非 scoped 样式改为根 class 命名空间后,其他渲染入口漏加 class(服务大厅登录页实证)
|
|
515
|
+
|
|
516
|
+
仅在 F15 已证明需要调整隔离且选择根命名空间方案时,把 `<style scoped>` 改成 `<style>`,并将规则包进 `.epx-xxx { ... }`;所有实际渲染入口都必须在该命名空间内。检查 `extends` 子组件覆盖的 `render()`、父组件的自定义页面分支、动态 `<component>` 和多分支根节点;只给默认分支加 class 不够。此问题与 `data-v` hash 失配不同:CSS 已加载、DOM 也有原来的内部类,但根节点缺少 `.epx-xxx`,整组选择器匹配不到,构建和控制台通常正常。
|
|
517
|
+
|
|
518
|
+
- **实证**:`Login.vue` 样式改为 `.epx-login` 包裹后,默认登录分支有该类;`ClientLogin.vue` 覆盖的根节点及配置 `LOGIN_PAGE` 走的 `renderPage()` 没有。`#/hall/login/frame` 渲染出 `.PCcontainer` 和 `.loginCon`,却没有 `.epx-login` 祖先,登录页失去布局。给 `ClientLogin` 根和 `renderPage()` 的页面容器补 class 后,浏览器实测 `.loginCon` 恢复绝对定位、宽 357px,`.PCcontainer` 铺满 1440×900 视口。
|
|
519
|
+
- **升级检查**:凡将 scoped 样式改为根 class 命名空间,逐个打开使用该组件的实际页面/分支,检查 `el.closest('.epx-xxx')` 是否存在,并用 `getComputedStyle(el)` 验证宽高、定位等关键规则。若 CSS 已加载但命名空间不存在,在相应渲染根或共同祖先补 class;不要靠失效的 `__scopeId` 转写补丁。静态扫描只能提示风险,页面验收仍需运行时确认。
|
|
520
|
+
|
|
521
|
+
### F20. Element Plus 选择框占满工具栏,升级后按钮换行(HR 组织架构图)
|
|
522
|
+
|
|
523
|
+
**现象与判断**:旧产品在 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 属性。
|
|
524
|
+
|
|
525
|
+
**修法与边界**:在产品拥有的工具栏内设置选择框宽度、按钮间距及对齐方式,加入迁移人工检查项;不要给全局 `.el-select` 统一定宽,也不要为了布局让 Vue3 产品重新走 Vue2 垫片。全局改宽度会影响表单、查询面板和弹窗中需要 100% 宽度的选择框。无法仅凭标签自动判断工具栏还是表单,因此不对所有 `el-select` 做机械替换。平台自有工具栏出现同类问题时,在对应平台组件修复。
|
|
526
|
+
|
|
527
|
+
例如 HR 组织图右侧容器增加 `class="org-chart-actions"`,层级选择框增加 `class="org-chart-level"` 并删除无效的 `marginright`;在现有 scoped 样式中加入(宽度以原页面设计为准,202px 是旧垫片参考值):
|
|
528
|
+
|
|
529
|
+
```scss
|
|
530
|
+
.org-header > .org-chart-actions {
|
|
531
|
+
display: flex;
|
|
532
|
+
align-items: center;
|
|
533
|
+
gap: 20px;
|
|
534
|
+
}
|
|
535
|
+
.org-chart-actions > .org-chart-level {
|
|
536
|
+
width: 202px;
|
|
537
|
+
flex: 0 0 202px;
|
|
538
|
+
}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
不要仅把父容器改成 `display:flex` 而让选择框继续以 100% 宽度参与收缩;也不要只修间距拼写。先确认实际根 class、CSS 已加载且选择器命中;无模板的 JSX/render scoped 问题另按 F15 处理。
|
|
542
|
+
|
|
543
|
+
**验收**:用实际 Vue3 产品产物加载页面,确认宽度约等于设定值,选择框和按钮纵向中心对齐;切换层级、打开/关闭导出弹窗,检查对话框中格式选择框仍按其自身布局显示。分别检查常用桌面宽度和产品支持的最窄视口,窄屏按产品设计允许整组换行或收缩,避免按钮挤出容器。记录实际页面截图与尺寸,构建成功不能代替布局验收。
|
|
544
|
+
|
|
545
|
+
### F21. Element Plus 日期格式 token 迁移遗漏(HR 历史组织机构图)
|
|
546
|
+
|
|
547
|
+
**已定位实例**:HR `OrgHistoryChart.vue` 日期控件仍写 `value-format="yyyy-MM-dd"`。本地已复现:绑定日期为 `2026-10-20`,输入框却显示 `2026-02-01`,下方数据时点仍正确;不是仅凭截图就能判定历史查询接口出错。产品已把该处改为 `YYYY-MM-DD` 并通过 lint,须重新构建、部署后验证浏览器。此前报告中的日期绑定/历史查询“待核”应按此根因更新,不把源码修复当成部署已生效。
|
|
548
|
+
|
|
549
|
+
**根因与边界**: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)。
|
|
550
|
+
|
|
551
|
+
**技能执行要求:发现同类问题就修,不只加说明**。
|
|
552
|
+
|
|
553
|
+
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。
|
|
554
|
+
2. `migrate.mjs` 在常规 `--apply` 中自动改可静态确定的格式;不需要额外开启 gogocode。单独补修:`node tools/epx-migrate/fix-date-formats.mjs <目标工程> --apply`。只读预览去掉 `--apply`;修复幂等,保留 scoped、注释和其它代码。`detect-vue3-risks.mjs` 的 R2 在发布前检查残留。
|
|
555
|
+
3. 动态变量、`v-bind`、JSX展开、函数返回或共享配置需追踪来源:确认该值给 Element Plus 控件消费后,自动修复相应配置或在控件侧生成正确格式。若同一常量还服务后端/其它日期库,分别处理消费场景,不能直接改共享常量。工具不能安全确定的位置须由执行技能的代理继续分析并修复;只有信息不足才列未解决项。不要扫描或回写 `node_modules`、`lib`、`dist` 构建产物来代替源代码修复。
|
|
556
|
+
4. 原生 `runtime: vue3` 包不再通过旧 Vue2 render 垫片转换格式。不要去掉 runtime 标识、回挂旧垫片或给所有日期控件打全局运行时补丁;修复源组件并重新构建发布。
|
|
557
|
+
|
|
558
|
+
**验收**:至少检查 `2026-10-20` 初始回显不漂移;选择另一日期后输入框、绑定值、历史提示/数据时点及接口日期参数一致;月份/年份控件保留粒度,时间保持分钟与秒。检查实际完整包或懒加载产物与CSS同批部署。记录修复文件/行号、转换前后格式、动态来源处理结果和部署后截图。lint/build通过不等于日期逻辑已验收;没有部署权限时明确写“源码已修,待重新构建部署复测”。
|
|
559
|
+
|
|
560
|
+
### F22. 旧卡片从配置中的 propsData 读取组件标题(薪酬月度卡片)
|
|
561
|
+
|
|
562
|
+
**现象与判据**:Vue3 平台加载旧薪酬库时,`epx-wage-month-card` 在显示标题的分支抛出 `Cannot read properties of undefined (reading 'compTitle')`。已用已加载库的渲染函数与属性访问探针复现,读取链为 `$configs.propsData.compTitle`;不能仅凭属性名判断标题 prop 本身未传入。
|
|
563
|
+
|
|
564
|
+
**根因**:旧组件依赖配置对象中附带的 propsData 读取实例属性,这不是新版 `$configs` 的正式接口;组件标题应直接读 `compTitle`。平台 Vue2 垫片只为旧编译组件补充当前实例的 propsData 读取兼容,不污染公共 component mixin;兼容值不可枚举,不参与配置复制与持久化,显式配置仍优先。
|
|
565
|
+
|
|
566
|
+
**执行升级技能时必须处理源码,不只记录待办**:
|
|
567
|
+
|
|
568
|
+
1. 在产品 `src` 的 Vue template/script、JS/TS、JSX/TSX 中检索 `$configs.propsData` 与 `$options.propsData`,沿别名、解构和动态访问确认实际消费字段。排除 `node_modules`、`lib`、`dist` 等产物;不要把 `new Vue({ propsData })` 等其它用途作同样替换。
|
|
569
|
+
2. 确认字段来自本组件已声明的 prop 后,脚本或 render 中将 `this.$configs.propsData.compTitle` 改为 `this.compTitle`,模板中改为 `compTitle`;其它字段按实际 prop 名逐项适配。若 propsData 是明确配置的业务对象或读取其它组件的属性,保留业务语义并追踪来源,不能全仓机械删除 propsData 层。
|
|
570
|
+
3. CLI 当前没有该专用转换规则,由执行技能的 AI 继续定位和修复;迁移报告为空也不代表无此问题。原生 Vue3 产品应修源代码,不回挂旧垫片、删除 runtime 标识或在公共 `$configs` 中增加旧接口。
|
|
571
|
+
|
|
572
|
+
**验收**:验证显示标题与隐藏标题两条分支、多个卡片标题互不串用、父级标题更新,以及配置整体替换后的显示;保留明确配置的业务对象。使用最终产品产物复验原页面无该异常,区分源码修复、已发布与页面验证状态。旧库尚不能重建时,只使用平台 Vue2 垫片过渡,并明确该兼容不代表产品全部业务已完成迁移。
|
|
573
|
+
|
|
574
|
+
### F23. 列表已有字段名称却显示 ID/代码(旧单写插槽返回协议)
|
|
575
|
+
|
|
576
|
+
**已定位实例**:薪酬 `epx-wage-move-list` 继承平台 EntityList 并重写 renderField。接口和组件行数据均有 `GROUP#`、`CHANGE_TYPE#`、`TYPE#` 名称,但对应 ElTableColumn 的插槽只有 scopedSlots,没有 default,最终显示原始 GROUP、CHANGE_TYPE、TYPE。页面临时归一化返回结构后,这三列立即显示名称;不是后端未翻译或元数据未配置。
|
|
577
|
+
|
|
578
|
+
**根因**:旧方法返回 `{ scopedSlots: { default: ({ row }) => ... } }`,Vue3 平台继承的 renderTable 通过 `v-slots={this.renderField(...)}` 消费,需要 `{ default: ({ row }) => ... }`。旧作用域插槽被当成名为 scopedSlots 的插槽,默认渲染回退到原始字段;构建和控制台可能完全正常。renderColumnOperates 的旧返回结构也会使操作按钮不显示。
|
|
579
|
+
|
|
580
|
+
**执行升级技能时必须关闭此项**:
|
|
581
|
+
|
|
582
|
+
1. 扫描产品 src 中的 renderField、renderColumnOperates 及其引用的辅助函数、父类回退分支,检查返回值是否仍嵌套 scopedSlots;覆盖 Vue script、JS/TS、JSX/TSX。CLI 的 JSX `scopedSlots=` 属性转换不处理这种 `scopedSlots:` 对象返回,不能据 CLI 完成就跳过。
|
|
583
|
+
2. 确认调用方消费的是 Vue3 插槽对象后,去掉返回值外层 scopedSlots,保留内部 default/header 等插槽名、参数、字段名称优先级、标签样式和事件。例如脚本中返回 `{ default: ({ row }) => h('span', row[field.attriName + '#'] || row[field.attriName]) }`;父类回退直接保留新版父类的返回对象。不要把所有 vnode data 中的 scopedSlots 一律扁平化,也不要全仓替换 `scopedSlots:`。
|
|
584
|
+
3. 核对同一条记录的请求列、接口响应、组件行数据与可见单元格:列表请求应包含相应 `字段#`,名称存在时实际显示名称,原值仍用于提交与操作。未返回名称的情况另查取数或标签解析,不用改字段类型或硬编码名称掩盖。
|
|
585
|
+
4. 用最终 Vue3 产品产物验证对象、代码项及关联字段列的显示、标签样式、名称更新、点击与操作列。关键列的 ElTableColumn 应有 default 插槽;只剩 scopedSlots 表示此项未完成。已有旧垫片能显示名称不能作为升级完成依据,须确认产品返回值已是 Vue3 结构且不依赖旧方法归一化;原生 Vue3 独立挂载回归也应通过。
|
|
586
|
+
|
|
587
|
+
**兼容边界**:暂时不能重建的旧库由平台 Vue2 垫片在实例上包装 renderField、renderColumnOperates,提供 Vue3 可枚举插槽,并保留不可枚举的旧 scopedSlots 读取入口;不修改平台原生列表或共享方法定义。升级产品应修改源码,保留 runtime 标识,不以回挂垫片替代迁移。存在未修复分支或缺少目标环境时明确记录待验证,不能声称升级后已消除问题。
|
|
588
|
+
|
|
589
|
+
## G. 产品升级的跨会话缺陷回归
|
|
590
|
+
|
|
591
|
+
本节从近期产品故障的提交及交接记录提炼检查场景,补充 E2 的逐组件主题验收。历史记录证明曾出现过这些问题,不证明当前目标环境已发布修复;其中只有临时浏览器实验、单测或尚待现场验证的结论,不能提升为已部署通过。具体来源及证据层次保留在平台仓 `docs/handoff/tasks/` 的原任务中,技能只保留可复用检查方法。
|
|
592
|
+
|
|
593
|
+
### 适用判定与结果记录
|
|
594
|
+
|
|
595
|
+
以真实组件入口、继承/共享方法、内嵌子页和浮层为检查范围。在 E2 台账增加“G 节命中项/不适用依据/结果”列,逐组件判断以下场景;只检查实际支持的能力,不为凑清单新增移动版、设计器或业务动作。无适用能力时写明理由;未能运行写待验证,不能写不适用。跨组件的登录、导航及导出链路另建场景行,并关联参与组件。
|
|
596
|
+
|
|
597
|
+
| 编号 | 触发场景与近期故障类型 | 升级时检查 | 可观察的验收证据 |
|
|
598
|
+
| --- | --- | --- | --- |
|
|
599
|
+
| G01 | 表单、多选、字段配置:原生事件覆盖组件值,或数组原地变化不回写 | 核对声明事件、modelValue/update 协议、回调参数及数组监听深度;检查单选/多选/清空、初始回显和再次编辑。既有响应问题参见 F16/F18 | 修改后父表单值及提交参数一致;不会变成 DOM Event、误清空或重复触发;取消不保存 |
|
|
600
|
+
| G02 | 列表有数据却空白、名称显示 ID、卡片标题/样式丢失 | 按 F14/F15/F19/F22/F23 核对插槽、props 与实际样式归属;查旧代码对 vnode.data.style、组件内部字段等 Vue2 私有结构的读写,按 Vue3 公开方式适配并保留原事件 | 响应→行数据→实际 DOM 对得上;名称显示但操作仍用原 ID,多实例互不串值,完整产品包不靠旧垫片维持 |
|
|
601
|
+
| G03 | 弹窗打开报非法 DOM 属性、快捷菜单点击无效、勾选状态不回显 | 检查参数展开是否将 `$refesh`/`$refresh` 等组件回调传到原生 DOM;核对旧 hide/关闭方法及独立 checkbox 布尔契约,不能只改方法名或任意丢掉回调 | 弹窗可打开、确认、取消和再次打开;父页按契约刷新一次,菜单切换与勾选初值一致,DOM 无非法属性异常 |
|
|
602
|
+
| G04 | 数据/PDF 已到但遮罩不退,详情返回后异步初始化报错 | 分开检查 loading 状态、真实遮罩、指令卸载;覆盖加载分支复用根 DOM、异常/取消、元数据晚返回、nextTick、离开页面与 KeepAlive。不要只手删遮罩或屏蔽异常 | 正常和失败路径均退出加载,内容保留且可交互;卸载后不再初始化/写已清理配置,缓存组件按预期恢复 |
|
|
603
|
+
| G05 | 对象树/源字段选择弹层偏移、内容长高后出界 | 排查 Element UI 的 popperElm/updatePopper 等旧私有接口和手算 top/left;按目标版本 Popper/虚拟锚点契约处理异步内容、换行切换、滚动、resize 和销毁清理 | 实际输入框与浮层边缘及间隔正确,上下自动翻转;长树能滚到末项,点击外部可关闭,监听不重复、不泄漏 |
|
|
604
|
+
| G06 | 旧 van-popup 内容直接铺在表单中,遮罩/关闭失效 | 核对目标平台支持的移动弹层组件、显隐与状态回写、位置、尺寸及滚动锁;可重建产品应适配源码,旧库兼容仅作过渡,保留产品明确注册的实现 | 关闭时内容不外露,打开时在正确位置;关闭按钮/遮罩/程序关闭同步且只回写一次,页面滚动锁可释放 |
|
|
605
|
+
| G07 | 卡片增高、嵌套间距翻倍、宫格按内容收缩、关闭标题仍留空栏 | 核对默认栅格与显式 flex、height:100% 与 margin 的总高度、列宽及卡片插槽;默认布局不能因升级隐式改语义,主题检查见 E2 | 同后端/视口下卡片列数、边距、标题显隐正确;窄屏响应式正常;自定义宽度和显式 flex 仍有效 |
|
|
606
|
+
| G08 | 更多查询页签重叠、长卡片标题/分页/导入弹窗裁切 | 按 F20 验工具栏选择器宽度,再查 flex 最小尺寸、固定高度、换行、overflow 和 CSS 晚加载;避免为一个弹窗全局改所有确认框 | 窄宽和矮视口下分页及按钮完整可点,长标题不被截断;基础 CSS 先/后加载均正确,普通确认框不受影响 |
|
|
607
|
+
| G09 | 固定卡片内容不可滚、悬浮横条能显示却拖不动 | 检查内容区 min-height/overflow、真实滚动宿主及异步挂载后监听;显示、隐藏、重载与卸载不能重复绑定或错过首次绑定 | 最后一条内容可达;拖悬浮条能带动实际表体,表体滚动也同步滑块;标题区保持设计位置,离开后释放监听 |
|
|
608
|
+
| G10 | 角色切换仍进入旧菜单、刷新侧栏消失、跨应用“设置”打不开 | 分清角色入口与菜单直达地址,核对持久化顺序、旧 menuKey 清理、导航来源恢复及程序化跨应用跳转;透传实际所选记录参数,不以绕过权限补路由 | 换角色后菜单与角色一致,同角色选择不无故刷新;刷新、返回、跨应用保持正确记录;应用/角色/用户不串上下文 |
|
|
609
|
+
| G11 | 产品自定义登录后请求仍用旧账号,退出后凭证残留 | 使用平台目标版本 Token/存储接口,检查产品自己的 Storage/cookie 调用;区分部署路径、客户、local/session 及续期,禁止复制其它部署的历史凭证作为修复 | 请求使用当前凭证;同部署共享、不同部署隔离,刷新/续期/切账号/退出一致。只在实际执行的浏览器上记通过 |
|
|
610
|
+
| G12 | 移动端子集新增露出主记录字段,配置中心新增误只读 | 核对父记录绑定、隐藏字段、数量限制、目标页与刷新回调;使用平台统一系统/客户上下文,不假设 `$query.SYSTEM` 总存在,不将权限问题用可编辑样式掩盖 | 从真实菜单进入的新增/编辑参数正确;移动和桌面归属一致,角色/系统维护范围正确。只读权限未放宽,取消不写数据 |
|
|
611
|
+
| G13 | 统计合并表头盖住数值列、暗色报表纸张不可读 | 核对合并起点与固定列偏移;检查纸张区域和外围工具栏的不同语义,保留模板显式颜色,不给打印纸张盲目套暗色 | 合并标题与表体对应列边缘对齐,滚动/刷新后仍正确;默认纸张白底黑字及模板色保留,外围主题正常 |
|
|
612
|
+
| G14 | 设计器宫格预览成缩略图、背景保存后不回填、链接类型无默认选中 | 核对主组件/visual/容器预览分发、递归子项、默认配置与旧值兼容;检查读取→草稿→保存→重进链路及 0/false/空值,不能只验证运行态 | 实际设计器中容器、样式面板和运行态一致;显式配置优先,已有菜单/页面目标不变,取消不会改远端配置 |
|
|
613
|
+
| G15 | 单元格编辑/字段选择卡顿,拖拽报旧库错误或不能回写排序 | 按 F6/F14 查旧拖拽依赖与插槽;检查大列表是否全量挂载,优先已有虚拟列表;拖动时避免文本误选,浮层/拖影不被裁切 | 记录选项数/挂载节点数及可复现耗时;搜索、手输、清空、拖拽排序及回写正确。性能改善不能代替原有编辑语义验收 |
|
|
614
|
+
| G16 | 同步、查询报空指针,或字段权限误报登录过期 | 先取失败请求/响应/上下文,再沿实际 Action/BO 与元数据、关联路径、字段映射定位;有方案 ID 不代表内部关联仍有效,禁止仅加空判断、吞错或反复刷新 Token | 区分配置失效、后端缺陷、权限校验与前端漏参,保留明确证据。未复现原堆栈不冒称完整根因;未获授权不执行覆盖同步或修改权限 |
|
|
615
|
+
|
|
616
|
+
### 执行与证据边界
|
|
617
|
+
|
|
618
|
+
- **先核实目标制品**:记录平台版本、产品 JS/CSS/清单及页面/角色,确认来自同一批构建;平台修复与产品源码迁移分别列状态。先前某个会话提交或单测通过,不说明目标环境已加载修复。
|
|
619
|
+
- **区分产品、宿主与配置**:平台公共修复优先通过目标平台版本验证,升级产品不复制一份平台内部实现;可确认的产品源码问题按当前任务修复。涉及后端或跨记录配置调整时按任务授权边界处理,不能因为前端升级默认改业务口径或扩权。
|
|
620
|
+
- **复用现有测试**:先运行目标项目可用的相关回归,再在真实调用链验证。平台仓用例可用于定位模式;产品没有相同文件时不虚构命令,也不要求为每个低影响样式修改新增镜像单测。
|
|
621
|
+
- **安全采证**:打开、查询、取消与抓取请求可先完成;实际保存/导入/审核/同步等写操作需处于授权的测试范围。拦截请求只能证明待发送参数,临时注入 CSS/修正实例只能证明诊断假设,不能记作保存成功或部署生效。
|
|
622
|
+
- **状态真实**:将已验证、已改待发布、已发布待验证、失败/阻塞分别记录。未关闭的适用场景进入升级报告的未完成清单,不用“均无异常”覆盖缺环境、被权限阻断或只做静态检查的项。
|
|
623
|
+
|
|
624
|
+
已有专项修法优先使用 A–F 与 E2;G 节不要求看到历史模式就修改。静态命中需结合当前消费契约及运行结果,保留仍正确的产品实现,不盲目套用旧事故中的选择器、像素值、组件名或业务字段。
|