@epxing/business 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 (98) hide show
  1. package/API.md +115 -111
  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
@@ -1,61 +1,28 @@
1
1
  <script>
2
- // @jsx-vue3(T30 JSX 批:Vue3 JSX 语法,@vue/babel-plugin-jsx 编译)
3
-
4
- import EntityFindVue from '@epxing/components/src/entity-list-find'
5
- import { MobileGrid, MobileGridItem } from '@epxing/components/src/mobile'
6
- export default {
7
- name: 'epx-icon-font-library',
8
- components: { MobileGrid, MobileGridItem },
9
- extends: EntityFindVue,
10
- pageFlag: true,
11
- pageSize: 100,
12
- data () {
13
- return {
14
- renderComponents: [this.renderSearch, this.renderTable]
15
- }
16
- },
17
- methods: {
18
- renderTable () {
19
- return (
20
- <div ref="grid" >
21
- <MobileGrid gutter="10" style={{ minHeight: '40px' }}>
22
- {
23
- this.data.length === 0 && (
24
- <MobileGridItem text="暂无更多" v-slots={{ icon: () => (<i style={{ fontSize: '30px', marginBottom: '12px' }} class="el-icon-folder"></i>) }}>
25
- </MobileGridItem>
26
- )
27
- }
28
- {
29
- this.data.map(item => (
30
- <MobileGridItem key={item.ID} text={item.NAME} onClick={this.selectEntity.bind(this, null, item)} v-slots={{ icon: () => (<i style={{ fontSize: '30px', marginBottom: '12px' }} class={[item.TYPE !== '2' && 'fa', item.CODE]}></i>) }}>
31
- </MobileGridItem>
32
- ))
33
- }
34
- </MobileGrid>
35
- {
36
- this.$configs.pageFlag && (
37
- <div class="pagination">
38
- <el-pagination
39
- v-loading={this.loading}
40
- currentPage={this.currentPage}
41
- pageSizes={[5, 10, 15, 20, 30, 50, 100, 200]}
42
- pageSize={this.pageSize}
43
- layout="prev, pager, next, jumper, sizes,total"
44
- total={this.total}
45
- onSizeChange={this.sizeChange}
46
- onCurrentChange={this.currentChange}
47
- >
48
- </el-pagination>
49
- </div>
50
- )
51
- }
52
- </div>
53
- )
54
- }
55
- }
56
- }
57
- </script>
58
-
59
- <style>
60
-
61
- </style>
2
+ import EntityFind from '@epxing/components/src/entity-list-find'
3
+ import gallery from './icon-gallery/mixin.jsx'
4
+
5
+ export default {
6
+ name: 'epx-icon-font-library',
7
+ extends: EntityFind,
8
+ mixins: [gallery],
9
+ created () {
10
+ if (this.$isMobile) this.openWatchSelection()
11
+ },
12
+ methods: {
13
+ chooseIcon (icon) {
14
+ if (this.multiple) {
15
+ // 多选仍由父类监听器及确认按钮统一回填,保留原有字段协议。
16
+ this.selectionIds = this.selectionIds.includes(icon.CODE)
17
+ ? this.selectionIds.filter(code => code !== icon.CODE)
18
+ : [...this.selectionIds, icon.CODE]
19
+ } else {
20
+ this.selectEntity(null, icon)
21
+ }
22
+ }
23
+ },
24
+ render () {
25
+ return this.renderGallery(true)
26
+ }
27
+ }
28
+ </script>
@@ -1,75 +1,24 @@
1
1
  <script>
2
- // @jsx-vue3(T30 JSX 批:Vue3 JSX 语法,@vue/babel-plugin-jsx 编译)
3
-
4
2
  import List from '@epxing/components/src/entity-list'
