@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
@@ -0,0 +1,284 @@
1
+ <template>
2
+ <section class="jvm-memory">
3
+ <div class="jvm-memory__toolbar">
4
+ <div><h2>内存监控</h2><p>当前 JVM 的内存使用、GC 与请求保护状态</p></div>
5
+ <div class="jvm-memory__actions">
6
+ <el-switch v-model="autoRefresh" active-text="自动刷新" @change="changeRefresh" />
7
+ <el-button size="small" icon="el-icon-refresh" :loading="loading" @click="refresh">刷新</el-button>
8
+ <el-button size="small" icon="el-icon-document" :loading="jstackLoading" @click="captureJstack">获取 jstack</el-button>
9
+ </div>
10
+ </div>
11
+ <el-alert v-if="error" :title="error" type="error" :closable="false" show-icon />
12
+ <el-alert v-else-if="stale" title="采样数据已过期,请检查 JVM 监控线程或网络。下方保留最后一次采样。" type="warning" :closable="false" show-icon />
13
+ <template v-if="snapshot">
14
+ <div class="jvm-memory__status">
15
+ <el-tag :type="statusType">{{ statusText }}</el-tag>
16
+ <span>采样时间:{{ time(snapshot.sampledAt) }}</span>
17
+ <span>采样间隔:{{ snapshot.sampleIntervalMs / 1000 }} 秒</span>
18
+ <span>保护策略:{{ snapshot.rejectRequests ? '高压时拒绝新请求' : '仅监控' }}</span>
19
+ </div>
20
+ <div class="jvm-memory__summary-row">
21
+ <div class="jvm-memory__panel jvm-memory__policy">
22
+ <h3>监控与保护规则</h3>
23
+ <p>预警 {{ ratio(snapshot.warningThreshold) }};连续 {{ snapshot.highSamples }} 次达到 {{ ratio(snapshot.rejectThreshold) }},或单次达到 {{ ratio(snapshot.emergencyThreshold) }} 时进入保护。</p>
24
+ <p>连续 {{ snapshot.recoverySamples }} 次降至 {{ ratio(snapshot.recoveryThreshold) }} 以下后恢复。保护期间新业务请求返回 503,已接收的请求继续执行。</p>
25
+ <p>压力取堆内存、有明确上限的非堆内存池及已配置预算的直接内存使用率的最大值。监控不主动触发 GC,也不采集对象明细。</p>
26
+ <small>实例 {{ snapshot.instanceId }} · JVM 启动于 {{ time(snapshot.startedAt) }}。只展示本次请求命中的 JVM,切换实例时清空趋势。</small>
27
+ </div>
28
+ <div v-if="snapshot.risk" class="jvm-memory__panel jvm-memory__risk">
29
+ <div class="jvm-memory__chart-title"><h3>溢出与死锁风险预测</h3><span>{{ snapshot.risk.reason }}</span></div>
30
+ <p>内存风险:<el-tag size="small" :type="riskType(snapshot.risk.memoryRisk)">{{ snapshot.risk.memoryRisk }}</el-tag>
31
+ <span class="jvm-memory__risk-gap">死锁风险:</span><el-tag size="small" :type="riskType(snapshot.risk.deadlockRisk)">{{ snapshot.risk.deadlockRisk }}</el-tag></p>
32
+ <p v-if="snapshot.risk.memoryGrowthBytesPerSecond > 0">最近窗口堆增长 {{ bytesPerSecond(snapshot.risk.memoryGrowthBytesPerSecond) }};预计达到拒绝线 {{ seconds(snapshot.risk.predictedSecondsToReject) }},达到紧急线 {{ seconds(snapshot.risk.predictedSecondsToEmergency) }}。</p>
33
+ <p v-else>最近窗口未观察到持续堆增长,预测仅供预警参考。</p>
34
+ </div>
35
+ </div>
36
+ <div v-if="snapshot.heap" class="jvm-memory__cards">
37
+ <div class="jvm-memory__card">
38
+ <span>堆内存</span><strong>{{ bytes(snapshot.heap.used) }}</strong>
39
+ <el-progress :percentage="percent(snapshot.heap.used, snapshot.heap.max)" :color="pressureColor" />
40
+ <small>上限 {{ bytes(snapshot.heap.max) }} · 已提交 {{ bytes(snapshot.heap.committed) }}</small>
41
+ </div>
42
+ <div class="jvm-memory__card">
43
+ <span>非堆内存</span><strong>{{ bytes(snapshot.nonHeap.used) }}</strong>
44
+ <small>上限 {{ bytes(snapshot.nonHeap.max) }} · 已提交 {{ bytes(snapshot.nonHeap.committed) }}</small>
45
+ </div>
46
+ <div class="jvm-memory__card">
47
+ <span>直接 / 映射缓冲区</span><strong>{{ bytes(snapshot.directBytes) }}</strong>
48
+ <small>映射 {{ bytes(snapshot.mappedBytes) }}</small>
49
+ <small>直接内存预算 {{ snapshot.directMemoryLimitBytes ? bytes(snapshot.directMemoryLimitBytes) : '未配置,仅监控' }}</small>
50
+ </div>
51
+ <div class="jvm-memory__card">
52
+ <span>GC 累计次数 / 耗时</span><strong>{{ snapshot.gcCount }} 次</strong>
53
+ <small>{{ (snapshot.gcTimeMillis / 1000).toFixed(2) }} 秒 · JVM 启动以来累计</small>
54
+ <small>已拒绝请求 {{ snapshot.rejectedRequests }} 次</small>
55
+ </div>
56
+ </div>
57
+ <div class="jvm-memory__panel">
58
+ <div class="jvm-memory__chart-title"><h3>内存压力趋势</h3><span>当前 {{ (snapshot.pressureRatio * 100).toFixed(1) }}% · 当前页面最近 60 个采样点</span></div>
59
+ <svg v-if="history.length > 1" class="jvm-memory__chart" viewBox="0 0 600 120" preserveAspectRatio="none" role="img" aria-label="内存压力趋势,橙色虚线为保护阈值">
60
+ <line x1="0" :y1="120 * (1 - snapshot.rejectThreshold)" x2="600" :y2="120 * (1 - snapshot.rejectThreshold)" stroke="#e6a23c" stroke-dasharray="5 5" />
61
+ <polyline :points="trendPoints" fill="none" stroke="#3074ef" stroke-width="2" vector-effect="non-scaling-stroke" />
62
+ </svg>
63
+ <p v-else class="jvm-memory__empty">{{ snapshot.enabled ? '等待下一次采样形成趋势' : '监控已关闭' }}</p>
64
+ <div class="jvm-memory__chart-title"><small>{{ history.length ? time(history[0].sampledAt) : '—' }}</small><small>{{ time(snapshot.sampledAt) }}</small></div>
65
+ </div>
66
+ <div v-if="snapshot.threads" class="jvm-memory__panel jvm-memory__threads">
67
+ <div class="jvm-memory__chart-title"><h3>线程监控</h3><span>采样 {{ time(snapshot.threads.sampledAt) }}</span></div>
68
+ <div class="jvm-memory__thread-grid">
69
+ <span>活动线程 <strong>{{ snapshot.threads.live }}</strong></span>
70
+ <span>峰值线程 <strong>{{ snapshot.threads.peak }}</strong></span>
71
+ <span>RUNNABLE <strong>{{ snapshot.threads.runnable }}</strong></span>
72
+ <span>BLOCKED <strong>{{ snapshot.threads.blocked }}</strong></span>
73
+ <span>WAITING <strong>{{ snapshot.threads.waiting + snapshot.threads.timedWaiting }}</strong></span>
74
+ <span>死锁线程 <strong :class="{ 'is-danger': snapshot.threads.deadlocked > 0 }">{{ snapshot.threads.deadlocked }}</strong></span>
75
+ <span>线程栈估算 <strong>{{ snapshot.threads.stackBytesEstimated ? bytes(snapshot.threads.stackBytesEstimate) : '未知' }}</strong></span>
76
+ </div>
77
+ <p>线程栈是按活动线程数和 Xss 参数估算的保留空间,不等于实际 RSS;死锁扫描按 30 秒周期执行。</p>
78
+ </div>
79
+ </template>
80
+ <p v-else-if="!loading && !error" class="jvm-memory__empty">暂无采样数据</p>
81
+ <el-dialog v-model="jstackVisible" width="80%" append-to-body>
82
+ <template #title>
83
+ <div class="jvm-memory__dialog-title">
84
+ <span>JVM jstack(有界快照)</span>
85
+ <span class="jvm-memory__dialog-actions">
86
+ <el-button size="small" icon="el-icon-document-copy" :disabled="!jstackSnapshot" @click="copyJstack">复制</el-button>
87
+ </span>
88
+ </div>
89
+ </template>
90
+ <div v-if="jstackSnapshot" class="jvm-memory__jstack-meta">线程 {{ jstackSnapshot.threadCount }},输出 {{ jstackSnapshot.includedThreads }} 条,栈空间估算 {{ jstackSnapshot.stackBytesEstimated ? bytes(jstackSnapshot.stackBytesEstimate) : '未知' }}</div>
91
+ <pre v-if="jstackSnapshot" class="jvm-memory__jstack">{{ jstackSnapshot.text }}</pre>
92
+ </el-dialog>
93
+ </section>
94
+ </template>
95
+
96
+ <script>
97
+ import axios from 'axios'
98
+ import { markRaw } from 'vue'
99
+
100
+ export default {
101
+ name: 'epx-jvm-memory-monitor',
102
+ data () {
103
+ return {
104
+ snapshot: null,
105
+ history: [],
106
+ autoRefresh: true,
107
+ loading: false,
108
+ error: '',
109
+ stale: false,
110
+ active: false,
111
+ timer: null,
112
+ // 取消并作废离开页面前的请求,避免迟到响应重启轮询。
113
+ pendingRequest: null,
114
+ jstackLoading: false,
115
+ jstackVisible: false,
116
+ jstackSnapshot: null
117
+ }
118
+ },
119
+ computed: {
120
+ statusText () {
121
+ if (this.error || this.stale) return '状态未知'
122
+ const states = { NORMAL: '正常', WARNING: '内存预警', PROTECTED: this.snapshot.rejecting ? '请求保护中' : '高内存压力(仅监控)', STARTING: '等待采样', DISABLED: '监控已关闭' }
123
+ return states[this.snapshot.state] || '状态未知'
124
+ },
125
+ statusType () {
126
+ if (this.error || this.stale || !this.snapshot.enabled) return 'info'
127
+ return this.snapshot.state === 'PROTECTED' ? 'danger' : this.snapshot.state === 'WARNING' ? 'warning' : 'success'
128
+ },
129
+ pressureColor () {
130
+ return this.snapshot.pressureRatio >= this.snapshot.rejectThreshold ? '#f56c6c' : '#3074ef'
131
+ },
132
+ trendPoints () {
133
+ return this.history.map((point, index) => `${index * 600 / Math.max(1, this.history.length - 1)},${120 * (1 - Math.min(1, Math.max(0, point.pressureRatio)))}`).join(' ')
134
+ }
135
+ },
136
+ mounted () {
137
+ document.addEventListener('visibilitychange', this.onVisibilityChange)
138
+ this.activate()
139
+ },
140
+ activated () { this.activate() },
141
+ deactivated () { this.active = false; this.stop() },
142
+ beforeUnmount () {
143
+ this.active = false
144
+ this.stop()
145
+ document.removeEventListener('visibilitychange', this.onVisibilityChange)
146
+ },
147
+ methods: {
148
+ activate () {
149
+ if (this.active) return
150
+ this.active = true
151
+ if (!document.hidden) this.refresh()
152
+ },
153
+ stop () {
154
+ clearTimeout(this.timer)
155
+ this.timer = null
156
+ if (this.pendingRequest) this.pendingRequest.cancel()
157
+ this.pendingRequest = null
158
+ this.loading = false
159
+ },
160
+ onVisibilityChange () {
161
+ if (document.hidden) this.stop()
162
+ else if (this.active && this.autoRefresh) this.refresh()
163
+ },
164
+ changeRefresh () {
165
+ this.stop()
166
+ if (this.autoRefresh) this.refresh()
167
+ },
168
+ async refresh () {
169
+ if (!this.active || document.hidden || this.loading) return
170
+ clearTimeout(this.timer)
171
+ this.loading = true
172
+ const request = markRaw(axios.CancelToken.source())
173
+ this.pendingRequest = request
174
+ try {
175
+ // FM_SYSTEM 返回共享的最新 JVM 快照;接口不查询业务数据、不即时采样。
176
+ const data = await this.$http.get({ entityId: 'FM_SYSTEM', action: 'getJvmMemoryStatus' }, { timeout: 8000, cancelToken: request.token })
177
+ if (this.pendingRequest !== request) return
178
+ if (!data || !data.instanceId || !data.state) throw new Error('监控数据格式异常')
179
+ if (this.snapshot && this.snapshot.instanceId !== data.instanceId) this.history = []
180
+ this.snapshot = data
181
+ this.error = ''
182
+ this.stale = data.enabled && data.sampledAt > 0 && Date.now() - data.sampledAt > Math.max(30000, data.sampleIntervalMs * 3)
183
+ const last = this.history[this.history.length - 1]
184
+ if (data.sampledAt > 0 && (!last || last.sampledAt !== data.sampledAt)) {
185
+ this.history.push({ sampledAt: data.sampledAt, pressureRatio: data.pressureRatio })
186
+ if (this.history.length > 60) this.history.shift()
187
+ }
188
+ } catch (error) {
189
+ if (this.pendingRequest === request && !axios.isCancel(error)) this.error = (error && (error.msg || error.message)) || '内存监控获取失败'
190
+ } finally {
191
+ if (this.pendingRequest === request) {
192
+ this.pendingRequest = null
193
+ this.loading = false
194
+ if (this.active && this.autoRefresh && !document.hidden) {
195
+ this.timer = setTimeout(this.refresh, Math.max(10000, (this.snapshot && this.snapshot.sampleIntervalMs) || 0))
196
+ }
197
+ }
198
+ }
199
+ },
200
+ async captureJstack () {
201
+ if (this.jstackLoading) return
202
+ this.jstackLoading = true
203
+ try {
204
+ this.jstackSnapshot = await this.$http.get({ entityId: 'FM_SYSTEM', action: 'getJvmJstack' }, { timeout: 15000 })
205
+ this.jstackVisible = true
206
+ } catch (error) {
207
+ this.$message.error((error && (error.msg || error.message)) || 'jstack 获取失败')
208
+ } finally {
209
+ this.jstackLoading = false
210
+ }
211
+ },
212
+ async copyJstack () {
213
+ if (!this.jstackSnapshot || !this.jstackSnapshot.text) return
214
+ const text = this.jstackSnapshot.text
215
+ try {
216
+ if (navigator.clipboard && navigator.clipboard.writeText) {
217
+ await navigator.clipboard.writeText(text)
218
+ } else {
219
+ const input = document.createElement('textarea')
220
+ input.value = text
221
+ input.setAttribute('readonly', '')
222
+ input.style.position = 'fixed'
223
+ input.style.opacity = '0'
224
+ document.body.appendChild(input)
225
+ input.select()
226
+ document.execCommand('copy')
227
+ document.body.removeChild(input)
228
+ }
229
+ this.$message.success('jstack 已复制')
230
+ } catch (error) {
231
+ this.$message.error('复制失败,请手动选择文本复制')
232
+ }
233
+ },
234
+ bytes (value) {
235
+ if (value == null || value < 0) return '未限定'
236
+ return value >= 1073741824 ? `${(value / 1073741824).toFixed(2)} GB` : `${(value / 1048576).toFixed(1)} MB`
237
+ },
238
+ percent (used, max) { return max > 0 ? Math.min(100, Math.max(0, Math.round(used / max * 1000) / 10)) : 0 },
239
+ ratio (value) { return `${Math.round(value * 100)}%` },
240
+ riskType (value) { return value === 'HIGH' || value === 'DETECTED' ? 'danger' : value === 'WATCH' ? 'warning' : 'success' },
241
+ seconds (value) { return value == null || value < 0 ? '暂不可预测' : `${value} 秒` },
242
+ bytesPerSecond (value) { return `${this.bytes(value)}/秒` },
243
+ time (value) { return value > 0 ? new Date(value).toLocaleString() : '—' }
244
+ }
245
+ }
246
+ </script>
247
+
248
+ <style scoped>
249
+ .jvm-memory { padding: 24px; background: var(--epx-bg-card, #f3f5fa); color: var(--epx-text, #303133); min-height: 100%; box-sizing: border-box; }
250
+ .jvm-memory h2, .jvm-memory h3, .jvm-memory p { margin: 0; }
251
+ .jvm-memory h2 { font-size: 21px; margin-bottom: 8px; }
252
+ .jvm-memory h3 { font-size: 15px; }
253
+ .jvm-memory small, .jvm-memory__toolbar p { color: var(--epx-text-secondary, #727987); font-size: 13px; }
254
+ .jvm-memory__toolbar, .jvm-memory__chart-title { display: flex; justify-content: space-between; align-items: center; gap: 16px; }
255
+ .jvm-memory__toolbar { margin-bottom: 20px; }
256
+ .jvm-memory__actions, .jvm-memory__status { display: flex; gap: 18px; align-items: center; flex-wrap: wrap; }
257
+ .jvm-memory__status { margin: 18px 0; font-size: 13px; color: var(--epx-text, #606266); }
258
+ .jvm-memory__cards { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; }
259
+ .jvm-memory__summary-row + .jvm-memory__cards { margin-top: 18px; }
260
+ .jvm-memory__card, .jvm-memory__panel { background: var(--epx-bg-card, #fff); border: 1px solid var(--epx-border, #ebeef5); border-radius: 6px; padding: 20px; }
261
+ .jvm-memory__card { display: flex; flex-direction: column; gap: 12px; }
262
+ .jvm-memory__card strong { font-size: 27px; font-weight: 600; }
263
+ .jvm-memory__panel { margin-top: 18px; }
264
+ .jvm-memory__summary-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
265
+ .jvm-memory__summary-row .jvm-memory__panel { margin-top: 18px; }
266
+ .jvm-memory__chart-title span { color: var(--epx-text-secondary, #727987); font-size: 13px; }
267
+ .jvm-memory__chart { width: 100%; height: 160px; margin: 18px 0 10px; }
268
+ .jvm-memory__empty { padding: 40px 0; text-align: center; color: var(--epx-text-secondary, #909399); }
269
+ .jvm-memory__policy { line-height: 1.9; font-size: 13px; }
270
+ .jvm-memory__policy h3 { margin-bottom: 8px; }
271
+ .jvm-memory__policy small { overflow-wrap: anywhere; }
272
+ .jvm-memory__threads p, .jvm-memory__risk p { margin-top: 10px; color: var(--epx-text-secondary, #727987); font-size: 13px; }
273
+ .jvm-memory__thread-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-top: 16px; }
274
+ .jvm-memory__thread-grid span { padding: 12px; background: var(--epx-bg-card, #f7f8fa); border-radius: 4px; color: var(--epx-text, #606266); }
275
+ .jvm-memory__thread-grid strong { display: block; margin-top: 6px; color: var(--epx-text, #303133); font-size: 20px; }
276
+ .jvm-memory__thread-grid .is-danger { color: var(--el-color-danger, #f56c6c); }
277
+ .jvm-memory__jstack-meta { color: var(--epx-text, #606266); margin-bottom: 12px; }
278
+ .jvm-memory__dialog-title { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-right: 42px; }
279
+ .jvm-memory__dialog-actions { display: inline-flex; gap: 6px; }
280
+ .jvm-memory__jstack { max-height: 60vh; overflow: auto; padding: 14px; background: #1f2430; color: #d7dae0; white-space: pre-wrap; word-break: break-word; }
281
+ @media (max-width: 1100px) { .jvm-memory__cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
282
+ @media (max-width: 900px) { .jvm-memory__summary-row { grid-template-columns: 1fr; } }
283
+ @media (max-width: 650px) { .jvm-memory { padding: 14px; } .jvm-memory__cards { grid-template-columns: 1fr; } .jvm-memory__thread-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .jvm-memory__toolbar, .jvm-memory__chart-title { align-items: flex-start; flex-direction: column; } }
284
+ </style>
@@ -1,210 +1,4 @@
1
- <template>
2
- <div ref="module" id="module" class="module">
3
- <!-- 显示module页面 -->
4
- <!-- <epx-router-view v-if="initRoute" :init-route="initRoute"></epx-router-view> -->
5
- <!-- 创建容器 -->
6
- <epx-page
7
- @changeRouter="changeRouter"
8
- @back="back"
9
- v-if="showPage"
10
- :pageId="moduleRouter.pageId"
11
- :flag="moduleRouter.flag"
12
- :metaId="moduleRouter.metaId"
13
- :component="moduleRouter.component"
14
- :router="moduleRouter"
15
- :current-query="moduleRouter"
16
- />
17
- </div>
18
- </template>
19
-
20
- <script>
21
- // 容器页面
22
- import { DELETE_MODULE_ROUTER, SET_ROUTER } from '@epxing/core/src/store/actions'
23
- import { mapState } from 'vuex'
24
- export default {
25
- name: 'epx-module',
26
- props: {
27
- router: {
28
- type: Object,
29
- default () {
30
- return {}
31
- }
32
- }
33
- },
34
- data () {
35
- return {
36
- showPage: false,
37
- moduleIndexPage: '',
38
- moduleRouters: [],
39
- initRoute: null
40
- }
41
- },
42
- computed: {
43
- ...mapState({
44
- // routers: state => state.router.moduleRouters,
45
- // route: state => state.router.moduleRoute,
46
- user: state => state.user.user
47
- }),
48
- pageId () {
49
- return this.route.pageId || this.moduleIndexPage
50
- },
51
- // moduleRouters () {
52
- // const { children = [] } = this.router
53
- // return children
54
- // },
55
- moduleRouter () {
56
- return this.moduleRouters[this.moduleRouters.length - 1] || {}
57
- }
58
- },
59
- watch: {
60
- $routers: {
61
- handler (routers) {
62
- routers.forEach(item => {
63
- if (item.key === this.router.key) {
64
- const { children = [] } = item
65
- this.moduleRouters = [...children]
66
- }
67
- })
68
- },
69
- deep: true
70
- },
71
- moduleRouter: {
72
- handler (v, oldV) {
73
- // 页面更改、刷新页面
74
- if (!(v.pageId === oldV.pageId && v.flag === oldV.flag && v.metaId === oldV.metaId && v.component === oldV.component)) {
75
- this.reloadPageDebounce()
76
- }
77
- },
78
- deep: true
79
- }
80
- },
81
- provide () {
82
- return {
83
- $changeRouter: this.changeRouter,
84
- $reloadPage: this.reloadPage,
85
- $backPage: this.$popModuleRoute
86
- }
87
- },
88
- methods: {
89
- reloadPage () {
90
- this.showPage = false
91
- this.$nextTick(() => {
92
- if (this.moduleRouter.pageId || this.moduleRouter.flag || this.moduleIndexPage || this.moduleRouter.component) {
93
- const timer = setTimeout(() => {
94
- this.showPage = true
95
- clearTimeout(timer)
96
- }, 0)
97
- }
98
- })
99
- },
100
- getModule () {
101
- let { moduleId } = this.$route.query
102
- if (this.router.query) {
103
- moduleId = this.router.query.moduleId
104
- }
105
- if (!moduleId) {
106
- if (this.user.currentRoleEntity) {
107
- const { MENU: USER_MENU } = this.user.currentRoleEntity.data
108
- moduleId = USER_MENU
109
- }
110
- }
111
- // 获取当前模块信息
112
- // 如果当前模块没绑定页面,获取其子模块页面
113
- this.$http.get({
114
- entityId: 'FM_MODULE',
115
- id: moduleId
116
- })
117
- .then(data => {
118
- if (data) {
119
- const module = data.data || {}
120
- const date = new Date().getTime()
121
- // 初始化模块首页
122
- // this.$pushModuleRoute({
123
- // pageId: module.MAIN_PAGE
124
- // })
125
- if (!this.router.query) {
126
- this.$createPage({
127
- key: this.$routers.length === 0 ? 'nav-' + date : date,
128
- moduleId: module.ID,
129
- title: module.NAME,
130
- openType: '1'
131
- })
132
- this.initRoute = {
133
- key: this.$routers.length === 0 ? 'nav-' + date : date,
134
- moduleId: module.ID,
135
- title: module.NAME
136
- }
137
- } else {
138
- if (!this.router.children || this.router.children.length === 0) {
139
- const router = {
140
- ...this.router,
141
- children: [
142
- { key: date, pageId: module.MAIN_PAGE, moduleId: moduleId }
143
- ]
144
- }
145
- this.initRoute = { key: date, pageId: module.MAIN_PAGE, moduleId: moduleId }
146
- this.$store.commit(SET_ROUTER, { key: this.router.key, router })
147
- }
148
- }
149
- // this.$createPage({
150
- // pageId: module.MAIN_PAGE
151
- // })
152
- }
153
- })
154
- },
155
- addListenerRouter () {
156
- // ⚠️ T17 修复轮 2(定向复审 N4):这里原来还有两行
157
- // this.$bus.on('push', this.pushModuleRoute) / this.$bus.on('pop', this.popModuleRoute)
158
- // 两个 handler **在本组件里恒为 `undefined`**:它们全仓只定义在 `@epxing/core/src/Container.vue`
159
- // (:204-213),而本组件**没有 mixins/extends**,globalProperties 里也没有。
160
- // 往总线塞 `undefined` 只会让 `emit('push')` 抛 `h is not a function`,从来没做过事 ⇒ 删掉。
161
- // (两个不带 handler 的 off 是挂载侧既有行为,本次不动。)
162
- this.$bus.off('push')
163
- this.$bus.off('pop')
164
- },
165
- removeListenerRouter () {
166
- // ⚠️ T17 修复轮 2(定向复审 N4,顶回②的安全性论断被推翻):修复轮 1 认定这里「本来就传了
167
- // handler,是安全的」。计数没错(该按原修法改的确实是 5 个文件不是 6 个),**但安全性论断错了**:
168
- // `this.pushModuleRoute` / `this.popModuleRoute` 都是 `undefined`,而 `vue.bus.js` 的 off
169
- // 判的是 `if (!callback)` ⇒ `off(event, undefined)` 走「删光该事件全部监听」那条分支,
170
- // 与裸 `off('push')` 完全等价、一样连坐删掉别人的监听。
171
- // 本组件从来没有成功注册过任何 handler,没有东西可摘 ⇒ 两行 off 一并删掉,只留 commit。
172
- // 把二级路由清空
173
- this.$store.commit(DELETE_MODULE_ROUTER)
174
- },
175
- changeRouter (v) {
176
- // const { mode, router } = v
177
- // if (mode === 'replace') {
178
- // this.$popModuleRoute()
179
- // }
180
- // this.$pushModuleRoute(router)
181
- },
182
- back () {
183
- this.$popModuleRoute()
184
- }
185
- },
186
- created () {
187
- this.getModule()
188
- this.addListenerRouter()
189
- this.reloadPageDebounce = this.$_.debounce(this.reloadPage, 20)
190
- },
191
- beforeUnmount () {
192
- this.removeListenerRouter()
193
- }
194
- }
195
- </script>
196
-
197
- <style scoped>
198
- .module {
199
- width: 100%;
200
- height: 100%;
201
- padding: 16px;
202
- box-sizing: border-box;
203
- position: relative;
204
- }
205
- @media only screen and (max-width: 992px) {
206
- .module{
207
- padding: 0;
208
- }
209
- }
210
- </style>
1
+ <script>
2
+ // 保留业务包旧路径,基础路由页统一由 core 提供。
3
+ export { default } from '@epxing/core/src/views/Module.vue'
4
+ </script>
@@ -30,6 +30,14 @@
30
30
  <el-button size="small" @click="view = 'intake'">⬆ 上传原始文件</el-button>
31
31
  <el-button size="small" @click="view = 'config'">⚙ 配置</el-button>
32
32
  </template>
33
+ <template #doc-ops>
34
+ <span class="wiki-binding__doc-sep"></span>
35
+ <el-button v-if="space.SOURCE_TYPE && space.SOURCE_TYPE !== 'db'" type="success" plain size="small" icon="el-icon-refresh" :loading="syncing" @click="doSync">同步远端</el-button>
36
+ <el-button type="primary" size="small" icon="el-icon-s-tools" :loading="inspecting" @click="doInspect">
37
+ {{ inspecting ? '检查中…' : '体检与索引维护' }}
38
+ </el-button>
39
+ <el-button type="primary" plain size="small" icon="el-icon-share" @click="shareWiki">分享</el-button>
40
+ </template>
33
41
  </epx-wiki-site>
34
42
  </div>
35
43
 
@@ -205,6 +213,8 @@ export default {
205
213
  // 「这个空间里到底有什么」。配置收进右上角按钮
206
214
  view: 'content',
207
215
  contentHeight: 0,
216
+ syncing: false,
217
+ inspecting: false,
208
218
  form: { scope: '', retrieval: 'exact' }
209
219
  }
210
220
  },
@@ -393,6 +403,31 @@ export default {
393
403
  })
394
404
  },
395
405
 
406
+ shareWiki () {
407
+ const site = this.$refs.wikiSite
408
+ if (site) site.share()
409
+ },
410
+
411
+ doSync () {
412
+ this.syncing = true
413
+ this.$http.update({ action: 'sync', entityId: WIKI_META, wikiId: this.space.ID })
414
+ .then(d => {
415
+ this.$message.success((d && d.message) || '同步完成')
416
+ this.refreshWiki()
417
+ this.load()
418
+ })
419
+ .catch(e => this.$message.error((e && e.message) || '同步失败'))
420
+ .finally(() => { this.syncing = false })
421
+ },
422
+
423
+ doInspect () {
424
+ this.inspecting = true
425
+ this.$http.update({ action: 'inspect', entityId: WIKI_META, wikiId: this.space.ID, maintain: 'true' })
426
+ .then(d => this.$message.success((d && d.message) || '体检与索引维护完成'))
427
+ .catch(e => this.$message.error((e && e.message) || '体检与索引维护失败'))
428
+ .finally(() => { this.inspecting = false })
429
+ },
430
+
396
431
  /** 从入库记录点击生成页时,回到内容视图并打开同一页。 */
397
432
  openGeneratedPage (path) {
398
433
  this.view = 'content'
@@ -410,6 +445,13 @@ export default {
410
445
  .wiki-binding {
411
446
  padding: var(--padding-large) 0;
412
447
 
448
+ &__doc-sep {
449
+ flex: none;
450
+ width: 1px;
451
+ height: 16px;
452
+ background: var(--epx-bg, #e3e3e3);
453
+ }
454
+
413
455
  // 未绑定态:虚线框 + 居中,与设计稿 .bindbox 一致。
414
456
  // 虚线是有意的——它表示「这里还空着,等你填」,实线框看着像一块已经配好的内容
415
457
  &__bindbox {
package/types/app.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { App as default } from './index'
@@ -1 +1 @@
1
- export { DeptFind as default } from './index'
1
+ export { DeptFind as default } from './index'
package/types/index.d.ts CHANGED
@@ -1,14 +1,15 @@
1
- import type { App, DefineComponent, Plugin } from 'vue'
2
- export declare const UserFind: DefineComponent<any, any, any>
3
- export declare const DeptFind: DefineComponent<any, any, any>
4
- export declare const HeaderNav: DefineComponent<any, any, any>
5
- export declare const EntityTreeManage: DefineComponent<any, any, any>
6
- export declare const PageCatalog: DefineComponent<any, any, any>
7
- export declare const ReportFormList: DefineComponent<any, any, any>
8
- export declare const ReportFormListPage: DefineComponent<any, any, any>
9
- export declare const ExcelPreviewPage: DefineComponent<any, any, any>
10
- export declare function install(app: App): void
11
- declare const plugin: Plugin
12
- export default plugin
13
- export declare const systemComponents: Record<string, import('vue').Component>
14
- export declare const routeComponents: Record<string, import('vue').Component>
1
+ import type { App as VueApp, DefineComponent, Plugin } from 'vue'
2
+ export declare const App: DefineComponent<any, any, any>
3
+ export declare const UserFind: DefineComponent<any, any, any>
4
+ export declare const DeptFind: DefineComponent<any, any, any>
5
+ export declare const HeaderNav: DefineComponent<any, any, any>
6
+ export declare const EntityTreeManage: DefineComponent<any, any, any>
7
+ export declare const PageCatalog: DefineComponent<any, any, any>
8
+ export declare const ReportFormList: DefineComponent<any, any, any>
9
+ export declare const ReportFormListPage: DefineComponent<any, any, any>
10
+ export declare const ExcelPreviewPage: DefineComponent<any, any, any>
11
+ export declare function install(app: VueApp): void
12
+ declare const plugin: Plugin
13
+ export default plugin
14
+ export declare const systemComponents: Record<string, import('vue').Component>
15
+ export declare const routeComponents: Record<string, import('vue').Component>
package/types/popup.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- declare const popup: import('vue').Plugin
2
- export default popup
1
+ declare const popup: import('vue').Plugin
2
+ export default popup
@@ -1 +1 @@
1
- export { routeComponents as default } from './index'
1
+ export { routeComponents as default } from './index'
@@ -1 +1 @@
1
- export { UserFind as default } from './index'
1
+ export { UserFind as default } from './index'