@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.
Files changed (149) hide show
  1. package/API.md +30 -27
  2. package/README.md +5 -4
  3. package/dist/.cache/route-chart-release/frontend/frame/public/js/ecStat.min.js +4 -0
  4. package/dist/.cache/route-chart-release/frontend/frame/public/js/echarts-wordcloud.min.js +4 -0
  5. package/dist/.cache/route-chart-release/frontend/frame/public/js/echarts.min.js +4 -0
  6. package/dist/_virtual/_plugin-vue_export-helper.js +10 -0
  7. package/dist/_virtual/jsencrypt.min.js +1 -1
  8. package/dist/assets/ecStat.min-BrTYPOXX.js +1 -0
  9. package/dist/assets/echarts-wordcloud.min-nWR4Hzny.js +3 -0
  10. package/dist/assets/echarts.min-DROS9E5X.js +45 -0
  11. package/dist/assets/role_select-DTqRlFNz.webp +0 -0
  12. package/dist/{_vendor → frontend/frame/_vendor}/.pnpm/jsencrypt@3.5.4/_vendor/jsencrypt/bin/jsencrypt.min.js +1 -1
  13. package/dist/src/Container.js +1 -0
  14. package/dist/src/RootApp.js +54 -0
  15. package/dist/src/api/config.js +1 -0
  16. package/dist/src/api/createHttp.js +7 -0
  17. package/dist/src/api/index.js +1 -0
  18. package/dist/src/app-layout.js +1 -0
  19. package/dist/src/app-ui-settings.js +49 -0
  20. package/dist/src/app.js +1 -0
  21. package/dist/src/axios.js +11 -15
  22. package/dist/src/capture-ref.js +1 -0
  23. package/dist/src/chart-runtime-assets.js +9 -0
  24. package/dist/src/components/draggable/index.js +1 -0
  25. package/dist/src/create-app.js +4 -6
  26. package/dist/src/directives/clickoutside.js +1 -0
  27. package/dist/src/directives/global.js +1 -0
  28. package/dist/src/index.js +3 -0
  29. package/dist/src/lazy-library.js +1 -0
  30. package/dist/src/loadComponent.js +3 -2
  31. package/dist/src/loadLibrary.js +1 -0
  32. package/dist/src/menu-route.js +12 -0
  33. package/dist/src/mixins/component.js +4 -0
  34. package/dist/src/mixins/custom-option.js +1 -0
  35. package/dist/src/mixins/el-locale.js +1 -0
  36. package/dist/src/mixins/global.js +1 -0
  37. package/dist/src/mixins/index.js +1 -0
  38. package/dist/src/mixins/methods-to-prototype.js +1 -0
  39. package/dist/src/mixins/optimized-global.js +1 -0
  40. package/dist/src/mixins/popper.js +1 -0
  41. package/dist/src/navigation-context.js +47 -0
  42. package/dist/src/navigation.js +44 -12
  43. package/dist/src/package-install.js +4 -2
  44. package/dist/src/plugins/element-plus.js +9 -8
  45. package/dist/src/plugins/i18n.js +1 -0
  46. package/dist/src/plugins/library-locale.js +1 -0
  47. package/dist/src/progress-bar.js +1 -0
  48. package/dist/src/public-api.js +5 -2
  49. package/dist/src/register-components.js +1 -0
  50. package/dist/src/remote-deps.js +1 -0
  51. package/dist/src/route-components.js +9 -0
  52. package/dist/src/router/config.js +8 -8
  53. package/dist/src/router/index.js +1 -0
  54. package/dist/src/router/public.js +1 -0
  55. package/dist/src/runtime-services.js +2 -1
  56. package/dist/src/runtime-utils/fieldFactory.js +1 -0
  57. package/dist/src/runtime-utils/meta.js +4 -0
  58. package/dist/src/runtime-utils/pdf.viewer.js +32 -15
  59. package/dist/src/runtime-utils/util.js +30 -5
  60. package/dist/src/shallow-same.js +1 -0
  61. package/dist/src/shared-packages.js +2 -3
  62. package/dist/src/store/actions.js +1 -0
  63. package/dist/src/store/create-store.js +1 -0
  64. package/dist/src/store/index.js +1 -0
  65. package/dist/src/store/lru.js +1 -0
  66. package/dist/src/store/modules/config.js +1 -0
  67. package/dist/src/store/modules/file.js +1 -0
  68. package/dist/src/store/modules/index.js +1 -0
  69. package/dist/src/store/modules/list.js +26 -3
  70. package/dist/src/store/modules/locale.js +1 -0
  71. package/dist/src/store/modules/meta.js +50 -7
  72. package/dist/src/store/modules/page.js +1 -0
  73. package/dist/src/store/modules/router.js +1 -0
  74. package/dist/src/store/modules/tabs.js +1 -0
  75. package/dist/src/store/modules/user.js +1 -0
  76. package/dist/src/style-presets.js +5 -4
  77. package/dist/src/ui-settings.js +91 -18
  78. package/dist/src/viewport.js +1 -0
  79. package/dist/src/views/App.js +48 -0
  80. package/dist/src/views/AppRoleSelect.js +233 -0
  81. package/dist/src/views/Error.js +210 -0
  82. package/dist/src/views/Module.js +173 -0
  83. package/dist/src/vue.bus.js +1 -0
  84. package/dist/src/vue2-cancel-token-compat.js +1 -0
  85. package/dist/src/vue2-remote-shim.js +1 -0
  86. package/dist/src/vue2-render-compat.js +1 -0
  87. package/dist/src/vue2-select-style-compat.js +1 -0
  88. package/dist/src/watermark.js +1 -0
  89. package/dist/src/xss.js +1 -0
  90. package/dist/src/z-index.js +1 -0
  91. package/dist/style.css +251 -0
  92. package/package.json +6 -5
  93. package/src/RootApp.vue +75 -0
  94. package/src/api/createHttp.js +7 -0
  95. package/src/api/index.js +47 -47
  96. package/src/app-ui-settings.js +31 -0
  97. package/src/axios.js +311 -318
  98. package/src/chart-runtime-assets.js +7 -0
  99. package/src/create-app.js +3 -7
  100. package/src/index.js +28 -28
  101. package/src/install.js +157 -157
  102. package/src/loadComponent.js +187 -187
  103. package/src/loadLibrary.js +99 -99
  104. package/src/menu-route.js +12 -1
  105. package/src/mixins/component.js +1464 -1461
  106. package/src/navigation-context.js +49 -0
  107. package/src/navigation.js +208 -176
  108. package/src/package-install.js +2 -1
  109. package/src/plugins/element-plus.js +302 -302
  110. package/src/plugins/frame.js +178 -178
  111. package/src/public-api.js +1 -0
  112. package/src/route-components.js +7 -0
  113. package/src/router/config.js +861 -864
  114. package/src/router/public.js +1 -1
  115. package/src/runtime-services.js +1 -1
  116. package/src/runtime-utils/fieldFactory.js +179 -179
  117. package/src/runtime-utils/meta.js +399 -378
  118. package/src/runtime-utils/pdf.viewer.js +672 -644
  119. package/src/runtime-utils/util.js +1357 -1330
  120. package/src/shared-packages.js +1 -3
  121. package/src/store/modules/config.js +351 -351
  122. package/src/store/modules/list.js +30 -3
  123. package/src/store/modules/locale.js +236 -236
  124. package/src/store/modules/meta.js +314 -265
  125. package/src/style-presets.js +4 -4
  126. package/src/ui-settings.js +84 -23
  127. package/src/views/App.vue +53 -0
  128. package/src/views/AppRoleSelect.vue +334 -0
  129. package/src/views/Error.vue +63 -0
  130. package/src/views/Module.vue +210 -0
  131. package/src/xss.js +26 -26
  132. package/types/app.d.ts +10 -10
  133. package/types/axios.d.ts +1 -1
  134. package/types/component-mixin.d.ts +1 -1
  135. package/types/field-factory.d.ts +1 -1
  136. package/types/global-mixin.d.ts +1 -1
  137. package/types/http.d.ts +1 -1
  138. package/types/index.d.ts +3 -0
  139. package/types/meta.d.ts +1 -1
  140. package/types/navigation.d.ts +21 -21
  141. package/types/pdf-viewer.d.ts +1 -1
  142. package/types/runtime.d.ts +13 -13
  143. package/types/store.d.ts +1 -1
  144. package/types/util.d.ts +2 -2
  145. /package/dist/{build → .cache/route-chart-release/frontend/frame/build}/stubs/empty-components.js +0 -0
  146. /package/dist/{src → .cache/route-chart-release/frontend/frame/src}/element-icons.names.js +0 -0
  147. /package/dist/{src → .cache/route-chart-release/frontend/frame/src}/element-plus.lazy.js +0 -0
  148. /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
  149. /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,187 +1,187 @@
