@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,265 +1,314 @@
1
- /**
2
- * 获取页面配置
3
- */
4
- import { markRaw } from 'vue'
5
- import http from '@epxing/core/src/api'
6
- import Meta from '@epxing/core/src/runtime-utils/meta'
7
- import { ADD_META } from '@epxing/core/src/store/actions'
8
- import { prune, touch, touchKeys } from '@epxing/core/src/store/lru'
9
- const ADD_SIMPLE_META = 'ADD_SIMPLE_META'
10
- const ADD_META_RELATION_FIELDS = 'ADD_META_RELATION_FIELDS'
11
- const ADD_META_VIEW_FIELDS = 'ADD_META_VIEW_FIELDS'
12
- // 缓存容量:soft 是常态上限,hard 是极端场景兜底;正在使用的对象不会被淘汰,详见 @epxing/core/src/store/lru
13
- // 单个对象元数据实测约 110KB 堆(已在 @epxing/utils/src/meta 里冻结只读容器;不冻结时是 2.9MB),
14
- // 所以 metas 常态 50 个约 5MB、最坏 200 个约 22MB
15
- const LIMITS = {
16
- metas: { soft: 50, hard: 200 },
17
- metaRelationFields: { soft: 50, hard: 120 },
18
- metaViewFields: { soft: 50, hard: 200 }
19
- }
20
- // 按条件在 metas 里查找,命中的同时记录一次使用
21
- const findMeta = (metas, matcher) => {
22
- for (const key in metas) {
23
- const item = metas[key]
24
- if (item && matcher(item)) {
25
- touch('metas', key)
26
- return item
27
- }
28
- }
29
- return undefined
30
- }
31
- const formatFields = (arr, flag = '') => {
32
- arr.forEach(item => {
33
- if (!item.field) item.field = flag ? flag.toUpperCase() + '|' + item.code : item.code
34
- if (!item.codeKey) item.codeKey = item.relatedEntity
35
- if (item.relationMeta) {
36
- const { children, linkFlag, relationMetaList, id, text } = item.relationMeta
37
- item.children = children.map(child => ({ ...child, meta: id, metaLabel: text }))
38
- item.linkFlag = flag ? flag + '|' + linkFlag : linkFlag
39
- if (relationMetaList) {
40
- relationMetaList.forEach(meta => {
41
- meta.isSub = true
42
- meta.type = 'meta'
43
- meta.linkFlag = item.linkFlag + '|' + meta.linkFlag
44
- meta.field = meta.linkFlag
45
- meta.children = meta.children.map(child => ({ ...child, meta: meta.id, metaLabel: meta.text, relationSort: meta.relationType }))
46
- })
47
- item.children.push(...relationMetaList)
48
- }
49
- }
50
- if (item.children) formatFields(item.children, item.linkFlag)
51
- })
52
- }
53
-
54
- export default {
55
- state: {
56
- metas: {},
57
- simpleMetas: {},
58
- metaStatus: {},
59
- simpleMetaStatus: {},
60
- metaRelationFields: {},
61
- metaRelationFieldStatus: {},
62
- metaViewFields: {},
63
- metaViewFieldStatus: {}
64
-
65
- },
66
- mutations: {
67
- [ADD_META] (state, data) {
68
- touchKeys('metas', Object.keys(data))
69
- state.metas = prune('metas', { ...state.metas, ...data }, LIMITS.metas)
70
- },
71
- [ADD_SIMPLE_META] (state, data) {
72
- state.simpleMetas = { ...state.simpleMetas, ...data }
73
- },
74
- [ADD_META_RELATION_FIELDS] (state, data) {
75
- touchKeys('metaRelationFields', Object.keys(data))
76
- state.metaRelationFields = prune('metaRelationFields', { ...state.metaRelationFields, ...data }, LIMITS.metaRelationFields)
77
- },
78
- [ADD_META_VIEW_FIELDS] (state, data) {
79
- touchKeys('metaViewFields', Object.keys(data))
80
- state.metaViewFields = prune('metaViewFields', { ...state.metaViewFields, ...data }, LIMITS.metaViewFields)
81
- }
82
- },
83
- getters: {
84
- getMetaById: (state) => (id) => {
85
- const meta = state.metas[id]
86
- if (meta) touch('metas', id)
87
- return meta
88
- },
89
- getMeta: (state) => (id) => {
90
- return findMeta(state.metas, item => (item.id === id || item.code === id || (item.code + '@' + item.systemCode + '@' + item.clientCode) === id))
91
- },
92
- getSimpleMeta: (state) => (id) => {
93
- return state.simpleMetas[id] || findMeta(state.metas, item => (item.id === id || item.code === id))
94
- },
95
- getMetaRelationFields: (state) => (id) => {
96
- const fields = state.metaRelationFields[id]
97
- if (fields) touch('metaRelationFields', id)
98
- return fields
99
- },
100
- getMetaViewFields: (state) => (metaId, viewId) => {
101
- const key = metaId + (viewId || '')
102
- const fields = state.metaViewFields[key]
103
- if (fields) touch('metaViewFields', key)
104
- return fields
105
- }
106
- },
107
- actions: {
108
- [ADD_META] ({ state, commit }, payload = {}) {
109
- const { [payload.metaId]: metaPromise } = state.metaStatus
110
- if (metaPromise) return metaPromise
111
- // eslint-disable-next-line no-async-promise-executor
112
- const promise = new Promise((resolve, reject) => {
113
- if (!payload.metaId) {
114
- // 这条路径**不写在飞标记**(见函数末尾的 if 守卫):原来会以 undefined 为键把这个已 reject
115
- // 的 promise 存进 metaStatus,之后每次不带 metaId 的调用都命中开头的缓存判断、拿到它。
116
- reject(new Error('ADD_META 缺少 metaId'))
117
- return
118
- }
119
- http.getMeta({
120
- metaId: payload.metaId.includes('@system@') ? payload.metaId.split('@')[0] : payload.metaId
121
- })
122
- .then(data => {
123
- if (!data) {
124
- // ⚠️ 这是「后端 200 但 data 为空」,**不会**走下面的 .catch —— 在 .then 里调 reject
125
- // 只让外层 promise 变成 rejected,同一条链的 catch 不会被触发。
126
- // 漏删在飞标记的后果不是多发一次请求,而是**本次会话再也加载不出这个对象**:
127
- // action 开头的 `if (metaPromise) return metaPromise` 会一直返回这个已 reject 的
128
- // promise,表现为静默失败,且**没有任何重试能恢复**,直到用户整页刷新。
129
- delete state.metaStatus[payload.metaId]
130
- reject(new Error(`ADD_META 后端返回空数据:metaId=${payload.metaId}`))
131
- return
132
- }
133
- // markRaw:对象元数据(attributes/relations 大且只读、加载后不变)不做响应式,省深层 Proxy
134
- // 的内存与每次字段访问的 get/set 开销;元数据从不被响应式修改(见本模块只读+LRU 删除),安全。
135
- const meta = markRaw(new Meta(data))
136
- commit(ADD_META, { [payload.metaId]: meta })
137
- delete state.metaStatus[payload.metaId]
138
- resolve(meta)
139
- })
140
- .catch(err => {
141
- delete state.metaStatus[payload.metaId]
142
- reject(err)
143
- })
144
- })
145
- if (payload.metaId) state.metaStatus = { ...state.metaStatus, [payload.metaId]: promise }
146
- return promise
147
- },
148
- [ADD_SIMPLE_META] ({ state, commit }, payload = {}) {
149
- const { [payload.metaId]: metaPromise } = state.simpleMetaStatus
150
- if (metaPromise) return metaPromise
151
- // eslint-disable-next-line no-async-promise-executor
152
- const promise = new Promise((resolve, reject) => {
153
- if (!payload.metaId) {
154
- // 同 ADD_META:不写在飞标记,否则 undefined 键会把已 reject 的 promise 永久缓存下来。
155
- reject(new Error('ADD_SIMPLE_META 缺少 metaId'))
156
- return
157
- }
158
- http.getSimpleMeta({
159
- metaId: payload.metaId
160
- })
161
- .then(data => {
162
- if (!data) {
163
- // 与 ADD_META 同一个坑:这条分支不走 .catch,漏删在飞标记会让该 metaId 本次会话永久失败。
164
- delete state.simpleMetaStatus[payload.metaId]
165
- reject(new Error(`ADD_SIMPLE_META 后端返回空数据:metaId=${payload.metaId}`))
166
- return
167
- }
168
- commit(ADD_SIMPLE_META, { [payload.metaId]: markRaw(data) }) // 只读元数据,去响应式
169
- delete state.simpleMetaStatus[payload.metaId]
170
- // 原来这里 resolve 之后**没有 return**,成功路径也会继续走到下面的 reject(new Error())。
171
- // promise 已 settle 所以状态上看不出来,但每次成功都白构造一个 Error,
172
- // 更要紧的是这行让「成功」和「空数据」两条路径纠缠在一起,掩盖了上面那个漏删。
173
- resolve(data)
174
- })
175
- .catch(err => {
176
- delete state.simpleMetaStatus[payload.metaId]
177
- reject(err)
178
- })
179
- })
180
- if (payload.metaId) state.simpleMetaStatus = { ...state.simpleMetaStatus, [payload.metaId]: promise }
181
- return promise
182
- },
183
- [ADD_META_RELATION_FIELDS] ({ state, commit }, payload = {}) {
184
- const { [payload.id]: metaPromise } = state.metaRelationFieldStatus
185
- if (metaPromise) return metaPromise
186
- const promise = http.get({
187
- action: 'metaRelationTree',
188
- entityId: 'FM_ENTITY',
189
- metaId: payload.id,
190
- allSub: true
191
- })
192
- .then(data => {
193
- // data 为空时原来既不删在飞标记也不 reject ⇒ 永久缓存一个 resolve 成 undefined 的 promise,
194
- // 后续所有调用都直接拿到 undefined。这里只补清理、保持「resolve undefined」的既有语义
195
- // (改成 reject 会波及 88 处 dispatch 的错误处理,不在本次修复范围内)。
196
- if (!data) {
197
- delete state.metaRelationFieldStatus[payload.id]
198
- return
199
- }
200
- data.children = data.children.map(item => ({ ...item, meta: data.id }))
201
- if (data.relationMetaList) {
202
- data.relationMetaList.forEach(meta => {
203
- meta.isSub = true
204
- meta.type = 'meta'
205
- meta.isMeta = true
206
- meta.field = meta.linkFlag
207
- meta.children = meta.children.map(child => ({ ...child, relationSort: meta.relationType, meta: meta.id, field: meta.linkFlag.toUpperCase() + '|' + child.code }))
208
- })
209
- data.children.push(...data.relationMetaList)
210
- }
211
- data.isMain = true
212
- data.isUnFold = true
213
- const fields = [data]
214
- formatFields(fields)
215
- commit(ADD_META_RELATION_FIELDS, { [payload.id]: markRaw(fields) }) // 只读关联字段树,去响应式
216
- delete state.metaRelationFieldStatus[payload.id]
217
- return fields
218
- })
219
- .catch(() => {
220
- delete state.metaRelationFieldStatus[payload.id]
221
- })
222
- state.metaRelationFieldStatus = { ...state.metaRelationFieldStatus, [payload.id]: promise }
223
- return promise
224
- },
225
- [ADD_META_VIEW_FIELDS] ({ state, commit }, payload = {}) {
226
- const key = payload.metaId + (payload.viewId || '')
227
- const { [key]: metaPromise } = state.metaViewFieldStatus
228
- if (metaPromise) return metaPromise
229
- const promise = http.get({
230
- action: 'getEntityMetaView',
231
- entityId: 'FM_ENTITY_VIEW',
232
- metaId: payload.metaId,
233
- metaViewId: payload.viewId,
234
- allSub: true
235
- })
236
- .then(data => {
237
- // 同 ADD_META_RELATION_FIELDS:补上空数据时的在飞标记清理,语义保持不变。
238
- if (!data) {
239
- delete state.metaViewFieldStatus[key]
240
- return
241
- }
242
- data[0].isMain = true
243
- const formatData = (arr) => {
244
- return arr.filter(item => !!item).map(item => {
245
- item.field = item.type === 'meta' ? item.linkFlag || item.code : item.linkCode || item.code
246
- item.relationSort = item.relationType
247
- if (!item.codeKey) item.codeKey = item.relatedEntity
248
- if (item.type === 'meta' && item.relationType === '1') item.isSub = true
249
- if (item.children) item.children = formatData(item.children)
250
- return item
251
- })
252
- }
253
- const newData = formatData(data)
254
- commit(ADD_META_VIEW_FIELDS, { [key]: markRaw(newData) }) // 只读视图字段,去响应式
255
- delete state.metaViewFieldStatus[key]
256
- return newData
257
- })
258
- .catch(() => {
259
- delete state.metaViewFieldStatus[key]
260
- })
261
- state.metaViewFieldStatus = { ...state.metaViewFieldStatus, [key]: promise }
262
- return promise
263
- }
264
- }
265
- }
1
+ /**
2
+ * 获取页面配置
3
+ */
4
+ import { markRaw, reactive, toRaw } from 'vue'
5
+ import http from '@epxing/core/src/api'
6
+ import Meta from '@epxing/core/src/runtime-utils/meta'
7
+ import { ADD_META } from '@epxing/core/src/store/actions'
8
+ import { prune, touch, touchKeys } from '@epxing/core/src/store/lru'
9
+ const ADD_SIMPLE_META = 'ADD_SIMPLE_META'
10
+ const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key)
11
+ // 缓存逐 key 更新,getter 从当前 store 的 state 读取,兼容独立 store 和状态替换。
12
+ const metas = reactive({})
13
+ // 别名 → 存储 key:同一对象可按 id / code / code@system@client 三种写法读取
14
+ const metaAliases = new WeakMap()
15
+ const aliasesOf = (state) => {
16
+ const cache = toRaw(state.metas)
17
+ if (!metaAliases.has(cache)) metaAliases.set(cache, {})
18
+ return metaAliases.get(cache)
19
+ }
20
+ // 每个 key 一个响应式信号,读取方只订阅自己的 key;元数据到达时只通知它
21
+ const metaSignals = new Map()
22
+ const signalOf = (key) => {
23
+ let signal = metaSignals.get(key)
24
+ if (!signal) {
25
+ signal = reactive({ version: 0 })
26
+ metaSignals.set(key, signal)
27
+ }
28
+ return signal
29
+ }
30
+ const notifyMeta = (key) => {
31
+ const signal = metaSignals.get(key)
32
+ if (signal) signal.version++
33
+ }
34
+ const ADD_META_RELATION_FIELDS = 'ADD_META_RELATION_FIELDS'
35
+ const ADD_META_VIEW_FIELDS = 'ADD_META_VIEW_FIELDS'
36
+ // 缓存容量:soft 是常态上限,hard 是极端场景兜底;正在使用的对象不会被淘汰,详见 @epxing/core/src/store/lru
37
+ // 单个对象元数据实测约 110KB 堆(已在 @epxing/utils/src/meta 里冻结只读容器;不冻结时是 2.9MB),
38
+ // 所以 metas 常态 50 个约 5MB、最坏 200 个约 22MB
39
+ const LIMITS = {
40
+ metas: { soft: 50, hard: 200 },
41
+ metaRelationFields: { soft: 50, hard: 120 },
42
+ metaViewFields: { soft: 50, hard: 200 }
43
+ }
44
+ // 按条件在 metas 里查找,命中的同时记录一次使用
45
+ const findMeta = (metas, matcher) => {
46
+ for (const key in metas) {
47
+ const item = metas[key]
48
+ if (item && matcher(item)) {
49
+ touch('metas', key)
50
+ return item
51
+ }
52
+ }
53
+ return undefined
54
+ }
55
+ const formatFields = (arr, flag = '') => {
56
+ arr.forEach(item => {
57
+ if (!item.field) item.field = flag ? flag.toUpperCase() + '|' + item.code : item.code
58
+ if (!item.codeKey) item.codeKey = item.relatedEntity
59
+ if (item.relationMeta) {
60
+ const { children, linkFlag, relationMetaList, id, text } = item.relationMeta
61
+ item.children = children.map(child => ({ ...child, meta: id, metaLabel: text }))
62
+ item.linkFlag = flag ? flag + '|' + linkFlag : linkFlag
63
+ if (relationMetaList) {
64
+ relationMetaList.forEach(meta => {
65
+ meta.isSub = true
66
+ meta.type = 'meta'
67
+ meta.linkFlag = item.linkFlag + '|' + meta.linkFlag
68
+ meta.field = meta.linkFlag
69
+ meta.children = meta.children.map(child => ({ ...child, meta: meta.id, metaLabel: meta.text, relationSort: meta.relationType }))
70
+ })
71
+ item.children.push(...relationMetaList)
72
+ }
73
+ }
74
+ if (item.children) formatFields(item.children, item.linkFlag)
75
+ })
76
+ }
77
+
78
+ export default {
79
+ state: {
80
+ metas,
81
+ simpleMetas: {},
82
+ metaStatus: {},
83
+ simpleMetaStatus: {},
84
+ metaRelationFields: {},
85
+ metaRelationFieldStatus: {},
86
+ metaViewFields: {},
87
+ metaViewFieldStatus: {}
88
+
89
+ },
90
+ mutations: {
91
+ // 逐 key 写入并只通知该 key(及其别名)的信号,不整体替换 metas
92
+ [ADD_META] (state, data) {
93
+ touchKeys('metas', Object.keys(data))
94
+ const metaAlias = aliasesOf(state)
95
+ for (const key in data) {
96
+ const meta = data[key]
97
+ state.metas[key] = meta
98
+ const aliases = [meta.id, meta.code, `${meta.code}@${meta.systemCode}@${meta.clientCode}`]
99
+ aliases.forEach(alias => {
100
+ if (alias && alias !== key) metaAlias[alias] = key
101
+ })
102
+ notifyMeta(key)
103
+ aliases.forEach(notifyMeta)
104
+ }
105
+ // 容量淘汰:先在副本上算出保留集合,再原地删除被淘汰的 key(保护期内正在用的不会被淘汰)
106
+ const keep = prune('metas', { ...state.metas }, LIMITS.metas)
107
+ for (const key in state.metas) {
108
+ if (!hasOwn(keep, key)) delete state.metas[key]
109
+ }
110
+ },
111
+ [ADD_SIMPLE_META] (state, data) {
112
+ state.simpleMetas = { ...state.simpleMetas, ...data }
113
+ },
114
+ [ADD_META_RELATION_FIELDS] (state, data) {
115
+ touchKeys('metaRelationFields', Object.keys(data))
116
+ state.metaRelationFields = prune('metaRelationFields', { ...state.metaRelationFields, ...data }, LIMITS.metaRelationFields)
117
+ },
118
+ [ADD_META_VIEW_FIELDS] (state, data) {
119
+ touchKeys('metaViewFields', Object.keys(data))
120
+ state.metaViewFields = prune('metaViewFields', { ...state.metaViewFields, ...data }, LIMITS.metaViewFields)
121
+ }
122
+ },
123
+ getters: {
124
+ getMetaById: (state) => (id) => {
125
+ // eslint-disable-next-line no-unused-expressions
126
+ signalOf(id).version
127
+ const meta = state.metas[id]
128
+ if (meta) touch('metas', id)
129
+ return meta
130
+ },
131
+ getMeta: (state) => (id) => {
132
+ if (!id) return undefined
133
+ // 只订阅这一个 key 的信号,其它元数据的到达不会触发本组件重渲染
134
+ // eslint-disable-next-line no-unused-expressions
135
+ signalOf(id).version
136
+ const key = hasOwn(state.metas, id) ? id : aliasesOf(state)[id]
137
+ const meta = key ? state.metas[key] : undefined
138
+ if (meta) touch('metas', key)
139
+ return meta
140
+ },
141
+ getSimpleMeta: (state) => (id) => {
142
+ return state.simpleMetas[id] || findMeta(state.metas, item => (item.id === id || item.code === id))
143
+ },
144
+ getMetaRelationFields: (state) => (id) => {
145
+ const fields = state.metaRelationFields[id]
146
+ if (fields) touch('metaRelationFields', id)
147
+ return fields
148
+ },
149
+ getMetaViewFields: (state) => (metaId, viewId) => {
150
+ const key = metaId + (viewId || '')
151
+ const fields = state.metaViewFields[key]
152
+ if (fields) touch('metaViewFields', key)
153
+ return fields
154
+ }
155
+ },
156
+ actions: {
157
+ [ADD_META] ({ state, commit }, payload = {}) {
158
+ const { [payload.metaId]: metaPromise } = state.metaStatus
159
+ if (metaPromise) return metaPromise
160
+ // eslint-disable-next-line no-async-promise-executor
161
+ const promise = new Promise((resolve, reject) => {
162
+ if (!payload.metaId) {
163
+ // 这条路径**不写在飞标记**(见函数末尾的 if 守卫):原来会以 undefined 为键把这个已 reject
164
+ // 的 promise 存进 metaStatus,之后每次不带 metaId 的调用都命中开头的缓存判断、拿到它。
165
+ reject(new Error('ADD_META 缺少 metaId'))
166
+ return
167
+ }
168
+ http.getMeta({
169
+ metaId: payload.metaId.includes('@system@') ? payload.metaId.split('@')[0] : payload.metaId
170
+ })
171
+ .then(data => {
172
+ if (!data) {
173
+ // ⚠️ 这是「后端 200 但 data 为空」,**不会**走下面的 .catch —— 在 .then 里调 reject
174
+ // 只让外层 promise 变成 rejected,同一条链的 catch 不会被触发。
175
+ // 漏删在飞标记的后果不是多发一次请求,而是**本次会话再也加载不出这个对象**:
176
+ // action 开头的 `if (metaPromise) return metaPromise` 会一直返回这个已 reject 的
177
+ // promise,表现为静默失败,且**没有任何重试能恢复**,直到用户整页刷新。
178
+ delete state.metaStatus[payload.metaId]
179
+ reject(new Error(`ADD_META 后端返回空数据:metaId=${payload.metaId}`))
180
+ return
181
+ }
182
+ // markRaw:对象元数据(attributes/relations 大且只读、加载后不变)不做响应式,省深层 Proxy
183
+ // 的内存与每次字段访问的 get/set 开销;元数据从不被响应式修改(见本模块只读+LRU 删除),安全。
184
+ const meta = markRaw(new Meta(data))
185
+ commit(ADD_META, { [payload.metaId]: meta })
186
+ delete state.metaStatus[payload.metaId]
187
+ resolve(meta)
188
+ })
189
+ .catch(err => {
190
+ delete state.metaStatus[payload.metaId]
191
+ reject(err)
192
+ })
193
+ })
194
+ if (payload.metaId) state.metaStatus = { ...state.metaStatus, [payload.metaId]: promise }
195
+ return promise
196
+ },
197
+ [ADD_SIMPLE_META] ({ state, commit }, payload = {}) {
198
+ const { [payload.metaId]: metaPromise } = state.simpleMetaStatus
199
+ if (metaPromise) return metaPromise
200
+ // eslint-disable-next-line no-async-promise-executor
201
+ const promise = new Promise((resolve, reject) => {
202
+ if (!payload.metaId) {
203
+ // 同 ADD_META:不写在飞标记,否则 undefined 键会把已 reject 的 promise 永久缓存下来。
204
+ reject(new Error('ADD_SIMPLE_META 缺少 metaId'))
205
+ return
206
+ }
207
+ http.getSimpleMeta({
208
+ metaId: payload.metaId
209
+ })
210
+ .then(data => {
211
+ if (!data) {
212
+ // 与 ADD_META 同一个坑:这条分支不走 .catch,漏删在飞标记会让该 metaId 本次会话永久失败。
213
+ delete state.simpleMetaStatus[payload.metaId]
214
+ reject(new Error(`ADD_SIMPLE_META 后端返回空数据:metaId=${payload.metaId}`))
215
+ return
216
+ }
217
+ commit(ADD_SIMPLE_META, { [payload.metaId]: markRaw(data) }) // 只读元数据,去响应式
218
+ delete state.simpleMetaStatus[payload.metaId]
219
+ // 原来这里 resolve 之后**没有 return**,成功路径也会继续走到下面的 reject(new Error())。
220
+ // promise 已 settle 所以状态上看不出来,但每次成功都白构造一个 Error,
221
+ // 更要紧的是这行让「成功」和「空数据」两条路径纠缠在一起,掩盖了上面那个漏删。
222
+ resolve(data)
223
+ })
224
+ .catch(err => {
225
+ delete state.simpleMetaStatus[payload.metaId]
226
+ reject(err)
227
+ })
228
+ })
229
+ if (payload.metaId) state.simpleMetaStatus = { ...state.simpleMetaStatus, [payload.metaId]: promise }
230
+ return promise
231
+ },
232
+ [ADD_META_RELATION_FIELDS] ({ state, commit }, payload = {}) {
233
+ const { [payload.id]: metaPromise } = state.metaRelationFieldStatus
234
+ if (metaPromise) return metaPromise
235
+ const promise = http.get({
236
+ action: 'metaRelationTree',
237
+ entityId: 'FM_ENTITY',
238
+ metaId: payload.id,
239
+ allSub: true
240
+ })
241
+ .then(data => {
242
+ // data 为空时原来既不删在飞标记也不 reject ⇒ 永久缓存一个 resolve 成 undefined 的 promise,
243
+ // 后续所有调用都直接拿到 undefined。这里只补清理、保持「resolve undefined」的既有语义
244
+ // (改成 reject 会波及 88 处 dispatch 的错误处理,不在本次修复范围内)。
245
+ if (!data) {
246
+ delete state.metaRelationFieldStatus[payload.id]
247
+ return
248
+ }
249
+ data.children = data.children.map(item => ({ ...item, meta: data.id }))
250
+ if (data.relationMetaList) {
251
+ data.relationMetaList.forEach(meta => {
252
+ meta.isSub = true
253
+ meta.type = 'meta'
254
+ meta.isMeta = true
255
+ meta.field = meta.linkFlag
256
+ meta.children = meta.children.map(child => ({ ...child, relationSort: meta.relationType, meta: meta.id, field: meta.linkFlag.toUpperCase() + '|' + child.code }))
257
+ })
258
+ data.children.push(...data.relationMetaList)
259
+ }
260
+ data.isMain = true
261
+ data.isUnFold = true
262
+ const fields = [data]
263
+ formatFields(fields)
264
+ commit(ADD_META_RELATION_FIELDS, { [payload.id]: markRaw(fields) }) // 只读关联字段树,去响应式
265
+ delete state.metaRelationFieldStatus[payload.id]
266
+ return fields
267
+ })
268
+ .catch(() => {
269
+ delete state.metaRelationFieldStatus[payload.id]
270
+ })
271
+ state.metaRelationFieldStatus = { ...state.metaRelationFieldStatus, [payload.id]: promise }
272
+ return promise
273
+ },
274
+ [ADD_META_VIEW_FIELDS] ({ state, commit }, payload = {}) {
275
+ const key = payload.metaId + (payload.viewId || '')
276
+ const { [key]: metaPromise } = state.metaViewFieldStatus
277
+ if (metaPromise) return metaPromise
278
+ const promise = http.get({
279
+ action: 'getEntityMetaView',
280
+ entityId: 'FM_ENTITY_VIEW',
281
+ metaId: payload.metaId,
282
+ metaViewId: payload.viewId,
283
+ allSub: true
284
+ })
285
+ .then(data => {
286
+ // 同 ADD_META_RELATION_FIELDS:补上空数据时的在飞标记清理,语义保持不变。
287
+ if (!data) {
288
+ delete state.metaViewFieldStatus[key]
289
+ return
290
+ }
291
+ data[0].isMain = true
292
+ const formatData = (arr) => {
293
+ return arr.filter(item => !!item).map(item => {
294
+ item.field = item.type === 'meta' ? item.linkFlag || item.code : item.linkCode || item.code
295
+ item.relationSort = item.relationType
296
+ if (!item.codeKey) item.codeKey = item.relatedEntity
297
+ if (item.type === 'meta' && item.relationType === '1') item.isSub = true
298
+ if (item.children) item.children = formatData(item.children)
299
+ return item
300
+ })
301
+ }
302
+ const newData = formatData(data)
303
+ commit(ADD_META_VIEW_FIELDS, { [key]: markRaw(newData) }) // 只读视图字段,去响应式
304
+ delete state.metaViewFieldStatus[key]
305
+ return newData
306
+ })
307
+ .catch(() => {
308
+ delete state.metaViewFieldStatus[key]
309
+ })
310
+ state.metaViewFieldStatus = { ...state.metaViewFieldStatus, [key]: promise }
311
+ return promise
312
+ }
313
+ }
314
+ }
@@ -62,14 +62,14 @@ export const STYLE_AXES = [
62
62
  ]