3
+ import gallery from './icon-gallery/mixin.jsx'
4
+
5
5
  export default {
6
6
  name: 'epx-icon-font-list',
7
7
  extends: List,
8
+ mixins: [gallery],
8
9
  methods: {
9
- renderField (field) {
10
- const { isLabel } = field.attribute || {}
11
- return {
12
- default: ({ row }) => {
13
- if (field.dataType === 'blob' || field.dataType === 'photo') {
14
- return (
15
- <epx-file values={row} fieldConfigs={{ ...field, state: 'DETAIL' }}></epx-file>
16
- )
17
- }
18
- if (field.dataType === 'rule') {
19
- return (
20
- <font color={field.color}>
21
- {this.getChineseValue(row[field.attriName])}
22
- </font>
23
- )
24
- }
25
- if (field.dataType === 'icon') {
26
- return (
27
- <i style={{ fontSize: '20px' }} class={row[field.attriName]}></i>
28
- )
29
- }
30
- if (field.dataType === 'scope') {
31
- return (
32
- <font color={field.color}>
33
- {this.getScopeChinese(row[field.attriName])}
34
- </font>
35
- )
36
- }
37
- if (field.dataType === 'date' || field.dataType === 'date_ym') {
38
- return (
39
- <font color={field.color}>
40
- {row[field.attriName] === '2050-01-01' || row[field.attriName] === '2050-01' || row[field.attriName] === '9999-99' || row[field.attriName] === '9999' ? '至今' : row[field.attriName]}
41
- </font>
42
- )
43
- }
44
- if (field.dataType === 'tag') {
45
- if (!row[field.attriName]) return null
46
- const tagList = row[field.attriName].split(',')
47
- return (
48
- <span>
49
- {
50
- tagList.map((item, index) => (
51
- <el-tag style={{ marginRight: '8px' }} key={item}>{ item }
52
- { index < tagList.length - 1 &&
53
- <span style={{ opacity: 0, width: 0, float: 'left' }}>、</span>
54
- }
55
- </el-tag>
56
- ))
57
- }
58
- </span>
59
- )
60
- }
61
- return (
62
- <font class={{ 'is-label': (field.isLabel || isLabel === '1' || field.detailPageId) }} color={field.color} onClick={this.clickField.bind(this, field, row)}>
63
- { row[field.attriName + '#'] || row[field.attriName] }
64
- </font>
65
- )
66
- }
10
+ async chooseIcon (icon) {
11
+ try {
12
+ if (navigator.clipboard && window.isSecureContext) await navigator.clipboard.writeText(icon.CODE)
13
+ else this.$util.copyToClipboard(icon.CODE)
14
+ this.$message.success('已复制图标编号')
15
+ } catch (err) {
16
+ this.$message.error((err && err.message) || '复制失败,请手动复制图标编号')
67
17
  }
68
18
  }
19
+ },
20
+ render () {
21
+ return this.renderGallery()
69
22
  }
70
23
  }
71
24
  </script>
72
-
73
- <style>
74
-
75
- </style>
@@ -1,5 +1,5 @@
1
1
 
2
- <script>
2
+ <script lang="jsx">
3
3
  // @jsx-vue3(T30 JSX 批:Vue3 JSX 语法,@vue/babel-plugin-jsx 编译)
4
4
 
5
5
  import ListVue from '@epxing/components/src/entity-list'
@@ -67,7 +67,7 @@ export default {
67
67
  onUpdate:modelValue={v => (this.showFormTypeDialog = v)}
68
68
  width="800px"
69
69
  >
70
- <div>
70
+ <div class="epx-print-form-types">
71
71
  <ul class="create-form__list">
72
72
  <li class="create-form__item" onClick={this.createForm.bind(this, 'formReport')}>
73
73
  <i class="epx-icon-html" style={{ color: '#217346' }}></i>
@@ -492,31 +492,6 @@ $paddingSize: 16px;
492
492
  .dropdown-link {
493
493
  cursor: pointer;
494
494
  }
495
- .create-form__list {
496
- display: flex;
497
- justify-content: center;
498
- flex-wrap: wrap;
499
- padding: 20px;
500
- }
501
- .create-form__item {
502
- width: 140px;
503
- height: 140px;
504
- flex-shrink: 0;
505
- border: 1px solid var(--epx-border);
506
- display: flex;
507
- flex-direction: column;
508
- align-items: center;
509
- justify-content: center;
510
- margin: 10px;
511
- border-radius: var(--epx-radius-base);
512
- cursor: pointer;
513
- &:hover {
514
- box-shadow: 0px 6px 12px -3px rgba(0, 0, 0, 0.15);
515
- }
516
- i {
517
- font-size: 34px;
518
- }
519
- }
520
495
  .card-item__row {
521
496
  margin: 8px 0;
522
497
  font-size: var(--epx-font-size-base);
@@ -527,3 +502,34 @@ $paddingSize: 16px;
527
502
  text-overflow: ellipsis;
528
503
  }
529
504
  </style>
505
+
506
+ <style lang="scss">
507
+ // 弹窗内容由父类渲染函数调用,使用独立容器限定样式,避免依赖缺失的 scoped 标记。
508
+ .epx-print-form-types {
509
+ .create-form__list {
510
+ display: flex;
511
+ justify-content: center;
512
+ flex-wrap: wrap;
513
+ padding: 20px;
514
+ }
515
+ .create-form__item {
516
+ width: 140px;
517
+ height: 140px;
518
+ flex-shrink: 0;
519
+ border: 1px solid var(--epx-border);
520
+ display: flex;
521
+ flex-direction: column;
522
+ align-items: center;
523
+ justify-content: center;
524
+ margin: 10px;
525
+ border-radius: var(--epx-radius-base);
526
+ cursor: pointer;
527
+ &:hover {
528
+ box-shadow: 0px 6px 12px -3px rgba(0, 0, 0, 0.15);
529
+ }
530
+ i {
531
+ font-size: 34px;
532
+ }
533
+ }
534
+ }
535
+ </style>
@@ -7,7 +7,7 @@
7
7
  v-for="t in tabs"
8
8
  :key="t.key"
9
9
  :class="{ 'is-on': tab === t.key }"
10
- @click="tab = t.key"
10
+ @click="switchTab(t.key)"
11
11
  >{{ t.name }}</button>
12
12
  <span class="wiki-workbench__space">
13
13
  <b>{{ spaceName }}</b>
@@ -27,26 +27,31 @@
27
27
  placeholder="过滤页标题或路径"
28
28
  prefix-icon="el-icon-search"
29
29
  ></el-input>
30
- <!-- 目录操作只在编辑态出现:编辑就是那个总开关,
31
- 再给目录单配一个「管理目录」开关,等于同一件事要开两次 -->
32
- <div v-if="editing" class="wiki-workbench__tree-ops">
33
- <el-button type="primary" size="small" @click="newPage">+ 新建页</el-button>
34
- <el-button size="small" @click="newFolder">+ 文件夹</el-button>
30
+ <div class="wiki-workbench__tree-ops">
31
+ <el-button :type="managing ? 'primary' : 'default'" size="small" @click="managing = !managing">
32
+ {{ managing ? '完成目录管理' : '目录管理' }}
33
+ </el-button>
34
+ </div>
35
+ <div v-if="managing" class="wiki-workbench__tree-ops">
36
+ <el-button size="small" @click="newPage('')">+ 根目录页</el-button>
37
+ <el-button size="small" @click="newFolder('')">+ 根文件夹</el-button>
35
38
  </div>
36
39
  </div>
37
- <div v-if="editing" class="wiki-workbench__tree-tip">
38
- 同级拖动 = 只改顺序,<b>不产生 commit</b>;改路径要走发布流程,暂未开放。
40
+ <div v-if="managing" class="wiki-workbench__tree-tip">
41
+ 节点右侧“操作”可新增下级或删除;同级拖动调整顺序。
39
42
  </div>
40
43
  <wiki-tree
41
44
  :nodes="nodes"
42
45
  :current="path"
43
46
  :keyword="filter"
44
- :sortable="editing"
45
- :deletable="editing"
47
+ :sortable="managing"
48
+ :manageable="managing"
46
49
  empty-text="这个空间还没有页。先同步远端把已有文档拉进来"
47
50
  @open="openPage"
48
51
  @reorder="onReorder"
49
52
  @remove="onRemove"
53
+ @create-page="newPage"
54
+ @create-folder="newFolder"
50
55
  ></wiki-tree>
51
56
  </div>
52
57
 
@@ -63,27 +68,32 @@
63
68
  </div>
64
69
  <div class="wiki-workbench__doc-ops">
65
70
  <template v-if="editing">
66
- <el-button size="small" @click="callDoc('cancelEdit')">退出编辑</el-button>
67
71
  <el-button size="small" @click="callDoc('saveDraft')">保存草稿</el-button>
68
72
  </template>
69
73
  <el-button v-else type="primary" size="small" :disabled="!doc.path" @click="callDoc('startEdit')">
70
- ✎ 编辑
74
+ ✎ 人工编辑
71
75
  </el-button>
76
+ <el-button type="warning" size="small" icon="el-icon-magic-stick" :disabled="!doc.path" @click="callDoc('aiEdit')">AI 编辑</el-button>
72
77
  <el-button
73
- type="primary"
78
+ type="success"
74
79
  size="small"
80
+ icon="el-icon-upload2"
75
81
  :disabled="!doc.path || (!editing && doc.state !== 'draft')"
76
82
  @click="callDoc(editing ? 'publish' : 'publishSavedDraft')"
77
83
  >发布</el-button>
78
84
  <span class="wiki-workbench__vsep"></span>
79
85
  <el-button
80
86
  v-if="detail.SOURCE_TYPE && detail.SOURCE_TYPE !== 'db'"
87
+ type="success"
88
+ plain
81
89
  size="small"
90
+ icon="el-icon-refresh"
82
91
  :loading="syncing"
83
92
  @click="doSync"
84
93
  >同步远端</el-button>
85
- <el-button size="small" :loading="inspecting" @click="doInspect">体检</el-button>
86
- <el-button size="small" :loading="reindexing" @click="doReindex">重建索引</el-button>
94
+ <el-button type="primary" size="small" icon="el-icon-s-tools" :loading="inspecting" @click="doInspect">
95
+ {{ inspecting ? '检查中…' : '体检与索引维护' }}
96
+ </el-button>
87
97
  <el-button size="small" @click="tab = 'intake'">⬆ 上传原始文件</el-button>
88
98
  </div>
89
99
  </div>
@@ -98,6 +108,7 @@
98
108
  @editing="editing = $event"
99
109
  @dirty="dirty = $event"
100
110
  @published="onPublished"
111
+ @ai-edited="load"
101
112
  ></wiki-doc>
102
113
 
103
114
  <!-- 底部条:发布会产生什么后果,在按发布之前就该看见 -->
@@ -115,10 +126,10 @@
115
126
  :wiki-id="wikiId"
116
127
  :path="path"
117
128
  :title="doc.title"
118
- @back="tab = 'content'"
129
+ @back="switchTab('content')"
119
130
  ></wiki-revisions>
120
131
  <wiki-intake
121
- v-if="tab === 'intake'"
132
+ v-show="tab === 'intake'"
122
133
  :wiki-id="wikiId"
123
134
  @open="openFromTab"
124
135
  @changed="refreshContent"
@@ -136,6 +147,7 @@
136
147
  </template>
137
148
 
138
149
  <script>
150
+ import { h } from 'vue'
139
151
  import component from '@epxing/core/src/mixins/component'
140
152
  import { WikiTree, WikiDoc } from '@epxing/components/src/wiki-site'
141
153
  import { WikiJobs, WikiRevisions, WikiCites, WikiIntake, WikiSpaceSettings } from './wiki-workbench'
@@ -173,11 +185,11 @@ export default {
173
185
  boxHeight: '100%',
174
186
  tab: 'content',
175
187
  editing: false,
188
+ managing: false,
176
189
  // 编辑器里的内容改过没有。由 WikiDoc 报上来——它才知道基准是草稿还是已发布正文
177
190
  dirty: false,
178
191
  syncing: false,
179
192
  inspecting: false,
180
- reindexing: false,
181
193
  tabs: [
182
194
  { key: 'content', name: '内容' },
183
195
  { key: 'intake', name: '原始文件' },
@@ -202,11 +214,6 @@ export default {
202
214
  initialPath () {
203
215
  return this.$query.path || this.$route.query.path || ''
204
216
  },
205
- /** 新建时放在当前页所在目录下——人多半是在「这一块」里继续加内容。 */
206
- currentDir () {
207
- const p = this.path || ''
208
- return p.includes('/') ? p.slice(0, p.lastIndexOf('/')) : ''
209
- },
210
217
  pageWithSpace () {
211
218
  return { ...this.doc, wikiId: this.wikiId }
212
219
  },
@@ -254,6 +261,12 @@ export default {
254
261
  }
255
262
  },
256
263
  methods: {
264
+ switchTab (key) {
265
+ if (key === this.tab) return
266
+ this.tab = key
267
+ if (key === 'content') this.refreshContent()
268
+ },
269
+
257
270
  /**
258
271
  * 量出可用高度。
259
272
  *
@@ -326,18 +339,18 @@ export default {
326
339
  this.space = (d && d.space) || {}
327
340
  this.nodes = (d && d.nodes) || []
328
341
  // 带了 path 就直接开那一页;否则落在第一页上
329
- if (!this.path && this.initialPath) {
330
- this.doOpen(this.initialPath)
342
+ if (!this.path && this.initialPath && this.nodes.some(n => n.path === this.initialPath)) {
343
+ return this.doOpen(this.initialPath)
331
344
  } else if (!this.path) {
332
345
  const first = this.nodes.find(n => n.type === 'page')
333
- if (first) this.openPage(first.path)
346
+ if (first) return this.doOpen(first.path)
334
347
  }
335
348
  })
336
349
  .catch(e => this.$message.error((e && e.message) || '空间加载失败'))
337
350
  .finally(() => { this.loading = false })
338
351
  },
339
352
 
340
- /** 原始文件处理完成后,目录与当前正文都可能改变,两部分必须一起重取。 */
353
+ /** 页发生增删或生成后,目录与当前正文都重新读取。 */
341
354
  refreshContent () {
342
355
  const current = this.path
343
356
  return this.load().then(() => {
@@ -345,33 +358,32 @@ export default {
345
358
  })
346
359
  },
347
360
 
348
- newPage () {
349
- this.$prompt('新页会先落成草稿,发布之后才写回源。', '新建页', {
361
+ newPage (dir) {
362
+ this.$prompt('位置:' + (dir || '根目录') + '。新页先保存为草稿,发布后写回源。', '新建页', {
350
363
  confirmButtonText: '创建', cancelButtonText: '取消', inputPlaceholder: '页标题'
351
364
  }).then(({ value }) => {
352
365
  if (!value || !value.trim()) return
353
366
  return this.$http.update({
354
- action: 'createPage', entityId: WIKI_META, wikiId: this.wikiId, title: value.trim(), dir: this.currentDir
367
+ action: 'createPage', entityId: WIKI_META, wikiId: this.wikiId, title: value.trim(), dir
355
368
  }).then(d => {
356
369
  this.$message.success((d && d.message) || '已新建')
357
- this.load()
358
- if (d && d.path) this.doOpen(d.path)
370
+ return this.load().then(() => { if (d && d.path) return this.doOpen(d.path) })
359
371
  })
360
- }).catch(() => {})
372
+ }).catch(e => { if (e !== 'cancel' && e !== 'close') this.$message.error((e && e.message) || '新建页失败') })
361
373
  },
362
374
 
363
- newFolder () {
364
- this.$prompt('文件夹只是路径的一段,不会往源里写文件。', '新建文件夹', {
375
+ newFolder (dir) {
376
+ this.$prompt('位置:' + (dir || '根目录') + '。文件夹不会往源里写文件。', '新建文件夹', {
365
377
  confirmButtonText: '创建', cancelButtonText: '取消', inputPlaceholder: '文件夹名'
366
378
  }).then(({ value }) => {
367
379
  if (!value || !value.trim()) return
368
380
  return this.$http.update({
369
- action: 'createFolder', entityId: WIKI_META, wikiId: this.wikiId, name: value.trim(), dir: this.currentDir
381
+ action: 'createFolder', entityId: WIKI_META, wikiId: this.wikiId, name: value.trim(), dir
370
382
  }).then(d => {
371
383
  this.$message.success((d && d.message) || '已新建')
372
384
  this.load()
373
385
  })
374
- }).catch(() => {})
386
+ }).catch(e => { if (e !== 'cancel' && e !== 'close') this.$message.error((e && e.message) || '新建文件夹失败') })
375
387
  },
376
388
 
377
389
  /**
@@ -380,13 +392,36 @@ export default {
380
392
  * 删除是一次真实的发布:已发布的页会从源里删掉文件并 push。所以确认框里
381
393
  * 把后果写清楚,而不是只问一句「确定删除吗」。
382
394
  */
383
- onRemove (node) {
395
+ async onRemove (node) {
384
396
  const isFolder = node.type === 'folder'
385
- const tip = isFolder
386
- ? '文件夹只是路径的一段,删它不会往源里写东西;里面还有内容时删不掉。'
387
- : '已发布的页会从源里删除文件并推送,这是一次真实的 commit;指向它的链接会变成死链。'
388
- this.$confirm(tip, '删除「' + (node.title || node.path) + '」', {
389
- type: 'warning', confirmButtonText: '删除', cancelButtonText: '取消', confirmButtonClass: 'el-button--danger'
397
+ if (isFolder && this.nodes.some(n => n.up === node.path)) {
398
+ this.$message.warning('文件夹下还有内容,请先处理子节点')
399
+ return
400
+ }
401
+ let links = []
402
+ if (!isFolder) {
403
+ try {
404
+ const d = await this.$http.get({ action: 'page', entityId: WIKI_META, wikiId: this.wikiId, pageId: node.id })
405
+ links = (d && d.backlinks) || []
406
+ } catch (e) {
407
+ this.$message.error((e && e.message) || '读取关联链接失败,无法确认删除影响')
408
+ return
409
+ }
410
+ }
411
+ const affected = [...new Set(links.map(link => link.path).filter(Boolean))]
412
+ const message = isFolder
413
+ ? '将删除空文件夹;文件夹在源里不是文件。'
414
+ : affected.length
415
+ ? '将删除此页;若已发布,源文件也会删除并推送。以下 ' + affected.length + ' 个页面指向它,删除后这些链接会失效:\n' + affected.join('\n')
416
+ : '将删除此页;若已发布,源文件也会删除并推送。当前没有指向它的页面链接。'
417
+ const body = h('div', {
418
+ style: { whiteSpace: 'pre-line', maxHeight: '40vh', overflowY: 'auto' }
419
+ }, message)
420
+ this.$confirm(body, '删除「' + (node.title || node.path) + '」', {
421
+ type: 'warning',
422
+ confirmButtonText: affected.length ? '仍要删除' : '删除',
423
+ cancelButtonText: '取消',
424
+ confirmButtonClass: 'el-button--danger'
390
425
  }).then(() => {
391
426
  return this.$http.update({ action: 'removePage', entityId: WIKI_META, wikiId: this.wikiId, pageId: node.id })
392
427
  .then(d => {
@@ -398,7 +433,7 @@ export default {
398
433
  this.doc = {}
399
434
  this.backlinks = []
400
435
  }
401
- this.load()
436
+ this.refreshContent()
402
437
  })
403
438
  .catch(e => this.$message.error((e && e.message) || '删除失败'))
404
439
  }).catch(() => {})
@@ -491,18 +526,6 @@ export default {
491
526
  .finally(() => { this.syncing = false })
492
527
  },
493
528
 
494
- /** 重建索引只重算投影,不碰源——所以它和同步是两个按钮,不是一个。 */
495
- doReindex () {
496
- this.reindexing = true
497
- this.$http.update({ action: 'reindex', entityId: WIKI_META, wikiId: this.wikiId })
498
- .then(d => {
499
- this.$message.success((d && d.message) || '索引已重建')
500
- this.loadDetail()
501
- })
502
- .catch(e => this.$message.error((e && e.message) || '重建索引失败'))
503
- .finally(() => { this.reindexing = false })
504
- },
505
-
506
529
  /** 从「引用与反馈」点开一页:切回内容页签,免得人以为点了没反应。 */
507
530
  openFromTab (path) {
508
531
  this.tab = 'content'
@@ -511,9 +534,12 @@ export default {
511
534
 
512
535
  doInspect () {
513
536
  this.inspecting = true
514
- this.$http.update({ action: 'inspect', entityId: WIKI_META, wikiId: this.wikiId })
515
- .then(d => this.$message.success((d && d.message) || '体检完成'))
516
- .catch(e => this.$message.error((e && e.message) || '体检失败'))
537
+ this.$http.update({ action: 'inspect', entityId: WIKI_META, wikiId: this.wikiId, maintain: 'true' })
538
+ .then(d => {
539
+ this.$message.success((d && d.message) || '体检与索引维护完成')
540
+ if (d && d.indexRebuilt) this.loadDetail()
541
+ })
542
+ .catch(e => this.$message.error((e && e.message) || '体检与索引维护失败'))
517
543
  .finally(() => { this.inspecting = false })
518
544
  },
519
545
 
@@ -684,9 +710,13 @@ export default {
684
710
  flex: none;
685
711
  display: flex;
686
712
  align-items: center;
687
- gap: 8px;
713
+ gap: 6px;
688
714
  flex-wrap: wrap;
689
715
  justify-content: flex-end;
716
+
717
+ .el-button + .el-button {
718
+ margin-left: 0;
719
+ }
690
720
  }
691
721
 
692
722
  // 这一页的动作与整个空间的动作之间画条线:前者只影响当前页,
@@ -92,6 +92,8 @@ export default {
92
92
  name: 'epx-authorization-interface',
93
93
  mixins: [component],
94
94
  props: {
95
+ // 接收父表单实例,避免被当作 DOM 属性透传。
96
+ form: Object,
95
97
  formData: {
96
98
  type: Object,
97
99
  default () {
@@ -1 +1 @@
1
- export { default } from '@epxing/components/src/progress-chart/vendor/custom-bpmn/CustomPalette'
1
+ export { default } from '@epxing/components/src/progress-chart/vendor/custom-bpmn/CustomPalette'
@@ -1 +1 @@
1
- export { default } from '@epxing/components/src/progress-chart/vendor/custom-bpmn/CustomRenderer'
1
+ export { default } from '@epxing/components/src/progress-chart/vendor/custom-bpmn/CustomRenderer'