@epxing/core 5.3.0-beta.1 → 5.3.0-beta.3
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 +30 -27
- package/README.md +5 -4
- package/dist/.cache/route-chart-release/frontend/frame/public/js/ecStat.min.js +4 -0
- package/dist/.cache/route-chart-release/frontend/frame/public/js/echarts-wordcloud.min.js +4 -0
- package/dist/.cache/route-chart-release/frontend/frame/public/js/echarts.min.js +4 -0
- package/dist/_virtual/_plugin-vue_export-helper.js +10 -0
- package/dist/_virtual/jsencrypt.min.js +1 -1
- package/dist/assets/ecStat.min-BrTYPOXX.js +1 -0
- package/dist/assets/echarts-wordcloud.min-nWR4Hzny.js +3 -0
- package/dist/assets/echarts.min-DROS9E5X.js +45 -0
- package/dist/assets/role_select-DTqRlFNz.webp +0 -0
- package/dist/{_vendor → frontend/frame/_vendor}/.pnpm/jsencrypt@3.5.4/_vendor/jsencrypt/bin/jsencrypt.min.js +1 -1
- package/dist/src/Container.js +1 -0
- package/dist/src/RootApp.js +54 -0
- package/dist/src/api/config.js +1 -0
- package/dist/src/api/createHttp.js +7 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/app-layout.js +1 -0
- package/dist/src/app-ui-settings.js +49 -0
- package/dist/src/app.js +1 -0
- package/dist/src/axios.js +11 -15
- package/dist/src/capture-ref.js +1 -0
- package/dist/src/chart-runtime-assets.js +9 -0
- package/dist/src/components/draggable/index.js +1 -0
- package/dist/src/create-app.js +4 -6
- package/dist/src/directives/clickoutside.js +1 -0
- package/dist/src/directives/global.js +1 -0
- package/dist/src/index.js +3 -0
- package/dist/src/lazy-library.js +1 -0
- package/dist/src/loadComponent.js +3 -2
- package/dist/src/loadLibrary.js +1 -0
- package/dist/src/menu-route.js +12 -0
- package/dist/src/mixins/component.js +4 -0
- package/dist/src/mixins/custom-option.js +1 -0
- package/dist/src/mixins/el-locale.js +1 -0
- package/dist/src/mixins/global.js +1 -0
- package/dist/src/mixins/index.js +1 -0
- package/dist/src/mixins/methods-to-prototype.js +1 -0
- package/dist/src/mixins/optimized-global.js +1 -0
- package/dist/src/mixins/popper.js +1 -0
- package/dist/src/navigation-context.js +47 -0
- package/dist/src/navigation.js +44 -12
- package/dist/src/package-install.js +4 -2
- package/dist/src/plugins/element-plus.js +9 -8
- package/dist/src/plugins/i18n.js +1 -0
- package/dist/src/plugins/library-locale.js +1 -0
- package/dist/src/progress-bar.js +1 -0
- package/dist/src/public-api.js +5 -2
- package/dist/src/register-components.js +1 -0
- package/dist/src/remote-deps.js +1 -0
- package/dist/src/route-components.js +9 -0
- package/dist/src/router/config.js +8 -8
- package/dist/src/router/index.js +1 -0
- package/dist/src/router/public.js +1 -0
- package/dist/src/runtime-services.js +2 -1
- package/dist/src/runtime-utils/fieldFactory.js +1 -0
- package/dist/src/runtime-utils/meta.js +4 -0
- package/dist/src/runtime-utils/pdf.viewer.js +32 -15
- package/dist/src/runtime-utils/util.js +30 -5
- package/dist/src/shallow-same.js +1 -0
- package/dist/src/shared-packages.js +2 -3
- package/dist/src/store/actions.js +1 -0
- package/dist/src/store/create-store.js +1 -0
- package/dist/src/store/index.js +1 -0
- package/dist/src/store/lru.js +1 -0
- package/dist/src/store/modules/config.js +1 -0
- package/dist/src/store/modules/file.js +1 -0
- package/dist/src/store/modules/index.js +1 -0
- package/dist/src/store/modules/list.js +26 -3
- package/dist/src/store/modules/locale.js +1 -0
- package/dist/src/store/modules/meta.js +50 -7
- package/dist/src/store/modules/page.js +1 -0
- package/dist/src/store/modules/router.js +1 -0
- package/dist/src/store/modules/tabs.js +1 -0
- package/dist/src/store/modules/user.js +1 -0
- package/dist/src/style-presets.js +5 -4
- package/dist/src/ui-settings.js +91 -18
- package/dist/src/viewport.js +1 -0
- package/dist/src/views/App.js +48 -0
- package/dist/src/views/AppRoleSelect.js +233 -0
- package/dist/src/views/Error.js +210 -0
- package/dist/src/views/Module.js +173 -0
- package/dist/src/vue.bus.js +1 -0
- package/dist/src/vue2-cancel-token-compat.js +1 -0
- package/dist/src/vue2-remote-shim.js +1 -0
- package/dist/src/vue2-render-compat.js +1 -0
- package/dist/src/vue2-select-style-compat.js +1 -0
- package/dist/src/watermark.js +1 -0
- package/dist/src/xss.js +1 -0
- package/dist/src/z-index.js +1 -0
- package/dist/style.css +251 -0
- package/package.json +6 -5
- package/src/RootApp.vue +75 -0
- package/src/api/createHttp.js +7 -0
- package/src/api/index.js +47 -47
- package/src/app-ui-settings.js +31 -0
- package/src/axios.js +311 -318
- package/src/chart-runtime-assets.js +7 -0
- package/src/create-app.js +3 -7
- package/src/index.js +28 -28
- package/src/install.js +157 -157
- package/src/loadComponent.js +187 -187
- package/src/loadLibrary.js +99 -99
- package/src/menu-route.js +12 -1
- package/src/mixins/component.js +1464 -1461
- package/src/navigation-context.js +49 -0
- package/src/navigation.js +208 -176
- package/src/package-install.js +2 -1
- package/src/plugins/element-plus.js +302 -302
- package/src/plugins/frame.js +178 -178
- package/src/public-api.js +1 -0
- package/src/route-components.js +7 -0
- package/src/router/config.js +861 -864
- package/src/router/public.js +1 -1
- package/src/runtime-services.js +1 -1
- package/src/runtime-utils/fieldFactory.js +179 -179
- package/src/runtime-utils/meta.js +399 -378
- package/src/runtime-utils/pdf.viewer.js +672 -644
- package/src/runtime-utils/util.js +1357 -1330
- package/src/shared-packages.js +1 -3
- package/src/store/modules/config.js +351 -351
- package/src/store/modules/list.js +30 -3
- package/src/store/modules/locale.js +236 -236
- package/src/store/modules/meta.js +314 -265
- package/src/style-presets.js +4 -4
- package/src/ui-settings.js +84 -23
- package/src/views/App.vue +53 -0
- package/src/views/AppRoleSelect.vue +334 -0
- package/src/views/Error.vue +63 -0
- package/src/views/Module.vue +210 -0
- package/src/xss.js +26 -26
- package/types/app.d.ts +10 -10
- package/types/axios.d.ts +1 -1
- package/types/component-mixin.d.ts +1 -1
- package/types/field-factory.d.ts +1 -1
- package/types/global-mixin.d.ts +1 -1
- package/types/http.d.ts +1 -1
- package/types/index.d.ts +3 -0
- package/types/meta.d.ts +1 -1
- package/types/navigation.d.ts +21 -21
- package/types/pdf-viewer.d.ts +1 -1
- package/types/runtime.d.ts +13 -13
- package/types/store.d.ts +1 -1
- package/types/util.d.ts +2 -2
- /package/dist/{build → .cache/route-chart-release/frontend/frame/build}/stubs/empty-components.js +0 -0
- /package/dist/{src → .cache/route-chart-release/frontend/frame/src}/element-icons.names.js +0 -0
- /package/dist/{src → .cache/route-chart-release/frontend/frame/src}/element-plus.lazy.js +0 -0
- /package/dist/{_vendor → frontend/frame/_vendor}/.pnpm/element-plus@2.14.3_vue@3.5.39_typescript@5.9.3_/_vendor/element-plus/dist/locale/en.js +0 -0
- /package/dist/{_vendor → frontend/frame/_vendor}/.pnpm/element-plus@2.14.3_vue@3.5.39_typescript@5.9.3_/_vendor/element-plus/dist/locale/zh-cn.js +0 -0
|
@@ -1,302 +1,302 @@
|
|
|
1
|
-
// T29:Element Plus 注册(取代 element.js 的 element-ui 注册)。
|
|
2
|
-
//
|
|
3
|
-
// ## 样式(**决策 A:保留 element-ui 图标字体**)
|
|
4
|
-
// EP 全量样式 + element-ui 的**两个**切片:
|
|
5
|
-
// - `icon.css`(16K,自带 @font-face + fonts/)→ 全仓 1691 处 `<i class="el-icon-xxx">` 零改动,
|
|
6
|
-
// 且**数据库页面配置里存的图标 class** 也继续有效(那部分本来就改不到,是选 A 的决定性理由)。
|
|
7
|
-
// - `color-picker.css` → 我方 vendor 的色板内件(sv-panel / hue-slider / …)仍用 element-ui 的类名。
|
|
8
|
-
// → 这两片 CSS + 字体已 vendor 入仓(theme/element-icons、color-picker/vendor),element-ui 依赖整包退役。
|
|
9
|
-
// (原先是 `element-ui/lib/theme-chalk/index.css` 全量 236K,现在只取这两片。)
|
|
10
|
-
//
|
|
11
|
-
// ## 不注册 EP 图标组件
|
|
12
|
-
// 决策 A 下 `<el-icon><Xxx/></el-icon>` 一处都不用,`@element-plus/icons-vue` 无需注册。
|
|
13
|
-
import { reactive, defineAsyncComponent, h } from 'vue'
|
|
14
|
-
// ⚠️ 全部走**深路径**:根导入 `from 'element-plus'` 完全不 tree-shake(实测 993K,与全量
|
|
15
|
-
// app.use 一模一样),一处就把整个 EP 拉回首屏。.eslintrc.js 有 no-restricted-imports 拦。
|
|
16
|
-
import { provideGlobalConfig } from 'element-plus/es/components/config-provider/index.mjs'
|
|
17
|
-
import { ElMessage } from 'element-plus/es/components/message/index.mjs'
|
|
18
|
-
import { ElMessageBox } from 'element-plus/es/components/message-box/index.mjs'
|
|
19
|
-
import { ElLoading, ElLoadingDirective } from 'element-plus/es/components/loading/index.mjs'
|
|
20
|
-
import { ElNotification } from 'element-plus/es/components/notification/index.mjs'
|
|
21
|
-
import { ElInfiniteScroll } from 'element-plus/es/components/infinite-scroll/index.mjs'
|
|
22
|
-
import zhCn from 'element-plus/es/locale/lang/zh-cn.mjs'
|
|
23
|
-
import en from 'element-plus/es/locale/lang/en.mjs'
|
|
24
|
-
|
|
25
|
-
// 首帧渲染到的控件(清单与理由见 src/element-plus.eager.js)
|
|
26
|
-
import { ElAvatar } from 'element-plus/es/components/avatar/index.mjs'
|
|
27
|
-
import { ElButton } from 'element-plus/es/components/button/index.mjs'
|
|
28
|
-
import { ElCard } from 'element-plus/es/components/card/index.mjs'
|
|
29
|
-
import { ElCol } from 'element-plus/es/components/col/index.mjs'
|
|
30
|
-
import { ElRow } from 'element-plus/es/components/row/index.mjs'
|
|
31
|
-
import { ElDrawer } from 'element-plus/es/components/drawer/index.mjs'
|
|
32
|
-
import { ElDropdown, ElDropdownMenu, ElDropdownItem } from 'element-plus/es/components/dropdown/index.mjs'
|
|
33
|
-
import { ElInput } from 'element-plus/es/components/input/index.mjs'
|
|
34
|
-
import { ElMenu, ElMenuItem, ElSubMenu } from 'element-plus/es/components/menu/index.mjs'
|
|
35
|
-
import { ElOverlay } from 'element-plus/es/components/overlay/index.mjs'
|
|
36
|
-
import { ElPopover, ElPopoverDirective } from 'element-plus/es/components/popover/index.mjs'
|
|
37
|
-
import { ElRadioGroup, ElRadioButton } from 'element-plus/es/components/radio/index.mjs'
|
|
38
|
-
import { ElScrollbar } from 'element-plus/es/components/scrollbar/index.mjs'
|
|
39
|
-
import { ElTooltip } from 'element-plus/es/components/tooltip/index.mjs'
|
|
40
|
-
import { ElTabs, ElTabPane } from 'element-plus/es/components/tabs/index.mjs'
|
|
41
|
-
import { ElCheckbox } from 'element-plus/es/components/checkbox/index.mjs'
|
|
42
|
-
import { ElIcon } from 'element-plus/es/components/icon/index.mjs'
|
|
43
|
-
|
|
44
|
-
// CSS 保持整份 eager(本期不拆):352K raw / 36K brotli,CSS 解析约 10–20ms,
|
|
45
|
-
// 对比 900K JS 的解析+执行约 200–400ms,量级差一个数量级;而拆 per-component 样式
|
|
46
|
-
// 要理清组件间的样式依赖(el-select 依赖 popper/input/scrollbar),漏一个就是错版。
|
|
47
|
-
// EP 的根 ESM 不带样式副作用,故 JS 拆分与 CSS 完全解耦。
|
|
48
|
-
import 'element-plus/dist/index.css'
|
|
49
|
-
// 图标底座(spec docs/superpowers/specs/2026-08-21-icon-system-design.md)。
|
|
50
|
-
// `<i class="el-icon-xxx">` 的写法与 DB 里存的 class 一个字不改,只是渲染实现从字体
|
|
51
|
-
// 换成了 CSS mask + SVG。EPX_ICON_LEGACY=1 时本别名指回字体路径。
|
|
52
|
-
//
|
|
53
|
-
// fix round 1(首屏预算闸门踩穿后的裁决,__check-firstpaint.mjs 的 0.52M brotli 闸门):
|
|
54
|
-
// 461 条逐图标规则(.generated/icons.css,体积大头)从这份 eager 底座里搬出去、改走下面
|
|
55
|
-
// 的 import(),不再阻塞首屏;只有结构(base.scss)留在这里。
|
|
56
|
-
import '@epxing/theme/src/theme/icons/index.scss'
|
|
57
|
-
// 3 处历史遗留调用点(Object.vue / ListFirst.vue / OperateItem.vue)直接引用
|
|
58
|
-
// var(--epx-i-el-icon-check) 等变量、不经过 <i class>,取不到 ::before 上的 --epx-i。
|
|
59
|
-
// 这份 CSS 体积可忽略(3 条 :root 变量),EPX_ICON_LEGACY 开关两条路径都要——回退模式
|
|
60
|
-
// 下 mask CSS 整体不生成,若不单独保这一份,这三处会失去取值、渲染成实心色块。
|
|
61
|
-
import '@epxing/theme/src/theme/icons/.generated/icon-vars.css'
|
|
62
|
-
// 彩色图标库(epx-icon-color-*,24 个 COLR/CPAL 字形)。2026-09-02 从 index.html 那条
|
|
63
|
-
// render-blocking 的 <link href="/font/font_2612462/iconfont.css"> 挪进来。
|
|
64
|
-
//
|
|
65
|
-
// 为什么放这里而不是 theme/src/index.scss:那是 .scss,`url()` 会按外层 scss 的目录
|
|
66
|
-
// rebase,字体在产物里会静默找不到(见 vite.config.mjs 的 EPX_ICON_LEGACY 别名旁那段
|
|
67
|
-
// 实测记录)。由 JS 直接 import 真实 .css,相对路径以该 css 自己的目录为基准。
|
|
68
|
-
//
|
|
69
|
-
// 为什么必须 eager(不像 461 条 mask 规则那样走 import()):它带的是 @font-face 声明,
|
|
70
|
-
// 而字体文件本身由浏览器惰性下载——页面上没有彩色图标元素时一个字节都不下。也就是说
|
|
71
|
-
// 这份 eager 的代价只有约 2K CSS,把它异步化换不来字节,只会换来图标闪一下。
|
|
72
|
-
// 同理它在库/包/远程构建里也照常静态生效,与 __EPX_ICON_ASYNC__ 开关无关。
|
|
73
|
-
//
|
|
74
|
-
// 宿主规则(font-family / display / vertical-align)在 theme/src/index.scss 的
|
|
75
|
-
// `[class*="epx-icon-color-"]`;base.scss 用 :not(:where([class*="epx-icon-color-"]))
|
|
76
|
-
// 把彩色类排除在 mask 之外——两边都别动,彩色和单色是两条渲染通道。
|
|
77
|
-
import '@epxing/theme/src/theme/icons-color/iconfont.css'
|
|
78
|
-
import '@epxing/theme/src/theme/color-picker.css' // vendor 色板内件的样式(同上入仓)
|
|
79
|
-
|
|
80
|
-
import epLazy from '@/element-plus.lazy'
|
|
81
|
-
// 图标字形名清单(codegen 产物,见 build/gen-element-icons.mjs)
|
|
82
|
-
import iconNames from '@/element-icons.names'
|
|
83
|
-
|
|
84
|
-
/* global __EPX_ICON_LEGACY__, __EPX_ICON_ASYNC__ */
|
|
85
|
-
// 461 条逐图标规则:平台应用异步加载(不在首屏渲染阻塞路径上,只影响图标本身多晚被画
|
|
86
|
-
// 出来——旧的字体方案同样要等字体文件下载,量级并不比这更快);库/包/远程构建走静态
|
|
87
|
-
// (收尾修复波裁决——两条路径各走各的,见 theme/icons/index.lib.scss 头注释)。
|
|
88
|
-
//
|
|
89
|
-
// ⚠️ 为什么不是"想办法让动态 import 在库模式下也 work":JS 动态 import() 拉 CSS 这件事,
|
|
90
|
-
// 在 Vite library 模式下语义不成立——实测(收尾修复波报告 §0c):CJS 多入口输出把它编译
|
|
91
|
-
// 成 require() 一个从未生成的 chunk,package.json 的 main/require 消费方触发时直接抛
|
|
92
|
-
// `Cannot find module`;ESM 分模块输出编译成 Promise.resolve({}) 空操作,静默丢图标、不
|
|
93
|
-
// 报错,比前者更隐蔽。库本来也不需要"异步"这个语义:那是给平台应用首屏预算服务的,库的
|
|
94
|
-
// 消费方拿到的是产物源码,自己决定怎么分包。
|
|
95
|
-
//
|
|
96
|
-
// __EPX_ICON_ASYNC__ 是这个开关的构建期常量:五个库/包/远程配置(vite.lib*.config.mjs /
|
|
97
|
-
// build/vite.pkg.config.mjs / build/vite.remote.config.mjs)define 成 false,让下面这
|
|
98
|
-
// 整个 if 连同 import() 一起被 tree-shake——那五个配置同时把 index.scss 的别名指到
|
|
99
|
-
// index.lib.scss,461 条规则已经静态内联在那份产物里,这条 import 留着不但没用,还会让
|
|
100
|
-
// 内容在库产物里重复出现。主 vite.config.mjs **不定义**这个常量:
|
|
101
|
-
// typeof __EPX_ICON_ASYNC__ === 'undefined' 在那边恒为 true,平台应用异步加载行为不变,
|
|
102
|
-
// 与 __EPX_ICON_LEGACY__ 未定义时的处理同一个模式。EPX_ICON_LEGACY=1 时这条 import()
|
|
103
|
-
// 所在的整块依然会被 tree-shake,不会尝试解析一份没被任何东西消费的产物,不受这次改动
|
|
104
|
-
// 影响。
|
|
105
|
-
//
|
|
106
|
-
// (放在静态 import 段之后而不是紧跟 icon-vars.css:import/first 不允许静态 import 后面
|
|
107
|
-
// 混非 import 语句,动态 import() 是表达式、不受这条约束,挪到最后统一执行。)
|
|
108
|
-
if ((typeof __EPX_ICON_ASYNC__ === 'undefined' || __EPX_ICON_ASYNC__) &&
|
|
109
|
-
(typeof __EPX_ICON_LEGACY__ === 'undefined' || !__EPX_ICON_LEGACY__)) {
|
|
110
|
-
// eslint-disable-next-line no-console
|
|
111
|
-
import('@epxing/theme/src/theme/icons/.generated/icons.css').catch(err => console.error('[icons] 异步图标 CSS 加载失败', err))
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// 语言:沿用 010=中文 / 011=英文(clientConfig.LANGUAGE_DEFAULT),同时识别
|
|
115
|
-
// INITIALIZE_LOCALE(packages/core/src/store/modules/locale.js,经 library-locale.js 转call)
|
|
116
|
-
// 传入的规范化 BCP47 代码 'en'/'en-US'(大小写不敏感)——两条调用链路并存,都要认。
|
|
117
|
-
const EN_LOCALE_CODES = new Set(['011', 'en', 'en-us'])
|
|
118
|
-
export function elementPlusLocale (lang) {
|
|
119
|
-
return EN_LOCALE_CODES.has(String(lang || '').toLowerCase()) ? en : zhCn
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// T33:语言在 clientConfig 拉回来之后才知道(router/config.js 的 getI18nMessages),
|
|
123
|
-
// 但 EP 的 locale 是 install 时注入的。element-ui 时代靠 `locale.use()` 运行时改全局;
|
|
124
|
-
// EP 走 provide/inject —— 把整个 config 做成 reactive,改 `epConfig.locale` 即可让所有 EP
|
|
125
|
-
// 组件跟着变(EP 的 provideGlobalConfig 内部是 `computed(() => unref(config))`)。
|
|
126
|
-
export const epConfig = reactive({ locale: zhCn })
|
|
127
|
-
|
|
128
|
-
export function setElementPlusLocale (lang) {
|
|
129
|
-
epConfig.locale = elementPlusLocale(lang)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// 非 eager 的 EP 组件共用一个 chunk。取根入口(不 shake,993K),但上面深路径静态引走的
|
|
133
|
-
// 模块会被 rollup 提到共享(eager)chunk,故本 chunk 实际约 993 − 295 = 700K,无重复。
|
|
134
|
-
let restPending = null
|
|
135
|
-
// C-1(终审 Critical):92 个非 eager 组件的失败提示只弹一次——它们大多在同一时刻一起渲染,
|
|
136
|
-
// 一次抖动会让它们几乎同时 fail(),不需要每个组件都弹一条 ElMessage。
|
|
137
|
-
let restFailNotified = false
|
|
138
|
-
export function loadElementPlusRest () {
|
|
139
|
-
if (restPending) return restPending
|
|
140
|
-
restPending = import('element-plus').then(EP => {
|
|
141
|
-
// e2e 探针:首屏若被静默拖入 EP-rest(整份 700K 的 chunk),回归了 Task 5 的收益本身,
|
|
142
|
-
// 靠这个标志判定比按 chunk 文件名匹配稳(文件名带 hash 会变)。几字节,不影响运行时行为。
|
|
143
|
-
if (typeof window !== 'undefined') window.__EP_REST_LOADED__ = true
|
|
144
|
-
return EP
|
|
145
|
-
}).catch(err => {
|
|
146
|
-
// ⚠️ 这行的措辞曾经写反过——终审 C-1 实测:浏览器原生 ES 模块加载器把同一个 chunk URL 的
|
|
147
|
-
// 失败结果**永久缓存**在当前页面的 module map 里,同一 specifier 再 `import()` 一次会
|
|
148
|
-
// 直接复用那条 rejection,不会有新的网络请求(实测:idle 预取阶段 abort 一次 ui-*.js 后,
|
|
149
|
-
// 网络完全恢复,渲染 el-table/el-select/… 仍然拿到同一条缓存的失败,新发起的 ui-*.js
|
|
150
|
-
// 请求数 = 0)。真正能让下次渲染重新发请求的路径只有**整页刷新**(拿到全新的 module map)。
|
|
151
|
-
// `restPending = null` 在这里唯一还有意义的场景,是「本次调用的 import() 因为某种原因
|
|
152
|
-
// 从未真正发出就同步失败」(目前没有这样的路径,纯防御)——不要再指望它能让网络失败
|
|
153
|
-
// 自愈。真正对用户可见的兜底在下面 defineAsyncComponent 的 onError 里:重试耗尽后弹一次
|
|
154
|
-
// ElMessage 引导刷新,处置口径与 Task 3 给 jsencrypt 的 13 个调用点一致
|
|
155
|
-
// (见 packages/utils/src/lazy-jsencrypt.js 同款注释)。
|
|
156
|
-
restPending = null
|
|
157
|
-
throw err
|
|
158
|
-
})
|
|
159
|
-
return restPending
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const EAGER_COMPONENTS = [
|
|
163
|
-
ElAvatar, ElButton, ElCard, ElCol, ElRow, ElDrawer,
|
|
164
|
-
ElDropdown, ElDropdownMenu, ElDropdownItem, ElInput,
|
|
165
|
-
ElMenu, ElMenuItem, ElSubMenu, ElOverlay, ElPopover,
|
|
166
|
-
ElRadioGroup, ElRadioButton, ElScrollbar, ElTooltip,
|
|
167
|
-
ElTabs, ElTabPane, ElCheckbox, ElIcon
|
|
168
|
-
]
|
|
169
|
-
|
|
170
|
-
// 图标名垫片:把每个 el-icon-* 字形名注册成一个渲染 `<i class="el-icon-xxx">` 的极小组件。
|
|
171
|
-
//
|
|
172
|
-
// ## 为什么需要
|
|
173
|
-
// 「决策 A」(见文件顶部)保住了 `<i class="el-icon-xxx">` 这一形态,却漏了 **prop 形态**:
|
|
174
|
-
//
|
|
175
|
-
// <el-button icon="el-icon-search"> <el-input suffix-icon="el-icon-search">
|
|
176
|
-
//
|
|
177
|
-
// element-ui 时代 icon prop 就是**类名**,直接渲染成 `<i class="el-icon-search">`;
|
|
178
|
-
// EP 拿它当**组件**用,实测渲染成:
|
|
179
|
-
//
|
|
180
|
-
// <i class="el-icon"><el-icon-search></el-icon-search></i>
|
|
181
|
-
//
|
|
182
|
-
// 外层类是 `el-icon`(**没有尾横线**),匹配不到 icon.css 的
|
|
183
|
-
// `[class*=" el-icon-"],[class^=el-icon-]`;内层是个没注册过的名字,Vue 3 的
|
|
184
|
-
// resolveDynamicComponent 解析不到就原样当未知原生元素渲染 —— 什么都不画。
|
|
185
|
-
// 结果是图标凭空消失,**不报错、不告警**,只能靠肉眼发现。
|
|
186
|
-
//
|
|
187
|
-
// 全仓实测 201 处这样静默失效(模板 172 / JSX 29;suffix-icon 116、icon 71、prefix-icon 14),
|
|
188
|
-
// 涉及 91 个文件、20 个不同图标名,全部在字体里有字形。
|
|
189
|
-
//
|
|
190
|
-
// ## 为什么是垫片而不是改 201 处模板
|
|
191
|
-
// 另有 11 处是 `:icon="operate.iconCls"` / `:icon="item.icon"` 这类动态绑定,**值存在数据库的
|
|
192
|
-
// 页面配置里**——改模板根本够不着。这恰恰是决策 A 当初拿来当决定性理由的那类情况,
|
|
193
|
-
// 所以正确的做法是补上决策 A 的缺口,而不是逐处重写调用点。
|
|
194
|
-
// 顺带的好处:EP 自带的「图标 + 文字」间距(.el-icon + span)继续生效;若改写成
|
|
195
|
-
// `<el-button><i class="..."/>文字</el-button>` 反而要为 201 处各自补间距。
|
|
196
|
-
//
|
|
197
|
-
// ## 名字从哪来
|
|
198
|
-
// build/gen-element-icons.mjs 从 icon.css 枚举(幂等 codegen,产物已 gitignore)。
|
|
199
|
-
// 不硬编:字体换了/加了图标,清单自动跟随。build/__check-element-icons.mjs 守着不漂。
|
|
200
|
-
export function registerElementIconShims (app) {
|
|
201
|
-
for (const name of iconNames) {
|
|
202
|
-
// 已被别处占用就不覆盖(正常不会发生,但覆盖真身组件的代价远大于少一个图标)
|
|
203
|
-
if (app.component(name)) continue
|
|
204
|
-
app.component(name, {
|
|
205
|
-
name,
|
|
206
|
-
// 无状态、无 props,只画一个字体图标。写成 render 而不是模板:本文件不过编译器。
|
|
207
|
-
render: () => h('i', { class: name })
|
|
208
|
-
})
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export default function installElementPlus (app) {
|
|
213
|
-
// ① eager 组件:同步注册真身
|
|
214
|
-
for (const c of EAGER_COMPONENTS) {
|
|
215
|
-
if (c && c.name) app.component(c.name, c)
|
|
216
|
-
}
|
|
217
|
-
// ② 其余组件:注册异步包装器。远程模块/页面配置里的 <el-table> 走
|
|
218
|
-
// resolveComponent('el-table') 命中这里,写法完全不变(vue2-render-compat.js 的
|
|
219
|
-
// compatH 对带 '-' 的字符串标签也走 resolveComponent,故 JSX/render 形态同样覆盖)。
|
|
220
|
-
for (const tag of epLazy.tags) {
|
|
221
|
-
const exportName = epLazy.exportOf[tag]
|
|
222
|
-
const wrapper = defineAsyncComponent({
|
|
223
|
-
loader: () => loadElementPlusRest().then(EP => EP[exportName]),
|
|
224
|
-
// Vue 的重试判据只看 attempts,err 本身无需处理(重试次数耗尽后失败态由
|
|
225
|
-
// defineAsyncComponent 内部负责往上抛)。⚠️ 这几次 retry() 救不回网络失败——见上面
|
|
226
|
-
// loadElementPlusRest 的 catch 注释,module map 缓存的 rejection 不会因为再 import()
|
|
227
|
-
// 一次而产生新请求。C-1(终审 Critical):改造前 EP 是 eager 的,一次抖动会让应用直接
|
|
228
|
-
// 启动不了(响亮、用户会本能刷新);改造后退化成外壳正常、内容静默变成一片空注释
|
|
229
|
-
// (<!---->),没有任何提示。重试耗尽后补一次 ElMessage.error,把「响亮」这个属性找
|
|
230
|
-
// 回来,处置口径与 Task 3 给 jsencrypt 13 个调用点做的一致(见 lazy-jsencrypt.js)。
|
|
231
|
-
// eslint-disable-next-line handle-callback-err
|
|
232
|
-
onError: (err, retry, fail, attempts) => {
|
|
233
|
-
if (attempts <= 2) {
|
|
234
|
-
retry()
|
|
235
|
-
return
|
|
236
|
-
}
|
|
237
|
-
if (!restFailNotified) {
|
|
238
|
-
restFailNotified = true
|
|
239
|
-
ElMessage.error('界面组件加载失败,请刷新页面重试')
|
|
240
|
-
}
|
|
241
|
-
fail()
|
|
242
|
-
}
|
|
243
|
-
})
|
|
244
|
-
// ⚠️ 必须顶着真身的 name 注册。defineAsyncComponent 返回的组件对象 name 恒为
|
|
245
|
-
// 'AsyncComponentWrapper',而 EP **自己**有多处靠 `vnode.type.name === 'ElXxx'` 内省子节点:
|
|
246
|
-
// - table-column:`childNode.type?.name === 'ElTableColumn' || childNode.shapeFlag & 2`
|
|
247
|
-
// (实测包装 vnode 的 shapeFlag 也是 36,&2 为 0 —— 两个判据双双失败)→ 子列 children
|
|
248
|
-
// 恒空、嵌套子列从不挂载、insertColumn 从不调用,**不报错也不告警**;
|
|
249
|
-
// 同文件 render-helper 的 `item.vParent = instance` 也吊在同一条判据上;
|
|
250
|
-
// - carousel 的 PlaceholderItem 按 'ElCarouselItem' 过滤(两张轮播图的 loop 复制);
|
|
251
|
-
// - container 按 name 分辨 ElHeader/ElFooter 决定 is-vertical。
|
|
252
|
-
// 这些内省看的是**注册进 vnode.type 的那个对象**,也就是包装器本身,所以只能在这里补名。
|
|
253
|
-
// nameOf 由 codegen 从 EP 运行时导出面取(build/gen-element-plus-lazy.mjs),不硬编。
|
|
254
|
-
wrapper.name = epLazy.nameOf[tag]
|
|
255
|
-
app.component(tag, wrapper)
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// 止血开关:window.EPX_EP_LAZY = false 时**额外**立刻把整个 EP 拉起来,语义是
|
|
259
|
-
// 「放弃按需、立刻全量」—— 所有按需间隙一次性消失。
|
|
260
|
-
// ⚠️ 它**不跳过**上面的包装器注册。早先的写法是「开关打开就只做一次异步 app.use、
|
|
261
|
-
// 不注册包装器」,那会让首帧渲染时所有 EP 组件都还没注册、页面先坏一下再好 ——
|
|
262
|
-
// 比不开开关更糟,而这恰恰是线上出事时要走的那条路。
|
|
263
|
-
if (typeof window !== 'undefined' && window.EPX_EP_LAZY === false) {
|
|
264
|
-
loadElementPlusRest().catch(err => console.error('[element-plus] 立刻全量加载失败', err))
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// ②b 图标名垫片:补上「决策 A」漏掉的 **prop 形态**(详见下面 registerElementIconShims)
|
|
268
|
-
registerElementIconShims(app)
|
|
269
|
-
|
|
270
|
-
// ③ 指令:Vue 没有 defineAsyncDirective,resolveDirective 拿到 Promise 不工作 —— 必须 eager
|
|
271
|
-
app.directive('loading', ElLoadingDirective)
|
|
272
|
-
app.directive('infinite-scroll', ElInfiniteScroll)
|
|
273
|
-
// I-4(终审 Important):改造前 app.use(ElementPlus) 会通过 EP 自带的插件清单顺带注册
|
|
274
|
-
// v-popover;拆掉 app.use 后这行漏掉了,3 处活代码(Bpmn.vue ×2、PageVariablePanel.vue ×1,
|
|
275
|
-
// 均无其它触发锚点)静默失效——生产构建下 Vue 对 resolveDirective 拿到 undefined 是完全
|
|
276
|
-
// 静默的 no-op,不抛错不告警。ElPopover 本来就在 eager 白名单里、这行从同一个模块深路径
|
|
277
|
-
// 带出 ElPopoverDirective,零体积代价。
|
|
278
|
-
app.directive('popover', ElPopoverDirective)
|
|
279
|
-
|
|
280
|
-
// ④ 全局配置(locale):原来靠 app.use(ElementPlus, epConfig) 注入,去掉 app.use 之后
|
|
281
|
-
// 必须自己调,否则 setElementPlusLocale() 改 epConfig 不再有任何效果 —— **静默失效**,
|
|
282
|
-
// 表现是切了语言没反应,不报错。e2e 有守卫。
|
|
283
|
-
provideGlobalConfig(epConfig, app, true)
|
|
284
|
-
|
|
285
|
-
// ⑤ 命令式服务:函数调用,没有渲染时机可挂钩,必须 eager
|
|
286
|
-
const gp = app.config.globalProperties
|
|
287
|
-
gp.$message = ElMessage
|
|
288
|
-
gp.$msgbox = ElMessageBox
|
|
289
|
-
gp.$alert = ElMessageBox.alert
|
|
290
|
-
gp.$confirm = ElMessageBox.confirm
|
|
291
|
-
gp.$prompt = ElMessageBox.prompt
|
|
292
|
-
gp.$notify = ElNotification
|
|
293
|
-
gp.$loading = ElLoading.service
|
|
294
|
-
// element-ui 遗留:全仓有 `(this.$ELEMENT || {}).size` 之类读取,留兜底避免 render 告警。
|
|
295
|
-
gp.$ELEMENT = { size: '', zIndex: 2000 }
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// e2e 守卫③用:暴露 setElementPlusLocale 给页面里直接调用,验证 provideGlobalConfig 没漏。
|
|
299
|
-
// 只挂函数、不挂数据,不构成新的对外契约(window.frame ABI 未变)。
|
|
300
|
-
if (typeof window !== 'undefined') {
|
|
301
|
-
window.__EPX_EP_PLUGIN__ = { setElementPlusLocale }
|
|
302
|
-
}
|
|
1
|
+
// T29:Element Plus 注册(取代 element.js 的 element-ui 注册)。
|
|
2
|
+
//
|
|
3
|
+
// ## 样式(**决策 A:保留 element-ui 图标字体**)
|
|
4
|
+
// EP 全量样式 + element-ui 的**两个**切片:
|
|
5
|
+
// - `icon.css`(16K,自带 @font-face + fonts/)→ 全仓 1691 处 `<i class="el-icon-xxx">` 零改动,
|
|
6
|
+
// 且**数据库页面配置里存的图标 class** 也继续有效(那部分本来就改不到,是选 A 的决定性理由)。
|
|
7
|
+
// - `color-picker.css` → 我方 vendor 的色板内件(sv-panel / hue-slider / …)仍用 element-ui 的类名。
|
|
8
|
+
// → 这两片 CSS + 字体已 vendor 入仓(theme/element-icons、color-picker/vendor),element-ui 依赖整包退役。
|
|
9
|
+
// (原先是 `element-ui/lib/theme-chalk/index.css` 全量 236K,现在只取这两片。)
|
|
10
|
+
//
|
|
11
|
+
// ## 不注册 EP 图标组件
|
|
12
|
+
// 决策 A 下 `<el-icon><Xxx/></el-icon>` 一处都不用,`@element-plus/icons-vue` 无需注册。
|
|
13
|
+
import { reactive, defineAsyncComponent, h } from 'vue'
|
|
14
|
+
// ⚠️ 全部走**深路径**:根导入 `from 'element-plus'` 完全不 tree-shake(实测 993K,与全量
|
|
15
|
+
// app.use 一模一样),一处就把整个 EP 拉回首屏。.eslintrc.js 有 no-restricted-imports 拦。
|
|
16
|
+
import { provideGlobalConfig } from 'element-plus/es/components/config-provider/index.mjs'
|
|
17
|
+
import { ElMessage } from 'element-plus/es/components/message/index.mjs'
|
|
18
|
+
import { ElMessageBox } from 'element-plus/es/components/message-box/index.mjs'
|
|
19
|
+
import { ElLoading, ElLoadingDirective } from 'element-plus/es/components/loading/index.mjs'
|
|
20
|
+
import { ElNotification } from 'element-plus/es/components/notification/index.mjs'
|
|
21
|
+
import { ElInfiniteScroll } from 'element-plus/es/components/infinite-scroll/index.mjs'
|
|
22
|
+
import zhCn from 'element-plus/es/locale/lang/zh-cn.mjs'
|
|
23
|
+
import en from 'element-plus/es/locale/lang/en.mjs'
|
|
24
|
+
|
|
25
|
+
// 首帧渲染到的控件(清单与理由见 src/element-plus.eager.js)
|
|
26
|
+
import { ElAvatar } from 'element-plus/es/components/avatar/index.mjs'
|
|
27
|
+
import { ElButton } from 'element-plus/es/components/button/index.mjs'
|
|
28
|
+
import { ElCard } from 'element-plus/es/components/card/index.mjs'
|
|
29
|
+
import { ElCol } from 'element-plus/es/components/col/index.mjs'
|
|
30
|
+
import { ElRow } from 'element-plus/es/components/row/index.mjs'
|
|
31
|
+
import { ElDrawer } from 'element-plus/es/components/drawer/index.mjs'
|
|
32
|
+
import { ElDropdown, ElDropdownMenu, ElDropdownItem } from 'element-plus/es/components/dropdown/index.mjs'
|
|
33
|
+
import { ElInput } from 'element-plus/es/components/input/index.mjs'
|
|
34
|
+
import { ElMenu, ElMenuItem, ElSubMenu } from 'element-plus/es/components/menu/index.mjs'
|
|
35
|
+
import { ElOverlay } from 'element-plus/es/components/overlay/index.mjs'
|
|
36
|
+
import { ElPopover, ElPopoverDirective } from 'element-plus/es/components/popover/index.mjs'
|
|
37
|
+
import { ElRadioGroup, ElRadioButton } from 'element-plus/es/components/radio/index.mjs'
|
|
38
|
+
import { ElScrollbar } from 'element-plus/es/components/scrollbar/index.mjs'
|
|
39
|
+
import { ElTooltip } from 'element-plus/es/components/tooltip/index.mjs'
|
|
40
|
+
import { ElTabs, ElTabPane } from 'element-plus/es/components/tabs/index.mjs'
|
|
41
|
+
import { ElCheckbox } from 'element-plus/es/components/checkbox/index.mjs'
|
|
42
|
+
import { ElIcon } from 'element-plus/es/components/icon/index.mjs'
|
|
43
|
+
|
|
44
|
+
// CSS 保持整份 eager(本期不拆):352K raw / 36K brotli,CSS 解析约 10–20ms,
|
|
45
|
+
// 对比 900K JS 的解析+执行约 200–400ms,量级差一个数量级;而拆 per-component 样式
|
|
46
|
+
// 要理清组件间的样式依赖(el-select 依赖 popper/input/scrollbar),漏一个就是错版。
|
|
47
|
+
// EP 的根 ESM 不带样式副作用,故 JS 拆分与 CSS 完全解耦。
|
|
48
|
+
import 'element-plus/dist/index.css'
|
|
49
|
+
// 图标底座(spec docs/superpowers/specs/2026-08-21-icon-system-design.md)。
|
|
50
|
+
// `<i class="el-icon-xxx">` 的写法与 DB 里存的 class 一个字不改,只是渲染实现从字体
|
|
51
|
+
// 换成了 CSS mask + SVG。EPX_ICON_LEGACY=1 时本别名指回字体路径。
|
|
52
|
+
//
|
|
53
|
+
// fix round 1(首屏预算闸门踩穿后的裁决,__check-firstpaint.mjs 的 0.52M brotli 闸门):
|
|
54
|
+
// 461 条逐图标规则(.generated/icons.css,体积大头)从这份 eager 底座里搬出去、改走下面
|
|
55
|
+
// 的 import(),不再阻塞首屏;只有结构(base.scss)留在这里。
|
|
56
|
+
import '@epxing/theme/src/theme/icons/index.scss'
|
|
57
|
+
// 3 处历史遗留调用点(Object.vue / ListFirst.vue / OperateItem.vue)直接引用
|
|
58
|
+
// var(--epx-i-el-icon-check) 等变量、不经过 <i class>,取不到 ::before 上的 --epx-i。
|
|
59
|
+
// 这份 CSS 体积可忽略(3 条 :root 变量),EPX_ICON_LEGACY 开关两条路径都要——回退模式
|
|
60
|
+
// 下 mask CSS 整体不生成,若不单独保这一份,这三处会失去取值、渲染成实心色块。
|
|
61
|
+
import '@epxing/theme/src/theme/icons/.generated/icon-vars.css'
|
|
62
|
+
// 彩色图标库(epx-icon-color-*,24 个 COLR/CPAL 字形)。2026-09-02 从 index.html 那条
|
|
63
|
+
// render-blocking 的 <link href="/font/font_2612462/iconfont.css"> 挪进来。
|
|
64
|
+
//
|
|
65
|
+
// 为什么放这里而不是 theme/src/index.scss:那是 .scss,`url()` 会按外层 scss 的目录
|
|
66
|
+
// rebase,字体在产物里会静默找不到(见 vite.config.mjs 的 EPX_ICON_LEGACY 别名旁那段
|
|
67
|
+
// 实测记录)。由 JS 直接 import 真实 .css,相对路径以该 css 自己的目录为基准。
|
|
68
|
+
//
|
|
69
|
+
// 为什么必须 eager(不像 461 条 mask 规则那样走 import()):它带的是 @font-face 声明,
|
|
70
|
+
// 而字体文件本身由浏览器惰性下载——页面上没有彩色图标元素时一个字节都不下。也就是说
|
|
71
|
+
// 这份 eager 的代价只有约 2K CSS,把它异步化换不来字节,只会换来图标闪一下。
|
|
72
|
+
// 同理它在库/包/远程构建里也照常静态生效,与 __EPX_ICON_ASYNC__ 开关无关。
|
|
73
|
+
//
|
|
74
|
+
// 宿主规则(font-family / display / vertical-align)在 theme/src/index.scss 的
|
|
75
|
+
// `[class*="epx-icon-color-"]`;base.scss 用 :not(:where([class*="epx-icon-color-"]))
|
|
76
|
+
// 把彩色类排除在 mask 之外——两边都别动,彩色和单色是两条渲染通道。
|
|
77
|
+
import '@epxing/theme/src/theme/icons-color/iconfont.css'
|
|
78
|
+
import '@epxing/theme/src/theme/color-picker.css' // vendor 色板内件的样式(同上入仓)
|
|
79
|
+
|
|
80
|
+
import epLazy from '@/element-plus.lazy'
|
|
81
|
+
// 图标字形名清单(codegen 产物,见 build/gen-element-icons.mjs)
|
|
82
|
+
import iconNames from '@/element-icons.names'
|
|
83
|
+
|
|
84
|
+
/* global __EPX_ICON_LEGACY__, __EPX_ICON_ASYNC__ */
|
|
85
|
+
// 461 条逐图标规则:平台应用异步加载(不在首屏渲染阻塞路径上,只影响图标本身多晚被画
|
|
86
|
+
// 出来——旧的字体方案同样要等字体文件下载,量级并不比这更快);库/包/远程构建走静态
|
|
87
|
+
// (收尾修复波裁决——两条路径各走各的,见 theme/icons/index.lib.scss 头注释)。
|
|
88
|
+
//
|
|
89
|
+
// ⚠️ 为什么不是"想办法让动态 import 在库模式下也 work":JS 动态 import() 拉 CSS 这件事,
|
|
90
|
+
// 在 Vite library 模式下语义不成立——实测(收尾修复波报告 §0c):CJS 多入口输出把它编译
|
|
91
|
+
// 成 require() 一个从未生成的 chunk,package.json 的 main/require 消费方触发时直接抛
|
|
92
|
+
// `Cannot find module`;ESM 分模块输出编译成 Promise.resolve({}) 空操作,静默丢图标、不
|
|
93
|
+
// 报错,比前者更隐蔽。库本来也不需要"异步"这个语义:那是给平台应用首屏预算服务的,库的
|
|
94
|
+
// 消费方拿到的是产物源码,自己决定怎么分包。
|
|
95
|
+
//
|
|
96
|
+
// __EPX_ICON_ASYNC__ 是这个开关的构建期常量:五个库/包/远程配置(vite.lib*.config.mjs /
|
|
97
|
+
// build/vite.pkg.config.mjs / build/vite.remote.config.mjs)define 成 false,让下面这
|
|
98
|
+
// 整个 if 连同 import() 一起被 tree-shake——那五个配置同时把 index.scss 的别名指到
|
|
99
|
+
// index.lib.scss,461 条规则已经静态内联在那份产物里,这条 import 留着不但没用,还会让
|
|
100
|
+
// 内容在库产物里重复出现。主 vite.config.mjs **不定义**这个常量:
|
|
101
|
+
// typeof __EPX_ICON_ASYNC__ === 'undefined' 在那边恒为 true,平台应用异步加载行为不变,
|
|
102
|
+
// 与 __EPX_ICON_LEGACY__ 未定义时的处理同一个模式。EPX_ICON_LEGACY=1 时这条 import()
|
|
103
|
+
// 所在的整块依然会被 tree-shake,不会尝试解析一份没被任何东西消费的产物,不受这次改动
|
|
104
|
+
// 影响。
|
|
105
|
+
//
|
|
106
|
+
// (放在静态 import 段之后而不是紧跟 icon-vars.css:import/first 不允许静态 import 后面
|
|
107
|
+
// 混非 import 语句,动态 import() 是表达式、不受这条约束,挪到最后统一执行。)
|
|
108
|
+
if ((typeof __EPX_ICON_ASYNC__ === 'undefined' || __EPX_ICON_ASYNC__) &&
|
|
109
|
+
(typeof __EPX_ICON_LEGACY__ === 'undefined' || !__EPX_ICON_LEGACY__)) {
|
|
110
|
+
// eslint-disable-next-line no-console
|
|
111
|
+
import('@epxing/theme/src/theme/icons/.generated/icons.css').catch(err => console.error('[icons] 异步图标 CSS 加载失败', err))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 语言:沿用 010=中文 / 011=英文(clientConfig.LANGUAGE_DEFAULT),同时识别
|
|
115
|
+
// INITIALIZE_LOCALE(packages/core/src/store/modules/locale.js,经 library-locale.js 转call)
|
|
116
|
+
// 传入的规范化 BCP47 代码 'en'/'en-US'(大小写不敏感)——两条调用链路并存,都要认。
|
|
117
|
+
const EN_LOCALE_CODES = new Set(['011', 'en', 'en-us'])
|
|
118
|
+
export function elementPlusLocale (lang) {
|
|
119
|
+
return EN_LOCALE_CODES.has(String(lang || '').toLowerCase()) ? en : zhCn
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// T33:语言在 clientConfig 拉回来之后才知道(router/config.js 的 getI18nMessages),
|
|
123
|
+
// 但 EP 的 locale 是 install 时注入的。element-ui 时代靠 `locale.use()` 运行时改全局;
|
|
124
|
+
// EP 走 provide/inject —— 把整个 config 做成 reactive,改 `epConfig.locale` 即可让所有 EP
|
|
125
|
+
// 组件跟着变(EP 的 provideGlobalConfig 内部是 `computed(() => unref(config))`)。
|
|
126
|
+
export const epConfig = reactive({ locale: zhCn })
|
|
127
|
+
|
|
128
|
+
export function setElementPlusLocale (lang) {
|
|
129
|
+
epConfig.locale = elementPlusLocale(lang)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 非 eager 的 EP 组件共用一个 chunk。取根入口(不 shake,993K),但上面深路径静态引走的
|
|
133
|
+
// 模块会被 rollup 提到共享(eager)chunk,故本 chunk 实际约 993 − 295 = 700K,无重复。
|
|
134
|
+
let restPending = null
|
|
135
|
+
// C-1(终审 Critical):92 个非 eager 组件的失败提示只弹一次——它们大多在同一时刻一起渲染,
|
|
136
|
+
// 一次抖动会让它们几乎同时 fail(),不需要每个组件都弹一条 ElMessage。
|
|
137
|
+
let restFailNotified = false
|
|
138
|
+
export function loadElementPlusRest () {
|
|
139
|
+
if (restPending) return restPending
|
|
140
|
+
restPending = import('element-plus').then(EP => {
|
|
141
|
+
// e2e 探针:首屏若被静默拖入 EP-rest(整份 700K 的 chunk),回归了 Task 5 的收益本身,
|
|
142
|
+
// 靠这个标志判定比按 chunk 文件名匹配稳(文件名带 hash 会变)。几字节,不影响运行时行为。
|
|
143
|
+
if (typeof window !== 'undefined') window.__EP_REST_LOADED__ = true
|
|
144
|
+
return EP
|
|
145
|
+
}).catch(err => {
|
|
146
|
+
// ⚠️ 这行的措辞曾经写反过——终审 C-1 实测:浏览器原生 ES 模块加载器把同一个 chunk URL 的
|
|
147
|
+
// 失败结果**永久缓存**在当前页面的 module map 里,同一 specifier 再 `import()` 一次会
|
|
148
|
+
// 直接复用那条 rejection,不会有新的网络请求(实测:idle 预取阶段 abort 一次 ui-*.js 后,
|
|
149
|
+
// 网络完全恢复,渲染 el-table/el-select/… 仍然拿到同一条缓存的失败,新发起的 ui-*.js
|
|
150
|
+
// 请求数 = 0)。真正能让下次渲染重新发请求的路径只有**整页刷新**(拿到全新的 module map)。
|
|
151
|
+
// `restPending = null` 在这里唯一还有意义的场景,是「本次调用的 import() 因为某种原因
|
|
152
|
+
// 从未真正发出就同步失败」(目前没有这样的路径,纯防御)——不要再指望它能让网络失败
|
|
153
|
+
// 自愈。真正对用户可见的兜底在下面 defineAsyncComponent 的 onError 里:重试耗尽后弹一次
|
|
154
|
+
// ElMessage 引导刷新,处置口径与 Task 3 给 jsencrypt 的 13 个调用点一致
|
|
155
|
+
// (见 packages/utils/src/lazy-jsencrypt.js 同款注释)。
|
|
156
|
+
restPending = null
|
|
157
|
+
throw err
|
|
158
|
+
})
|
|
159
|
+
return restPending
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const EAGER_COMPONENTS = [
|
|
163
|
+
ElAvatar, ElButton, ElCard, ElCol, ElRow, ElDrawer,
|
|
164
|
+
ElDropdown, ElDropdownMenu, ElDropdownItem, ElInput,
|
|
165
|
+
ElMenu, ElMenuItem, ElSubMenu, ElOverlay, ElPopover,
|
|
166
|
+
ElRadioGroup, ElRadioButton, ElScrollbar, ElTooltip,
|
|
167
|
+
ElTabs, ElTabPane, ElCheckbox, ElIcon
|
|
168
|
+
]
|
|
169
|
+
|
|
170
|
+
// 图标名垫片:把每个 el-icon-* 字形名注册成一个渲染 `<i class="el-icon-xxx">` 的极小组件。
|
|
171
|
+
//
|
|
172
|
+
// ## 为什么需要
|
|
173
|
+
// 「决策 A」(见文件顶部)保住了 `<i class="el-icon-xxx">` 这一形态,却漏了 **prop 形态**:
|
|
174
|
+
//
|
|
175
|
+
// <el-button icon="el-icon-search"> <el-input suffix-icon="el-icon-search">
|
|
176
|
+
//
|
|
177
|
+
// element-ui 时代 icon prop 就是**类名**,直接渲染成 `<i class="el-icon-search">`;
|
|
178
|
+
// EP 拿它当**组件**用,实测渲染成:
|
|
179
|
+
//
|
|
180
|
+
// <i class="el-icon"><el-icon-search></el-icon-search></i>
|
|
181
|
+
//
|
|
182
|
+
// 外层类是 `el-icon`(**没有尾横线**),匹配不到 icon.css 的
|
|
183
|
+
// `[class*=" el-icon-"],[class^=el-icon-]`;内层是个没注册过的名字,Vue 3 的
|
|
184
|
+
// resolveDynamicComponent 解析不到就原样当未知原生元素渲染 —— 什么都不画。
|
|
185
|
+
// 结果是图标凭空消失,**不报错、不告警**,只能靠肉眼发现。
|
|
186
|
+
//
|
|
187
|
+
// 全仓实测 201 处这样静默失效(模板 172 / JSX 29;suffix-icon 116、icon 71、prefix-icon 14),
|
|
188
|
+
// 涉及 91 个文件、20 个不同图标名,全部在字体里有字形。
|
|
189
|
+
//
|
|
190
|
+
// ## 为什么是垫片而不是改 201 处模板
|
|
191
|
+
// 另有 11 处是 `:icon="operate.iconCls"` / `:icon="item.icon"` 这类动态绑定,**值存在数据库的
|
|
192
|
+
// 页面配置里**——改模板根本够不着。这恰恰是决策 A 当初拿来当决定性理由的那类情况,
|
|
193
|
+
// 所以正确的做法是补上决策 A 的缺口,而不是逐处重写调用点。
|
|
194
|
+
// 顺带的好处:EP 自带的「图标 + 文字」间距(.el-icon + span)继续生效;若改写成
|
|
195
|
+
// `<el-button><i class="..."/>文字</el-button>` 反而要为 201 处各自补间距。
|
|
196
|
+
//
|
|
197
|
+
// ## 名字从哪来
|
|
198
|
+
// build/gen-element-icons.mjs 从 icon.css 枚举(幂等 codegen,产物已 gitignore)。
|
|
199
|
+
// 不硬编:字体换了/加了图标,清单自动跟随。build/__check-element-icons.mjs 守着不漂。
|
|
200
|
+
export function registerElementIconShims (app) {
|
|
201
|
+
for (const name of iconNames) {
|
|
202
|
+
// 已被别处占用就不覆盖(正常不会发生,但覆盖真身组件的代价远大于少一个图标)
|
|
203
|
+
if (app.component(name)) continue
|
|
204
|
+
app.component(name, {
|
|
205
|
+
name,
|
|
206
|
+
// 无状态、无 props,只画一个字体图标。写成 render 而不是模板:本文件不过编译器。
|
|
207
|
+
render: () => h('i', { class: name })
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export default function installElementPlus (app) {
|
|
213
|
+
// ① eager 组件:同步注册真身
|
|
214
|
+
for (const c of EAGER_COMPONENTS) {
|
|
215
|
+
if (c && c.name) app.component(c.name, c)
|
|
216
|
+
}
|
|
217
|
+
// ② 其余组件:注册异步包装器。远程模块/页面配置里的 <el-table> 走
|
|
218
|
+
// resolveComponent('el-table') 命中这里,写法完全不变(vue2-render-compat.js 的
|
|
219
|
+
// compatH 对带 '-' 的字符串标签也走 resolveComponent,故 JSX/render 形态同样覆盖)。
|
|
220
|
+
for (const tag of epLazy.tags) {
|
|
221
|
+
const exportName = epLazy.exportOf[tag]
|
|
222
|
+
const wrapper = defineAsyncComponent({
|
|
223
|
+
loader: () => loadElementPlusRest().then(EP => EP[exportName]),
|
|
224
|
+
// Vue 的重试判据只看 attempts,err 本身无需处理(重试次数耗尽后失败态由
|
|
225
|
+
// defineAsyncComponent 内部负责往上抛)。⚠️ 这几次 retry() 救不回网络失败——见上面
|
|
226
|
+
// loadElementPlusRest 的 catch 注释,module map 缓存的 rejection 不会因为再 import()
|
|
227
|
+
// 一次而产生新请求。C-1(终审 Critical):改造前 EP 是 eager 的,一次抖动会让应用直接
|
|
228
|
+
// 启动不了(响亮、用户会本能刷新);改造后退化成外壳正常、内容静默变成一片空注释
|
|
229
|
+
// (<!---->),没有任何提示。重试耗尽后补一次 ElMessage.error,把「响亮」这个属性找
|
|
230
|
+
// 回来,处置口径与 Task 3 给 jsencrypt 13 个调用点做的一致(见 lazy-jsencrypt.js)。
|
|
231
|
+
// eslint-disable-next-line handle-callback-err
|
|
232
|
+
onError: (err, retry, fail, attempts) => {
|
|
233
|
+
if (attempts <= 2) {
|
|
234
|
+
retry()
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
if (!restFailNotified) {
|
|
238
|
+
restFailNotified = true
|
|
239
|
+
ElMessage.error('界面组件加载失败,请刷新页面重试')
|
|
240
|
+
}
|
|
241
|
+
fail()
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
// ⚠️ 必须顶着真身的 name 注册。defineAsyncComponent 返回的组件对象 name 恒为
|
|
245
|
+
// 'AsyncComponentWrapper',而 EP **自己**有多处靠 `vnode.type.name === 'ElXxx'` 内省子节点:
|
|
246
|
+
// - table-column:`childNode.type?.name === 'ElTableColumn' || childNode.shapeFlag & 2`
|
|
247
|
+
// (实测包装 vnode 的 shapeFlag 也是 36,&2 为 0 —— 两个判据双双失败)→ 子列 children
|
|
248
|
+
// 恒空、嵌套子列从不挂载、insertColumn 从不调用,**不报错也不告警**;
|
|
249
|
+
// 同文件 render-helper 的 `item.vParent = instance` 也吊在同一条判据上;
|
|
250
|
+
// - carousel 的 PlaceholderItem 按 'ElCarouselItem' 过滤(两张轮播图的 loop 复制);
|
|
251
|
+
// - container 按 name 分辨 ElHeader/ElFooter 决定 is-vertical。
|
|
252
|
+
// 这些内省看的是**注册进 vnode.type 的那个对象**,也就是包装器本身,所以只能在这里补名。
|
|
253
|
+
// nameOf 由 codegen 从 EP 运行时导出面取(build/gen-element-plus-lazy.mjs),不硬编。
|
|
254
|
+
wrapper.name = epLazy.nameOf[tag]
|
|
255
|
+
app.component(tag, wrapper)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// 止血开关:window.EPX_EP_LAZY = false 时**额外**立刻把整个 EP 拉起来,语义是
|
|
259
|
+
// 「放弃按需、立刻全量」—— 所有按需间隙一次性消失。
|
|
260
|
+
// ⚠️ 它**不跳过**上面的包装器注册。早先的写法是「开关打开就只做一次异步 app.use、
|
|
261
|
+
// 不注册包装器」,那会让首帧渲染时所有 EP 组件都还没注册、页面先坏一下再好 ——
|
|
262
|
+
// 比不开开关更糟,而这恰恰是线上出事时要走的那条路。
|
|
263
|
+
if (typeof window !== 'undefined' && window.EPX_EP_LAZY === false) {
|
|
264
|
+
loadElementPlusRest().catch(err => console.error('[element-plus] 立刻全量加载失败', err))
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ②b 图标名垫片:补上「决策 A」漏掉的 **prop 形态**(详见下面 registerElementIconShims)
|
|
268
|
+
registerElementIconShims(app)
|
|
269
|
+
|
|
270
|
+
// ③ 指令:Vue 没有 defineAsyncDirective,resolveDirective 拿到 Promise 不工作 —— 必须 eager
|
|
271
|
+
app.directive('loading', ElLoadingDirective)
|
|
272
|
+
app.directive('infinite-scroll', ElInfiniteScroll)
|
|
273
|
+
// I-4(终审 Important):改造前 app.use(ElementPlus) 会通过 EP 自带的插件清单顺带注册
|
|
274
|
+
// v-popover;拆掉 app.use 后这行漏掉了,3 处活代码(Bpmn.vue ×2、PageVariablePanel.vue ×1,
|
|
275
|
+
// 均无其它触发锚点)静默失效——生产构建下 Vue 对 resolveDirective 拿到 undefined 是完全
|
|
276
|
+
// 静默的 no-op,不抛错不告警。ElPopover 本来就在 eager 白名单里、这行从同一个模块深路径
|
|
277
|
+
// 带出 ElPopoverDirective,零体积代价。
|
|
278
|
+
app.directive('popover', ElPopoverDirective)
|
|
279
|
+
|
|
280
|
+
// ④ 全局配置(locale):原来靠 app.use(ElementPlus, epConfig) 注入,去掉 app.use 之后
|
|
281
|
+
// 必须自己调,否则 setElementPlusLocale() 改 epConfig 不再有任何效果 —— **静默失效**,
|
|
282
|
+
// 表现是切了语言没反应,不报错。e2e 有守卫。
|
|
283
|
+
provideGlobalConfig(epConfig, app, true)
|
|
284
|
+
|
|
285
|
+
// ⑤ 命令式服务:函数调用,没有渲染时机可挂钩,必须 eager
|
|
286
|
+
const gp = app.config.globalProperties
|
|
287
|
+
gp.$message = ElMessage
|
|
288
|
+
gp.$msgbox = ElMessageBox
|
|
289
|
+
gp.$alert = ElMessageBox.alert
|
|
290
|
+
gp.$confirm = ElMessageBox.confirm
|
|
291
|
+
gp.$prompt = ElMessageBox.prompt
|
|
292
|
+
gp.$notify = ElNotification
|
|
293
|
+
gp.$loading = ElLoading.service
|
|
294
|
+
// element-ui 遗留:全仓有 `(this.$ELEMENT || {}).size` 之类读取,留兜底避免 render 告警。
|
|
295
|
+
gp.$ELEMENT = { size: '', zIndex: 2000 }
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// e2e 守卫③用:暴露 setElementPlusLocale 给页面里直接调用,验证 provideGlobalConfig 没漏。
|
|
299
|
+
// 只挂函数、不挂数据,不构成新的对外契约(window.frame ABI 未变)。
|
|
300
|
+
if (typeof window !== 'undefined') {
|
|
301
|
+
window.__EPX_EP_PLUGIN__ = { setElementPlusLocale }
|
|
302
|
+
}
|