63
63
 
64
64
  // 应用一套预设:把四个轴值写进 uiSettings(触发 applyTheme + 差量落盘)
65
- export function applyStylePreset (presetKey) {
65
+ export function applyStylePreset (presetKey, settings = uiSettings) {
66
66
  const preset = STYLE_PRESETS.find(p => p.key === presetKey)
67
67
  if (!preset) return
68
- Object.keys(AXIS_ATTRS).forEach(k => { uiSettings[k] = preset[k] })
68
+ Object.keys(AXIS_ATTRS).forEach(k => { settings[k] = preset[k] })
69
69
  }
70
70
 
71
71
  // 当前轴值组合命中哪套预设(设置面板高亮用);都不命中返回 ''(= 用户自定义了组合)
72
- export function matchStylePreset () {
72
+ export function matchStylePreset (settings = uiSettings) {
73
73
  // 先把四个轴全读进 cur 再比,而不是在 every() 回调里现读:every()/find() 都会短路,现读的话
74
74
  // 「第一个不匹配的轴」之后的轴根本不会被读到,也就不会被 Vue 登记为响应式依赖——依赖集完不完整
75
75
  // 变成了「预设表长什么样」的函数。
@@ -80,7 +80,7 @@ export function matchStylePreset () {
80
80
  // 这里改成先全读,是把「依赖完整」从一条依赖预设表形状的隐性推理,变成无条件成立的事实——
81
81
  // 于是调用方(AppSettings 的 currentPreset)可以直接 return,不必再挂一个「依赖预读」数组兜底。
82
82
  const cur = {}
83
- for (const k of Object.keys(AXIS_ATTRS)) cur[k] = uiSettings[k]
83
+ for (const k of Object.keys(AXIS_ATTRS)) cur[k] = settings[k]
84
84
  const hit = STYLE_PRESETS.find(p => Object.keys(AXIS_ATTRS).every(k => cur[k] === p[k]))
85
85
  return hit ? hit.key : ''
86
86
  }