@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
@@ -31,6 +31,7 @@ const LEGACY_PERSIST = 'epx_frame_tabs_persist'
31
31
  export const DEFAULTS = {
32
32
  themeMode: 'light', // 主题模式 light|dark|auto(auto 跟随系统 prefers-color-scheme)
33
33
  themeColor: '', // 主题色预设名(classicblue/purple/...;'' = 默认蓝,走既有 data-theme 兜底)
34
+ customTheme: null, // 用户选择的自定义主题色快照 { code, color },刷新后也能在引导期恢复
34
35
  grayMode: false, // 灰色模式(全站置灰,哀悼日用)
35
36
  weakMode: false, // 色弱模式(invert 反色,参考 pure-admin;与灰色模式可叠加)
36
37
  menuAccordion: true, // 菜单手风琴(唯一展开)——参考 pure-admin;false=可同时展开多组
@@ -54,7 +55,7 @@ export const DEFAULTS = {
54
55
  tabsPosition: 'below', // 标签行位置 below|nav|none
55
56
  tabsStyle: 'smart', // 页签风格 smart|card|chrome(谷歌)
56
57
  tabsKeepAlive: true, // 页签缓存(会话级 keep-alive):开=切走保活/点回保持原样;关=切走即销毁/每次点开重新加载。不落盘不整组恢复
57
- settingsGroups: ['theme', 'layout', 'tabs', 'page', 'security'] // 设置抽屉折叠分组的展开态(记住用户的折叠选择,跨会话)
58
+ settingsGroups: ['theme', 'style', 'layout', 'tabs', 'page', 'security'] // 设置抽屉折叠分组的展开态(记住用户的折叠选择,跨会话)
58
59
  }
59
60
 
60
61
  // 导航布局模式 → 顶部/侧边导航显示 + 双列(供 AppIndex/module-index-page 派生显隐)
@@ -173,19 +174,17 @@ let _suppressPersist = false
173
174
  // applyAppDefaults 注入的“应用默认值”(键→值):persistDiff 据此把它排除在用户偏好之外(跨 HMR 保号)
174
175
  const _appDefaultVals = (_hot && _hot.data.appDefaultVals) || {}
175
176
  if (_hot) _hot.data.appDefaultVals = _appDefaultVals
176
- // 只把“用户显式改过”的键写进 localStorage(差量):排除等于代码默认、以及等于应用默认注入值的键。
177
- // 关键:localStorage 只存用户偏好差量,applyAppDefaults 的 userSet[k]===undefined 判定才准确,
178
- // 不会把应用默认当成用户选择去遮蔽其它应用的默认(旧实现整包 JSON.stringify 落盘正是此 bug 根源)。
179
- function persistDiff () {
177
+ // 只记录本次由用户改变的字段。默认值注入期间关闭监听,避免把应用默认记成个人选择。
178
+ function persistDiff (current, previous) {
180
179
  try {
181
- const out = {}
180
+ const out = JSON.parse(window.localStorage.getItem(KEY) || '{}') || {}
181
+ const next = JSON.parse(current)
182
+ const before = JSON.parse(previous)
182
183
  for (const k of Object.keys(DEFAULTS)) {
183
- if (k in _appDefaultVals && uiSettings[k] === _appDefaultVals[k]) continue
184
- // 数组键按值比较(JSON),标量键按引用/值比较——只把「和默认不同」的写进用户差量
185
- const cur = uiSettings[k]; const def = DEFAULTS[k]
186
- const changed = Array.isArray(def) ? JSON.stringify(cur) !== JSON.stringify(def) : cur !== def
187
- if (changed) out[k] = cur
184
+ // 显式选回 false、空串或平台默认值同样是个人覆盖,不能按值相等丢弃。
185
+ if (k !== 'tabsKeepAlive' && JSON.stringify(next[k]) !== JSON.stringify(before[k])) out[k] = next[k]
188
186
  }
187
+ delete out.tabsKeepAlive
189
188
  window.localStorage.setItem(KEY, JSON.stringify(out))
190
189
  } catch (e) {}
191
190
  }
@@ -195,10 +194,10 @@ export function persistUiSettings () {
195
194
  if (_hot) _hot.data.watching = true
196
195
  else _watching = true
197
196
  // flush:'sync' —— 让 _suppressPersist 的开合与写盘同步生效(设置变更低频,同步落盘无性能负担)
198
- watch(uiSettings, () => {
197
+ watch(() => JSON.stringify(Object.fromEntries(Object.keys(DEFAULTS).map(k => [k, uiSettings[k]]))), (current, previous) => {
199
198
  if (_suppressPersist) return
200
- persistDiff()
201
- }, { deep: true, flush: 'sync' })
199
+ persistDiff(current, previous)
200
+ }, { flush: 'sync' })
202
201
  }
203
202
  // import 即开始持久化(副作用一次)
204
203
  persistUiSettings()
@@ -208,11 +207,41 @@ let _mql = null
208
207
  // 用户主题色之外的“底色” data-theme(角色/自定义肤色,initTheme 时快照)——清空主题色时回退到它,
209
208
  // 否则选过预设色再清空/恢复默认时 data-theme 会卡住(老 applyTheme 无 else 分支的 bug)。
210
209
  let _baseTheme = ''
210
+ // --color-primary 使用 RGB 三元组,不能直接写入 #hex 或 rgb(...)。
211
+ export function normalizeThemeColor (color) {
212
+ if (typeof color !== 'string') return ''
213
+ const value = color.trim()
214
+ const hex = value.match(/^#([\da-f]{3}|[\da-f]{6})$/i)
215
+ if (hex) {
216
+ const full = hex[1].length === 3 ? [...hex[1]].map(c => c + c).join('') : hex[1]
217
+ return [0, 2, 4].map(i => parseInt(full.slice(i, i + 2), 16)).join(',')
218
+ }
219
+ const rgb = value.match(/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/i)
220
+ return rgb && rgb.slice(1).every(c => Number(c) <= 255) ? rgb.slice(1).map(Number).join(',') : ''
221
+ }
222
+
211
223
  export function applyTheme () {
212
224
  const html = document.documentElement
225
+ const preset = THEME_COLORS.find(c => c.name === uiSettings.themeColor)
226
+ const custom = uiSettings.customTheme?.code === uiSettings.themeColor ? uiSettings.customTheme : null
227
+ const channels = normalizeThemeColor(preset?.color || custom?.color)
228
+ // 显式选择覆盖旧换肤写在 body 上的颜色;选择预设和自定义色走同一条路径。
229
+ let colorStyle = document.getElementById('epx-ui-theme-color')
230
+ if (channels) {
231
+ html.style.removeProperty('--color-primary')
232
+ if (!colorStyle) {
233
+ colorStyle = document.createElement('style')
234
+ colorStyle.id = 'epx-ui-theme-color'
235
+ document.head.appendChild(colorStyle)
236
+ }
237
+ const selector = `html[data-theme="${preset ? preset.name : 'custom'}"]`
238
+ colorStyle.textContent = `${selector}, ${selector} body { --color-primary: ${channels}; }`
239
+ } else if (colorStyle) {
240
+ colorStyle.remove()
241
+ }
213
242
  // 主题色:非空 → data-theme=预设名(既有换肤链);为空 → 回退底色(角色/自定义肤色),无底色则移除属性
214
243
  if (uiSettings.themeColor) {
215
- html.setAttribute('data-theme', uiSettings.themeColor)
244
+ html.setAttribute('data-theme', custom && channels ? 'custom' : uiSettings.themeColor)
216
245
  } else if (_baseTheme) {
217
246
  html.setAttribute('data-theme', _baseTheme)
218
247
  } else {
@@ -248,7 +277,7 @@ export function initTheme () {
248
277
  if (!_baseTheme) _baseTheme = document.documentElement.getAttribute('data-theme') || ''
249
278
  applyTheme()
250
279
  watch(() => [
251
- uiSettings.themeMode, uiSettings.themeColor, uiSettings.grayMode, uiSettings.weakMode,
280
+ uiSettings.themeMode, uiSettings.themeColor, uiSettings.customTheme, uiSettings.grayMode, uiSettings.weakMode,
252
281
  uiSettings.styleRadius, uiSettings.styleFont, uiSettings.styleDensity, uiSettings.styleElevation
253
282
  ], applyTheme)
254
283
  if (window.matchMedia) {
@@ -261,22 +290,29 @@ export function initTheme () {
261
290
 
262
291
  // 应用级默认注入:页面配置里配了、且用户没在设置里改过的项,用应用默认兜底。
263
292
  // 只覆盖“用户未显式设过”的项——已在 localStorage 的用户偏好不动。
264
- export function applyAppDefaults (defaults = {}) {
293
+ export function applyAppDefaults (defaults = {}, { replace = false } = {}) {
265
294
  let userSet = {}
266
295
  try { userSet = JSON.parse(window.localStorage.getItem(KEY) || '{}') || {} } catch (e) {}
267
296
  // 注入期暂停落盘:应用默认只改内存生效值,不写进用户偏好(否则会跨应用串味、且遮蔽后续应用的默认)
268
297
  _suppressPersist = true
269
298
  try {
299
+ if (replace) {
300
+ Object.keys(_appDefaultVals).forEach(k => { delete _appDefaultVals[k] })
301
+ _baseTheme = ''
302
+ document.getElementById('custom-theme')?.remove()
303
+ document.documentElement.style.removeProperty('--color-primary')
304
+ }
270
305
  for (const k of Object.keys(DEFAULTS)) {
271
- // 仅兜底“用户没设过、或存量等于代码默认(含旧版整包落盘的历史数据)”的项——用户显式改过的值优先
272
- if (defaults[k] !== undefined && (userSet[k] === undefined || userSet[k] === DEFAULTS[k])) {
273
- uiSettings[k] = defaults[k]
274
- _appDefaultVals[k] = defaults[k] // 记录应用默认注入值,persistDiff 据此不把它当用户偏好落盘
306
+ if (defaults[k] !== undefined) _appDefaultVals[k] = defaults[k]
307
+ if (replace || defaults[k] !== undefined) {
308
+ const value = userSet[k] !== undefined ? userSet[k] : (_appDefaultVals[k] ?? DEFAULTS[k])
309
+ uiSettings[k] = Array.isArray(value) ? [...value] : value
275
310
  }
276
311
  }
277
312
  } finally {
278
313
  _suppressPersist = false
279
314
  }
315
+ if (replace) applyTheme()
280
316
  }
281
317
 
282
318
  // 老单值主题键(epx_frame_theme:预设名 / 'dark' / 自定义肤色码)→ ui-settings 的迁移。
@@ -302,14 +338,39 @@ export function migrateLegacyTheme (src) {
302
338
 
303
339
  // 恢复默认(设置面板「恢复默认」):回到代码默认 + 清底色/旧键/自定义肤色 style,并即时应用
304
340
  export function resetUiSettings () {
305
- Object.assign(uiSettings, DEFAULTS) // 用户操作,不抑制落盘——把默认值写回覆盖存量
341
+ _suppressPersist = true
342
+ Object.assign(uiSettings, DEFAULTS, { settingsGroups: [...DEFAULTS.settingsGroups] })
343
+ _suppressPersist = false
344
+ Object.keys(_appDefaultVals).forEach(k => { delete _appDefaultVals[k] })
345
+ try { window.localStorage.removeItem(KEY) } catch (e) {}
306
346
  _baseTheme = '' // 清除角色/自定义肤色底色,真正回到平台默认
307
347
  try {
308
348
  const legacyKeys = ['epx_frame_theme', LEGACY_STYLE, LEGACY_PERSIST]
309
- legacyKeys.forEach(k => window.localStorage.removeItem(k))
349
+ legacyKeys.forEach(k => {
350
+ window.localStorage.removeItem(k)
351
+ window.sessionStorage.removeItem(k)
352
+ })
310
353
  } catch (e) {}
311
354
  const custom = document.getElementById('custom-theme')
312
355
  if (custom && custom.parentNode) custom.parentNode.removeChild(custom)
356
+ document.documentElement.style.removeProperty('--color-primary')
357
+ applyTheme()
358
+ }
359
+
360
+ // 清除个人覆盖后重新采用当前应用默认,应用默认始终不写入个人存储。
361
+ export function restoreAppUiDefaults () {
362
+ const defaults = { ..._appDefaultVals }
363
+ const baseTheme = _baseTheme
364
+ const legacyColor = document.getElementById('custom-theme')?.textContent
365
+ resetUiSettings()
366
+ applyAppDefaults(defaults, { replace: true })
367
+ _baseTheme = baseTheme
368
+ if (legacyColor) {
369
+ const style = document.createElement('style')
370
+ style.id = 'custom-theme'
371
+ style.textContent = legacyColor
372
+ document.head.appendChild(style)
373
+ }
313
374
  applyTheme()
314
375
  }
315
376
 
@@ -0,0 +1,53 @@
1
+ <template>
2
+ <!-- app入口 -->
3
+ <component v-if="load" :is="component" :page="firstPage"></component>
4
+ </template>
5
+
6
+ <script>
7
+ /**
8
+ * 应用入口 获取应用配置模板
9
+ */
10
+ export default {
11
+ name: 'epx-app',
12
+ computed: {
13
+ appConfig () {
14
+ return this.$store.state.config.appConfig
15
+ }
16
+ },
17
+
18
+ data () {
19
+ return {
20
+ load: false,
21
+ component: 'epx-app-index',
22
+ firstPage: {}
23
+ }
24
+ },
25
+ methods: {
26
+ getPage () {
27
+ const { FIRST_PAGE, MOBILE_FIRST_PAGE } = this.appConfig
28
+ this.$store.dispatch({ // 加回调
29
+ type: 'ADD_PAGE',
30
+ pageId: this.$isMobile ? MOBILE_FIRST_PAGE : FIRST_PAGE
31
+ })
32
+ .then((page = {}) => {
33
+ const { pagePath } = page
34
+ this.firstPage = page
35
+ this.component = this.$loadComponent(pagePath, this.component)
36
+ this.load = true
37
+ })
38
+ .catch(() => {
39
+ // this.component = this.$loadComponent(null, this.component)
40
+ this.load = true
41
+ })
42
+ }
43
+ },
44
+ created () {
45
+ this.getPage()
46
+ }
47
+
48
+ }
49
+ </script>
50
+
51
+ <style>
52
+
53
+ </style>
@@ -0,0 +1,334 @@
1
+ <template>
2
+ <div :class="{'role-select': true, 'm-role-select': $isMobile}" v-if="roleList">
3
+ <div class="role-select__bg" :style="clientConfig.BACKGROUND_CHOICEROLE? {backgroundImage: `url(${$util.getFileUrl(clientConfig.BACKGROUND_CHOICEROLE)})`}: {}"></div>
4
+ <div class="select__modal">
5
+ <div class="app__title">
6
+ <span class="app__prefix" :style="{background: appConfig.COLOR}">
7
+ <i :class="['app__icon fa',appConfig.ICON || 'el-icon-menu' ]"></i>
8
+ </span>
9
+ <i class="app__name">{{appConfig.NAME}}</i>
10
+ </div>
11
+ <div class="role__title">请选择您的角色</div>
12
+ <template v-if="roleList.length>0">
13
+ <el-radio-group class="radio-group" v-model="mainRole" @change="setMainRole">
14
+ <ul :class="{'role__list': true, 'm_role_list': $isMobile}">
15
+ <li class="role__item" v-for="role in roleList" :key="role.ID" @click="openApp(role)">
16
+ <span class="role__prefix" :style="{background: appConfig.ROLE_COLOR}">
17
+ <i :class="['fa',role.ICON || 'el-icon-user-solid']"></i>
18
+ </span>
19
+ <span class="role__name">{{role.NAME}}</span>
20
+ <span class="role__opera" @click.stop>
21
+ <el-radio class="radio" :label="role.ID" :value="role.ID">设为默认</el-radio>
22
+ </span>
23
+ </li>
24
+ </ul>
25
+ </el-radio-group>
26
+ </template>
27
+ <div class="no-role" v-else>
28
+ <p>对不起,您还未得到该应用的访问授权!</p>
29
+ <el-button type="primary" @click="logout">退出登录</el-button>
30
+ </div>
31
+ </div>
32
+ </div>
33
+ </template>
34
+
35
+ <script>
36
+ import { mapState } from 'vuex'
37
+ import { roleStorage } from '@epxing/core/src/menu-route'
38
+ import { migrateLegacyTheme, initTheme } from '@epxing/core/src/ui-settings'
39
+ import storage from '@epxing/utils/src/storage'
40
+ // 应用角色选择
41
+ export default {
42
+ name: 'epx-app-role-select',
43
+ data () {
44
+ return {
45
+ roleList: null,
46
+ radio: '',
47
+ mainRole: null
48
+ }
49
+ },
50
+ computed: {
51
+ ...mapState({
52
+ clientConfig: state => state.config.clientConfig || {},
53
+ appConfig: state => state.config.appConfig || {}, // 应用配置
54
+ $isMobile: state => state.config.isMobile // 是否为移动端
55
+ })
56
+ },
57
+
58
+ methods: {
59
+ setTheme () {
60
+ const { 'CLIENT|THEME': clientTheme, 'SYSTEM|THEME': systemTheme } = this.appConfig
61
+ // 主题应用统一交给 core/ui-settings:个性化设置抽屉里选的主题色/暗色优先,老单值键
62
+ // epx_frame_theme 与应用配置(SYSTEM|THEME / CLIENT|THEME)只作兜底。
63
+ // 老实现在这里直接 setAttribute('data-theme', 老键),会把用户在设置抽屉里选的主题色**盖掉**
64
+ // ——本页是顶层路由、不渲染 AppIndex,与「登录页个性化主题色不生效」是同一个病根。
65
+ // 预设名单也不再在本文件手抄一份(此前 AppIndex/本文件各一份,改一处即漂移)。
66
+ // 读取用 storage.get(sessionStorage 优先、localStorage 兜底,隐私模式/禁用存储时不抛错)而非裸 localStorage。
67
+ const skinCode = migrateLegacyTheme(storage.get('epx_frame_theme') || systemTheme || clientTheme)
68
+ // 自定义肤色码:查 SKIN_COLOR 取色注入 body{--color-primary}(data-theme=custom 迁移函数已写好)
69
+ if (skinCode) {
70
+ this.$http.find({
71
+ entityId: 'SKIN_COLOR',
72
+ 'scope.CODE': skinCode
73
+ })
74
+ .then(data => {
75
+ const flag = /rgb\(([\d,\s]+)\)/.test(data.data.COLOR)
76
+ if (flag) {
77
+ const style = `body {--color-primary: ${RegExp.$1}}`
78
+ var css = document.createElement('style')
79
+ css.type = 'text/css'
80
+ css.id = 'custom-theme'
81
+ if (css.styleSheet) {
82
+ css.styleSheet.cssText = style
83
+ } else {
84
+ css.appendChild(document.createTextNode(style))
85
+ }
86
+ document.head.appendChild(css)
87
+ }
88
+ })
89
+ }
90
+ // 引导期 src/App.vue 已 initTheme(),此处幂等 no-op;保留是为了不依赖某个外壳一定调过它。
91
+ initTheme()
92
+ },
93
+ getServiceRoles () {
94
+ return this.$http.get({
95
+ action: 'getServiceRoles',
96
+ entityId: 'FM_ROLE',
97
+ serviceId: this.appConfig.ID,
98
+ order_fields: 'ORDER_NUM,LOG_C_TIME',
99
+ order_mode: 'asc,asc'
100
+ })
101
+ .then(role => {
102
+ if (role.entitys.length === 1) {
103
+ this.openApp(role.entitys[0])
104
+ } else {
105
+ const mainRole = role.entitys.find(role => role.MAIN_ROLE === 'true')
106
+ if (mainRole) {
107
+ this.mainRole = mainRole.ID
108
+ this.openApp(mainRole)
109
+ } else {
110
+ // 多个角色且没有设置默认角色
111
+ this.roleList = [...role.entitys]
112
+ }
113
+ }
114
+ })
115
+ },
116
+ setMainRole () {
117
+ this.$http.save({
118
+ action: 'setMainRole',
119
+ entityId: 'FM_USER_SERVICE_MAIN_ROLE',
120
+ serviceId: this.appConfig.ID,
121
+ roleId: this.mainRole
122
+ })
123
+ .then(() => {
124
+ this.$message.success('设置成功')
125
+ })
126
+ },
127
+ openApp (role) {
128
+ const roleCode = role.CODE + (role.TYPE === 'client' ? '-client' : '')
129
+ // 获取应用参数
130
+ const { CODE, 'SYSTEM|CODE': systemCode, 'CLIENT|CODE': clientCode } = this.appConfig
131
+ const { client = clientCode, system = systemCode, name: lastRouteName, ...otherParams } = this.$route.params
132
+ // 选定角色持久化(菜单直达路由不带 role 段,取数点从 localStorage 读)
133
+ roleStorage.set(client, system, roleCode)
134
+ let name = 'app1'
135
+ if (systemCode === 'epxing-frame') {
136
+ switch (CODE) {
137
+ case 'FM_SERVICE_SYSTEM':
138
+ name = 'system'
139
+ break
140
+ case 'FM_SERVICE_CLIENT':
141
+ name = 'client'
142
+ break
143
+ case 'FM_SERVICE_HALL':
144
+ name = 'hall'
145
+ break
146
+ }
147
+ }
148
+ // 从菜单直达链接被拦到本页的(守卫塞了 backMenu):选完角色回到原菜单 URL
149
+ const { backMenu, ...restQuery } = this.$route.query
150
+ if (backMenu) {
151
+ return this.$router.replace({
152
+ name: name === 'hall' ? 'hallMenu' : 'appMenu',
153
+ params: { client, system, app: CODE, menuKey: backMenu },
154
+ query: restQuery
155
+ })
156
+ }
157
+ this.$router.replace({
158
+ name: lastRouteName || name,
159
+ params: {
160
+ client,
161
+ system,
162
+ app: CODE,
163
+ role: roleCode,
164
+ ...otherParams
165
+ },
166
+ query: { ...this.$route.query }
167
+ })
168
+ },
169
+ logout () {
170
+ this.$http.logout()
171
+ .then(() => {
172
+ this.$util.toLogin(true)
173
+ })
174
+ }
175
+ },
176
+ created () {
177
+ this.getServiceRoles()
178
+ this.setTheme()
179
+ }
180
+ }
181
+ </script>
182
+
183
+ <style lang="scss" scoped>
184
+ .role-select{
185
+ height: 100%;
186
+ background: #F1F3F7;
187
+ }
188
+ .role-select__bg {
189
+ height: 50%;
190
+ background: url('@/assets/images/role_select.webp') no-repeat center;
191
+ background-size: cover;
192
+ }
193
+ .select__modal {
194
+ width: 1200px;
195
+ height: 520px;
196
+ overflow: auto;
197
+ box-sizing: border-box;
198
+ padding: 30px;
199
+ background: var(--epx-bg-card);
200
+ position: relative;
201
+ left: 50%;
202
+ top: -260px;
203
+ margin-left: -600px;
204
+ }
205
+ .app__prefix {
206
+ float: left;
207
+ width: 56px;
208
+ height: 56px;
209
+ border-radius: 50%;
210
+ @include bg_color_primary;
211
+ margin-right: 20px;
212
+ text-align: center;
213
+ i {
214
+ font-size: 24px;
215
+ color: #fff;
216
+ line-height: 56px;
217
+ }
218
+ }
219
+ .app__name {
220
+ font-size: var(--epx-font-size-xl);
221
+ line-height: 56px;
222
+ }
223
+ .role__title {
224
+ font-size: 18px;
225
+ line-height: 48px;
226
+ text-align: center;
227
+ position: relative;
228
+ &::after {
229
+ content: '';
230
+ display: block;
231
+ position: absolute;
232
+ left: 50%;
233
+ bottom: 0;
234
+ margin-left: -38px;
235
+ width: 76px;
236
+ height: 2px;
237
+ background: #333333;
238
+ }
239
+ }
240
+ .radio-group {
241
+ width: 100%;
242
+ }
243
+ .role__list {
244
+ display: flex;
245
+ justify-content: center;
246
+ flex-wrap: wrap;
247
+ margin-top: 48px;
248
+ }
249
+ .role__item {
250
+ width: 200px;
251
+ height: 220px;
252
+ display: flex;
253
+ flex-direction: column;
254
+ align-items: center;
255
+ justify-content: center;
256
+ margin: 0 12px;
257
+ cursor: pointer;
258
+ &:hover {
259
+ box-shadow: 0px 1px 16px 2px rgba(0, 0, 0, 0.1);
260
+ .role__opera .radio {
261
+ display: block;
262
+ }
263
+ }
264
+ }
265
+
266
+ .role__prefix {
267
+ display: block;
268
+ width: 82px;
269
+ height: 82px;
270
+ border-radius: 50%;
271
+ @include bg_color_primary;
272
+ text-align: center;
273
+ i {
274
+ font-size: 36px;
275
+ color: #fff;
276
+ line-height: 82px;
277
+ }
278
+ }
279
+ .role__name {
280
+ display: inline-block;
281
+ font-size: var(--epx-font-size-l);
282
+ padding: 20px 0;
283
+ }
284
+ .role__opera {
285
+ display: block;
286
+ height: 20px;
287
+ .radio {
288
+ display: none;
289
+ }
290
+ }
291
+ .no-role {
292
+ margin-top: 60px;
293
+ text-align: center;
294
+ >p {
295
+ font-size: 30px;
296
+ margin-bottom: 40px;
297
+ color: red;
298
+ }
299
+ }
300
+ .m-role-select {
301
+ .select__modal{
302
+ width: 100%;
303
+ height: 100%;
304
+ left: 0;
305
+ margin-left: 0;
306
+ padding: 10px 0;
307
+ }
308
+ .app__title {
309
+ display: none;
310
+ }
311
+ .role__list {
312
+ margin-top: 24px;
313
+ justify-content: flex-start;
314
+ }
315
+ .role__item {
316
+ width: calc(33.3% - 24px);
317
+ height: auto;
318
+ margin: 0 12px;
319
+ }
320
+ .role__prefix {
321
+ width: 48px;
322
+ height: 48px;
323
+ i {
324
+ font-size: 24px;
325
+ line-height: 48px;
326
+ }
327
+ }
328
+ .role__name {
329
+ font-size: var(--epx-font-size-base);
330
+ padding: 10px 0;
331
+ }
332
+ }
333
+
334
+ </style>
@@ -0,0 +1,63 @@
1
+ <template>
2
+ <div class="error off" v-if="errCode==='error.service.off'">
3
+ <svg t="1646217353171" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2495" width="360" height="360"><path d="M0 759.466667a512 125.155556 0 1 0 1024 0 512 125.155556 0 1 0-1024 0Z" fill="#E5E5E5" p-id="2496"></path><path d="M910.222222 238.674489H113.777778V159.891911C113.777778 148.562489 122.936889 139.377778 134.240711 139.377778h755.518578C901.063111 139.377778 910.222222 148.562489 910.222222 159.891911v78.782578z" fill="#253369" p-id="2497"></path><path d="M889.759289 745.244444H134.240711C122.936889 745.244444 113.777778 736.059733 113.777778 724.730311V231.796622h796.444444v492.933689c0 11.329422-9.159111 20.514133-20.462933 20.514133" fill="#E9EEF7" p-id="2498"></path><path d="M162.133333 263.085511m2.844445 0l699.733333 0q2.844444 0 2.844445 2.844445l0 34.133333q0 2.844444-2.844445 2.844444l-699.733333 0q-2.844444 0-2.844445-2.844444l0-34.133333q0-2.844444 2.844445-2.844445Z" fill="#D5DCE9" opacity=".398" p-id="2499"></path><path d="M162.133333 328.507733m2.844445 0l179.2 0q2.844444 0 2.844444 2.844445l0 199.111111q0 2.844444-2.844444 2.844444l-179.2 0q-2.844444 0-2.844445-2.844444l0-199.111111q0-2.844444 2.844445-2.844445Z" fill="#D5DCE9" opacity=".398" p-id="2500"></path><path d="M369.777778 328.507733m2.844444 0l492.088889 0q2.844444 0 2.844445 2.844445l0 199.111111q0 2.844444-2.844445 2.844444l-492.088889 0q-2.844444 0-2.844444-2.844444l0-199.111111q0-2.844444 2.844444-2.844445Z" fill="#D5DCE9" opacity=".398" p-id="2501"></path><path d="M162.133333 558.907733m2.844445 0l492.088889 0q2.844444 0 2.844444 2.844445l0 145.066666q0 2.844444-2.844444 2.844445l-492.088889 0q-2.844444 0-2.844445-2.844445l0-145.066666q0-2.844444 2.844445-2.844445Z" fill="#D5DCE9" opacity=".398" p-id="2502"></path><path d="M682.666667 558.907733m2.844444 0l179.2 0q2.844444 0 2.844445 2.844445l0 145.066666q0 2.844444-2.844445 2.844445l-179.2 0q-2.844444 0-2.844444-2.844445l0-145.066666q0-2.844444 2.844444-2.844445Z" fill="#D5DCE9" opacity=".398" p-id="2503"></path><path d="M180.590933 185.585778c0 9.708089-7.850667 17.578667-17.530311 17.578666-9.682489 0-17.530311-7.870578-17.530311-17.578666 0-9.705244 7.850667-17.572978 17.530311-17.572978 9.682489 0 17.530311 7.867733 17.530311 17.572978" fill="#FF7946" p-id="2504"></path><path d="M246.055822 185.585778c0 9.708089-7.847822 17.578667-17.530311 17.578666-9.679644 0-17.527467-7.870578-17.527467-17.578666 0-9.705244 7.847822-17.572978 17.527467-17.572978 9.682489 0 17.530311 7.867733 17.530311 17.572978" fill="#FFC53D" p-id="2505"></path><path d="M311.523556 185.585778c0 9.708089-7.850667 17.578667-17.530312 17.578666-9.682489 0-17.530311-7.870578-17.530311-17.578666 0-9.705244 7.850667-17.572978 17.530311-17.572978 9.682489 0 17.530311 7.867733 17.530312 17.572978" fill="#BAE637" p-id="2506"></path><path d="M142.279111 648.533333s-26.601244 68.107378-23.665778 95.544889c2.121956 20.181333 26.493156 18.7392 29.354667 3.862756 5.532444-28.814222-5.688889-99.407644-5.688889-99.407645" fill="#75AE16" p-id="2507"></path><path d="M98.642489 682.459022s-9.597156 63.451022 0.568889 84.807111c7.486578 15.678578 32.147911 6.951822 31.212089-6.001777-1.820444-25.076622-31.780978-78.805333-31.780978-78.805334" fill="#5F960C" p-id="2508"></path><path d="M56.888889 692.969244s7.04 64.603022 22.175289 83.470223c11.087644 13.878044 32.381156 0 28.182755-12.529778C99.108978 739.569778 56.888889 692.969244 56.888889 692.969244" fill="#4B8108" p-id="2509"></path><path d="M170.197333 708.206933s-38.200889 35.134578-40.9088 53.341867c-1.979733 13.397333 20.969244 19.740444 26.567111 11.184356 10.851556-16.554667 14.341689-64.526222 14.341689-64.526223" fill="#2A6A13" p-id="2510"></path><path d="M61.604978 778.746311l3.794489-7.2704a13.175467 13.175467 0 0 1 8.684089-6.701511 13.371733 13.371733 0 0 1 10.823111 2.133333c1.991111 1.427911 4.753067 1.1776 6.448355-0.585955l3.925334-4.104534a17.194667 17.194667 0 0 1 13.357511-5.228088 17.129244 17.129244 0 0 1 12.669155 6.678755 4.952178 4.952178 0 0 0 5.944889 1.501867l2.542933-1.137778a20.622222 20.622222 0 0 1 19.029334 1.166222l7.890489 4.755911a4.969244 4.969244 0 0 0 4.639289 0.256 4.972089 4.972089 0 0 1 3.953777-0.079644c1.271467 0.517689 2.264178 1.541689 2.730667 2.816l2.338133 6.371555c0.540444 1.479111 0.318578 3.126044-0.603022 4.411734-0.918756 1.288533-2.417778 2.053689-4.010667 2.053689H65.948444a4.920889 4.920889 0 0 1-4.175644-2.321067 4.7616 4.7616 0 0 1-0.170667-4.716089" fill="#424242" p-id="2511"></path><path d="M108.168533 720.517689c2.929778 5.831111 6.411378 10.763378 6.920534 10.567111 0.509156-0.196267-0.557511-5.566578-3.447467-11.406222-2.8928-5.8368-6.638933-9.921422-7.145244-9.728-0.509156 0.201956 0.750933 4.738844 3.672177 10.567111z" fill="#8FAE16" p-id="2512"></path><path d="M140.814222 703.587556c0.605867 0 1.635556-4.8128 1.635556-10.749156 0-5.933511-1.058133-10.746311-1.635556-10.746311-0.577422 0-1.627022 4.8128-1.627022 10.746311 0 5.936356 1.021156 10.749156 1.627022 10.749156z" fill="#B3CE4F" p-id="2513"></path><path d="M152.5504 747.207111c-2.599822 5.671822-3.785956 10.4192-3.316622 10.609778 0.463644 0.187733 3.478756-4.260978 6.072889-9.938489 2.596978-5.674667 3.800178-10.402133 3.328-10.589867-0.463644-0.210489-3.478756 4.238222-6.084267 9.915734zM78.6176 733.954844c4.252444 5.262222 8.570311 9.264356 8.994133 8.96 0.426667-0.304356-2.153244-4.835556-6.4-10.089244-4.243911-5.256533-8.576-9.244444-9.005511-8.96-0.426667 0.312889 2.153244 4.827022 6.411378 10.089244z" fill="#6A960C" p-id="2514"></path><path d="M924.529778 583.111111s-39.899022 101.438578-35.498667 142.301867c3.185778 30.057244 39.739733 27.909689 44.032 5.751466 8.300089-42.916978-8.533333-148.053333-8.533333-148.053333" fill="#75AE16" p-id="2515"></path><path d="M859.073422 626.619733s-14.395733 94.498133 0.859022 126.3104c11.227022 23.3472 48.219022 10.350933 46.813867-8.942933-2.733511-37.347556-47.672889-117.367467-47.672889-117.367467" fill="#5F960C" p-id="2516"></path><path d="M796.444444 649.289956s10.561422 96.219022 33.260089 124.322133c16.631467 20.664889 48.574578 0 42.279823-18.6624C859.773156 718.697244 796.444444 649.289956 796.444444 649.289956" fill="#4B8108" p-id="2517"></path><path d="M966.405689 671.985778s-57.298489 52.3264-61.3632 79.445333c-2.9696 19.953778 31.453867 29.397333 39.850667 16.659911 16.278756-24.655644 21.512533-96.105244 21.512533-96.105244" fill="#2A6A13" p-id="2518"></path><path d="M803.518578 771.413333l5.688889-10.823111a19.740444 19.740444 0 0 1 13.0304-9.981155 20.1728 20.1728 0 0 1 16.233244 3.177244 7.486578 7.486578 0 0 0 9.671111-0.873244l5.888-6.112711a25.890133 25.890133 0 0 1 20.036267-7.788089c7.5264 0.418133 14.478222 4.059022 19.006578 9.949866a7.463822 7.463822 0 0 0 8.917333 2.235734l3.811556-1.692445a31.1296 31.1296 0 0 1 28.549688 1.732267l11.832889 7.085511c2.104889 1.262933 4.721778 1.408 6.957511 0.384a7.509333 7.509333 0 0 1 5.930667-0.125156c1.908622 0.776533 3.393422 2.301156 4.096 4.201245l3.5072 9.486222a7.051378 7.051378 0 0 1-0.904533 6.570667 7.404089 7.404089 0 0 1-6.018845 3.057778h-149.720177a7.392711 7.392711 0 0 1-6.260623-3.458845 7.048533 7.048533 0 0 1-0.256-7.025778" fill="#424242" p-id="2519"></path><path d="M873.366756 690.321067c4.388978 8.686933 9.614222 16.031289 10.376533 15.738311 0.762311-0.290133-0.836267-8.291556-5.168356-16.984178-4.340622-8.698311-9.955556-14.782578-10.720711-14.4896-0.762311 0.298667 1.1264 7.054222 5.512534 15.735467z" fill="#8FAE16" p-id="2520"></path><path d="M922.333867 665.105067c0.907378 0 2.451911-7.168 2.451911-16.005689 0-8.840533-1.5872-16.005689-2.451911-16.005689-0.8704 0-2.440533 7.165156-2.440534 16.005689 0 8.840533 1.530311 16.005689 2.440534 16.005689z" fill="#B3CE4F" p-id="2521"></path><path d="M939.938133 730.069333c-3.902578 8.450844-5.680356 15.522133-4.977777 15.803734 0.696889 0.284444 5.219556-6.345956 9.110755-14.799645 3.896889-8.453689 5.697422-15.493689 4.994845-15.772444-0.696889-0.312889-5.219556 6.314667-9.127823 14.768355zM829.036089 710.337422c6.380089 7.839289 12.856889 13.795556 13.4912 13.340445 0.642844-0.452267-3.228444-7.196444-9.597156-15.024356-6.368711-7.827911-12.868267-13.7728-13.511111-13.340444-0.634311 0.4608 3.231289 7.185067 9.614222 15.024355z" fill="#6A960C" p-id="2522"></path><path d="M418.178844 463.644444c0 51.913956 42.083556 94.000356 94.000356 94.000356 51.911111 0 93.997511-42.0864 93.997511-94.000356 0-51.913956-42.083556-94.000356-94.000355-94.000355-51.911111 0-93.997511 42.0864-93.997512 94.000355z" fill="#FF7575" p-id="2523"></path><path d="M505.179022 481.132089c1.1776 3.512889 3.510044 5.822578 6.997334 5.822578 3.512889 0 5.822578-2.332444 7.000177-5.822578l4.667734-64.1536c0-7.000178-5.822578-11.667911-11.665067-11.667911-7.000178 0-11.667911 5.822578-11.667911 12.845511l4.664889 62.976z m6.997334 17.513244c-6.997333 0-11.665067 4.664889-11.665067 11.665067 0 7.000178 4.664889 11.667911 11.665067 11.667911 7.000178 0 11.667911-4.664889 11.667911-11.667911 0-6.997333-4.664889-11.665067-11.665067-11.665067z" fill="#FFFFFF" p-id="2524"></path></svg>
4
+ <p>该应用已停止使用</p>
5
+ </div>
6
+ <div class="error" v-else>
7
+ <i class="epx-icon-color-a-500"></i>
8
+ <p>应用加载错误,<a @click="reload">点击刷新</a></p>
9
+ </div>
10
+ </template>
11
+
12
+ <script>
13
+ export default {
14
+ name: 'epx-error',
15
+ data () {
16
+ return {
17
+ errCode: ''
18
+ }
19
+ },
20
+ methods: {
21
+ reload () {
22
+ window.location.reload(true)
23
+ }
24
+ },
25
+ created () {
26
+ const url = this.$util.search('url')
27
+ this.errCode = this.$util.search('code')
28
+ setTimeout(() => {
29
+ window.history.replaceState(null, null, url)
30
+ }, 10)
31
+ }
32
+ }
33
+ </script>
34
+
35
+ <style lang="scss" scoped>
36
+ .error {
37
+ font-size: 140px;
38
+ text-align: center;
39
+ line-height: 1;
40
+ position: absolute;
41
+ top: 50%;
42
+ width: 100%;
43
+ margin-top: -150px;
44
+ p {
45
+ font-size: 18px;
46
+ color:var(--epx-text);
47
+ margin-top: 30px
48
+ }
49
+ a {
50
+ cursor: pointer;
51
+ }
52
+ }
53
+ .off {
54
+ margin-top: -170px;
55
+ svg {
56
+ width: 300px;
57
+ height: 300px;
58
+ }
59
+ p {
60
+ margin-top: -10px;
61
+ }
62
+ }
63
+ </style>