@epxing/business 5.3.0-beta.2 → 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 (98) hide show
  1. package/API.md +8 -4
  2. package/README.md +44 -44
  3. package/dist/src/base/AiSkillEditor.js +1 -1
  4. package/dist/src/base/AiSkillEditor.vue.js +2 -0
  5. package/dist/src/base/AppConfigFormItem.js +1 -3
  6. package/dist/src/base/AppConfigFormItem.vue.js +210 -85
  7. package/dist/src/base/CommonFieldList.js +1 -1
  8. package/dist/src/base/CommonFieldList.vue.js +2 -1
  9. package/dist/src/base/ConfigModuleIndex.js +38 -1
  10. package/dist/src/base/ManageAppNav.js +1 -1
  11. package/dist/src/base/ManageMenu.js +1 -1
  12. package/dist/src/base/RoleBindTrace.js +253 -0
  13. package/dist/src/base/RoleGroupManage.js +1037 -0
  14. package/dist/src/base/ServiceMenu.js +7 -6
  15. package/dist/src/components/AiList.js +1278 -3
  16. package/dist/src/components/EntityCheckboxFind.vue.js +14 -10
  17. package/dist/src/components/EntityRightFormItem.js +1 -5
  18. package/dist/src/components/EntityRightFormItem.vue.js +3 -1
  19. package/dist/src/components/EntitySystemEr.js +492 -0
  20. package/dist/src/components/HallNav.vue.js +7 -36
  21. package/dist/src/components/IconFontLibrary.js +22 -1
  22. package/dist/src/components/IconFontList.js +21 -1
  23. package/dist/src/components/PrintFormList.js +2 -1
  24. package/dist/src/components/PrintFormList.vue.js +3 -1
  25. package/dist/src/components/WikiWorkbench.js +153 -129
  26. package/dist/src/components/authorization/AuthorizationInterface.js +3 -1
  27. package/dist/src/components/custom-bpmn/PropertiesView.js +1991 -1942
  28. package/dist/src/components/icon-gallery/IconGallery.js +193 -0
  29. package/dist/src/components/icon-gallery/mixin.js +150 -0
  30. package/dist/src/components/wiki-workbench/WikiIntake.js +86 -93
  31. package/dist/src/components/wiki-workbench/WikiIntake.vue.js +153 -73
  32. package/dist/src/index.js +26 -20
  33. package/dist/src/public-components.js +16 -14
  34. package/dist/src/route-components.js +5 -5
  35. package/dist/src/system.component.index.js +1 -1
  36. package/dist/src/system.component.js +8 -3
  37. package/dist/src/system.component.lazy.js +1 -1
  38. package/dist/src/views/Add.js +1 -1
  39. package/dist/src/views/Add.vue.js +8 -3
  40. package/dist/src/views/AiToolManager.js +792 -317
  41. package/dist/src/views/App.js +3 -45
  42. package/dist/src/views/AppRoleSelect.js +3 -230
  43. package/dist/src/views/Error.js +3 -207
  44. package/dist/src/views/ExcelDesign.js +326 -316
  45. package/dist/src/views/JvmMemoryMonitor.js +463 -0
  46. package/dist/src/views/Module.js +3 -170
  47. package/dist/src/views/WikiBinding.js +88 -25
  48. package/dist/style.css +2867 -1487
  49. package/package.json +12 -6
  50. package/src/base/AiSkillEditor.vue +2 -0
  51. package/src/base/AppConfigFormItem.vue +211 -145
  52. package/src/base/CommonFieldList.vue +9 -6
  53. package/src/base/ConfigModuleIndex.vue +37 -1
  54. package/src/base/ManageAppNav.vue +1188 -1188
  55. package/src/base/ManageMenu.vue +933 -933
  56. package/src/base/RoleBindTrace.vue +364 -0
  57. package/src/base/RoleGroupManage.vue +1160 -0
  58. package/src/base/ServiceMenu.vue +6 -5
  59. package/src/components/AiList.vue +439 -113
  60. package/src/components/EntityCheckboxFind.vue +12 -8
  61. package/src/components/EntityRightFormItem.vue +9 -10
  62. package/src/components/EntitySystemEr.vue +517 -0
  63. package/src/components/HallNav.vue +6 -33
  64. package/src/components/IconFontLibrary.vue +27 -60
  65. package/src/components/IconFontList.vue +13 -64
  66. package/src/components/PrintFormList.vue +33 -27
  67. package/src/components/WikiWorkbench.vue +90 -60
  68. package/src/components/authorization/AuthorizationInterface.vue +2 -0
  69. package/src/components/custom-bpmn/CustomPalette.js +1 -1
  70. package/src/components/custom-bpmn/CustomRenderer.js +1 -1
  71. package/src/components/custom-bpmn/PropertiesView.vue +35 -4
  72. package/src/components/custom-bpmn/index.js +1 -1
  73. package/src/components/icon-gallery/IconGallery.vue +130 -0
  74. package/src/components/icon-gallery/mixin.jsx +109 -0
  75. package/src/components/wiki-workbench/WikiIntake.vue +178 -104
  76. package/src/index.js +24 -20
  77. package/src/public-components.js +1 -0
  78. package/src/route-components.js +4 -4
  79. package/src/system.component.index.js +1 -1
  80. package/src/views/Add.vue +8 -5
  81. package/src/views/AiToolManager.vue +326 -4
  82. package/src/views/App.vue +4 -53
  83. package/src/views/AppRoleSelect.vue +4 -334
  84. package/src/views/Error.vue +4 -63
  85. package/src/views/ExcelDesign.vue +24 -7
  86. package/src/views/JvmMemoryMonitor.vue +284 -0
  87. package/src/views/Module.vue +4 -210
  88. package/src/views/WikiBinding.vue +42 -0
  89. package/types/app.d.ts +1 -0
  90. package/types/dept-find.d.ts +1 -1
  91. package/types/index.d.ts +15 -14
  92. package/types/popup.d.ts +2 -2
  93. package/types/route-components.d.ts +1 -1
  94. package/types/user-find.d.ts +1 -1
  95. package/dist/assets/role_select-DTqRlFNz.webp +0 -0
  96. package/dist/src/components/AiList.vue.js +0 -93
  97. package/dist/src/components/IconFontLibrary.vue.js +0 -67
  98. package/dist/src/components/IconFontList.vue.js +0 -79
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epxing/business",
3
- "version": "5.3.0-beta.2",
3
+ "version": "5.3.0-beta.3",
4
4
  "description": "平台业务组件与页面(Login/base/custom-bpmn 等;不含 designer 懒注册)",
