@epxing/core 5.3.0-beta.2 → 5.3.0-beta.4

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 (100) hide show
  1. package/API.md +715 -714
  2. package/README.md +3 -1
  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 +6 -0
  5. package/dist/.cache/route-chart-release/frontend/frame/public/js/echarts.min.js +49 -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/role_select-DTqRlFNz.webp +0 -0
  9. package/dist/{_vendor → frontend/frame/_vendor}/.pnpm/jsencrypt@3.5.4/_vendor/jsencrypt/bin/jsencrypt.min.js +1 -1
  10. package/dist/src/RootApp.js +54 -0
  11. package/dist/src/api/createHttp.js +6 -0
  12. package/dist/src/app-ui-settings.js +49 -0
  13. package/dist/src/axios.js +10 -15
  14. package/dist/src/chart-runtime-assets.js +19 -0
  15. package/dist/src/chart-runtime-sources.js +8 -0
  16. package/dist/src/create-app.js +3 -6
  17. package/dist/src/index.js +2 -0
  18. package/dist/src/loadComponent.js +2 -2
  19. package/dist/src/menu-route.js +11 -0
  20. package/dist/src/mixins/component.js +3 -0
  21. package/dist/src/navigation-context.js +47 -0
  22. package/dist/src/navigation.js +43 -12
  23. package/dist/src/package-install.js +3 -2
  24. package/dist/src/plugins/element-plus.js +8 -8
  25. package/dist/src/public-api.js +4 -2
  26. package/dist/src/route-components.js +9 -0
  27. package/dist/src/router/config.js +7 -8
  28. package/dist/src/runtime-services.js +1 -1
  29. package/dist/src/runtime-utils/meta.js +3 -0
  30. package/dist/src/runtime-utils/pdf.viewer.js +31 -15
  31. package/dist/src/runtime-utils/util.js +28 -5
  32. package/dist/src/shared-packages.js +1 -3
  33. package/dist/src/store/modules/list.js +25 -3
  34. package/dist/src/store/modules/meta.js +49 -7
  35. package/dist/src/style-presets.js +4 -4
  36. package/dist/src/ui-settings.js +90 -18
  37. package/dist/src/views/App.js +48 -0
  38. package/dist/src/views/AppRoleSelect.js +233 -0
  39. package/dist/src/views/Error.js +210 -0
  40. package/dist/src/views/Module.js +173 -0
  41. package/dist/style.css +251 -0
  42. package/package.json +4 -4
  43. package/src/RootApp.vue +75 -0
  44. package/src/api/createHttp.js +7 -0
  45. package/src/api/index.js +47 -47
  46. package/src/app-ui-settings.js +31 -0
  47. package/src/axios.js +311 -318
  48. package/src/chart-runtime-assets.js +13 -0
  49. package/src/chart-runtime-sources.js +5 -0
  50. package/src/create-app.js +3 -7
  51. package/src/index.js +28 -28
  52. package/src/install.js +157 -157
  53. package/src/loadComponent.js +187 -187
  54. package/src/loadLibrary.js +99 -99
  55. package/src/menu-route.js +12 -1
  56. package/src/mixins/component.js +1464 -1461
  57. package/src/navigation-context.js +49 -0
  58. package/src/navigation.js +208 -176
  59. package/src/package-install.js +2 -1
  60. package/src/plugins/element-plus.js +302 -302
  61. package/src/plugins/frame.js +178 -178
  62. package/src/public-api.js +1 -0
  63. package/src/route-components.js +7 -0
  64. package/src/router/config.js +861 -864
  65. package/src/router/public.js +1 -1
  66. package/src/runtime-services.js +1 -1
  67. package/src/runtime-utils/fieldFactory.js +179 -179
  68. package/src/runtime-utils/meta.js +399 -378
  69. package/src/runtime-utils/pdf.viewer.js +672 -644
  70. package/src/runtime-utils/util.js +1359 -1330
  71. package/src/shared-packages.js +1 -3
  72. package/src/store/modules/config.js +351 -351
  73. package/src/store/modules/list.js +30 -3
  74. package/src/store/modules/locale.js +236 -236
  75. package/src/store/modules/meta.js +314 -265
  76. package/src/style-presets.js +4 -4
  77. package/src/ui-settings.js +84 -23
  78. package/src/views/App.vue +53 -0
  79. package/src/views/AppRoleSelect.vue +334 -0
  80. package/src/views/Error.vue +63 -0
  81. package/src/views/Module.vue +210 -0
  82. package/src/xss.js +26 -26
  83. package/types/app.d.ts +10 -10
  84. package/types/axios.d.ts +1 -1
  85. package/types/component-mixin.d.ts +1 -1
  86. package/types/field-factory.d.ts +1 -1
  87. package/types/global-mixin.d.ts +1 -1
  88. package/types/http.d.ts +1 -1
  89. package/types/index.d.ts +3 -0
  90. package/types/meta.d.ts +1 -1
  91. package/types/navigation.d.ts +21 -21
  92. package/types/pdf-viewer.d.ts +1 -1
  93. package/types/runtime.d.ts +13 -13
  94. package/types/store.d.ts +1 -1
  95. package/types/util.d.ts +2 -2
  96. /package/dist/{build → .cache/route-chart-release/frontend/frame/build}/stubs/empty-components.js +0 -0
  97. /package/dist/{src → .cache/route-chart-release/frontend/frame/src}/element-icons.names.js +0 -0
  98. /package/dist/{src → .cache/route-chart-release/frontend/frame/src}/element-plus.lazy.js +0 -0
  99. /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
  100. /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,10 +1,23 @@
