aegon-lv82 1.0.0

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 (148) hide show
  1. package/package.json +12 -0
  2. package/src/App.vue +44 -0
  3. package/src/api/index.ts +19 -0
  4. package/src/api/modules/build-report/reportEditor.ts +526 -0
  5. package/src/api/modules/build-report/reportPermission.ts +64 -0
  6. package/src/api/modules/index.ts +69 -0
  7. package/src/api/modules/industry/green_mortgage.ts +42 -0
  8. package/src/api/modules/user/index.ts +4 -0
  9. package/src/api/request.ts +124 -0
  10. package/src/assets/sample-access-icon.png +0 -0
  11. package/src/assets/sample-pie-chart.png +0 -0
  12. package/src/assets/vue.svg +1 -0
  13. package/src/components/CapsuleScrollbar.vue +7 -0
  14. package/src/components/Export/ExcelExport.vue +592 -0
  15. package/src/components/Export/ExcelExport2.vue +494 -0
  16. package/src/components/Export/ExcelExport3.vue +342 -0
  17. package/src/components/Export/ExcelExport4.vue +665 -0
  18. package/src/components/Export/excelExport.js +547 -0
  19. package/src/components/Export/excelExport.ts +551 -0
  20. package/src/components/GEN-AI/index.vue +140 -0
  21. package/src/components/GEN-AI/index1.vue +422 -0
  22. package/src/components/GEN-AI/index10.vue +5 -0
  23. package/src/components/GEN-AI/index2.vue +548 -0
  24. package/src/components/GEN-AI/index3.vue +605 -0
  25. package/src/components/GEN-AI/index4.vue +609 -0
  26. package/src/components/GEN-AI/index5.vue +558 -0
  27. package/src/components/GEN-AI/index6.vue +634 -0
  28. package/src/components/GEN-AI/index7.vue +688 -0
  29. package/src/components/GEN-AI/index8.vue +458 -0
  30. package/src/components/GEN-AI/index9.vue +1217 -0
  31. package/src/components/GEN-AI/types.ts +12 -0
  32. package/src/components/GEN-AI/utils.ts +42 -0
  33. package/src/components/HelloWorld.vue +41 -0
  34. package/src/components/PageCard.vue +124 -0
  35. package/src/components/PageHeader.vue +32 -0
  36. package/src/components/backup/index5 copy.vue +556 -0
  37. package/src/components/backup/index5.vue +620 -0
  38. package/src/components/backup/index9 copy.vue +1029 -0
  39. package/src/components/backup/index9-pro.vue +1065 -0
  40. package/src/components/backup/index9.vue +1057 -0
  41. package/src/directives/btnLoading.ts +455 -0
  42. package/src/directives/debounce copy.ts +670 -0
  43. package/src/directives/debounce.ts +98 -0
  44. package/src/directives/index.ts +25 -0
  45. package/src/layouts/MainLayout.vue +129 -0
  46. package/src/main.ts +76 -0
  47. package/src/router/index.ts +142 -0
  48. package/src/router/menus.ts +28 -0
  49. package/src/store/modules/gen-ai/index.ts +1 -0
  50. package/src/store/modules/gen-ai/reportBuild.ts +37 -0
  51. package/src/style.css +79 -0
  52. package/src/styles/report-config-popover.css +39 -0
  53. package/src/styles/report-icon-btn.css +19 -0
  54. package/src/test/mock.ts +101 -0
  55. package/src/test/test1.vue +402 -0
  56. package/src/test/test2.vue +1689 -0
  57. package/src/types/gen-ai/build-report/reportEditor.ts +684 -0
  58. package/src/utils/function.ts +46 -0
  59. package/src/utils/gen-ai-navigation.ts +1 -0
  60. package/src/utils/gen-ai-scroll.ts +1 -0
  61. package/src/utils/openDataLoaderWordExport.ts +33 -0
  62. package/src/utils/pageScrollbar.ts +115 -0
  63. package/src/utils/randomTranscode.ts +87 -0
  64. package/src/utils/reportPdfExport.ts +44 -0
  65. package/src/views/Blank.vue +68 -0
  66. package/src/views/Home.vue +29 -0
  67. package/src/views/TemplateCenter/Knowledge.ts +83 -0
  68. package/src/views/TemplateCenter/data.d.ts +10 -0
  69. package/src/views/TemplateCenter/index.vue +1128 -0
  70. package/src/views/TemplateCenter/service.ts +69 -0
  71. package/src/views/gen-ai/build-report/chartStreamRefs.ts +174 -0
  72. package/src/views/gen-ai/build-report/coherence-check/index.vue +559 -0
  73. package/src/views/gen-ai/build-report/components/ReportChapterWorkspace.vue +4258 -0
  74. package/src/views/gen-ai/build-report/components/ReportConfigPanel.vue +727 -0
  75. package/src/views/gen-ai/build-report/components/ReportGenerateSettingsPanel.vue +2361 -0
  76. package/src/views/gen-ai/build-report/components/ReportGeneratingPanel.vue +1765 -0
  77. package/src/views/gen-ai/build-report/components/ReportPromptPanel.vue +614 -0
  78. package/src/views/gen-ai/build-report/components/ReportSourceFileSummary.vue +656 -0
  79. package/src/views/gen-ai/build-report/components/ReportSourcesPanel.vue +1327 -0
  80. package/src/views/gen-ai/build-report/components/ReportTemplateDetail.vue +191 -0
  81. package/src/views/gen-ai/build-report/components/ReportTemplateEditorFormPanel.vue +667 -0
  82. package/src/views/gen-ai/build-report/components/ReportTemplatePicker.vue +302 -0
  83. package/src/views/gen-ai/build-report/components/ReportWorkflowSidebar.vue +148 -0
  84. package/src/views/gen-ai/build-report/components/paramsSetting.vue +886 -0
  85. package/src/views/gen-ai/build-report/components/reportEdit.vue +2283 -0
  86. package/src/views/gen-ai/build-report/index.vue +856 -0
  87. package/src/views/gen-ai/build-report/report-domain.ts +1133 -0
  88. package/src/views/gen-ai/build-report/report-template-data.ts +295 -0
  89. package/src/views/gen-ai/build-report/useReportWorkflow.ts +1408 -0
  90. package/src/views/gen-ai/common/assets/NotoSans-SC.woff2 +0 -0
  91. package/src/views/gen-ai/common/assets/NotoSans-TC.woff2 +0 -0
  92. package/src/views/gen-ai/common/assets/NotoSans.woff2 +0 -0
  93. package/src/views/gen-ai/common/assets/fonts.css +278 -0
  94. package/src/views/gen-ai/common/assets/pagination.css +163 -0
  95. package/src/views/gen-ai/common/directives/btnLoading.ts +455 -0
  96. package/src/views/gen-ai/common/directives/debounce.ts +98 -0
  97. package/src/views/gen-ai/common/directives/index.ts +30 -0
  98. package/src/views/gen-ai/common/en.md +417 -0
  99. package/src/views/gen-ai/common/index.ts +36 -0
  100. package/src/views/gen-ai/common/types/component-expose.ts +9 -0
  101. package/src/views/gen-ai/common/utils/api-res-status.ts +7 -0
  102. package/src/views/gen-ai/common/utils/navigation.ts +59 -0
  103. package/src/views/gen-ai/common/utils/remote-request.ts +45 -0
  104. package/src/views/gen-ai/common/utils/scroll.ts +577 -0
  105. package/src/views/gen-ai/components/AIExport/docx-export.ts +950 -0
  106. package/src/views/gen-ai/components/AIExport/docxExport.ts +2566 -0
  107. package/src/views/gen-ai/components/AIExport/index.ts +52 -0
  108. package/src/views/gen-ai/components/AIExport/parseMarkdownHtml.ts +649 -0
  109. package/src/views/gen-ai/components/CapsuleScrollbar.vue +149 -0
  110. package/src/views/gen-ai/components/ChapterTitleScroll.vue +468 -0
  111. package/src/views/gen-ai/components/EntryHeroNavCards.vue +379 -0
  112. package/src/views/gen-ai/components/GenAiPagination.vue +52 -0
  113. package/src/views/gen-ai/components/ModelCard.vue +1114 -0
  114. package/src/views/gen-ai/components/Preview/FilePreviewPage.vue +947 -0
  115. package/src/views/gen-ai/components/Preview/PreviewCapsuleScroll.vue +205 -0
  116. package/src/views/gen-ai/components/Preview/PreviewOutlineTree.vue +148 -0
  117. package/src/views/gen-ai/components/Preview/index.ts +11 -0
  118. package/src/views/gen-ai/components/Preview/preview.ts +722 -0
  119. package/src/views/gen-ai/components/PromptField.vue +505 -0
  120. package/src/views/gen-ai/components/RecentReportsTable.vue +974 -0
  121. package/src/views/gen-ai/components/ReportCollapsibleSection.vue +172 -0
  122. package/src/views/gen-ai/components/ReportHistoryList.vue +637 -0
  123. package/src/views/gen-ai/components/ReportSectionConfirmPopover.vue +251 -0
  124. package/src/views/gen-ai/components/editor/ChapterTiptapEditor.vue +3139 -0
  125. package/src/views/gen-ai/components/editor/StructureBlockView.vue +441 -0
  126. package/src/views/gen-ai/components/editor/TiptapToolbar.vue +1036 -0
  127. package/src/views/gen-ai/components/editor/TypewriterDocEditor.vue +398 -0
  128. package/src/views/gen-ai/components/editor/_StructureOrgNode.vue +326 -0
  129. package/src/views/gen-ai/components/editor/index.ts +12 -0
  130. package/src/views/gen-ai/components/editor/parseMarkdownHtml.ts +667 -0
  131. package/src/views/gen-ai/gen-entry/api/index.ts +32 -0
  132. package/src/views/gen-ai/gen-entry/index.vue +287 -0
  133. package/src/views/gen-ai/gen-entry/types/index.ts +4 -0
  134. package/src/views/gen-ai/i.mark +2 -0
  135. package/src/views/gen-ai/management-center/index.vue +1815 -0
  136. package/src/views/gen-ai/management-center/mockData.ts +248 -0
  137. package/src/views/gen-ai/management-center/types/index.ts +37 -0
  138. package/src/views/gen-ai/model-manager/TemplateEditorFormPanel.vue +661 -0
  139. package/src/views/gen-ai/model-manager/api/index.ts +32 -0
  140. package/src/views/gen-ai/model-manager/api/mockApi.ts +33 -0
  141. package/src/views/gen-ai/model-manager/index.vue +1583 -0
  142. package/src/views/gen-ai/model-manager/mockData.ts +177 -0
  143. package/src/views/gen-ai/model-manager/template-detail-shared.ts +154 -0
  144. package/src/views/gen-ai/model-manager/template-editor.vue +345 -0
  145. package/src/views/gen-ai/model-manager/types/index.ts +40 -0
  146. package/src/views/gen-ai/model-manager/utils.ts +25 -0
  147. package/src/views/greenMortgage.vue +1834 -0
  148. package/src/vite-env.d.ts +46 -0