5
5
  "type": "module",
6
6
  "exports": {
@@ -10,6 +10,11 @@
10
10
  "default": "./dist/src/index.js"
11
11
  },
12
12
  "./src": "./dist/src/index.js",
13
+ "./app": {
14
+ "types": "./types/app.d.ts",
15
+ "import": "./dist/src/views/App.js",
16
+ "default": "./dist/src/views/App.js"
17
+ },
13
18
  "./user-find": {
14
19
  "types": "./types/user-find.d.ts",
15
20
  "import": "./dist/src/components/UserFind.js",
@@ -100,11 +105,12 @@
100
105
  "tiny-svg": "^4.1.4",
101
106
  "@hufe921/canvas-editor": "^0.9.100",
102
107
  "@hufe921/canvas-editor-plugin-docx": "^0.0.5",
103
- "@epxing/core": "^5.3.0-beta.2",
104
- "@epxing/components": "^5.3.0-beta.2",
105
- "@epxing/utils": "^5.3.0-beta.2",
106
- "@epxing/theme": "^5.3.0-beta.2",
107
- "@epxing/dashboard": "^5.3.0-beta.2"
108
+ "@epxing/components": "^5.3.0-beta.3",
109
+ "@epxing/dashboard": "^5.3.0-beta.3",
110
+ "@epxing/core": "^5.3.0-beta.3",
111
+ "@epxing/icons": "^5.3.0-beta.3",
112
+ "@epxing/utils": "^5.3.0-beta.3",
113
+ "@epxing/theme": "^5.3.0-beta.3"
108
114
  },