1
- import { loadBusinessComponents } from './runtime-services'
2
- // 按页面配置的 pagePath 推导组件名,并(首次遇到时)懒注册整个 system.component 组件组。
3
- //
4
- // T29:原实现用 **Vue2 的 resolve 回调式异步组件**:
5
- // Vue.component(name, function (resolve) { import(...).then(m => resolve(m.default[name])) })
6
- // 这在 @vue/compat 下靠 `COMPONENT_ASYNC` 开关兜着。但 Element Plus 与 compat 共存**必须**
7
- // 关掉 COMPONENT_ASYNC(否则 compat 把 EP 的函数组件误当 Vue2 异步组件转换 → 登录页白屏),
8
- // 于是这里的 `resolve` 不再被注入 → `TypeError: resolve is not a function`,
9
- // **所有走懒注册的页面组件(entity-list 等)全部加载失败、表格消失**。
10
- //
11
- // ⇒ 改用 Vue3 原生 `defineAsyncComponent`(loader 返回 Promise<Component>)。
12
- // 行为等价:首次渲染该组件名时才真正 import;import 完顺带把整组注册进 app。
13
- //
14
- // (顺带纠正一条记档:此前断言"全仓无 Vue2 异步组件用法、关 COMPONENT_ASYNC 安全"——**是错的**,
15
- // 漏了这一处 resolve 回调式注册。它不是 `() => import()` 也不是 `() => ({component})`,
16
- // 所以按那两种形态 grep 时没命中。)
17
- import { defineAsyncComponent } from 'vue'
18
- import ensureRemoteDeps from '@epxing/core/src/remote-deps'
19
-
20
- /* global __EPX_BUSINESS_REMOTE__ */
21
- // ADR-010 阶段2:__EPX_BUSINESS_REMOTE__ 是构建期常量(vite define,见 vite.config.mjs)。
22
- // - 未定义 / false(默认):业务视图随平台构建,本地 import system.component.js(行为不变)。
23
- // - true(EXTERNALIZE_BUSINESS=true 构建):平台【不含】业务视图(本地分支被 tree-shake,
24
- // system.component.js 掉出产物 → 瘦平台)。运行时从 window.__EPX_BUSINESS_REMOTE_URL__
25
- // 指向的【独立部署】import() 拉起 business remote bundle(vue/vuex/http 走 window.frame 共享)。
26
-
27
- // remote 的 style.css 只注入一次
28
- let remoteCssInjected = false
29
- function injectRemoteCss (href) {
30
- if (remoteCssInjected || !href) return
31
- remoteCssInjected = true
32
- const link = document.createElement('link')
33
- link.rel = 'stylesheet'
34
- link.href = href
35
- document.head.appendChild(link)
36
- }
37
-
38
- // 可选能力组(designer/dashboard)运行时懒加载:
39
- // 原为启动时静态注册(component.designer/dashboard.js 经 @platform-optional/* spread 进 113 表),
40
- // 设计器 210 文件被 eager 打进 frame chunk,而绝大多数用户从不打开设计器。
41
- // 现平台构建把静态别名【永远】指空桩,本处经 -lazy 说明符按需 import:
42
- // 业务组(system.component)没命中 → dashboard(小)→ designer(大),谁命中用谁。
43
- // INCLUDE_DESIGNER/DASHBOARD=false 时 -lazy 也指空桩(裁剪版行为与 ADR-009 一致:兜底报错)。
44
- // ⚠️ 注册须兑现 .install 语义(Dashboard/PageDesigner 等聚合的 install 会注册一组子组件),
45
- // 用「录名 guard」捕获 install 里每个 name→comp(异步组件名已被 wrapper 占位,须从表取真身)。
46
- // 按需形态严格模式(可插拔子路径入口,PROJECT-GUIDE §三):window.EPX_DISABLE_LAZY_PACKS = true
47
- // 时,未显式 app.use() 安装的能力包(business/designer/dashboard)**不做运行时懒加载兜底**——
48
- // 页面配置引用到其组件名走 loadComponent 报错兜底(不渲染 + console 报名字),"没装就不出现"。
49
- // 默认关:产品 dev 壳登录后落的是平台页面(服务大厅等由 business 组件拼成),兜底保证不白屏。
50
- const strictPacks = () => typeof window !== 'undefined' && window.EPX_DISABLE_LAZY_PACKS
51
-
52
- const optionalGroupCache = {}
53
- const OPTIONAL_GROUPS = {
54
- dashboard: () => import('@platform-optional/dashboard-lazy'),
55
- designer: () => import('@platform-optional/designer-lazy')
56
- }
57
- function loadOptionalGroup (app, which) {
58
- if (strictPacks()) return Promise.resolve({})
59
- if (!optionalGroupCache[which]) {
60
- optionalGroupCache[which] = OPTIONAL_GROUPS[which]().then(m => {
61
- const group = m.default || {}
62
- const byName = {}
63
- const guard = new Proxy(app, {
64
- get (target, prop, receiver) {
65
- if (prop === 'component') {
66
- return (name, comp) => {
67
- if (comp === undefined) return target.component(name)
68
- byName[name] = comp
69
- if (typeof window !== 'undefined' && window.componentLibrary) window.componentLibrary[name] = comp
70
- if (!target.component(name)) target.component(name, comp)
71
- return guard
72
- }
73
- }
74
- const v = Reflect.get(target, prop, receiver)
75
- return typeof v === 'function' ? v.bind(target) : v
76
- }
77
- })
78
- for (const key in group) {
79
- const comp = group[key]
80
- if (!comp) continue
81
- if (comp.install) comp.install(guard)
82
- else guard.component(comp.name || key, comp)
83
- }
84
- return byName
85
- })
86
- }
87
- return optionalGroupCache[which]
88
- }
89
-
90
- // 业务组解析(两种模式,只 import 一次):
91
- // - 瘦平台(__EPX_BUSINESS_REMOTE__):remote bundle 整组加载(远程产物本就单包,不拆)
92
- // - 本地默认:**按名文件级懒加载**(system.component.lazy.js)——名→文件索引 + async wrapper
93
- // 全量注册(业务视图模板互相按名引用可命中);索引未命中/校验不符自动回退整组,
94
- // 最坏情况 = 旧整组行为。返回统一的 { resolve(app, name) } 接口。
95
- let systemComponentsPromise = null
96
- function loadSystemComponents (app) {
97
- // 严格模式只拦「隐式懒加载兜底」;瘦平台的 remote business 是显式部署配置
98
- // (EXTERNALIZE_BUSINESS 构建 + config.js 配 URL)="已安装",不受严格模式影响。
99
- const isRemote = typeof __EPX_BUSINESS_REMOTE__ !== 'undefined' && __EPX_BUSINESS_REMOTE__
100
- if (strictPacks() && !isRemote) {
101
- return Promise.resolve({ resolve: () => Promise.resolve(null) })
102
- }
103
- if (!systemComponentsPromise) {
104
- if (typeof __EPX_BUSINESS_REMOTE__ !== 'undefined' && __EPX_BUSINESS_REMOTE__) {
105
- // 瘦平台:从独立部署的 remote bundle 拉业务视图(整组注册)
106
- const remote = (typeof window !== 'undefined' && window.__EPX_BUSINESS_REMOTE_URL__) || {}
107
- injectRemoteCss(remote.css)
108
- // 先备好该远程包要用到的懒加载平台组件,再 import 它(见 core/remote-deps.js)。
109
- // .catch(() => {}) 是硬约束「绝不因自动 ensure 挡住远程加载」的护栏:ensure 只是优化,
110
- // 它 reject 了也必须继续 import remote(真缺组件时由 frame.js 的自解释 shim 兜底报错)。
111
- systemComponentsPromise = ensureRemoteDeps([remote.js]).catch(() => {}).then(() => import(/* @vite-ignore */ remote.js))
112
- .then(mod => mod.components || (mod.default && mod.default.components) || mod.default)
113
- .then(components => {
114
- for (const key in components) {
115
- if (!app.component(key)) app.component(key, components[key])
116
- }
117
- return { resolve: (_app, name) => Promise.resolve(components[name]) }
118
- })
119
- } else {
120
- // 默认:按名文件级懒加载(含整组兜底与 wrapper 全量注册)
121
- systemComponentsPromise = loadBusinessComponents(app)
122
- }
123
- }
124
- return systemComponentsPromise
125
- }
126
-
127
- // 注:入参在 T30b app 化后是 **app 实例**(不再是 compat 的全局 Vue)。
128
- // `app.component(name)` / `app.component(name, comp)` 与 `Vue.component` 同签名,故调用点无需改。
129
- export default function loadComponentFactory (app) {
130
- return (pagePath, defaultComponent) => {
131
- let pathName
132
- let componentName = 'epx-' + defaultComponent
133
- if (pagePath) {
134
- const rootMatch = pagePath.match(/^@\/(frame\/|product\/|project\/)?/)
135
- if (rootMatch) {
136
- pathName = pagePath.split(rootMatch[0])[1].replace('.vue', '')
137
- }
138
- const componentPath = pathName.replace(/\//g, '-').replace(/(\w)([A-Z])/g, '$1-$2').replace('views-', '').replace('components-', '').replace('packages-', '').toLowerCase()
139
- componentName = 'epx-' + componentPath
140
- }
141
- if (!app.component(componentName)) {
142
- app.component(componentName, defineAsyncComponent(() =>
143
- loadSystemComponents(app).then(m => m.resolve(app, componentName)).then(comp => {
144
- if (comp) return comp
145
- // 产品/项目远程库的组件表兜底。
146
- //
147
- // 为什么必须有这一环:本工厂与产品库的 install 都是「没人占我才占」——
148
- // 平台 ↓ 本文件 产品 ↓ 其 src/index.js
149
- // if (!app.component(name)) … if (!app.component(key)) …
150
- // 谁先跑谁赢。页面配置引用到某个产品组件时 Page.vue 会先按名字调本工厂
151
- // (Page.vue:234/330),此时远程库可能还没 install,占位就落位了;随后产品库
152
- // install 看到「已存在」直接跳过 —— 槽位归占位,解析时在平台组件里找不到就抛
153
- // `system.component 里没有 epx-xxx`,**产品组件永远出不来**。
154
- // 2026-09-03 product/hr 的 epx-hr-search 实测就是这条路径。
155
- //
156
- // window.componentLibrary 由 util.loadJs 的 onload 按组件名汇总各远程库的组件,
157
- // 到解析时(渲染时机,loadLibrary 早已在路由守卫里 await 过)必然已就绪。
158
- //
159
- // ⚠️ 放在可选能力组**之前**:产品组件走到这里就该命中,否则会为了一个
160
- // epx-hr-* 白拉一遍 dashboard 和体积很大的 designer。
161
- // ⚠️ 只认对象/函数:loadJs 会把远程库导出的 name/version 这些标量也一并汇总进来。
162
- const fromProduct = (typeof window !== 'undefined' && window.componentLibrary) || {}
163
- const pc = fromProduct[componentName]
164
- if (pc && (typeof pc === 'object' || typeof pc === 'function')) return pc
165
- // 可选能力组兜底:dashboard(小)→ designer(大),运行时按需拉起
166
- return loadOptionalGroup(app, 'dashboard').then(dash => {
167
- if (dash[componentName]) return dash[componentName]
168
- return loadOptionalGroup(app, 'designer').then(des => {
169
- const c = des[componentName]
170
- if (!c) {
171
- throw new Error(strictPacks()
172
- ? `[loadComponent] 严格模式(EPX_DISABLE_LAZY_PACKS)下未做懒加载兜底:${componentName} 若来自 business/designer/dashboard,请 app.use(对应 epx-frame 子路径入口) 显式安装`
173
- : `[loadComponent] 找不到 ${componentName}:平台组件、产品远程库` +
174
- '(window.componentLibrary)、dashboard、designer 里都没有。\n' +
175
- ' 若它来自产品/项目远程库:确认该库已在 FM_SYSTEM_WEBPACK 配好且加载成功' +
176
- '(控制台看 window.componentLibrary 有没有这个名字),' +
177
- '并检查库里组件的 name 与页面配置引用的名字是否一致。')
178
- }
179
- return c
180
- })
181
- })
182
- })
183
- ))
184
- }
185
- return componentName
186
- }
187
- }
1
+ import { loadBusinessComponents } from './runtime-services'
2
+ // 按页面配置的 pagePath 推导组件名,并(首次遇到时)懒注册整个 system.component 组件组。
3
+ //
4
+ // T29:原实现用 **Vue2 的 resolve 回调式异步组件**:
5
+ // Vue.component(name, function (resolve) { import(...).then(m => resolve(m.default[name])) })
6
+ // 这在 @vue/compat 下靠 `COMPONENT_ASYNC` 开关兜着。但 Element Plus 与 compat 共存**必须**
7
+ // 关掉 COMPONENT_ASYNC(否则 compat 把 EP 的函数组件误当 Vue2 异步组件转换 → 登录页白屏),
8
+ // 于是这里的 `resolve` 不再被注入 → `TypeError: resolve is not a function`,
9
+ // **所有走懒注册的页面组件(entity-list 等)全部加载失败、表格消失**。
10
+ //
11
+ // ⇒ 改用 Vue3 原生 `defineAsyncComponent`(loader 返回 Promise<Component>)。
12
+ // 行为等价:首次渲染该组件名时才真正 import;import 完顺带把整组注册进 app。
13
+ //
14
+ // (顺带纠正一条记档:此前断言"全仓无 Vue2 异步组件用法、关 COMPONENT_ASYNC 安全"——**是错的**,
15
+ // 漏了这一处 resolve 回调式注册。它不是 `() => import()` 也不是 `() => ({component})`,
16
+ // 所以按那两种形态 grep 时没命中。)
17
+ import { defineAsyncComponent } from 'vue'
18
+ import ensureRemoteDeps from '@epxing/core/src/remote-deps'
19
+
20
+ /* global __EPX_BUSINESS_REMOTE__ */
21
+ // ADR-010 阶段2:__EPX_BUSINESS_REMOTE__ 是构建期常量(vite define,见 vite.config.mjs)。
22
+ // - 未定义 / false(默认):业务视图随平台构建,本地 import system.component.js(行为不变)。
23
+ // - true(EXTERNALIZE_BUSINESS=true 构建):平台【不含】业务视图(本地分支被 tree-shake,
24
+ // system.component.js 掉出产物 → 瘦平台)。运行时从 window.__EPX_BUSINESS_REMOTE_URL__
25
+ // 指向的【独立部署】import() 拉起 business remote bundle(vue/vuex/http 走 window.frame 共享)。
26
+
27
+ // remote 的 style.css 只注入一次
28
+ let remoteCssInjected = false
29
+ function injectRemoteCss (href) {
30
+ if (remoteCssInjected || !href) return
31
+ remoteCssInjected = true
32
+ const link = document.createElement('link')
33
+ link.rel = 'stylesheet'
34
+ link.href = href
35
+ document.head.appendChild(link)
36
+ }
37
+
38
+ // 可选能力组(designer/dashboard)运行时懒加载:
39
+ // 原为启动时静态注册(component.designer/dashboard.js 经 @platform-optional/* spread 进 113 表),
40
+ // 设计器 210 文件被 eager 打进 frame chunk,而绝大多数用户从不打开设计器。
41
+ // 现平台构建把静态别名【永远】指空桩,本处经 -lazy 说明符按需 import:
42
+ // 业务组(system.component)没命中 → dashboard(小)→ designer(大),谁命中用谁。
43
+ // INCLUDE_DESIGNER/DASHBOARD=false 时 -lazy 也指空桩(裁剪版行为与 ADR-009 一致:兜底报错)。
44
+ // ⚠️ 注册须兑现 .install 语义(Dashboard/PageDesigner 等聚合的 install 会注册一组子组件),
45
+ // 用「录名 guard」捕获 install 里每个 name→comp(异步组件名已被 wrapper 占位,须从表取真身)。
46
+ // 按需形态严格模式(可插拔子路径入口,PROJECT-GUIDE §三):window.EPX_DISABLE_LAZY_PACKS = true
47
+ // 时,未显式 app.use() 安装的能力包(business/designer/dashboard)**不做运行时懒加载兜底**——
48
+ // 页面配置引用到其组件名走 loadComponent 报错兜底(不渲染 + console 报名字),"没装就不出现"。
49
+ // 默认关:产品 dev 壳登录后落的是平台页面(服务大厅等由 business 组件拼成),兜底保证不白屏。
50
+ const strictPacks = () => typeof window !== 'undefined' && window.EPX_DISABLE_LAZY_PACKS
51
+
52
+ const optionalGroupCache = {}
53
+ const OPTIONAL_GROUPS = {
54
+ dashboard: () => import('@platform-optional/dashboard-lazy'),
55
+ designer: () => import('@platform-optional/designer-lazy')
56
+ }
57
+ function loadOptionalGroup (app, which) {
58
+ if (strictPacks()) return Promise.resolve({})
59
+ if (!optionalGroupCache[which]) {
60
+ optionalGroupCache[which] = OPTIONAL_GROUPS[which]().then(m => {
61
+ const group = m.default || {}
62
+ const byName = {}
63
+ const guard = new Proxy(app, {
64
+ get (target, prop, receiver) {
65
+ if (prop === 'component') {
66
+ return (name, comp) => {
67
+ if (comp === undefined) return target.component(name)
68
+ byName[name] = comp
69
+ if (typeof window !== 'undefined' && window.componentLibrary) window.componentLibrary[name] = comp
70
+ if (!target.component(name)) target.component(name, comp)
71
+ return guard
72
+ }
73
+ }
74
+ const v = Reflect.get(target, prop, receiver)
75
+ return typeof v === 'function' ? v.bind(target) : v
76
+ }
77
+ })
78
+ for (const key in group) {
79
+ const comp = group[key]
80
+ if (!comp) continue
81
+ if (comp.install) comp.install(guard)
82
+ else guard.component(comp.name || key, comp)
83
+ }
84
+ return byName
85
+ })
86
+ }
87
+ return optionalGroupCache[which]
88
+ }
89
+
90
+ // 业务组解析(两种模式,只 import 一次):
91
+ // - 瘦平台(__EPX_BUSINESS_REMOTE__):remote bundle 整组加载(远程产物本就单包,不拆)
92
+ // - 本地默认:**按名文件级懒加载**(system.component.lazy.js)——名→文件索引 + async wrapper
93
+ // 全量注册(业务视图模板互相按名引用可命中);索引未命中/校验不符自动回退整组,
94
+ // 最坏情况 = 旧整组行为。返回统一的 { resolve(app, name) } 接口。
95
+ let systemComponentsPromise = null
96
+ function loadSystemComponents (app) {
97
+ // 严格模式只拦「隐式懒加载兜底」;瘦平台的 remote business 是显式部署配置
98
+ // (EXTERNALIZE_BUSINESS 构建 + config.js 配 URL)="已安装",不受严格模式影响。
99
+ const isRemote = typeof __EPX_BUSINESS_REMOTE__ !== 'undefined' && __EPX_BUSINESS_REMOTE__
100
+ if (strictPacks() && !isRemote) {
101
+ return Promise.resolve({ resolve: () => Promise.resolve(null) })
102
+ }
103
+ if (!systemComponentsPromise) {
104
+ if (typeof __EPX_BUSINESS_REMOTE__ !== 'undefined' && __EPX_BUSINESS_REMOTE__) {
105
+ // 瘦平台:从独立部署的 remote bundle 拉业务视图(整组注册)
106
+ const remote = (typeof window !== 'undefined' && window.__EPX_BUSINESS_REMOTE_URL__) || {}
107
+ injectRemoteCss(remote.css)
108
+ // 先备好该远程包要用到的懒加载平台组件,再 import 它(见 core/remote-deps.js)。
109
+ // .catch(() => {}) 是硬约束「绝不因自动 ensure 挡住远程加载」的护栏:ensure 只是优化,
110
+ // 它 reject 了也必须继续 import remote(真缺组件时由 frame.js 的自解释 shim 兜底报错)。
111
+ systemComponentsPromise = ensureRemoteDeps([remote.js]).catch(() => {}).then(() => import(/* @vite-ignore */ remote.js))
112
+ .then(mod => mod.components || (mod.default && mod.default.components) || mod.default)
113
+ .then(components => {
114
+ for (const key in components) {
115
+ if (!app.component(key)) app.component(key, components[key])
116
+ }
117
+ return { resolve: (_app, name) => Promise.resolve(components[name]) }
118
+ })
119
+ } else {
120
+ // 默认:按名文件级懒加载(含整组兜底与 wrapper 全量注册)
121
+ systemComponentsPromise = loadBusinessComponents(app)
122
+ }
123
+ }
124
+ return systemComponentsPromise
125
+ }
126
+
127
+ // 注:入参在 T30b app 化后是 **app 实例**(不再是 compat 的全局 Vue)。
128
+ // `app.component(name)` / `app.component(name, comp)` 与 `Vue.component` 同签名,故调用点无需改。
129
+ export default function loadComponentFactory (app) {
130
+ return (pagePath, defaultComponent) => {
131
+ let pathName
132
+ let componentName = 'epx-' + defaultComponent
133
+ if (pagePath) {
134
+ const rootMatch = pagePath.match(/^@\/(frame\/|product\/|project\/)?/)
135
+ if (rootMatch) {
136
+ pathName = pagePath.split(rootMatch[0])[1].replace('.vue', '')
137
+ }
138
+ const componentPath = pathName.replace(/\//g, '-').replace(/(\w)([A-Z])/g, '$1-$2').replace('views-', '').replace('components-', '').replace('packages-', '').toLowerCase()
139
+ componentName = 'epx-' + componentPath
140
+ }
141
+ if (!app.component(componentName)) {
142
+ app.component(componentName, defineAsyncComponent(() =>
143
+ loadSystemComponents(app).then(m => m.resolve(app, componentName)).then(comp => {
144
+ if (comp) return comp
145
+ // 产品/项目远程库的组件表兜底。
146
+ //
147
+ // 为什么必须有这一环:本工厂与产品库的 install 都是「没人占我才占」——
148
+ // 平台 ↓ 本文件 产品 ↓ 其 src/index.js
149
+ // if (!app.component(name)) … if (!app.component(key)) …
150
+ // 谁先跑谁赢。页面配置引用到某个产品组件时 Page.vue 会先按名字调本工厂
151
+ // (Page.vue:234/330),此时远程库可能还没 install,占位就落位了;随后产品库
152
+ // install 看到「已存在」直接跳过 —— 槽位归占位,解析时在平台组件里找不到就抛
153
+ // `system.component 里没有 epx-xxx`,**产品组件永远出不来**。
154
+ // 2026-09-03 product/hr 的 epx-hr-search 实测就是这条路径。
155
+ //
156
+ // window.componentLibrary 由 util.loadJs 的 onload 按组件名汇总各远程库的组件,
157
+ // 到解析时(渲染时机,loadLibrary 早已在路由守卫里 await 过)必然已就绪。
158
+ //
159
+ // ⚠️ 放在可选能力组**之前**:产品组件走到这里就该命中,否则会为了一个
160
+ // epx-hr-* 白拉一遍 dashboard 和体积很大的 designer。
161
+ // ⚠️ 只认对象/函数:loadJs 会把远程库导出的 name/version 这些标量也一并汇总进来。
162
+ const fromProduct = (typeof window !== 'undefined' && window.componentLibrary) || {}
163
+ const pc = fromProduct[componentName]
164
+ if (pc && (typeof pc === 'object' || typeof pc === 'function')) return pc
165
+ // 可选能力组兜底:dashboard(小)→ designer(大),运行时按需拉起
166
+ return loadOptionalGroup(app, 'dashboard').then(dash => {
167
+ if (dash[componentName]) return dash[componentName]
168
+ return loadOptionalGroup(app, 'designer').then(des => {
169
+ const c = des[componentName]
170
+ if (!c) {
171
+ throw new Error(strictPacks()
172
+ ? `[loadComponent] 严格模式(EPX_DISABLE_LAZY_PACKS)下未做懒加载兜底:${componentName} 若来自 business/designer/dashboard,请 app.use(对应 epx-frame 子路径入口) 显式安装`
173
+ : `[loadComponent] 找不到 ${componentName}:平台组件、产品远程库` +
174
+ '(window.componentLibrary)、dashboard、designer 里都没有。\n' +
175
+ ' 若它来自产品/项目远程库:确认该库已在 FM_SYSTEM_WEBPACK 配好且加载成功' +
176
+ '(控制台看 window.componentLibrary 有没有这个名字),' +
177
+ '并检查库里组件的 name 与页面配置引用的名字是否一致。')
178
+ }
179
+ return c
180
+ })
181
+ })
182
+ })
183
+ ))
184
+ }
185
+ return componentName
186
+ }
187
+ }
@@ -1,99 +1,99 @@
1
- import util from '@epxing/core/src/runtime-utils/util'
2
- import http from '@epxing/core/src/api'
3
- import ensureRemoteDeps from '@epxing/core/src/remote-deps'
4
- import { registerLegacySelectStyles } from './vue2-select-style-compat'
5
-
6
- // 同一个系统正在加载中的 Promise。结果缓存(window.loadSystemLibrarys)要等
7
- // listWebpackUrl 回来且所有 js/css 都落地后才写入,这中间若不复用,并发调用方
8
- // (路由守卫重跑、多个 epx-page 同时挂载)会各自再发一次 listWebpackUrl 并重复
9
- // 执行产品库脚本;产品库 install 里带 router.replace,于是又反过来触发守卫重跑。
10
- const loadingLibrarys = {}
11
-
12
- export default function (params) {
13
- if (!window.loadSystemLibrarys) window.loadSystemLibrarys = {}
14
- if (window.loadSystemLibrarys[params.busiSystemCode]) return Promise.resolve(window.loadSystemLibrarys[params.busiSystemCode])
15
- if (loadingLibrarys[params.busiSystemCode]) return loadingLibrarys[params.busiSystemCode]
16
- const promise = loadLibrary(params)
17
- .then(data => {
18
- delete loadingLibrarys[params.busiSystemCode]
19
- return data
20
- })
21
- loadingLibrarys[params.busiSystemCode] = promise
22
- return promise
23
- }
24
-
25
- function loadLibrary (params) {
26
- return new Promise(resolve => {
27
- http.get({
28
- action: 'listWebpackUrl',
29
- entityId: 'FM_SYSTEM_WEBPACK',
30
- ...params
31
- })
32
- .then(data => {
33
- // 记录加载前各库的引用快照。webpack UMD 每次执行都会给 window.library[name]
34
- // 赋一个新对象引用,所以用「引用是否变化」来定位本次真正加载的产品库,
35
- // 既能识别新注册的库,也能识别 key 已存在但被重新加载的库,
36
- // 避免 util 里因 isEqual(cloneDeep 丢失函数) 失效而把上一个产品的库串过来
37
- const beforeRefs = { ...(window.library || {}) }
38
- const finish = () => {
39
- const lib = window.library || {}
40
- const changed = Object.keys(lib).filter(k => lib[k] && lib[k] !== beforeRefs[k])
41
- const chosen = changed.map(k => lib[k]).find(l => l.version) ||
42
- (changed.length && lib[changed[0]]) || {}
43
- window.loadSystemLibrarys[params.busiSystemCode] = chosen
44
- resolve(chosen)
45
- }
46
- let fileLength = 0
47
- let loadFile = 0
48
- const jsFiles = []
49
- data.entitys.forEach(item => {
50
- item.path.forEach(file => {
51
- if (!file.includes('https://') && !file.includes('http://')) file = `${window.IP.slice(0, window.IP.length - 1)}${file}`
52
- fileLength++
53
- if (/\.js$/.test(file)) {
54
- // 加载池
55
- jsFiles.push(file)
56
- } else if (/\.css$/.test(file)) {
57
- util.loadCss({
58
- url: file,
59
- callback: () => {
60
- registerLegacySelectStyles(file)
61
- loadFile++
62
- if (fileLength === loadFile) finish()
63
- }
64
- })
65
- } else {
66
- loadFile++
67
- }
68
- })
69
- })
70
- if (jsFiles.length > 0) {
71
- // 先把这些远程 JS 要用到的懒加载平台组件备好,再开始串行加载(见 core/remote-deps.js)。
72
- // ⚠️ 这条链上的两个 catch 都不能省:本函数返回的 Promise 是路由守卫里 await 的
73
- // (router/config.js 的 loadLibrary 调用点),一旦有 rejection 逃逸,外层
74
- // new Promise 永不 resolve → 守卫卡死 → 白屏。改造前这段逻辑在 http.get 的
75
- // .then 内部,同步抛错会落进下面那个 .catch(resolve),是有保护的;搬到这个游离的
76
- // .then 里就失去了。「绝不因自动 ensure 白屏」不该押在 remote-deps 的实现细节上。
77
- ensureRemoteDeps(jsFiles).catch(() => {}).then(() => {
78
- let loadIndex = 0
79
- const cb = () => {
80
- if (fileLength === ++loadFile) finish()
81
- loadIndex++
82
- if (loadIndex < jsFiles.length) {
83
- util.loadJs(jsFiles[loadIndex], cb)
84
- }
85
- }
86
- util.loadJs(jsFiles[loadIndex], cb)
87
- }).catch(err => {
88
- console.error(err)
89
- finish()
90
- })
91
- }
92
- if (fileLength === loadFile) finish()
93
- })
94
- .catch(err => {
95
- console.error(err)
96
- resolve()
97
- })
98
- })
99
- }
1
+ import util from '@epxing/core/src/runtime-utils/util'
2
+ import http from '@epxing/core/src/api'
3
+ import ensureRemoteDeps from '@epxing/core/src/remote-deps'
4
+ import { registerLegacySelectStyles } from './vue2-select-style-compat'
5
+
6
+ // 同一个系统正在加载中的 Promise。结果缓存(window.loadSystemLibrarys)要等
7
+ // listWebpackUrl 回来且所有 js/css 都落地后才写入,这中间若不复用,并发调用方
8
+ // (路由守卫重跑、多个 epx-page 同时挂载)会各自再发一次 listWebpackUrl 并重复
9
+ // 执行产品库脚本;产品库 install 里带 router.replace,于是又反过来触发守卫重跑。
10
+ const loadingLibrarys = {}
11
+
12
+ export default function (params) {
13
+ if (!window.loadSystemLibrarys) window.loadSystemLibrarys = {}
14
+ if (window.loadSystemLibrarys[params.busiSystemCode]) return Promise.resolve(window.loadSystemLibrarys[params.busiSystemCode])
15
+ if (loadingLibrarys[params.busiSystemCode]) return loadingLibrarys[params.busiSystemCode]
16
+ const promise = loadLibrary(params)
17
+ .then(data => {
18
+ delete loadingLibrarys[params.busiSystemCode]
19
+ return data
20
+ })
21
+ loadingLibrarys[params.busiSystemCode] = promise
22
+ return promise
23
+ }
24
+
25
+ function loadLibrary (params) {
26
+ return new Promise(resolve => {
27
+ http.get({
28
+ action: 'listWebpackUrl',
29
+ entityId: 'FM_SYSTEM_WEBPACK',
30
+ ...params
31
+ })
32
+ .then(data => {
33
+ // 记录加载前各库的引用快照。webpack UMD 每次执行都会给 window.library[name]
34
+ // 赋一个新对象引用,所以用「引用是否变化」来定位本次真正加载的产品库,
35
+ // 既能识别新注册的库,也能识别 key 已存在但被重新加载的库,
36
+ // 避免 util 里因 isEqual(cloneDeep 丢失函数) 失效而把上一个产品的库串过来
37
+ const beforeRefs = { ...(window.library || {}) }
38
+ const finish = () => {
39
+ const lib = window.library || {}
40
+ const changed = Object.keys(lib).filter(k => lib[k] && lib[k] !== beforeRefs[k])
41
+ const chosen = changed.map(k => lib[k]).find(l => l.version) ||
42
+ (changed.length && lib[changed[0]]) || {}
43
+ window.loadSystemLibrarys[params.busiSystemCode] = chosen
44
+ resolve(chosen)
45
+ }
46
+ let fileLength = 0
47
+ let loadFile = 0
48
+ const jsFiles = []
49
+ data.entitys.forEach(item => {
50
+ item.path.forEach(file => {
51
+ if (!file.includes('https://') && !file.includes('http://')) file = `${window.IP.slice(0, window.IP.length - 1)}${file}`
52
+ fileLength++
53
+ if (/\.js$/.test(file)) {
54
+ // 加载池
55
+ jsFiles.push(file)
56
+ } else if (/\.css$/.test(file)) {
57
+ util.loadCss({
58
+ url: file,
59
+ callback: () => {
60
+ registerLegacySelectStyles(file)
61
+ loadFile++
62
+ if (fileLength === loadFile) finish()
63
+ }
64
+ })
65
+ } else {
66
+ loadFile++
67
+ }
68
+ })
69
+ })
70
+ if (jsFiles.length > 0) {
71
+ // 先把这些远程 JS 要用到的懒加载平台组件备好,再开始串行加载(见 core/remote-deps.js)。
72
+ // ⚠️ 这条链上的两个 catch 都不能省:本函数返回的 Promise 是路由守卫里 await 的
73
+ // (router/config.js 的 loadLibrary 调用点),一旦有 rejection 逃逸,外层
74
+ // new Promise 永不 resolve → 守卫卡死 → 白屏。改造前这段逻辑在 http.get 的
75
+ // .then 内部,同步抛错会落进下面那个 .catch(resolve),是有保护的;搬到这个游离的
76
+ // .then 里就失去了。「绝不因自动 ensure 白屏」不该押在 remote-deps 的实现细节上。
77
+ ensureRemoteDeps(jsFiles).catch(() => {}).then(() => {
78
+ let loadIndex = 0
79
+ const cb = () => {
80
+ if (fileLength === ++loadFile) finish()
81
+ loadIndex++
82
+ if (loadIndex < jsFiles.length) {
83
+ util.loadJs(jsFiles[loadIndex], cb)
84
+ }
85
+ }
86
+ util.loadJs(jsFiles[loadIndex], cb)
87
+ }).catch(err => {
88
+ console.error(err)
89
+ finish()
90
+ })
91
+ }
92
+ if (fileLength === loadFile) finish()
93
+ })
94
+ .catch(err => {
95
+ console.error(err)
96
+ resolve()
97
+ })
98
+ })
99
+ }
package/src/menu-route.js CHANGED
@@ -87,12 +87,23 @@ export function effectiveRouteName (name) {
87
87
  return MENU_ROUTE_ALIAS[name] || name
88
88
  }
89
89
 
90
- // 由当前路由 + menuKey 生成菜单直达位置(保留 client/system/app;不带 role——role 在 storage)。
90
+ // 配置中心兼容旧的查询参数链接,新链接统一使用菜单路径段。
91
+ export function getRouteMenuKey (route) {
92
+ const key = route.params?.menuKey || (effectiveRouteName(route.name) === 'system' ? route.query?.menuKey : '')
93
+ return typeof key === 'string' ? key : ''
94
+ }
95
+
96
+ // 生成菜单直达位置;角色通过已有持久化机制恢复,兼容旧地址中的角色段。
91
97
  // query 传了则整体替换(页面参数进 URL,刷新/分享自然保留,页面从 $route.query 读),
92
98
  // 不传沿用当前 URL 的 query。
93
99
  export function menuLocation (route, key, query) {
94
100
  const q = query !== undefined ? query : route.query
95
101
  const { client, system, app } = route.params || {}
102
+ if (effectiveRouteName(route.name) === 'system') {
103
+ const systemQuery = { ...q }
104
+ delete systemQuery.menuKey
105
+ return { name: 'system', params: { client, system, menuKey: key }, query: systemQuery }
106
+ }
96
107
  if (effectiveRouteName(route.name) === 'hall') {
97
108
  return { name: 'hallMenu', params: { client, menuKey: key }, query: q }
98
109
  }