@@ -0,0 +1,656 @@
1
+ <template>
2
+ <ReportCollapsibleSection
3
+ ref="sectionRef"
4
+ v-model:open="panelOpen"
5
+ title="編輯者"
6
+ card-hover
7
+ @before-collapse="handleBeforeCollapse"
8
+ >
9
+ <template #actions>
10
+ <button
11
+ v-if="canManageReportEditors"
12
+ ref="addBtnRef"
13
+ type="button"
14
+ class="report-icon-btn"
15
+ aria-label="添加"
16
+ @click.stop="toggleAddOpen"
17
+ >
18
+ <el-icon>
19
+ <Plus />
20
+ </el-icon>
21
+ </button>
22
+ <button
23
+ v-if="canManageReportEditors"
24
+ ref="shareBtnRef"
25
+ type="button"
26
+ class="report-icon-btn"
27
+ aria-label="轉移發起人"
28
+ @click.stop="toggleShareOpen"
29
+ >
30
+ <el-icon>
31
+ <Share />
32
+ </el-icon>
33
+ </button>
34
+ </template>
35
+
36
+ <div class="report-editor-panel">
37
+ <ul class="report-editor-list">
38
+ <li v-for="editor in editors" :key="editor.id" class="report-editor-list__row">
39
+ <span class="report-editor-list__name" :title="`${editor.name}(${editor.employeeId})`">
40
+ {{ editor.name }}({{ editor.employeeId }})
41
+ </span>
42
+ <span class="report-editor-list__dept">{{ editor.department }}</span>
43
+ <span class="report-editor-list__role">{{ editorRoleLabel(editor.role) }}</span>
44
+ <span class="report-editor-list__action">
45
+ <button
46
+ v-if="canManageReportEditors && editor.role !== 'initiator'"
47
+ type="button"
48
+ class="report-icon-btn"
49
+ :aria-label="`移除${editor.name}`"
50
+ @click="requestRemoveEditor(editor, $event)"
51
+ >
52
+ <el-icon>
53
+ <Close />
54
+ </el-icon>
55
+ </button>
56
+ </span>
57
+ </li>
58
+ </ul>
59
+ </div>
60
+ </ReportCollapsibleSection>
61
+
62
+ <el-popover
63
+ :visible="addOpen"
64
+ :virtual-ref="shareToggleRef"
65
+ placement="bottom-start"
66
+ :width="popoverWidth"
67
+ :popper-options="popperOptions"
68
+ :popper-style="addPopperStyle"
69
+ popper-class="report-config-popover"
70
+ :transition="popoverInstant ? 'report-config-popover-instant' : 'report-config-popover-fade'"
71
+ :gpu-acceleration="false"
72
+ :teleported="true"
73
+ >
74
+ <div class="report-source-transfer">
75
+ <h4 class="report-source-transfer__title">添加編輯者</h4>
76
+ <label class="report-source-transfer__field">
77
+ <span class="report-source-transfer__label">用戶</span>
78
+ <el-select
79
+ v-model="addSelectedUser"
80
+ class="report-source-transfer__select"
81
+ filterable
82
+ clearable
83
+ placeholder="請選擇用戶"
84
+ >
85
+ <el-option
86
+ v-for="user in addEditorOptions"
87
+ :key="user.id"
88
+ :label="`${user.name}(${user.employeeId})`"
89
+ :value="user.id"
90
+ />
91
+ </el-select>
92
+ </label>
93
+ <div class="report-source-transfer__actions">
94
+ <button type="button" class="report-source-transfer__btn" @click="addOpen = false">
95
+ 取消
96
+ </button>
97
+ <button
98
+ type="button"
99
+ class="report-source-transfer__btn report-source-transfer__btn--primary"
100
+ @click="confirmAdd"
101
+ >
102
+ 確認
103
+ </button>
104
+ </div>
105
+ </div>
106
+ </el-popover>
107
+
108
+ <el-popover
109
+ :visible="shareOpen"
110
+ :virtual-ref="shareToggleRef"
111
+ placement="bottom-start"
112
+ :width="popoverWidth"
113
+ :popper-options="popperOptions"
114
+ :popper-style="sharePopperStyle"
115
+ popper-class="report-config-popover"
116
+ :transition="popoverInstant ? 'report-config-popover-instant' : 'report-config-popover-fade'"
117
+ :gpu-acceleration="false"
118
+ :teleported="true"
119
+ >
120
+ <div class="report-source-transfer">
121
+ <h4 class="report-source-transfer__title">轉移報告發起人權限</h4>
122
+ <label class="report-source-transfer__field">
123
+ <span class="report-source-transfer__label">用戶</span>
124
+ <el-select
125
+ v-model="transferSelectedUser"
126
+ class="report-source-transfer__select"
127
+ filterable
128
+ clearable
129
+ placeholder="請選擇用戶"
130
+ >
131
+ <el-option
132
+ v-for="user in transferUserOptions"
133
+ :key="user.id"
134
+ :label="`${user.name}(${user.employeeId})`"
135
+ :value="user.id"
136
+ />
137
+ </el-select>
138
+ </label>
139
+ <div class="report-source-transfer__actions">
140
+ <button type="button" class="report-source-transfer__btn" @click="shareOpen = false">
141
+ 取消
142
+ </button>
143
+ <button
144
+ type="button"
145
+ class="report-source-transfer__btn report-source-transfer__btn--primary"
146
+ @click="confirmTransfer"
147
+ >
148
+ 確認
149
+ </button>
150
+ </div>
151
+ </div>
152
+ </el-popover>
153
+
154
+ <ReportSectionConfirmPopover
155
+ :visible="deleteConfirmOpen"
156
+ :virtual-ref="deleteConfirmVirtualRef"
157
+ :boundary-el="sectionBoundaryEl"
158
+ :trigger-btn="deleteConfirmTriggerBtn"
159
+ :message="deleteConfirmMessage"
160
+ :instant="popoverInstant"
161
+ @confirm="confirmRemoveEditor"
162
+ @cancel="closeDeleteConfirm"
163
+ />
164
+ </template>
165
+
166
+ <script setup lang="ts">
167
+ import { Close, Plus, Share } from '@element-plus/icons-vue'
168
+ import { ElIcon, ElMessage, ElOption, ElPopover, ElSelect } from 'element-plus'
169
+ import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
170
+ import {
171
+ MOCK_UMS_EDITOR_USERS,
172
+ type ReportEditor,
173
+ type ReportEditorRole,
174
+ } from '../useReportWorkflow'
175
+ import { useReportWorkflow } from '../useReportWorkflow'
176
+ import { ReportCollapsibleSection, ReportSectionConfirmPopover } from '../../common'
177
+ const workflow = useReportWorkflow()
178
+ const { reportEditors, canManageReportEditors } = workflow
179
+
180
+ const editors = reportEditors
181
+ const addOpen = ref(false)
182
+ const shareOpen = ref(false)
183
+ const popoverInstant = ref(false)
184
+ const addSelectedUser = ref('')
185
+ const transferSelectedUser = ref('')
186
+
187
+ const addBtnRef = ref<HTMLElement | null>(null)
188
+ const shareBtnRef = ref<HTMLElement | null>(null)
189
+ const sectionRef = ref<InstanceType<typeof ReportCollapsibleSection> | null>(null)
190
+
191
+ const shareToggleRef = computed(() => sectionRef.value?.toggleRef ?? null)
192
+
193
+ const popoverWidthPx = ref(240)
194
+ const arrowAlignKey = ref(0)
195
+ const popoverWidth = computed(() => popoverWidthPx.value)
196
+
197
+ const addEditorOptions = computed(() => {
198
+ const existingIds = new Set(editors.value.map((editor) => editor.employeeId))
199
+ return MOCK_UMS_EDITOR_USERS.filter((user) => !existingIds.has(user.employeeId))
200
+ })
201
+
202
+ const transferUserOptions = computed(() => {
203
+ const currentInitiator = editors.value.find((editor) => editor.role === 'initiator')
204
+ const candidates = new Map<string, (typeof MOCK_UMS_EDITOR_USERS)[number]>()
205
+
206
+ for (const user of MOCK_UMS_EDITOR_USERS) {
207
+ if (user.id !== currentInitiator?.id) {
208
+ candidates.set(user.id, user)
209
+ }
210
+ }
211
+ for (const editor of editors.value) {
212
+ if (editor.id !== currentInitiator?.id) {
213
+ candidates.set(editor.id, {
214
+ id: editor.id,
215
+ name: editor.name,
216
+ employeeId: editor.employeeId,
217
+ department: editor.department,
218
+ })
219
+ }
220
+ }
221
+
222
+ return Array.from(candidates.values())
223
+ })
224
+
225
+ const panelOpen = ref(true)
226
+
227
+ const deleteConfirmOpen = ref(false)
228
+ const deleteConfirmMessage = ref('')
229
+ const deleteConfirmVirtualRef = ref<HTMLElement | null>(null)
230
+ const deleteConfirmTriggerBtn = ref<HTMLElement | null>(null)
231
+ const deleteTargetId = ref<string | null>(null)
232
+
233
+ const sectionBoundaryEl = computed(() => sectionRef.value?.rootRef ?? null)
234
+
235
+ function editorRoleLabel(role: ReportEditorRole) {
236
+ return role === 'initiator' ? '發起人' : '編輯者'
237
+ }
238
+
239
+ function requestRemoveEditor(editor: ReportEditor, event: MouseEvent) {
240
+ if (!canManageReportEditors.value) return
241
+ const btn = event.currentTarget as HTMLElement
242
+ deleteConfirmVirtualRef.value = (btn.closest('.report-editor-list__row') as HTMLElement) ?? btn
243
+ deleteConfirmTriggerBtn.value = btn
244
+ deleteTargetId.value = editor.id
245
+ deleteConfirmMessage.value = `確定刪除「${editor.name}(${editor.employeeId})」?`
246
+ deleteConfirmOpen.value = true
247
+ }
248
+
249
+ function confirmRemoveEditor() {
250
+ if (deleteTargetId.value) {
251
+ editors.value = editors.value.filter((item) => item.id !== deleteTargetId.value)
252
+ }
253
+ closeDeleteConfirm()
254
+ }
255
+
256
+ function closeDeleteConfirm() {
257
+ deleteConfirmOpen.value = false
258
+ deleteTargetId.value = null
259
+ }
260
+
261
+ function hasOpenOverlays() {
262
+ return addOpen.value || shareOpen.value || deleteConfirmOpen.value
263
+ }
264
+
265
+ function closeAllOverlays() {
266
+ addOpen.value = false
267
+ shareOpen.value = false
268
+ deleteConfirmOpen.value = false
269
+ deleteTargetId.value = null
270
+ }
271
+
272
+ function handleBeforeCollapse(done: (hadOpenOverlays: boolean) => void) {
273
+ const hadOpen = hasOpenOverlays()
274
+ if (hadOpen) {
275
+ popoverInstant.value = true
276
+ closeAllOverlays()
277
+ window.setTimeout(() => {
278
+ popoverInstant.value = false
279
+ }, 320)
280
+ }
281
+ done(hadOpen)
282
+ }
283
+
284
+ function getSectionContentWidth() {
285
+ const sectionEl = sectionRef.value?.rootRef
286
+ if (!sectionEl) return 240
287
+ const styles = getComputedStyle(sectionEl)
288
+ const paddingX = parseFloat(styles.paddingLeft) + parseFloat(styles.paddingRight)
289
+ return Math.round(sectionEl.clientWidth - paddingX)
290
+ }
291
+
292
+ function getArrowPopperStyle(triggerBtn: HTMLElement | null): Record<string, string> {
293
+ void arrowAlignKey.value
294
+ void panelOpen.value
295
+ const sectionEl = sectionRef.value?.rootRef
296
+ if (!triggerBtn || !sectionEl) return {}
297
+ const sectionRect = sectionEl.getBoundingClientRect()
298
+ const paddingLeft = parseFloat(getComputedStyle(sectionEl).paddingLeft)
299
+ const btnRect = triggerBtn.getBoundingClientRect()
300
+ const left = btnRect.left + btnRect.width / 2 - sectionRect.left - paddingLeft - 8
301
+ return { '--report-config-arrow-left': `${left}px` }
302
+ }
303
+
304
+ const addPopperStyle = computed(() => getArrowPopperStyle(addBtnRef.value))
305
+
306
+ const sharePopperStyle = computed(() => getArrowPopperStyle(shareBtnRef.value))
307
+
308
+ const popperOptions = computed(() => {
309
+ const boundary = sectionRef.value?.rootRef
310
+ return {
311
+ modifiers: [
312
+ {
313
+ name: 'preventOverflow',
314
+ options: {
315
+ boundary: boundary ?? 'viewport',
316
+ padding: 0,
317
+ altAxis: true,
318
+ },
319
+ },
320
+ {
321
+ name: 'flip',
322
+ enabled: false,
323
+ },
324
+ ],
325
+ }
326
+ })
327
+
328
+ function syncPopoverWidth() {
329
+ popoverWidthPx.value = getSectionContentWidth()
330
+ arrowAlignKey.value += 1
331
+ }
332
+
333
+ function toggleAddOpen() {
334
+ if (!canManageReportEditors.value) return
335
+ syncPopoverWidth()
336
+ shareOpen.value = false
337
+ addOpen.value = !addOpen.value
338
+ if (addOpen.value) {
339
+ void nextTick(() => {
340
+ arrowAlignKey.value += 1
341
+ })
342
+ }
343
+ }
344
+
345
+ function toggleShareOpen() {
346
+ if (!canManageReportEditors.value) return
347
+ syncPopoverWidth()
348
+ addOpen.value = false
349
+ shareOpen.value = !shareOpen.value
350
+ if (shareOpen.value) {
351
+ void nextTick(() => {
352
+ arrowAlignKey.value += 1
353
+ })
354
+ }
355
+ }
356
+
357
+ function confirmAdd() {
358
+ if (!canManageReportEditors.value) {
359
+ addOpen.value = false
360
+ return
361
+ }
362
+
363
+ const candidate = MOCK_UMS_EDITOR_USERS.find((user) => user.id === addSelectedUser.value)
364
+ if (!candidate) {
365
+ addOpen.value = false
366
+ return
367
+ }
368
+
369
+ const alreadyExists = editors.value.some((editor) => editor.employeeId === candidate.employeeId)
370
+ if (alreadyExists) {
371
+ ElMessage.warning('該用戶已是報告編輯者')
372
+ addOpen.value = false
373
+ addSelectedUser.value = ''
374
+ return
375
+ }
376
+
377
+ editors.value.push({
378
+ id: candidate.id,
379
+ name: candidate.name,
380
+ employeeId: candidate.employeeId,
381
+ department: candidate.department,
382
+ role: 'editor',
383
+ })
384
+ addSelectedUser.value = ''
385
+ addOpen.value = false
386
+ ElMessage.success('已添加編輯者')
387
+ }
388
+
389
+ function confirmTransfer() {
390
+ if (!canManageReportEditors.value) {
391
+ shareOpen.value = false
392
+ return
393
+ }
394
+
395
+ const targetId = transferSelectedUser.value
396
+ if (!targetId) {
397
+ shareOpen.value = false
398
+ return
399
+ }
400
+
401
+ const currentInitiator = editors.value.find((editor) => editor.role === 'initiator')
402
+ let target = editors.value.find((editor) => editor.id === targetId)
403
+
404
+ if (!target) {
405
+ const umsUser = MOCK_UMS_EDITOR_USERS.find((user) => user.id === targetId)
406
+ if (!umsUser) {
407
+ shareOpen.value = false
408
+ return
409
+ }
410
+ target = {
411
+ id: umsUser.id,
412
+ name: umsUser.name,
413
+ employeeId: umsUser.employeeId,
414
+ department: umsUser.department,
415
+ role: 'editor',
416
+ }
417
+ editors.value.push(target)
418
+ }
419
+
420
+ if (currentInitiator) {
421
+ currentInitiator.role = 'editor'
422
+ }
423
+ target.role = 'initiator'
424
+
425
+ transferSelectedUser.value = ''
426
+ shareOpen.value = false
427
+ ElMessage.success('已轉移報告發起人權限')
428
+ }
429
+
430
+ watch(panelOpen, (open) => {
431
+ if (!open) closeAllOverlays()
432
+ })
433
+
434
+ let sectionResizeObserver: ResizeObserver | null = null
435
+
436
+ onMounted(() => {
437
+ void nextTick(() => {
438
+ syncPopoverWidth()
439
+ const sectionEl = sectionRef.value?.rootRef
440
+ if (sectionEl) {
441
+ sectionResizeObserver = new ResizeObserver(() => syncPopoverWidth())
442
+ sectionResizeObserver.observe(sectionEl)
443
+ }
444
+ })
445
+ window.addEventListener('resize', syncPopoverWidth)
446
+ })
447
+
448
+ onUnmounted(() => {
449
+ window.removeEventListener('resize', syncPopoverWidth)
450
+ sectionResizeObserver?.disconnect()
451
+ })
452
+ </script>
453
+
454
+ <style scoped>
455
+ .report-editor-panel {
456
+ box-sizing: border-box;
457
+ }
458
+
459
+ .report-editor-list {
460
+ margin: 0;
461
+ padding: 0;
462
+ list-style: none;
463
+ max-height: calc(36px * 20);
464
+ overflow-x: hidden;
465
+ overflow-y: auto;
466
+ scrollbar-width: thin;
467
+ scrollbar-color: #999 transparent;
468
+ }
469
+
470
+ .report-editor-list::-webkit-scrollbar {
471
+ width: 3px !important;
472
+ height: 8px !important;
473
+ }
474
+
475
+ .report-editor-list::-webkit-scrollbar-track {
476
+ background: transparent !important;
477
+ }
478
+
479
+ .report-editor-list::-webkit-scrollbar-thumb {
480
+ width: 3px !important;
481
+ min-height: 8px !important;
482
+ border-radius: 4px;
483
+ background-color: #999 !important;
484
+ }
485
+
486
+ .report-editor-list::-webkit-scrollbar-corner {
487
+ background: transparent !important;
488
+ }
489
+
490
+ .report-editor-list__row {
491
+ display: grid;
492
+ grid-template-columns: minmax(0, 1fr) auto auto 24px;
493
+ align-items: center;
494
+ gap: 10px;
495
+ height: 36px;
496
+ padding: 0 10px;
497
+ border-radius: 4px;
498
+ font-size: 12px;
499
+ line-height: 1.5;
500
+ color: #303133;
501
+ background: #fff;
502
+ box-sizing: border-box;
503
+ }
504
+
505
+ .report-editor-list__name {
506
+ overflow: hidden;
507
+ text-overflow: ellipsis;
508
+ white-space: nowrap;
509
+ }
510
+
511
+ .report-editor-list__dept,
512
+ .report-editor-list__role {
513
+ white-space: nowrap;
514
+ color: #303133;
515
+ }
516
+
517
+ .report-editor-list__action {
518
+ display: flex;
519
+ justify-content: flex-end;
520
+ }
521
+
522
+ .report-source-transfer__title {
523
+ margin: 0 0 12px;
524
+ font-size: 14px;
525
+ font-weight: 700;
526
+ color: #303133;
527
+ }
528
+
529
+ .report-source-transfer__field {
530
+ display: grid;
531
+ gap: 6px;
532
+ }
533
+
534
+ .report-source-transfer__label {
535
+ font-size: 13px;
536
+ color: #909399;
537
+ }
538
+
539
+ .report-source-transfer__select {
540
+ width: 100%;
541
+ }
542
+
543
+ .report-source-transfer__select :deep(.el-select__wrapper) {
544
+ min-height: 32px;
545
+ }
546
+
547
+ .report-source-transfer__actions {
548
+ display: flex;
549
+ justify-content: flex-end;
550
+ gap: 8px;
551
+ margin-top: 12px;
552
+ }
553
+
554
+ .report-source-transfer__btn {
555
+ min-width: 72px;
556
+ padding: 6px 10px;
557
+ border: 1px solid #dcdfe6;
558
+ border-radius: 4px;
559
+ background: #fff;
560
+ font-size: 12px;
561
+ color: #606266;
562
+ cursor: pointer;
563
+ }
564
+
565
+ .report-source-transfer__btn--primary {
566
+ border-color: #c53355;
567
+ background: #c53355;
568
+ color: #fff;
569
+ }
570
+
571
+ .report-source-transfer__btn--primary:hover {
572
+ background: #fff;
573
+ color: #c53355;
574
+ }
575
+ </style>
576
+
577
+ <style>
578
+ /* 編輯者列表滾動條強制尺寸(避免 scoped 未生效) */
579
+ .report-editor-list::-webkit-scrollbar {
580
+ width: 3px !important;
581
+ height: 8px !important;
582
+ }
583
+
584
+ .report-editor-list::-webkit-scrollbar-track {
585
+ background: transparent !important;
586
+ }
587
+
588
+ .report-editor-list::-webkit-scrollbar-thumb {
589
+ width: 3px !important;
590
+ min-height: 8px !important;
591
+ border-radius: 4px;
592
+ background-color: #999 !important;
593
+ }
594
+
595
+ .report-editor-list::-webkit-scrollbar-corner {
596
+ background: transparent !important;
597
+ }
598
+
599
+ .report-icon-btn {
600
+ display: inline-flex;
601
+ align-items: center;
602
+ justify-content: center;
603
+ width: 24px;
604
+ height: 24px;
605
+ padding: 0;
606
+ border: 0;
607
+ background: none;
608
+ color: #4e5969;
609
+ cursor: pointer;
610
+ border-radius: 4px;
611
+ vertical-align: middle;
612
+ }
613
+
614
+ .report-icon-btn:hover {
615
+ color: #c53355;
616
+ background: #fff5f6;
617
+ }
618
+
619
+ .report-config-popover.el-popover.el-popper {
620
+ max-width: 100%;
621
+ padding: 12px;
622
+ box-sizing: border-box;
623
+ overflow: hidden;
624
+ }
625
+
626
+ .report-config-popover.el-popper[data-popper-placement^='bottom'] .el-popper__arrow {
627
+ left: var(--report-config-arrow-left) !important;
628
+ right: auto !important;
629
+ top: -6px !important;
630
+ transform: translateX(-50%) !important;
631
+ }
632
+
633
+ .report-config-popover.el-popper[data-popper-placement^='bottom'] .el-popper__arrow::before {
634
+ bottom: -1px;
635
+ }
636
+
637
+ .report-config-popover-fade-enter-active,
638
+ .report-config-popover-fade-leave-active {
639
+ transition: opacity 0.12s ease;
640
+ }
641
+
642
+ .report-config-popover-fade-enter-from,
643
+ .report-config-popover-fade-leave-to {
644
+ opacity: 0;
645
+ }
646
+
647
+ .report-config-popover-instant-enter-active,
648
+ .report-config-popover-instant-leave-active {
649
+ transition: none !important;
650
+ }
651
+
652
+ .report-config-popover-instant-enter-from,
653
+ .report-config-popover-instant-leave-to {
654
+ opacity: 0;
655
+ }
656
+ </style>