1
1
  /**
2
2
  * 获取页面配置
3
3
  */
4
- import { markRaw } from 'vue'
4
+ import { markRaw, reactive } from 'vue'
5
5
  import http from '@epxing/core/src/api'
6
6
  import { ADD_LIST, ADD_OBJECT, ADD_OBJECT_COUNT, ADD_TAG } from '../actions'
7
7
  import { prune, touch, touchKeys } from '@epxing/core/src/store/lru'
8
+ const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key)
9
+ // 缓存逐 key 更新,getter 从当前 store 的 state 读取,兼容独立 store 和状态替换。
10
+ const lists = reactive({})
11
+ // 每个 key 一个响应式信号,读取方只订阅自己的 key
12
+ const listSignals = new Map()
13
+ const signalOf = (key) => {
14
+ let signal = listSignals.get(key)
15
+ if (!signal) {
16
+ signal = reactive({ version: 0 })
17
+ listSignals.set(key, signal)
18
+ }
19
+ return signal
20
+ }
8
21
 
9
22
  const formatData = (array, values = [], list) => {
10
23
  return array.map(item => {
@@ -67,7 +80,7 @@ const markPayloadRaw = data => {
67
80
 
68
81
  export default {
69
82
  state: {
70
- lists: {},
83
+ lists,
71
84
  treeList: {},
72
85
  objects: {},
73
86
  object_counts: {},
@@ -76,6 +89,9 @@ export default {
76
89
  },
77
90
  getters: {
78
91
  getCodeById: (state) => (id) => {
92
+ // 只订阅这一个 key 的信号,其它代码表的到达不会触发本组件重渲染
93
+ // eslint-disable-next-line no-unused-expressions
94
+ signalOf(id).version
79
95
  const list = state.lists[id]
80
96
  if (list) {
81
97
  touch('lists', id)
@@ -101,12 +117,23 @@ export default {
101
117
  getTagListByMetaId: (state) => (id) => state.tags[id]
102
118
  },
103
119
  mutations: {
120
+ // 逐 key 写入并只通知该 key 的信号,不整体替换 lists
104
121
  [ADD_LIST] (state, data) {
105
122
  touchKeys('lists', Object.keys(data))
106
- state.lists = prune('lists', { ...state.lists, ...markPayloadRaw(data) }, LIMITS.lists)
123
+ const rawData = markPayloadRaw(data)
124
+ for (const key in rawData) {
125
+ state.lists[key] = rawData[key]
126
+ signalOf(key).version++
127
+ }
128
+ // 容量淘汰:先在副本上算出保留集合,再原地删除被淘汰的 key
129
+ const keep = prune('lists', { ...state.lists }, LIMITS.lists)
130
+ for (const key in state.lists) {
131
+ if (!hasOwn(keep, key)) delete state.lists[key]
132
+ }
107
133
  },
108
134
  'DELETE_LIST' (state, payload) {
109
135
  delete state.lists[payload.key]
136
+ signalOf(payload.key).version++
110
137
  },
111
138
  'ADD_TREE_LIST' (state, data) {
112
139
  touchKeys('treeList', Object.keys(data))
@@ -1,236 +1,236 @@
1
- import http from '@epxing/core/src/api'
2
- import util from '@epxing/core/src/runtime-utils/util'
3
- import storage from '@epxing/utils/src/storage'
4
- import { applyLibraryLocale } from '@epxing/core/src/plugins/library-locale'
5
- import {
6
- configuredLocales,
7
- createRuntimeMessages,
8
- createUiMessageCatalog,
9
- languagePreferenceKey,
10
- resolveCurrentLocale
11
- } from '@epxing/utils/src/locale'
12
-
13
- const CACHE_PREFIX = 'epx-i18n-bundle'
14
- const UI_SCOPE = 'UI'
15
-
16
- function bundleCacheKey ({ clientCode, systemId, locale }) {
17
- return [CACHE_PREFIX, clientCode, systemId, locale, UI_SCOPE].join(':')
18
- }
19
-
20
- function readBundleCache (context) {
21
- try {
22
- const value = storage.get(bundleCacheKey(context))
23
- if (!value) return null
24
- const bundle = JSON.parse(value)
25
- if (!bundle || typeof bundle.content !== 'object' || Array.isArray(bundle.content)) return null
26
- return bundle
27
- } catch (error) {
28
- return null
29
- }
30
- }
31
-
32
- function writeBundleCache (context, bundle) {
33
- try {
34
- storage.set(bundleCacheKey(context), JSON.stringify({
35
- etag: bundle.etag || '',
36
- content: bundle.content || {}
37
- }))
38
- } catch (error) {
39
- // 浏览器禁用或空间不足时退化为当前页面内存缓存。
40
- }
41
- }
42
-
43
- function removeBundleCache (context) {
44
- try {
45
- storage.remove(bundleCacheKey(context))
46
- } catch (error) {
47
- // localStorage 不可用不影响语言回退。
48
- }
49
- }
50
-
51
- function decodeMessages (data) {
52
- return (data.entitys || []).map(item => {
53
- if (data.encrypt !== '1') return item
54
- return {
55
- ...item,
56
- NAME: util.utf8to16(window.atob(item.NAME)),
57
- CONTENT: util.utf8to16(window.atob(item.CONTENT))
58
- }
59
- })
60
- }
61
-
62
- function loadSourceCatalog (systemId, defaultLocale) {
63
- return http.list({
64
- entityId: 'FM_MESSAGE',
65
- scopeParams: JSON.stringify([
66
- {
67
- field: 'TYPE',
68
- values: 'page'
69
- },
70
- {
71
- lb: '(',
72
- field: 'SCOPE',
73
- values: 'frame',
74
- link: 'or'
75
- },
76
- {
77
- field: 'SYSTEM',
78
- values: systemId,
79
- rb: ')'
80
- }
81
- ])
82
- }, {
83
- headers: {
84
- Language: defaultLocale
85
- }
86
- }).then(data => createUiMessageCatalog(decodeMessages(data), systemId))
87
- }
88
-
89
- function requestPublishedBundle (context, etag) {
90
- return http.$request({
91
- action: 'publishedTranslations',
92
- entityId: 'FM_I18N_RESOURCE',
93
- localeCode: context.locale,
94
- scope: UI_SCOPE
95
- }, {
96
- method: 'get',
97
- headers: etag ? { 'If-None-Match': etag } : {}
98
- })
99
- }
100
-
101
- function acceptPublishedBundle (context, bundle) {
102
- if (!bundle || !bundle.published) {
103
- removeBundleCache(context)
104
- return { published: false, etag: '', content: {} }
105
- }
106
- writeBundleCache(context, bundle)
107
- return bundle
108
- }
109
-
110
- function loadPublishedBundle (context) {
111
- const cached = readBundleCache(context)
112
- return requestPublishedBundle(context, cached && cached.etag).then(bundle => {
113
- if (!bundle || !bundle.notModified) return acceptPublishedBundle(context, bundle)
114
- const content = cached && cached.content
115
- if (content && Object.keys(content).length > 0) {
116
- return { published: true, etag: cached.etag, content }
117
- }
118
- // 服务端确认未变更,但本地缓存已丢失或为空:丢掉 ETag 重新拉完整包,
119
- // 否则会把空内容当成已发布语言包,整站文案回退默认语言。
120
- removeBundleCache(context)
121
- return requestPublishedBundle(context, null)
122
- .then(full => acceptPublishedBundle(context, full))
123
- })
124
- }
125
-
126
- function applyMessages (locale, messages, fallbackMessages) {
127
- const libraryLocale = applyLibraryLocale(locale)
128
- if (globalThis.i18n) {
129
- globalThis.i18n.setLocale(locale)
130
- globalThis.i18n.setMessage(messages, fallbackMessages)
131
- }
132
- return libraryLocale
133
- }
134
-
135
- export default {
136
- state: {
137
- enabled: false,
138
- clientCode: '',
139
- systemId: '',
140
- currentLocale: 'zh-CN',
141
- defaultLocale: 'zh-CN',
142
- supportedLocales: ['zh-CN'],
143
- libraryLocale: 'zh-CN',
144
- messages: {},
145
- fallbackMessages: {},
146
- etag: '',
147
- published: false,
148
- loaded: false,
149
- loading: false
150
- },
151
- mutations: {
152
- SET_LOCALE_CONTEXT (state, context) {
153
- Object.assign(state, context)
154
- },
155
- SET_LOCALE_LOADING (state, loading) {
156
- state.loading = loading
157
- },
158
- SET_LOCALE_MESSAGES (state, payload) {
159
- state.messages = payload.messages
160
- state.fallbackMessages = payload.fallbackMessages
161
- state.etag = payload.etag || ''
162
- state.published = payload.published
163
- state.libraryLocale = payload.libraryLocale
164
- state.loaded = true
165
- state.loading = false
166
- }
167
- },
168
- actions: {
169
- async INITIALIZE_LOCALE ({ state, commit, rootState }, payload = {}) {
170
- const clientConfig = rootState.config.clientConfig || {}
171
- const userInfo = rootState.user.userInfo || {}
172
- const appConfig = rootState.config.appConfig || {}
173
- const configuration = configuredLocales(clientConfig)
174
- const clientCode = clientConfig.CODE || ''
175
- const systemId = payload.systemId || appConfig['SYSTEM|ID'] || appConfig.SYSTEM || ''
176
- const currentLocale = resolveCurrentLocale(clientConfig, userInfo)
177
- const context = {
178
- enabled: configuration.enabled,
179
- clientCode,
180
- systemId,
181
- currentLocale,
182
- defaultLocale: configuration.defaultLocale,
183
- supportedLocales: configuration.supportedLocales
184
- }
185
- if (!payload.force && state.loaded && state.clientCode === clientCode && state.systemId === systemId && state.currentLocale === currentLocale) {
186
- return state
187
- }
188
-
189
- commit('SET_LOCALE_CONTEXT', context)
190
- commit('SET_LOCALE_LOADING', true)
191
- let catalog = { fallbackMessages: {}, aliases: {} }
192
- try {
193
- catalog = await loadSourceCatalog(systemId, configuration.defaultLocale)
194
- } catch (error) {
195
- // 源消息失败时仍允许页面启动,$t 最终回退到调用方传入的 Key。
196
- }
197
-
198
- let publishedBundle = { published: false, etag: '', content: {} }
199
- // 登录、注册页也要加载界面文案包:这些页面整页文案都走 $t,拿不到包就整页回退默认语言。
200
- // 服务端对匿名请求只放行 UI 范围,配置资源包仍要求登录。
201
- const usePublishedBundle = configuration.enabled && currentLocale !== configuration.defaultLocale
202
- if (usePublishedBundle) {
203
- try {
204
- publishedBundle = await loadPublishedBundle({ clientCode, systemId, locale: currentLocale })
205
- } catch (error) {
206
- // 已发布包暂时不可用时回退默认语言,不让应用启动失败。
207
- }
208
- }
209
-
210
- const messages = publishedBundle.published
211
- ? createRuntimeMessages(publishedBundle.content, catalog)
212
- : catalog.fallbackMessages
213
- const libraryLocale = applyMessages(currentLocale, messages, catalog.fallbackMessages)
214
- commit('SET_LOCALE_MESSAGES', {
215
- messages,
216
- fallbackMessages: catalog.fallbackMessages,
217
- etag: publishedBundle.etag,
218
- published: publishedBundle.published,
219
- libraryLocale
220
- })
221
- return state
222
- },
223
- CHANGE_LOCALE ({ commit, rootState }, value) {
224
- const locale = typeof value === 'string' ? value : value.locale
225
- const clientConfig = rootState.config.clientConfig || {}
226
- const configuration = configuredLocales(clientConfig)
227
- const selected = configuration.supportedLocales.find(item => item.toLowerCase() === String(locale || '').toLowerCase())
228
- if (!selected) throw new Error(`当前客户未启用语言:${locale}`)
229
- const clientCode = clientConfig.CODE || ''
230
- storage.set(languagePreferenceKey(clientCode), selected)
231
- const libraryLocale = applyLibraryLocale(selected)
232
- commit('SET_LOCALE_CONTEXT', { clientCode, currentLocale: selected, libraryLocale })
233
- return selected
234
- }
235
- }
236
- }
1
+ import http from '@epxing/core/src/api'
2
+ import util from '@epxing/core/src/runtime-utils/util'
3
+ import storage from '@epxing/utils/src/storage'
4
+ import { applyLibraryLocale } from '@epxing/core/src/plugins/library-locale'
5
+ import {
6
+ configuredLocales,
7
+ createRuntimeMessages,
8
+ createUiMessageCatalog,
9
+ languagePreferenceKey,
10
+ resolveCurrentLocale
11
+ } from '@epxing/utils/src/locale'
12
+
13
+ const CACHE_PREFIX = 'epx-i18n-bundle'
14
+ const UI_SCOPE = 'UI'
15
+
16
+ function bundleCacheKey ({ clientCode, systemId, locale }) {
17
+ return [CACHE_PREFIX, clientCode, systemId, locale, UI_SCOPE].join(':')
18
+ }
19
+
20
+ function readBundleCache (context) {
21
+ try {
22
+ const value = storage.get(bundleCacheKey(context))
23
+ if (!value) return null
24
+ const bundle = JSON.parse(value)
25
+ if (!bundle || typeof bundle.content !== 'object' || Array.isArray(bundle.content)) return null
26
+ return bundle
27
+ } catch (error) {
28
+ return null
29
+ }
30
+ }
31
+
32
+ function writeBundleCache (context, bundle) {
33
+ try {
34
+ storage.set(bundleCacheKey(context), JSON.stringify({
35
+ etag: bundle.etag || '',
36
+ content: bundle.content || {}
37
+ }))
38
+ } catch (error) {
39
+ // 浏览器禁用或空间不足时退化为当前页面内存缓存。
40
+ }
41
+ }
42
+
43
+ function removeBundleCache (context) {
44
+ try {
45
+ storage.remove(bundleCacheKey(context))
46
+ } catch (error) {
47
+ // localStorage 不可用不影响语言回退。
48
+ }
49
+ }
50
+
51
+ function decodeMessages (data) {
52
+ return (data.entitys || []).map(item => {
53
+ if (data.encrypt !== '1') return item
54
+ return {
55
+ ...item,
56
+ NAME: util.utf8to16(window.atob(item.NAME)),
57
+ CONTENT: util.utf8to16(window.atob(item.CONTENT))
58
+ }
59
+ })
60
+ }
61
+
62
+ function loadSourceCatalog (systemId, defaultLocale) {
63
+ return http.list({
64
+ entityId: 'FM_MESSAGE',
65
+ scopeParams: JSON.stringify([
66
+ {
67
+ field: 'TYPE',
68
+ values: 'page'
69
+ },
70
+ {
71
+ lb: '(',
72
+ field: 'SCOPE',
73
+ values: 'frame',
74
+ link: 'or'
75
+ },
76
+ {
77
+ field: 'SYSTEM',
78
+ values: systemId,
79
+ rb: ')'
80
+ }
81
+ ])
82
+ }, {
83
+ headers: {
84
+ Language: defaultLocale
85
+ }
86
+ }).then(data => createUiMessageCatalog(decodeMessages(data), systemId))
87
+ }
88
+
89
+ function requestPublishedBundle (context, etag) {
90
+ return http.$request({
91
+ action: 'publishedTranslations',
92
+ entityId: 'FM_I18N_RESOURCE',
93
+ localeCode: context.locale,
94
+ scope: UI_SCOPE
95
+ }, {
96
+ method: 'get',
97
+ headers: etag ? { 'If-None-Match': etag } : {}
98
+ })
99
+ }
100
+
101
+ function acceptPublishedBundle (context, bundle) {
102
+ if (!bundle || !bundle.published) {
103
+ removeBundleCache(context)
104
+ return { published: false, etag: '', content: {} }
105
+ }
106
+ writeBundleCache(context, bundle)
107
+ return bundle
108
+ }
109
+
110
+ function loadPublishedBundle (context) {
111
+ const cached = readBundleCache(context)
112
+ return requestPublishedBundle(context, cached && cached.etag).then(bundle => {
113
+ if (!bundle || !bundle.notModified) return acceptPublishedBundle(context, bundle)
114
+ const content = cached && cached.content
115
+ if (content && Object.keys(content).length > 0) {
116
+ return { published: true, etag: cached.etag, content }
117
+ }
118
+ // 服务端确认未变更,但本地缓存已丢失或为空:丢掉 ETag 重新拉完整包,
119
+ // 否则会把空内容当成已发布语言包,整站文案回退默认语言。
120
+ removeBundleCache(context)
121
+ return requestPublishedBundle(context, null)
122
+ .then(full => acceptPublishedBundle(context, full))
123
+ })
124
+ }
125
+
126
+ function applyMessages (locale, messages, fallbackMessages) {
127
+ const libraryLocale = applyLibraryLocale(locale)
128
+ if (globalThis.i18n) {
129
+ globalThis.i18n.setLocale(locale)
130
+ globalThis.i18n.setMessage(messages, fallbackMessages)
131
+ }
132
+ return libraryLocale
133
+ }
134
+
135
+ export default {
136
+ state: {
137
+ enabled: false,
138
+ clientCode: '',
139
+ systemId: '',
140
+ currentLocale: 'zh-CN',
141
+ defaultLocale: 'zh-CN',
142
+ supportedLocales: ['zh-CN'],
143
+ libraryLocale: 'zh-CN',
144
+ messages: {},
145
+ fallbackMessages: {},
146
+ etag: '',
147
+ published: false,
148
+ loaded: false,
149
+ loading: false
150
+ },
151
+ mutations: {
152
+ SET_LOCALE_CONTEXT (state, context) {
153
+ Object.assign(state, context)
154
+ },
155
+ SET_LOCALE_LOADING (state, loading) {
156
+ state.loading = loading
157
+ },
158
+ SET_LOCALE_MESSAGES (state, payload) {
159
+ state.messages = payload.messages
160
+ state.fallbackMessages = payload.fallbackMessages
161
+ state.etag = payload.etag || ''
162
+ state.published = payload.published
163
+ state.libraryLocale = payload.libraryLocale
164
+ state.loaded = true
165
+ state.loading = false
166
+ }
167
+ },
168
+ actions: {
169
+ async INITIALIZE_LOCALE ({ state, commit, rootState }, payload = {}) {
170
+ const clientConfig = rootState.config.clientConfig || {}
171
+ const userInfo = rootState.user.userInfo || {}
172
+ const appConfig = rootState.config.appConfig || {}
173
+ const configuration = configuredLocales(clientConfig)
174
+ const clientCode = clientConfig.CODE || ''
175
+ const systemId = payload.systemId || appConfig['SYSTEM|ID'] || appConfig.SYSTEM || ''
176
+ const currentLocale = resolveCurrentLocale(clientConfig, userInfo)
177
+ const context = {
178
+ enabled: configuration.enabled,
179
+ clientCode,
180
+ systemId,
181
+ currentLocale,
182
+ defaultLocale: configuration.defaultLocale,
183
+ supportedLocales: configuration.supportedLocales
184
+ }
185
+ if (!payload.force && state.loaded && state.clientCode === clientCode && state.systemId === systemId && state.currentLocale === currentLocale) {
186
+ return state
187
+ }
188
+
189
+ commit('SET_LOCALE_CONTEXT', context)
190
+ commit('SET_LOCALE_LOADING', true)
191
+ let catalog = { fallbackMessages: {}, aliases: {} }
192
+ try {
193
+ catalog = await loadSourceCatalog(systemId, configuration.defaultLocale)
194
+ } catch (error) {
195
+ // 源消息失败时仍允许页面启动,$t 最终回退到调用方传入的 Key。
196
+ }
197
+
198
+ let publishedBundle = { published: false, etag: '', content: {} }
199
+ // 登录、注册页也要加载界面文案包:这些页面整页文案都走 $t,拿不到包就整页回退默认语言。
200
+ // 服务端对匿名请求只放行 UI 范围,配置资源包仍要求登录。
201
+ const usePublishedBundle = configuration.enabled && currentLocale !== configuration.defaultLocale
202
+ if (usePublishedBundle) {
203
+ try {
204
+ publishedBundle = await loadPublishedBundle({ clientCode, systemId, locale: currentLocale })
205
+ } catch (error) {
206
+ // 已发布包暂时不可用时回退默认语言,不让应用启动失败。
207
+ }
208
+ }
209
+
210
+ const messages = publishedBundle.published
211
+ ? createRuntimeMessages(publishedBundle.content, catalog)
212
+ : catalog.fallbackMessages
213
+ const libraryLocale = applyMessages(currentLocale, messages, catalog.fallbackMessages)
214
+ commit('SET_LOCALE_MESSAGES', {
215
+ messages,
216
+ fallbackMessages: catalog.fallbackMessages,
217
+ etag: publishedBundle.etag,
218
+ published: publishedBundle.published,
219
+ libraryLocale
220
+ })
221
+ return state
222
+ },
223
+ CHANGE_LOCALE ({ commit, rootState }, value) {
224
+ const locale = typeof value === 'string' ? value : value.locale
225
+ const clientConfig = rootState.config.clientConfig || {}
226
+ const configuration = configuredLocales(clientConfig)
227
+ const selected = configuration.supportedLocales.find(item => item.toLowerCase() === String(locale || '').toLowerCase())
228
+ if (!selected) throw new Error(`当前客户未启用语言:${locale}`)
229
+ const clientCode = clientConfig.CODE || ''
230
+ storage.set(languagePreferenceKey(clientCode), selected)
231
+ const libraryLocale = applyLibraryLocale(selected)
232
+ commit('SET_LOCALE_CONTEXT', { clientCode, currentLocale: selected, libraryLocale })
233
+ return selected
234
+ }
235
+ }
236
+ }