109
115
  "peerDependencies": {
110
116
  "vue": "3.5.39",
@@ -435,6 +435,8 @@ export default {
435
435
  name: 'epx-base-ai-skill-editor',
436
436
  mixins: [component],
437
437
  props: {
438
+ // 接收父表单实例,避免被作为 DOM 属性透传导致记忆设置渲染失败。
439
+ form: Object,
438
440
  value: {
439
441
  type: String,
440
442
  default: ''
@@ -2,18 +2,41 @@
2
2
  // @jsx-vue3(T30 JSX 批:Vue3 JSX 语法,@vue/babel-plugin-jsx 编译)
3
3
 
4
4
  import AddFormItem from '@epxing/components/src/add-form-item'
5
+ import TableForm from '@epxing/components/src/form'
6
+ import AppSettings from '@epxing/components/src/app-settings/AppSettings.vue'
7
+ import AppAppearancePreview from '@epxing/components/src/app-settings/AppAppearancePreview.vue'
8
+ import { createAppUiDraft } from '@epxing/core/src/app-ui-settings'
9
+ import { captureRef } from '@epxing/core/src/capture-ref'
5
10
  export default {
6
11
  name: 'epx-base-app-config-form-item',
7
12
  extends: AddFormItem,
13
+ components: { AppSettings, AppAppearancePreview, TableForm },
8
14
  data () {
9
15
  return {
10
16
  config: {},
11
- fieldsConfigs: []
17
+ fieldsConfigs: [],
18
+ layoutFieldsConfigs: [],
19
+ activeGroup: 'theme',
20
+ uiDraft: {},
21
+ initialConfig: '{}',
22
+ syncingDraft: true,
23
+ settingsGroups: [
24
+ { key: 'theme', label: '主题与配色' },
25
+ { key: 'layout', label: '导航与布局' },
26
+ { key: 'style', label: '界面风格' },
27
+ { key: 'tabs', label: '页签设置' },
28
+ { key: 'page', label: '页面与效果' }
29
+ ]
12
30
  }
13
31
  },
14
32
  computed: {
33
+ readOnly () { return ['DETAIL', 'READONLY'].includes(this.componentType) },
15
34
  showFieldsConfigs () {
16
- return this.fieldsConfigs.filter(item => {
35
+ return this.layoutFieldsConfigs.filter(item => {
36
+ const themeKeys = ['navBackgroundColorType', 'navBackgroundColor']
37
+ const pageKeys = ['pageBackgroundType', 'pageBackgroundColor', 'pageBackgroundGradient', 'pageBackgroundImage', 'pageBackgroundSize']
38
+ const group = themeKeys.includes(item.key) ? 'theme' : pageKeys.includes(item.key) ? 'page' : 'layout'
39
+ if (group !== this.activeGroup) return false
17
40
  let flag = true
18
41
  if (item.showFlag) {
19
42
  for (const key in item.showFlag) {
@@ -30,10 +53,20 @@ export default {
30
53
  watch: {
31
54
  config: {
32
55
  handler (v) {
56
+ if (this.syncingDraft || this.readOnly) return
33
57
  const APP_CONFIG = JSON.stringify(v)
34
58
  this.$emit('change', { ...this.data, APP_CONFIG })
35
59
  },
36
60
  deep: true
61
+ },
62
+ uiDraft: {
63
+ deep: true,
64
+ handler (value) {
65
+ if (!this.syncingDraft && !this.readOnly) this.config.uiDefaults = JSON.parse(JSON.stringify(value))
66
+ }
67
+ },
68
+ 'formData.APP_CONFIG' (value) {
69
+ if (value !== JSON.stringify(this.config)) this.loadConfig(value)
37
70
  }
38
71
  },
39
72
  methods: {
@@ -76,6 +109,7 @@ export default {
76
109
  key: 'navBackgroundColor',
77
110
  label: '自定义导航背景色',
78
111
  type: 'color',
112
+ showFlag: { navBackgroundColorType: 'custom' },
79
113
  predefineColors: []
80
114
  },
81
115
  {
@@ -188,69 +222,34 @@ export default {
188
222
  defaultValue: 992
189
223
  }
190
224
  ]
191
- if (this.appConfig['SYSTEM|TYPE'] === 'hall') {
192
- fieldsConfigs.splice(9, 0, {
193
- key: 'componentMargin',
194
- label: '页面组件间距',
195
- type: 'radio',
196
- options: [
197
- {
198
- label: '小',
199
- value: 'small'
200
- },
201
- {
202
- label: '中',
203
- value: 'medium'
204
- },
205
- {
206
- label: '默认',
207
- value: 'base'
208
- }
209
- ],
210
- defaultValue: 'base'
211
- },
212
- {
213
- key: 'borderRadius',
214
- label: '圆角',
215
- type: 'radio',
216
- options: [
217
- {
218
- label: '无',
219
- value: 'zero'
220
- },
221
- {
222
- label: '默认',
223
- value: 'base'
224
- },
225
- {
226
- label: '中',
227
- value: 'medium'
228
- },
229
- {
230
- label: '大',
231
- value: 'large'
232
- }
233
- ],
234
- defaultValue: 'base'
235
- })
236
- }
237
225
  for (const item of fieldsConfigs) {
238
226
  item.state = pageType
239
227
  if (item.options) item.options = item.options.map(item => ({ id: item.value, code: item.value, name: item.label, preview: item.preview }))
240
228
  }
241
- this.fieldsConfigs = fieldsConfigs
229
+ this.layoutFieldsConfigs = fieldsConfigs
230
+ // 父表单只提交 APP_CONFIG;导航等草稿字段均封装在该 JSON 内。
231
+ this.fieldsConfigs = [{ attriName: 'APP_CONFIG', state: 'HIDDEN' }]
242
232
  },
243
233
  changeConfigItem (key, value) {
234
+ if (this.readOnly) return
244
235
  this.config[key] = value
245
236
  },
246
237
  save () {
238
+ if (this.readOnly) return
239
+ // 优先交给父表单整表提交,保留应用的名称、权限等其他字段。
240
+ if (this.form?.submitForm && !this.isEdit) {
241
+ this.$nextTick(() => this.form.submitForm())
242
+ return
243
+ }
247
244
  const params = {}
248
245
  const submitFlag = true
249
246
  const { submitParams: querySumitParams = {} } = this.$query
250
247
  if (this.formData[this.$metaKey]) {
251
248
  params[`bean.${this.$metaKey}`] = this.formData[this.$metaKey]
252
249
  }
253
- params['bean.APP_CONFIG'] = this.formData.APP_CONFIG
250
+ Object.entries({ ...this.formData, APP_CONFIG: JSON.stringify(this.config) }).forEach(([key, value]) => {
251
+ params[`bean.${key}`] = value
252
+ })
254
253
 
255
254
  // 表单校验
256
255
  const { componentEvents = {} } = this.$configs
@@ -276,53 +275,112 @@ export default {
276
275
  })
277
276
  },
278
277
  switchNav (value, item) {
279
- if (item.state !== 'DETAIL') {
278
+ if (!['DETAIL', 'READONLY'].includes(item.state)) {
280
279
  this.config[item.key] = value
281
280
  }
282
281
  },
282
+ loadConfig (value = '{}') {
283
+ this.syncingDraft = true
284
+ const config = JSON.parse(value || '{}')
285
+ this.initialConfig = JSON.stringify(config)
286
+ for (const item of this.layoutFieldsConfigs) {
287
+ if (config[item.key] === undefined && item.defaultValue !== undefined) config[item.key] = item.defaultValue
288
+ }
289
+ this.config = config
290
+ this.uiDraft = createAppUiDraft(config)
291
+ this.$nextTick(() => { this.syncingDraft = false })
292
+ },
293
+ restorePlatform () {
294
+ if (this.readOnly) return
295
+ this.uiDraft = createAppUiDraft()
296
+ const businessKeys = ['appPageType', 'pageId', 'navComponent', 'navOperates', 'mobileSize']
297
+ for (const field of this.layoutFieldsConfigs) {
298
+ if (businessKeys.includes(field.key)) continue
299
+ if (field.defaultValue !== undefined) this.config[field.key] = field.defaultValue
300
+ else delete this.config[field.key]
301
+ }
302
+ },
303
+ discardDraft () {
304
+ const initial = this.initialConfig
305
+ this.loadConfig(initial)
306
+ this.$emit('change', { ...this.data, APP_CONFIG: initial })
307
+ },
283
308
  renderForm () {
284
309
  return (
285
- <el-form class="app-config-form" inline label-position="right" label-width="140px">
310
+ <section class="epx-app-interface-settings">
311
+ <div hidden>
312
+ <TableForm ref={captureRef(this, 'form')} fieldsConfigs={this.fieldsConfigs} modelValue={{ ...this.data, APP_CONFIG: JSON.stringify(this.config) }} type="auto" showRelyFlag={false} valueRelyFlag={false} requiredRelyFlag={false} />
313
+ </div>
314
+ <div class="epx-app-interface-settings__heading">
315
+ <p>设置当前应用的默认外观;用户的个人选择优先。右侧预览不会改变当前界面。</p>
316
+ </div>
317
+ <div class="epx-app-interface-settings__workspace">
318
+ <nav class="epx-app-interface-settings__nav" aria-label="界面设置分组">
319
+ {this.settingsGroups.map(group => (
320
+ <button type="button" key={group.key} class={{ 'is-active': this.activeGroup === group.key }} aria-current={this.activeGroup === group.key ? 'page' : undefined} onClick={() => { this.activeGroup = group.key }}>{group.label}</button>
321
+ ))}
322
+ </nav>
323
+ <div class="epx-app-interface-settings__editor">
324
+ <h4>{this.settingsGroups.find(group => group.key === this.activeGroup)?.label}</h4>
325
+ <AppSettings embedded settings={this.uiDraft} sections={[this.activeGroup]} disabled={this.readOnly} />
326
+ {this.renderLayoutFields()}
327
+ </div>
328
+ <AppAppearancePreview config={this.config} settings={this.uiDraft} title={this.data.NAME || '应用名称'} />
329
+ </div>
330
+ {!this.readOnly && <div class="epx-app-interface-settings__actions">
331
+ <el-button onClick={this.restorePlatform}>恢复平台默认</el-button>
332
+ <el-button onClick={this.discardDraft}>撤销本次修改</el-button>
333
+ <el-button type="primary" onClick={this.save}>保存</el-button>
334
+ </div>}
335
+ </section>
336
+ )
337
+ },
338
+ renderLayoutFields () {
339
+ return (
340
+ <el-form disabled={this.readOnly} class="epx-base-app-config-form-item__form">
286
341
  {
287
342
  this.showFieldsConfigs.map(item => (
288
- <el-form-item label={`${item.label}`} style={item.block ? { width: '100%' } : null}>
289
- {
290
- item.key === 'navType' ? (
291
- <div class={['nav-type', { 'is-disabled': item.state === 'DETAIL' }]}>
292
- {
293
- item.options.map(option => (
294
- <div class={['nav-type__item', { 'is-active': this.config[item.key] === option.id }]} key={option.id} onClick={this.switchNav.bind(this, option.id, item)}>
295
- <div>
296
- <img src={option.preview} />
297
- </div>
298
- <p>{option.name}</p>
299
- </div>
300
- ))
301
- }
302
- </div>
303
- ) : item.type === 'radio' ? (
304
- <epx-radio v-model={this.config[item.key]} fieldConfigs={{ state: item.state, attriName: item.key, attriLabel: item.label, options: item.options }}></epx-radio>
305
- ) : item.type === 'object' ? (
306
- <epx-visual-config-object v-model={this.config[item.key]} label={item.label} object={item.object} flag={item.flag} dataKey={item.dataKey} state={item.state}></epx-visual-config-object>
307
- ) : item.type === 'color' ? (
308
- <el-color-picker v-model={this.config[item.key]} predefine={item.predefineColors} disabled={item.state === 'DETAIL'}></el-color-picker>
309
- ) : item.type === 'gradient' ? (
310
- <epx-color-picker v-model={this.config[item.key]} disabled={item.state === 'DETAIL'}></epx-color-picker>
311
- ) : item.type === 'photo' ? (
312
- <epx-photo v-model={this.config[item.key]} fieldConfigs={{ state: item.state, attriName: item.key, attriLabel: item.label }}></epx-photo>
313
- ) : item.type === 'num' ? (
314
- <epx-number v-model={this.config[item.key]} fieldConfigs={{ state: item.state, attriName: item.key, attriLabel: item.label, value: item.defaultValue }}></epx-number>
315
- ) : item.type === 'operates' ? (
316
- <epx-visual-config-operate
317
- value={this.config[item.key]}
318
- state={item.state}
319
- onChangeConfigs={this.changeConfigItem.bind(this, item.key)}
320
- />
321
- ) : (
322
- <epx-input class="input" v-model={this.config[item.key]} fieldConfigs={{ state: item.state, attriName: item.key, attriLabel: item.label }}></epx-input>
323
- )
324
- }
325
- </el-form-item>
343
+ <div key={item.key} class={['epx-app-interface-settings__field', { 'epx-app-interface-settings__field--block': item.block || item.type === 'radio' }]}>
344
+ <span class="epx-app-interface-settings__label">{item.label}</span>
345
+ <div class="epx-app-interface-settings__control">
346
+ {
347
+ item.key === 'navType' ? (
348
+ <div class="nav-type" role="group" aria-label={item.label}>
349
+ {
350
+ item.options.map(option => (
351
+ <button type="button" class={['nav-type__item', { 'is-active': this.config[item.key] === option.id }]} key={option.id} disabled={this.readOnly} aria-pressed={this.config[item.key] === option.id} onClick={this.switchNav.bind(this, option.id, item)}>
352
+ <img src={option.preview} alt="" />
353
+ <span>{option.name}</span>
354
+ </button>
355
+ ))
356
+ }
357
+ </div>
358
+ ) : item.type === 'radio' ? (
359
+ <el-radio-group v-model={this.config[item.key]} size="small" aria-label={item.label}>
360
+ {item.options.map(option => <el-radio-button key={option.id} value={option.id}>{option.name}</el-radio-button>)}
361
+ </el-radio-group>
362
+ ) : item.type === 'object' ? (
363
+ <epx-visual-config-object value={this.config[item.key]} onChangeConfigs={this.changeConfigItem.bind(this, item.key)} label={item.label} object={item.object} flag={item.flag} dataKey={item.dataKey} state={item.state}></epx-visual-config-object>
364
+ ) : item.type === 'color' ? (
365
+ <el-color-picker v-model={this.config[item.key]} predefine={item.predefineColors} disabled={this.readOnly}></el-color-picker>
366
+ ) : item.type === 'gradient' ? (
367
+ <epx-color-picker v-model={this.config[item.key]} disabled={this.readOnly}></epx-color-picker>
368
+ ) : item.type === 'photo' ? (
369
+ <epx-photo v-model={this.config[item.key]} fieldConfigs={{ state: item.state, attriName: item.key, attriLabel: item.label }}></epx-photo>
370
+ ) : ['num', 'number'].includes(item.type) ? (
371
+ <epx-number v-model={this.config[item.key]} fieldConfigs={{ state: item.state, attriName: item.key, attriLabel: item.label, value: item.defaultValue }}></epx-number>
372
+ ) : item.type === 'operates' ? (
373
+ <epx-visual-config-operate
374
+ value={this.config[item.key]}
375
+ state={item.state}
376
+ onChangeConfigs={this.changeConfigItem.bind(this, item.key)}
377
+ />
378
+ ) : (
379
+ <epx-input class="input" v-model={this.config[item.key]} fieldConfigs={{ state: item.state, attriName: item.key, attriLabel: item.label }}></epx-input>
380
+ )
381
+ }
382
+ </div>
383
+ </div>
326
384
  ))
327
385
  }
328
386
 
@@ -331,74 +389,82 @@ export default {
331
389
  }
332
390
  },
333
391
  created () {
334
- const { APP_CONFIG = '{}' } = this.data
335
- const appConfig = JSON.parse(APP_CONFIG)
336
- for (const item of this.fieldsConfigs) {
337
- if (!appConfig[item.key] && item.defaultValue) {
338
- appConfig[item.key] = item.defaultValue
339
- }
340
- }
341
- this.config = appConfig
392
+ this.loadConfig(this.data.APP_CONFIG)
342
393
  }
343
394
  }
344
395
  </script>
345
396
 
346
- <style lang="scss" scoped>
347
- .app-config-form {
348
-
349
- :deep(.el-form-item) {
350
- padding: 10px;
351
- width: calc(50% - 30px);
352
- margin-bottom: 0;
353
- }
354
- :deep(.el-form-item__label::after) {
355
- content: ': '
356
- }
357
-
358
- }
359
-
360
- .nav-type {
361
- display: flex;
362
- flex-wrap: wrap;
363
- &.is-disabled {
364
- .nav-type__item {
365
- >div {
366
- cursor: not-allowed;
367
- }
368
- }
397
+ <style lang="scss">
398
+ // 通过表单根类隔离样式,避免继承渲染时丢失 scoped 标记导致预览恢复原图尺寸。
399
+ .epx-base-app-config-form-item__form {
400
+ .nav-type {
401
+ display: grid;
402
+ grid-template-columns: repeat(4, minmax(0, 1fr));
403
+ gap: var(--epx-space-2);
369
404
  }
370
- }
371
405
 
372
- .nav-type__item {
373
- width: 200px;
374
- margin-right: 20px;
375
- text-align: center;
376
- >div {
377
- background: #eee;
406
+ .nav-type__item {
407
+ min-width: 0;
408
+ padding: var(--epx-space-1);
409
+ text-align: center;
410
+ font: inherit;
411
+ font-size: var(--epx-font-size-s);
412
+ color: var(--epx-text-secondary);
413
+ background: transparent;
414
+ border: 1px solid var(--epx-border);
378
415
  border-radius: var(--epx-radius-base);
379
- border: 1px solid transparent;
380
- overflow: hidden;
381
416
  cursor: pointer;
382
- transition: all .5s;
383
- }
384
- img {
385
- width: 100%;
386
- opacity: 0.5;
387
- transition: all .5s;
388
- }
389
- &:hover {
390
417
  img {
391
- opacity: 1;
418
+ display: block;
419
+ width: 100%;
420
+ aspect-ratio: 10 / 7;
421
+ object-fit: contain;
422
+ margin-bottom: var(--epx-space-1);
392
423
  }
393
- }
394
- &.is-active {
395
- @include font_color_primary;
396
- >div {
424
+ &:focus-visible {
425
+ outline: 2px solid var(--el-color-primary);
426
+ outline-offset: 2px;
427
+ }
428
+ &:hover:not(:disabled), &.is-active {
429
+ @include font_color_primary;
397
430
  @include border_color_primary;
398
431
  }
399
- img {
400
- opacity: 1;
432
+ &:disabled {
433
+ cursor: not-allowed;
434
+ opacity: .6;
401
435
  }
402
436
  }
403
437
  }
438
+
439
+ .epx-app-interface-settings {
440
+ &__heading { margin-bottom: 20px; }
441
+ &__heading p { margin: 0; color: var(--el-text-color-secondary); font-size: 13px; }
442
+ &__workspace { display: grid; grid-template-columns: 140px minmax(300px, 1fr) minmax(300px, 420px); gap: 20px; align-items: start; }
443
+ &__nav { display: flex; flex-direction: column; gap: 6px; }
444
+ &__nav button { border: 0; border-radius: 6px; background: transparent; color: inherit; text-align: left; padding: 12px; cursor: pointer; }
445
+ &__nav button.is-active { background: var(--el-color-primary-light-9); color: var(--el-color-primary); }
446
+ &__editor { min-width: 0; }
447
+ &__editor h4 { margin: 0 0 12px; }
448
+ &__field { display: flex; align-items: center; justify-content: space-between; gap: var(--epx-space-2); min-height: 40px; padding: var(--epx-space-1) var(--epx-space-4); }
449
+ &__label { flex-shrink: 0; font-size: var(--epx-font-size-base); color: var(--epx-text); }
450
+ &__control { min-width: 0; max-width: 100%; }
451
+ &__field--block { flex-direction: column; align-items: stretch; padding-bottom: var(--epx-space-2); }
452
+ &__field--block .el-radio-group { display: flex; width: 100%; }
453
+ &__field--block .el-radio-button { flex: 1; }
454
+ &__field--block .el-radio-button__inner { width: 100%; padding-left: 0; padding-right: 0; }
455
+ &__control .el-input-number { width: 150px; }
456
+ &__editor .epx-app-settings__body { padding: 0; }
457
+ &__editor .el-collapse { border: 0; }
458
+ &__editor .el-collapse-item__header { display: none; }
459
+ &__actions { display: flex; justify-content: flex-end; gap: 8px; border-top: 1px solid var(--el-border-color-light); padding-top: 16px; margin-top: 20px; }
460
+ @media (max-width: 1200px) {
461
+ &__workspace { grid-template-columns: 130px minmax(0, 1fr); }
462
+ .epx-app-appearance-preview { grid-column: 2; }
463
+ }
464
+ @media (max-width: 650px) {
465
+ &__workspace { display: flex; flex-direction: column; }
466
+ &__nav { flex-direction: row; flex-wrap: wrap; }
467
+ &__editor, .epx-app-appearance-preview { width: 100%; }
468
+ }
469
+ }
404
470
  </style>
@@ -1,4 +1,4 @@
1
- <script>
1
+ <script lang="jsx">
2
2
  // @jsx-vue3(T30 JSX 批③:scopedSlots 集群 Vue3 化)
3
3
  import EntitySubList from '@epxing/components/src/entity-sub-list'
4
4
  import draggable from '@epxing/core/src/components/draggable'
@@ -324,7 +324,7 @@ export default {
324
324
  modelValue={this.showFieldDialog}
325
325
  {...{ 'onUpdate:modelValue': v => { this.showFieldDialog = v } }}
326
326
  append-to-body={true}
327
- class="show-field-dialog"
327
+ class="show-field-dialog epx-common-field-dialog"
328
328
  destroy-on-close
329
329
  width="800px"
330
330
  top="10vh"
@@ -402,7 +402,7 @@ export default {
402
402
  },
403
403
  renderNameDialog () {
404
404
  return (
405
- <el-dialog modelValue={this.addCommonFieldDialod} {...{ 'onUpdate:modelValue': v => { this.addCommonFieldDialod = v } }} width="400px" title="新增常用导出字段" v-slots={{
405
+ <el-dialog class="epx-common-field-dialog" modelValue={this.addCommonFieldDialod} {...{ 'onUpdate:modelValue': v => { this.addCommonFieldDialod = v } }} width="400px" title="新增常用导出字段" v-slots={{
406
406
  footer: () => (<span class="dialog-footer">
407
407
  <el-button onClick={() => { this.addCommonFieldDialod = false }}>取 消</el-button>
408
408
  <el-button type="primary" onClick={this.saveCommonField.bind(this)}>确 定</el-button>
@@ -1248,7 +1248,9 @@ $paddingSize: 16px;
1248
1248
  }
1249
1249
  </style>
1250
1250
 
1251
- <style scoped lang="scss">
1251
+ <style lang="scss">
1252
+ // 父类调用渲染函数时不会附加本组件的 scoped 标记,弹窗使用独立容器限定样式。
1253
+ .epx-common-field-dialog {
1252
1254
  .field-setting {
1253
1255
  display: flex;
1254
1256
  width: 100%;
@@ -1301,7 +1303,7 @@ $paddingSize: 16px;
1301
1303
  flex: 1;
1302
1304
  overflow: auto;
1303
1305
 
1304
- :deep(.el-tree-node__content) {
1306
+ .el-tree-node__content {
1305
1307
  height: 48px;
1306
1308
  }
1307
1309
 
@@ -1342,7 +1344,7 @@ $paddingSize: 16px;
1342
1344
  .setting-field__row {
1343
1345
  margin-bottom: 10px;
1344
1346
 
1345
- :deep(>.el-form-item) {
1347
+ >.el-form-item {
1346
1348
  vertical-align: auto;
1347
1349
  width: 100%;
1348
1350
  display: flex;
@@ -1394,4 +1396,5 @@ $paddingSize: 16px;
1394
1396
  width: 90px;
1395
1397
  }
1396
1398
  }
1399
+ }
1397
1400
  </style>
@@ -2,7 +2,7 @@
2
2
  <div class="container">
3
3
  <div class="content" v-if="data.ID">
4
4
  <keep-alive>
5
- <epx-menu id="menu" v-if="showMenu && uiShowSideNav" :showNav="false" class="aside hidden-sm-and-down" :modules="menu" :moduleName="`${data.NAME || $t('application')}${$t('configuration')}`" :subTitle="data['CLIENT#']" :toPages="toPages" :clickItem="toPage"></epx-menu>
5
+ <epx-menu id="menu" v-if="showMenu && uiShowSideNav" :showNav="false" class="aside hidden-sm-and-down" :modules="menu" :activeMenuId="configMenuKey" :moduleName="`${data.NAME || $t('application')}${$t('configuration')}`" :subTitle="data['CLIENT#']" :toPages="toPages" :clickItem="toPage"></epx-menu>
6
6
  </keep-alive>
7
7
  <div class="main">
8
8
  <epx-router-view ref="router" v-if="menu.length>0" :init-route="initRoute"></epx-router-view>
@@ -20,6 +20,7 @@
20
20
  <script>
21
21
  import ModuleIndexVue from '@epxing/components/src/menu-index'
22
22
  import { openExternalMenu } from '@epxing/utils/src/menu-open'
23
+ import { findMenuByKey, getRouteMenuKey } from '@epxing/core/src/menu-route'
23
24
  const _comp = {
24
25
  name: 'epx-base-config-module-index',
25
26
  extends: ModuleIndexVue,
@@ -28,7 +29,42 @@ const _comp = {
28
29
  data: {}
29
30
  }
30
31
  },
32
+ computed: {
33
+ configMenuKey () {
34
+ return this.$route.name === 'system' ? getRouteMenuKey(this.$route) : ''
35
+ },
36
+ initRoute () {
37
+ const menu = findMenuByKey(this.menu, this.configMenuKey)
38
+ if (menu && menu.OPEN_TYPE !== '1') {
39
+ return { moduleId: menu.ID, component: 'module-list-page', pageId: menu.MAIN_PAGE, menuTitle: menu.text }
40
+ }
41
+ return ModuleIndexVue.computed.initRoute.call(this)
42
+ }
43
+ },
44
+ watch: {
45
+ configMenuKey: 'restoreMenuFromRoute',
46
+ menu: 'restoreMenuFromRoute',
47
+ 'data.ID': 'restoreMenuFromRoute'
48
+ },
31
49
  methods: {
50
+ restoreMenuFromRoute () {
51
+ if (this.$route.name !== 'system') return
52
+ // 等专用菜单和系统上下文就绪后,沿用原有点击入口,保留页面参数与打开方式。
53
+ this.$nextTick(() => {
54
+ const view = this.$refs.router
55
+ if (!view || !this.data.ID) return
56
+ const menu = findMenuByKey(this.menu, this.configMenuKey)
57
+ if (menu) {
58
+ if (view.route?.moduleId === menu.ID) return
59
+ const activeTab = this.$tabs.find(tab => tab.active)
60
+ if (menu.OPEN_TYPE === '1' && activeTab?.moduleId === menu.ID) return
61
+ this.toPage(menu)
62
+ } else if (!this.configMenuKey) {
63
+ view.clearPage()
64
+ view.createPage(this.initRoute)
65
+ }
66
+ })
67
+ },
32
68
  getInfo () {
33
69
  const { name, params = {}, query = {} } = this.$route
34
70
  const { system: querySystem = '' } = query