aegon-lv97 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 (78) hide show
  1. package/gen-ai/build-report/chartStreamRefs.ts +227 -0
  2. package/gen-ai/build-report/coherence-check/index.vue +559 -0
  3. package/gen-ai/build-report/components/ReportChapterWorkspace.vue +4571 -0
  4. package/gen-ai/build-report/components/ReportConfigPanel.vue +727 -0
  5. package/gen-ai/build-report/components/ReportGenerateSettingsPanel.vue +2361 -0
  6. package/gen-ai/build-report/components/ReportGeneratingPanel.vue +1765 -0
  7. package/gen-ai/build-report/components/ReportPromptPanel.vue +625 -0
  8. package/gen-ai/build-report/components/ReportSourceFileSummary.vue +665 -0
  9. package/gen-ai/build-report/components/ReportSourcesPanel.vue +1327 -0
  10. package/gen-ai/build-report/components/ReportTemplateDetail.vue +191 -0
  11. package/gen-ai/build-report/components/ReportTemplateEditorFormPanel.vue +667 -0
  12. package/gen-ai/build-report/components/ReportTemplatePicker.vue +302 -0
  13. package/gen-ai/build-report/components/ReportWorkflowSidebar.vue +148 -0
  14. package/gen-ai/build-report/components/paramsSetting.vue +886 -0
  15. package/gen-ai/build-report/components/reportEdit.vue +2283 -0
  16. package/gen-ai/build-report/index.vue +856 -0
  17. package/gen-ai/build-report/report-domain.ts +1133 -0
  18. package/gen-ai/build-report/report-template-data.ts +295 -0
  19. package/gen-ai/build-report/useReportWorkflow.ts +1386 -0
  20. package/gen-ai/common/assets/NotoSans-SC.woff2 +0 -0
  21. package/gen-ai/common/assets/NotoSans-TC.woff2 +0 -0
  22. package/gen-ai/common/assets/NotoSans.woff2 +0 -0
  23. package/gen-ai/common/assets/fonts.css +278 -0
  24. package/gen-ai/common/assets/pagination.css +163 -0
  25. package/gen-ai/common/directives/btnLoading.ts +455 -0
  26. package/gen-ai/common/directives/debounce.ts +98 -0
  27. package/gen-ai/common/directives/index.ts +30 -0
  28. package/gen-ai/common/en.md +417 -0
  29. package/gen-ai/common/index.ts +36 -0
  30. package/gen-ai/common/types/component-expose.ts +9 -0
  31. package/gen-ai/common/utils/api-res-status.ts +7 -0
  32. package/gen-ai/common/utils/navigation.ts +59 -0
  33. package/gen-ai/common/utils/remote-request.ts +45 -0
  34. package/gen-ai/common/utils/scroll.ts +577 -0
  35. package/gen-ai/components/AIExport/docx-export.ts +950 -0
  36. package/gen-ai/components/AIExport/docxExport.ts +2566 -0
  37. package/gen-ai/components/AIExport/index.ts +52 -0
  38. package/gen-ai/components/AIExport/parseMarkdownHtml.ts +653 -0
  39. package/gen-ai/components/CapsuleScrollbar.vue +149 -0
  40. package/gen-ai/components/ChapterTitleScroll.vue +468 -0
  41. package/gen-ai/components/EntryHeroNavCards.vue +379 -0
  42. package/gen-ai/components/GenAiPagination.vue +52 -0
  43. package/gen-ai/components/ModelCard.vue +1114 -0
  44. package/gen-ai/components/Preview/FilePreviewPage.vue +949 -0
  45. package/gen-ai/components/Preview/PreviewCapsuleScroll.vue +205 -0
  46. package/gen-ai/components/Preview/PreviewOutlineTree.vue +148 -0
  47. package/gen-ai/components/Preview/index.ts +11 -0
  48. package/gen-ai/components/Preview/preview.ts +724 -0
  49. package/gen-ai/components/PromptField.vue +505 -0
  50. package/gen-ai/components/RecentReportsTable.vue +974 -0
  51. package/gen-ai/components/ReportCollapsibleSection.vue +172 -0
  52. package/gen-ai/components/ReportHistoryList.vue +637 -0
  53. package/gen-ai/components/ReportSectionConfirmPopover.vue +251 -0
  54. package/gen-ai/components/editor/ChapterTiptapEditor.vue +3718 -0
  55. package/gen-ai/components/editor/StructureBlockView.vue +441 -0
  56. package/gen-ai/components/editor/TiptapToolbar.vue +1036 -0
  57. package/gen-ai/components/editor/TypewriterDocEditor.vue +422 -0
  58. package/gen-ai/components/editor/_StructureOrgNode.vue +326 -0
  59. package/gen-ai/components/editor/index.ts +28 -0
  60. package/gen-ai/components/editor/parseMarkdownHtml.ts +754 -0
  61. package/gen-ai/components/editor/reviewCitationTip.ts +163 -0
  62. package/gen-ai/gen-entry/api/index.ts +32 -0
  63. package/gen-ai/gen-entry/index.vue +287 -0
  64. package/gen-ai/gen-entry/types/index.ts +4 -0
  65. package/gen-ai/i.mark +2 -0
  66. package/gen-ai/management-center/index.vue +1815 -0
  67. package/gen-ai/management-center/mockData.ts +248 -0
  68. package/gen-ai/management-center/types/index.ts +37 -0
  69. package/gen-ai/model-manager/TemplateEditorFormPanel.vue +661 -0
  70. package/gen-ai/model-manager/api/index.ts +32 -0
  71. package/gen-ai/model-manager/api/mockApi.ts +33 -0
  72. package/gen-ai/model-manager/index.vue +1583 -0
  73. package/gen-ai/model-manager/mockData.ts +177 -0
  74. package/gen-ai/model-manager/template-detail-shared.ts +154 -0
  75. package/gen-ai/model-manager/template-editor.vue +345 -0
  76. package/gen-ai/model-manager/types/index.ts +40 -0
  77. package/gen-ai/model-manager/utils.ts +25 -0
  78. package/package.json +12 -0
@@ -0,0 +1,1815 @@
1
+ <template>
2
+ <div class="management-center-page">
3
+ <main class="management-center-page__body">
4
+ <section class="management-center-hero">
5
+ <h2 class="management-center-hero__title">管理中心</h2>
6
+ <p class="management-center-hero__desc gen-ai-page-hero-desc">
7
+ 角色1可以在此處對行業及視角進行管理
8
+ </p>
9
+ </section>
10
+
11
+ <div class="management-center-top">
12
+ <el-badge
13
+ :value="pendingList.length"
14
+ :max="99"
15
+ :hidden="pendingList.length === 0"
16
+ type="danger"
17
+ :badge-style="{ marginRight: '10px', marginTop: '-5px' }"
18
+ class="management-center-pending-badge"
19
+ >
20
+ <button
21
+ type="button"
22
+ class="mgmt-toolbar-btn gen-ai-action-btn mgmt-toolbar-btn--outline"
23
+ @click="openPendingDialog"
24
+ >
25
+ 待處理選項
26
+ </button>
27
+ </el-badge>
28
+ </div>
29
+
30
+ <section class="management-center-panel">
31
+ <el-tabs
32
+ v-model="activeTab"
33
+ type="card"
34
+ class="management-center-tabs"
35
+ @tab-change="handleTabChange"
36
+ >
37
+ <el-tab-pane v-for="tab in tabs" :key="tab.id" :label="tab.label" :name="tab.id" />
38
+ </el-tabs>
39
+
40
+ <div class="management-center-panel__card">
41
+ <template v-if="activeTab !== 'transfer'">
42
+ <div class="management-center-card__head">
43
+ <div class="management-center-card__head-left">
44
+ <button
45
+ type="button"
46
+ class="mgmt-toolbar-btn gen-ai-action-btn mgmt-toolbar-btn--primary"
47
+ @click="openAddDialog"
48
+ >
49
+ 新增
50
+ </button>
51
+ </div>
52
+ <h3 class="management-center-card__title gen-ai-page-hero-desc">{{ cardTitle }}</h3>
53
+
54
+ <div class="management-center-card__actions">
55
+ <label class="management-center-field">
56
+ <span class="gen-ai-filter-field-label">Search</span>
57
+ <el-input v-model="keyword" clearable class="management-center-field__input" />
58
+ </label>
59
+ <button
60
+ type="button"
61
+ class="mgmt-toolbar-btn gen-ai-action-btn mgmt-toolbar-btn--primary"
62
+ @click="handleRefresh"
63
+ >
64
+ Refresh
65
+ </button>
66
+ </div>
67
+ </div>
68
+
69
+ <el-table
70
+ :data="paginatedRows"
71
+ :fit="true"
72
+ table-layout="fixed"
73
+ class="management-center-table"
74
+ >
75
+ <el-table-column
76
+ label="名稱"
77
+ min-width="320"
78
+ header-align="left"
79
+ class-name="mgmt-col-name"
80
+ >
81
+ <template #default="{ row }">
82
+ <span class="management-center-table__name management-center-table__name--pill">{{
83
+ row.name
84
+ }}</span>
85
+ </template>
86
+ </el-table-column>
87
+ <el-table-column label="刪除" width="88" align="center" class-name="mgmt-col-delete">
88
+ <template #default="{ row }">
89
+ <button
90
+ type="button"
91
+ class="management-center-delete-btn"
92
+ aria-label="刪除"
93
+ @click="handleDelete(row)"
94
+ >
95
+ <el-icon>
96
+ <Delete />
97
+ </el-icon>
98
+ </button>
99
+ </template>
100
+ </el-table-column>
101
+ </el-table>
102
+
103
+ <GenAiPagination
104
+ v-model:current-page="currentPage"
105
+ v-model:page-size="pageSize"
106
+ :page-sizes="[5, 10, 20]"
107
+ :total="filteredRows.length"
108
+ />
109
+ </template>
110
+
111
+ <template v-else>
112
+ <div class="management-center-transfer-head">
113
+ <h3 class="management-center-transfer__title gen-ai-page-hero-desc">報告權限轉移</h3>
114
+ <div class="management-center-transfer-filters">
115
+ <label class="management-center-field">
116
+ <span class="gen-ai-filter-field-label">報告名稱</span>
117
+ <el-input
118
+ v-model="transferFilters.reportName"
119
+ clearable
120
+ :maxlength="TRANSFER_REPORT_NAME_MAX_LENGTH"
121
+ class="management-center-transfer-field__input management-center-transfer-field__input--report-name"
122
+ placeholder="香港房地產風控年報 2024"
123
+ />
124
+ </label>
125
+ <label class="management-center-field">
126
+ <span class="gen-ai-filter-field-label">所屬行業</span>
127
+ <el-select
128
+ v-model="transferFilters.industries"
129
+ multiple
130
+ clearable
131
+ collapse-tags
132
+ collapse-tags-tooltip
133
+ placeholder=""
134
+ class="management-center-transfer-field__select"
135
+ >
136
+ <el-option
137
+ v-for="item in industries"
138
+ :key="item.id"
139
+ :label="item.name"
140
+ :value="item.name"
141
+ />
142
+ </el-select>
143
+ </label>
144
+ <label class="management-center-field management-center-field--year">
145
+ <span class="gen-ai-filter-field-label">年份</span>
146
+ <el-date-picker
147
+ v-model="transferFilters.yearRange"
148
+ type="yearrange"
149
+ range-separator="-"
150
+ format="YYYY"
151
+ value-format="YYYY"
152
+ start-placeholder="2024"
153
+ end-placeholder="2026"
154
+ clearable
155
+ class="management-center-transfer-field__date"
156
+ />
157
+ </label>
158
+ <label class="management-center-field">
159
+ <span class="gen-ai-filter-field-label">發起人</span>
160
+ <el-select
161
+ v-model="transferFilters.initiators"
162
+ multiple
163
+ filterable
164
+ clearable
165
+ collapse-tags
166
+ collapse-tags-tooltip
167
+ placeholder=""
168
+ class="management-center-transfer-field__select"
169
+ >
170
+ <el-option
171
+ v-for="name in initiatorOptions"
172
+ :key="name"
173
+ :label="name"
174
+ :value="name"
175
+ />
176
+ </el-select>
177
+ </label>
178
+ <button
179
+ type="button"
180
+ class="mgmt-toolbar-btn gen-ai-action-btn mgmt-toolbar-btn--primary"
181
+ @click="handleTransferSearch"
182
+ >
183
+ 查詢
184
+ </button>
185
+ </div>
186
+ </div>
187
+
188
+ <el-table
189
+ :data="paginatedTransferReports"
190
+ :fit="true"
191
+ table-layout="fixed"
192
+ class="management-center-table management-center-transfer-table"
193
+ >
194
+ <el-table-column label="報告名稱" min-width="220" prop="name" />
195
+ <el-table-column label="行業" min-width="120" prop="industry" />
196
+ <el-table-column label="年份" width="88" align="center" prop="year" />
197
+ <el-table-column label="狀態" width="100" align="center" prop="status" />
198
+ <el-table-column label="發起人" min-width="140" prop="initiator" />
199
+ <el-table-column
200
+ label="編輯人"
201
+ min-width="220"
202
+ prop="editors"
203
+ show-overflow-tooltip
204
+ />
205
+ <el-table-column
206
+ width="140"
207
+ align="center"
208
+ header-align="center"
209
+ class-name="mgmt-col-transfer-action"
210
+ fixed="right"
211
+ >
212
+ <template #header>
213
+ <span style="margin-right: 0 !important" class="mgmt-transfer-action-label"
214
+ >操作</span
215
+ >
216
+ </template>
217
+ <template #default="{ row }">
218
+ <button
219
+ type="button"
220
+ class="mgmt-toolbar-btn gen-ai-action-btn mgmt-toolbar-btn--outline mgmt-transfer-btn"
221
+ @click="handleTransfer(row)"
222
+ >
223
+ 轉移
224
+ </button>
225
+ </template>
226
+ </el-table-column>
227
+ </el-table>
228
+
229
+ <GenAiPagination
230
+ v-model:current-page="transferReportPage"
231
+ v-model:page-size="transferReportPageSize"
232
+ :page-sizes="[5, 10, 20]"
233
+ :total="filteredTransferReports.length"
234
+ />
235
+
236
+ <div class="management-center-transfer__divider" />
237
+
238
+ <h3 class="management-center-transfer__title gen-ai-page-hero-desc">轉移記錄</h3>
239
+
240
+ <el-table
241
+ :data="paginatedTransferRecords"
242
+ :fit="true"
243
+ table-layout="fixed"
244
+ class="management-center-table management-center-transfer-table"
245
+ >
246
+ <el-table-column label="報告名稱" min-width="220" prop="reportName" />
247
+ <el-table-column label="原發起人" min-width="140" prop="originalInitiator" />
248
+ <el-table-column label="現發起人" min-width="140" prop="currentInitiator" />
249
+ <el-table-column label="權限操作人員" min-width="140" prop="operator" />
250
+ <el-table-column label="操作時間" min-width="180" prop="operatedAt" />
251
+ </el-table>
252
+
253
+ <GenAiPagination
254
+ v-model:current-page="transferHistoryPage"
255
+ v-model:page-size="transferHistoryPageSize"
256
+ :page-sizes="[5, 10, 20]"
257
+ :total="transferRecords.length"
258
+ />
259
+ </template>
260
+ </div>
261
+ </section>
262
+ </main>
263
+
264
+ <!-- 新增彈窗 -->
265
+ <el-dialog
266
+ v-model="addDialogVisible"
267
+ width="840px"
268
+ append-to-body
269
+ :show-close="false"
270
+ class="mgmt-dialog mgmt-dialog--add"
271
+ header-class="mgmt-dialog__header"
272
+ >
273
+ <template #header="{ close }">
274
+ <span class="mgmt-dialog__title">{{ addDialogTitle }}</span>
275
+ <button type="button" class="mgmt-dialog__close" aria-label="關閉" @click="close">×</button>
276
+ </template>
277
+ <div class="mgmt-add-form">
278
+ <el-input
279
+ v-model="addInput"
280
+ type="textarea"
281
+ :rows="10"
282
+ :maxlength="NAME_MAX_LENGTH"
283
+ show-word-limit
284
+ resize="none"
285
+ placeholder=""
286
+ class="mgmt-add-form__input"
287
+ />
288
+ </div>
289
+ <template #footer>
290
+ <div class="mgmt-dialog__footer">
291
+ <button
292
+ type="button"
293
+ class="mgmt-toolbar-btn gen-ai-action-btn mgmt-toolbar-btn--ghost"
294
+ @click="addDialogVisible = false"
295
+ >
296
+ 取消
297
+ </button>
298
+ <button
299
+ type="button"
300
+ class="mgmt-toolbar-btn gen-ai-action-btn mgmt-toolbar-btn--primary"
301
+ @click="confirmAdd"
302
+ >
303
+ 確認
304
+ </button>
305
+ </div>
306
+ </template>
307
+ </el-dialog>
308
+
309
+ <!-- 轉移報告發起人權限彈窗 -->
310
+ <el-dialog
311
+ v-model="transferDialogVisible"
312
+ width="840px"
313
+ append-to-body
314
+ :show-close="false"
315
+ class="mgmt-dialog mgmt-dialog--add mgmt-dialog--transfer"
316
+ header-class="mgmt-dialog__header"
317
+ >
318
+ <template #header="{ close }">
319
+ <span class="mgmt-dialog__title">轉移報告發起人權限</span>
320
+ <button type="button" class="mgmt-dialog__close" aria-label="關閉" @click="close">×</button>
321
+ </template>
322
+ <div class="mgmt-transfer-form">
323
+ <label class="mgmt-transfer-form__field">
324
+ <span class="mgmt-transfer-form__label">用戶</span>
325
+ <el-select
326
+ v-model="transferSelectedUser"
327
+ filterable
328
+ clearable
329
+ placeholder=""
330
+ class="mgmt-transfer-form__select"
331
+ >
332
+ <el-option
333
+ v-for="name in transferUserOptions"
334
+ :key="name"
335
+ :label="name"
336
+ :value="name"
337
+ />
338
+ </el-select>
339
+ </label>
340
+ </div>
341
+ <template #footer>
342
+ <div class="mgmt-dialog__footer">
343
+ <button
344
+ type="button"
345
+ class="mgmt-toolbar-btn gen-ai-action-btn mgmt-toolbar-btn--ghost"
346
+ @click="transferDialogVisible = false"
347
+ >
348
+ 取消
349
+ </button>
350
+ <button
351
+ type="button"
352
+ class="mgmt-toolbar-btn gen-ai-action-btn mgmt-toolbar-btn--primary"
353
+ @click="confirmTransfer"
354
+ >
355
+ 提交
356
+ </button>
357
+ </div>
358
+ </template>
359
+ </el-dialog>
360
+
361
+ <!-- 待處理選項彈窗 -->
362
+ <el-dialog
363
+ v-model="pendingDialogVisible"
364
+ width="60%"
365
+ append-to-body
366
+ :z-index="10000"
367
+ :show-close="false"
368
+ modal-class="mgmt-pending-overlay"
369
+ class="mgmt-dialog mgmt-dialog--pending"
370
+ header-class="mgmt-dialog__header"
371
+ >
372
+ <template #header="{ close }">
373
+ <span class="mgmt-dialog__title">待處理選項</span>
374
+ <button type="button" class="mgmt-dialog__close" aria-label="關閉" @click="close">×</button>
375
+ </template>
376
+
377
+ <el-table
378
+ :data="pendingList"
379
+ :fit="true"
380
+ table-layout="fixed"
381
+ class="management-center-table management-center-pending-table"
382
+ >
383
+ <el-table-column label="變更" width="88" header-align="left" align="left">
384
+ <template #default="{ row }">
385
+ {{ row.changeType }}
386
+ </template>
387
+ </el-table-column>
388
+ <el-table-column label="行業" min-width="160" header-align="left" align="left">
389
+ <template #default="{ row }">
390
+ <span
391
+ v-if="row.category === 'industry'"
392
+ class="management-center-table__name management-center-table__name--pill"
393
+ >{{ row.name }}</span
394
+ >
395
+ </template>
396
+ </el-table-column>
397
+ <el-table-column label="調研主要出發點" min-width="160" header-align="left" align="left">
398
+ <template #default="{ row }">
399
+ <span
400
+ v-if="row.category === 'research'"
401
+ class="management-center-table__name management-center-table__name--pill"
402
+ >{{ row.name }}</span
403
+ >
404
+ </template>
405
+ </el-table-column>
406
+ <el-table-column label="申請人" width="112" header-align="left" align="left">
407
+ <template #default="{ row }">
408
+ {{ row.applicant }}
409
+ </template>
410
+ </el-table-column>
411
+ <el-table-column
412
+ label="操作"
413
+ width="120"
414
+ align="center"
415
+ header-align="center"
416
+ class-name="mgmt-col-action"
417
+ >
418
+ <template #default="{ row }">
419
+ <div class="mgmt-pending-actions">
420
+ <button
421
+ type="button"
422
+ class="mgmt-pending-action"
423
+ :class="{ 'is-active': pendingDecisions[row.id] === 'approve' }"
424
+ aria-label="批准"
425
+ @click="togglePendingDecision(row.id, 'approve')"
426
+ >
427
+ ✓
428
+ </button>
429
+ <button
430
+ type="button"
431
+ class="mgmt-pending-action mgmt-pending-action--reject"
432
+ :class="{ 'is-active': pendingDecisions[row.id] === 'reject' }"
433
+ aria-label="拒絕"
434
+ @click="togglePendingDecision(row.id, 'reject')"
435
+ >
436
+ ✕
437
+ </button>
438
+ </div>
439
+ </template>
440
+ </el-table-column>
441
+ </el-table>
442
+
443
+ <template #footer>
444
+ <div class="mgmt-dialog__footer">
445
+ <button
446
+ type="button"
447
+ class="mgmt-toolbar-btn gen-ai-action-btn mgmt-toolbar-btn--ghost"
448
+ @click="pendingDialogVisible = false"
449
+ >
450
+ 取消
451
+ </button>
452
+ <button
453
+ type="button"
454
+ class="mgmt-toolbar-btn gen-ai-action-btn mgmt-toolbar-btn--primary"
455
+ @click="confirmPending"
456
+ >
457
+ 確認
458
+ </button>
459
+ </div>
460
+ </template>
461
+ </el-dialog>
462
+ </div>
463
+ </template>
464
+
465
+ <script setup lang="ts">
466
+ import '../common/assets/fonts.css'
467
+ import { Delete } from '@element-plus/icons-vue'
468
+ import { ElMessage, ElMessageBox } from 'element-plus'
469
+ import { computed, ref, watch } from 'vue'
470
+ import { GenAiPagination } from '../common'
471
+ import {
472
+ INDUSTRY_OPTIONS,
473
+ PENDING_OPTIONS,
474
+ RESEARCH_FOCUS_OPTIONS,
475
+ TRANSFER_RECORDS,
476
+ TRANSFER_REPORTS,
477
+ } from './mockData'
478
+ import type {
479
+ ManagementOption,
480
+ ManagementTab,
481
+ PendingDecision,
482
+ PendingOption,
483
+ TransferRecord,
484
+ TransferReport,
485
+ } from './types'
486
+
487
+ const NAME_MAX_LENGTH = 50
488
+ const TRANSFER_REPORT_NAME_MAX_LENGTH = 100
489
+
490
+ const tabs = [
491
+ { id: 'industry' as ManagementTab, label: '行業' },
492
+ { id: 'research' as ManagementTab, label: '調研主要出發點' },
493
+ { id: 'transfer' as ManagementTab, label: '報告權限轉移' },
494
+ ]
495
+
496
+ const activeTab = ref('industry' as ManagementTab)
497
+ const industries = ref([...INDUSTRY_OPTIONS])
498
+ const researchPoints = ref([...RESEARCH_FOCUS_OPTIONS])
499
+ const pendingList = ref([...PENDING_OPTIONS])
500
+
501
+ const keyword = ref('')
502
+ const currentPage = ref(1)
503
+ const pageSize = ref(10)
504
+
505
+ const addDialogVisible = ref(false)
506
+ const addInput = ref('')
507
+
508
+ const pendingDialogVisible = ref(false)
509
+ const pendingDecisions = ref({} as Record<string, PendingDecision>)
510
+
511
+ const transferReports = ref<TransferReport[]>([...TRANSFER_REPORTS])
512
+ const transferRecords = ref<TransferRecord[]>([...TRANSFER_RECORDS])
513
+ const transferFilters = ref({
514
+ reportName: '',
515
+ industries: [] as string[],
516
+ yearRange: null as [string, string] | null,
517
+ initiators: [] as string[],
518
+ })
519
+ const appliedTransferFilters = ref({
520
+ reportName: '',
521
+ industries: [] as string[],
522
+ yearRange: null as [string, string] | null,
523
+ initiators: [] as string[],
524
+ })
525
+ const transferReportPage = ref(1)
526
+ const transferReportPageSize = ref(10)
527
+ const transferHistoryPage = ref(1)
528
+ const transferHistoryPageSize = ref(10)
529
+
530
+ const transferDialogVisible = ref(false)
531
+ const transferTargetReport = ref<TransferReport | null>(null)
532
+ const transferSelectedUser = ref('')
533
+
534
+ const initiatorOptions = computed(() => {
535
+ const names = new Set<string>()
536
+ transferReports.value.forEach((row) => names.add(row.initiator))
537
+ transferRecords.value.forEach((row) => {
538
+ names.add(row.originalInitiator)
539
+ names.add(row.currentInitiator)
540
+ names.add(row.operator)
541
+ })
542
+ return Array.from(names).sort((a, b) => a.localeCompare(b, 'zh-Hant'))
543
+ })
544
+
545
+ const transferUserOptions = computed(() => {
546
+ const currentInitiator = transferTargetReport.value?.initiator
547
+ return initiatorOptions.value.filter((name) => name !== currentInitiator)
548
+ })
549
+
550
+ const cardTitle = computed(() =>
551
+ activeTab.value === 'industry' ? '管理行業選項' : '管理調研主要出發點'
552
+ )
553
+
554
+ const addDialogTitle = computed(() =>
555
+ activeTab.value === 'industry' ? '新增行業選項' : '新增調研主要出發點'
556
+ )
557
+
558
+ const currentList = computed(() =>
559
+ activeTab.value === 'industry' ? industries.value : researchPoints.value
560
+ )
561
+
562
+ const filteredRows = computed(() => {
563
+ const kw = keyword.value.trim().toLowerCase()
564
+ if (!kw) return currentList.value
565
+ return currentList.value.filter((item) => item.name.toLowerCase().includes(kw))
566
+ })
567
+
568
+ const paginatedRows = computed(() => {
569
+ const start = (currentPage.value - 1) * pageSize.value
570
+ return filteredRows.value.slice(start, start + pageSize.value)
571
+ })
572
+
573
+ function matchesTransferFilter(value: string, filter: string) {
574
+ const kw = filter.trim().toLowerCase()
575
+ if (!kw) return true
576
+ return value.toLowerCase().includes(kw)
577
+ }
578
+
579
+ function matchesIndustriesFilter(rowIndustry: string, selected: string[]) {
580
+ if (selected.length === 0) return true
581
+ return selected.includes(rowIndustry)
582
+ }
583
+
584
+ function matchesYearRangeFilter(rowYear: string, range: [string, string] | null) {
585
+ if (!range?.[0] || !range[1]) return true
586
+ const year = Number(rowYear)
587
+ const start = Number(range[0])
588
+ const end = Number(range[1])
589
+ if (Number.isNaN(year) || Number.isNaN(start) || Number.isNaN(end)) return true
590
+ return year >= start && year <= end
591
+ }
592
+
593
+ function matchesInitiatorsFilter(rowInitiator: string, selected: string[]) {
594
+ if (selected.length === 0) return true
595
+ return selected.includes(rowInitiator)
596
+ }
597
+
598
+ const filteredTransferReports = computed(() => {
599
+ const filters = appliedTransferFilters.value
600
+ return transferReports.value.filter(
601
+ (row) =>
602
+ matchesTransferFilter(row.name, filters.reportName) &&
603
+ matchesIndustriesFilter(row.industry, filters.industries) &&
604
+ matchesYearRangeFilter(row.year, filters.yearRange) &&
605
+ matchesInitiatorsFilter(row.initiator, filters.initiators)
606
+ )
607
+ })
608
+
609
+ const paginatedTransferReports = computed(() => {
610
+ const start = (transferReportPage.value - 1) * transferReportPageSize.value
611
+ return filteredTransferReports.value.slice(start, start + transferReportPageSize.value)
612
+ })
613
+
614
+ const paginatedTransferRecords = computed(() => {
615
+ const start = (transferHistoryPage.value - 1) * transferHistoryPageSize.value
616
+ return transferRecords.value.slice(start, start + transferHistoryPageSize.value)
617
+ })
618
+
619
+ function resetTransferFilters() {
620
+ const empty = {
621
+ reportName: '',
622
+ industries: [] as string[],
623
+ yearRange: null as [string, string] | null,
624
+ initiators: [] as string[],
625
+ }
626
+ transferFilters.value = { ...empty, industries: [], initiators: [] }
627
+ appliedTransferFilters.value = { ...empty, industries: [], initiators: [] }
628
+ transferReportPage.value = 1
629
+ transferHistoryPage.value = 1
630
+ }
631
+
632
+ function handleTabChange() {
633
+ currentPage.value = 1
634
+ keyword.value = ''
635
+ if (activeTab.value === 'transfer') {
636
+ resetTransferFilters()
637
+ }
638
+ }
639
+
640
+ function handleTransferSearch() {
641
+ appliedTransferFilters.value = {
642
+ ...transferFilters.value,
643
+ industries: [...transferFilters.value.industries],
644
+ initiators: [...transferFilters.value.initiators],
645
+ yearRange: transferFilters.value.yearRange
646
+ ? ([...transferFilters.value.yearRange] as [string, string])
647
+ : null,
648
+ }
649
+ transferReportPage.value = 1
650
+ }
651
+
652
+ function formatOperateTime(date: Date) {
653
+ const pad = (value: number) => String(value).padStart(2, '0')
654
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
655
+ }
656
+
657
+ function handleTransfer(row: TransferReport) {
658
+ transferTargetReport.value = row
659
+ transferSelectedUser.value = ''
660
+ transferDialogVisible.value = true
661
+ }
662
+
663
+ function confirmTransfer() {
664
+ const report = transferTargetReport.value
665
+ const user = transferSelectedUser.value.trim()
666
+ if (!report) return
667
+ if (!user) {
668
+ ElMessage.warning('請選擇用戶')
669
+ return
670
+ }
671
+
672
+ const originalInitiator = report.initiator
673
+ transferReports.value = transferReports.value.map((item) =>
674
+ item.id === report.id ? { ...item, initiator: user } : item
675
+ )
676
+
677
+ transferRecords.value = [
678
+ {
679
+ id: `rec-${Date.now()}`,
680
+ reportName: report.name,
681
+ originalInitiator,
682
+ currentInitiator: user,
683
+ operator: '陳小文(單位)',
684
+ operatedAt: formatOperateTime(new Date()),
685
+ },
686
+ ...transferRecords.value,
687
+ ]
688
+
689
+ transferDialogVisible.value = false
690
+ transferTargetReport.value = null
691
+ transferSelectedUser.value = ''
692
+ ElMessage.success('權限轉移成功')
693
+ }
694
+
695
+ function handleRefresh() {
696
+ keyword.value = ''
697
+ currentPage.value = 1
698
+ ElMessage.success('已重新整理')
699
+ }
700
+
701
+ function openAddDialog() {
702
+ addInput.value = ''
703
+ addDialogVisible.value = true
704
+ }
705
+
706
+ function isDuplicateName(name: string) {
707
+ const normalized = name.trim()
708
+ return currentList.value.some((item) => item.name === normalized)
709
+ }
710
+
711
+ function confirmAdd() {
712
+ const name = addInput.value.trim()
713
+ if (!name) {
714
+ ElMessage.warning('請輸入名稱')
715
+ return
716
+ }
717
+ if (name.length > NAME_MAX_LENGTH) {
718
+ ElMessage.warning(`名稱不可超過 ${NAME_MAX_LENGTH} 字`)
719
+ return
720
+ }
721
+ if (isDuplicateName(name)) {
722
+ ElMessage.warning('名稱已存在,請勿重複新增')
723
+ return
724
+ }
725
+
726
+ const item: ManagementOption = {
727
+ id: `${activeTab.value}-${Date.now()}`,
728
+ name,
729
+ }
730
+
731
+ if (activeTab.value === 'industry') {
732
+ industries.value = [...industries.value, item]
733
+ } else {
734
+ researchPoints.value = [...researchPoints.value, item]
735
+ }
736
+
737
+ addDialogVisible.value = false
738
+ ElMessage.success('新增成功')
739
+ }
740
+
741
+ async function handleDelete(row: ManagementOption) {
742
+ try {
743
+ await ElMessageBox.confirm(`確定刪除「${row.name}」?`, '確認刪除', {
744
+ confirmButtonText: '確認',
745
+ cancelButtonText: '取消',
746
+ type: 'warning',
747
+ })
748
+ } catch {
749
+ return
750
+ }
751
+
752
+ if (activeTab.value === 'industry') {
753
+ industries.value = industries.value.filter((item) => item.id !== row.id)
754
+ } else {
755
+ researchPoints.value = researchPoints.value.filter((item) => item.id !== row.id)
756
+ }
757
+ ElMessage.success('已刪除')
758
+ }
759
+
760
+ function openPendingDialog() {
761
+ pendingDecisions.value = {}
762
+ pendingList.value.forEach((item) => {
763
+ pendingDecisions.value[item.id] = null
764
+ })
765
+ pendingDialogVisible.value = true
766
+ }
767
+
768
+ function togglePendingDecision(id: string, decision: Exclude<PendingDecision, null>) {
769
+ const current = pendingDecisions.value[id]
770
+ pendingDecisions.value[id] = current === decision ? null : decision
771
+ }
772
+
773
+ function addOptionFromPending(item: PendingOption) {
774
+ const target = item.category === 'industry' ? industries.value : researchPoints.value
775
+ if (target.some((row) => row.name === item.name)) return
776
+
777
+ const entry: ManagementOption = {
778
+ id: `${item.category}-approved-${Date.now()}-${item.id}`,
779
+ name: item.name,
780
+ }
781
+
782
+ if (item.category === 'industry') {
783
+ industries.value = [...industries.value, entry]
784
+ } else {
785
+ researchPoints.value = [...researchPoints.value, entry]
786
+ }
787
+ }
788
+
789
+ function confirmPending() {
790
+ const remaining: PendingOption[] = []
791
+
792
+ pendingList.value.forEach((item) => {
793
+ const decision = pendingDecisions.value[item.id]
794
+ if (decision === 'approve') {
795
+ addOptionFromPending(item)
796
+ return
797
+ }
798
+ if (decision === 'reject') {
799
+ return
800
+ }
801
+ remaining.push(item)
802
+ })
803
+
804
+ pendingList.value = remaining
805
+ pendingDialogVisible.value = false
806
+ ElMessage.success('已更新待處理選項')
807
+ }
808
+
809
+ watch([keyword, activeTab], () => {
810
+ currentPage.value = 1
811
+ })
812
+
813
+ watch(filteredRows, (list) => {
814
+ const maxPage = Math.max(1, Math.ceil(list.length / pageSize.value))
815
+ if (currentPage.value > maxPage) currentPage.value = maxPage
816
+ })
817
+
818
+ watch(filteredTransferReports, (list) => {
819
+ const maxPage = Math.max(1, Math.ceil(list.length / transferReportPageSize.value))
820
+ if (transferReportPage.value > maxPage) transferReportPage.value = maxPage
821
+ })
822
+
823
+ watch(transferRecords, (list) => {
824
+ const maxPage = Math.max(1, Math.ceil(list.length / transferHistoryPageSize.value))
825
+ if (transferHistoryPage.value > maxPage) transferHistoryPage.value = maxPage
826
+ })
827
+ </script>
828
+
829
+ <style scoped>
830
+ .management-center-page {
831
+ --gen-ai-design-w: 1920;
832
+ --gen-ai-fs-xs: clamp(11px, calc(12 * 100vw / 1920), 12px);
833
+ --gen-ai-fs-sm: clamp(12px, calc(13 * 100vw / 1920), 13px);
834
+ --gen-ai-fs-base: clamp(13px, calc(14 * 100vw / 1920), 14px);
835
+ --gen-ai-fs-md: clamp(14px, calc(16 * 100vw / 1920), 16px);
836
+ --gen-ai-fs-lg: clamp(16px, calc(18 * 100vw / 1920), 18px);
837
+ --gen-ai-fs-2xl: clamp(20px, calc(24 * 100vw / 1920), 24px);
838
+ --gen-ai-fs-hero: clamp(26px, calc(32 * 100vw / 1920), 32px);
839
+ --gen-ai-lh-tight: 1.4;
840
+ --gen-ai-lh-normal: 1.6;
841
+ --gen-ai-fw-regular: 400;
842
+ --gen-ai-fw-semibold: 600;
843
+ --gen-ai-fw-bold: 700;
844
+ --gen-ai-page-body-h: 100vh;
845
+ --gen-ai-table-row-h: clamp(48px, calc(55.2 * 100vw / 1920), 55.2px);
846
+ --gen-ai-table-header-bg: rgb(249, 250, 251);
847
+ --gen-ai-management-tab-font-size: 15px;
848
+ --gen-ai-management-tab-font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
849
+ --gen-ai-management-tab-font-weight: 500;
850
+ --mgmt-accent: #c53355;
851
+ --mgmt-accent-soft: #fdf0f2;
852
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
853
+ font-size: var(--gen-ai-fs-base);
854
+ line-height: var(--gen-ai-lh-normal);
855
+ color: #2d3139;
856
+ display: flex;
857
+ flex-direction: column;
858
+ min-height: 100vh;
859
+ height: auto;
860
+ background: #fff;
861
+ }
862
+
863
+ .management-center-page :deep(.el-table),
864
+ .management-center-page :deep(.el-input),
865
+ .management-center-page :deep(.el-select),
866
+ .management-center-page :deep(.el-pagination) {
867
+ font-size: inherit;
868
+ }
869
+
870
+ .management-center-page :deep(.el-button) {
871
+ font-size: 18px;
872
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
873
+ }
874
+
875
+ .gen-ai-page-hero-desc {
876
+ font-size: 16px;
877
+ font-weight: 600;
878
+ line-height: 1.7;
879
+ color: #666;
880
+ }
881
+
882
+ .gen-ai-filter-field-label {
883
+ font-size: 14px;
884
+ color: #606266;
885
+ font-weight: 500;
886
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
887
+ white-space: nowrap;
888
+ }
889
+
890
+ .gen-ai-action-btn,
891
+ .mgmt-toolbar-btn {
892
+ font-size: 15px;
893
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
894
+ font-weight: 400;
895
+ }
896
+
897
+ .management-center-page__body {
898
+ flex: 1 0 auto;
899
+ display: flex;
900
+ flex-direction: column;
901
+ min-height: var(--gen-ai-page-body-h);
902
+ width: 100%;
903
+ padding: 32px 24px 40px;
904
+ box-sizing: border-box;
905
+ }
906
+
907
+ .management-center-panel {
908
+ display: flex;
909
+ flex-direction: column;
910
+ flex: 0 0 auto;
911
+ }
912
+
913
+ .management-center-hero {
914
+ text-align: center;
915
+ margin-bottom: 12px;
916
+ }
917
+
918
+ .management-center-hero__title {
919
+ margin: 0 0 2px;
920
+ font-size: var(--gen-ai-fs-hero);
921
+ font-weight: var(--gen-ai-fw-bold);
922
+ color: #1a1a1a;
923
+ }
924
+
925
+ .management-center-hero__desc {
926
+ margin: 0;
927
+ }
928
+
929
+ .management-center-top {
930
+ display: flex;
931
+ align-items: center;
932
+ margin-bottom: 16px;
933
+ }
934
+
935
+ .management-center-pending-badge :deep(.mgmt-toolbar-btn--outline:hover),
936
+ .management-center-pending-badge:hover :deep(.mgmt-toolbar-btn--outline) {
937
+ border-color: var(--mgmt-accent, #c53355);
938
+ color: var(--mgmt-accent, #c53355);
939
+ background: var(--mgmt-accent-soft, #fdf0f2);
940
+ }
941
+
942
+ .management-center-card {
943
+ border: none;
944
+ padding: 0;
945
+ background: transparent;
946
+ }
947
+
948
+ .management-center-tabs {
949
+ flex-shrink: 0;
950
+ position: relative;
951
+ z-index: 2;
952
+ }
953
+
954
+ .management-center-tabs :deep(.el-tabs__header) {
955
+ margin: 0;
956
+ }
957
+
958
+ .management-center-tabs :deep(.el-tabs__content) {
959
+ display: none;
960
+ }
961
+
962
+ .management-center-tabs.el-tabs--card :deep(.el-tabs__header .el-tabs__item) {
963
+ width: 180px;
964
+ min-width: 180px;
965
+ padding: 0 16px;
966
+ font-size: var(--gen-ai-management-tab-font-size);
967
+ font-family: var(--gen-ai-management-tab-font-family);
968
+ font-weight: var(--gen-ai-management-tab-font-weight);
969
+ text-align: center;
970
+ box-sizing: border-box;
971
+ transition:
972
+ color 0.2s ease,
973
+ background-color 0.2s ease;
974
+ }
975
+
976
+ .management-center-tabs.el-tabs--card :deep(.el-tabs__header .el-tabs__item:hover:not(.is-active)) {
977
+ color: var(--mgmt-accent, #c53355);
978
+ background-color: transparent;
979
+ background-image: linear-gradient(
980
+ var(--mgmt-accent-soft, #fdf0f2),
981
+ var(--mgmt-accent-soft, #fdf0f2)
982
+ );
983
+ background-size: 100% calc(100% - 1px);
984
+ background-position: 0 1px;
985
+ background-repeat: no-repeat;
986
+ border-radius: 4px;
987
+ border-color: var(--el-border-color-light, #e4e7ed);
988
+ }
989
+
990
+ .management-center-tabs.el-tabs--card :deep(.el-tabs__header .el-tabs__item.is-active) {
991
+ color: var(--mgmt-accent, #c53355);
992
+ }
993
+
994
+ .management-center-panel__card {
995
+ position: relative;
996
+ z-index: 1;
997
+ flex: 0 0 auto;
998
+ display: flex;
999
+ flex-direction: column;
1000
+ margin-top: -1px;
1001
+ border: 1px solid #dcdfe6;
1002
+ border-radius: 0 4px 4px 4px;
1003
+ background: #fff;
1004
+ padding: 16px 20px 20px;
1005
+ box-sizing: border-box;
1006
+ }
1007
+
1008
+ .management-center-card__head {
1009
+ display: flex;
1010
+ align-items: center;
1011
+ justify-content: space-between;
1012
+ position: relative;
1013
+ min-height: 36px;
1014
+ margin-bottom: 16px;
1015
+ }
1016
+
1017
+ .management-center-card__head-left {
1018
+ flex-shrink: 0;
1019
+ margin-right: 5px;
1020
+ z-index: 1;
1021
+ }
1022
+
1023
+ .management-center-card__head-left .mgmt-toolbar-btn {
1024
+ width: 100px !important;
1025
+ min-width: 100px !important;
1026
+ }
1027
+
1028
+ .management-center-card__title {
1029
+ position: absolute;
1030
+ left: 50%;
1031
+ top: 50%;
1032
+ transform: translate(-50%, -50%);
1033
+ margin: 0;
1034
+ text-align: center;
1035
+ white-space: nowrap;
1036
+ pointer-events: none;
1037
+ }
1038
+
1039
+ .management-center-card__actions {
1040
+ display: flex;
1041
+ align-items: center;
1042
+ justify-content: flex-end;
1043
+ gap: 12px;
1044
+ flex-wrap: wrap;
1045
+ flex-shrink: 0;
1046
+ margin-left: auto;
1047
+ z-index: 1;
1048
+ }
1049
+
1050
+ .mgmt-toolbar-btn {
1051
+ display: inline-flex;
1052
+ align-items: center;
1053
+ justify-content: center;
1054
+ box-sizing: border-box;
1055
+ height: 32px;
1056
+ min-width: 120px;
1057
+ padding: 0 14px;
1058
+ border-radius: 4px;
1059
+ line-height: 1;
1060
+ white-space: nowrap;
1061
+ cursor: pointer;
1062
+ transition: all 0.2s ease;
1063
+ }
1064
+
1065
+ .mgmt-toolbar-btn--primary {
1066
+ border: 1px solid var(--app-btn-border, var(--mgmt-accent, #c53355));
1067
+ background: var(--app-btn-bg, var(--mgmt-accent, #c53355));
1068
+ color: var(--app-btn-color, #fff);
1069
+ }
1070
+
1071
+ .mgmt-toolbar-btn--primary:hover {
1072
+ background: var(--app-btn-bg-hover, #fff);
1073
+ border-color: var(--app-btn-border-hover, var(--mgmt-accent, #c53355));
1074
+ color: var(--app-btn-color-hover, var(--mgmt-accent, #c53355));
1075
+ }
1076
+
1077
+ .mgmt-toolbar-btn--outline {
1078
+ border: 1px solid var(--mgmt-accent, #c53355);
1079
+ color: var(--mgmt-accent, #c53355);
1080
+ background: #fff;
1081
+ }
1082
+
1083
+ .mgmt-toolbar-btn--outline:hover {
1084
+ border-color: var(--mgmt-accent, #c53355);
1085
+ color: var(--mgmt-accent, #c53355);
1086
+ background: var(--mgmt-accent-soft, #fdf0f2);
1087
+ }
1088
+
1089
+ .mgmt-toolbar-btn--ghost {
1090
+ border: 1px solid #dcdfe6;
1091
+ background: #fff;
1092
+ color: #606266;
1093
+ }
1094
+
1095
+ .mgmt-toolbar-btn--ghost:hover {
1096
+ border-color: var(--app-btn-border, #c53355);
1097
+ color: var(--app-btn-border, #c53355);
1098
+ background: #fdf0f2;
1099
+ }
1100
+
1101
+ .management-center-field {
1102
+ display: inline-flex;
1103
+ align-items: center;
1104
+ gap: 8px;
1105
+ margin: 0;
1106
+ }
1107
+
1108
+ .management-center-field__input {
1109
+ width: 270px;
1110
+ }
1111
+
1112
+ .management-center-transfer__title {
1113
+ margin: 0 0 16px;
1114
+ text-align: center;
1115
+ }
1116
+
1117
+ .management-center-transfer-head {
1118
+ margin-bottom: 16px;
1119
+ }
1120
+
1121
+ .management-center-transfer-head .management-center-transfer-filters {
1122
+ display: flex;
1123
+ align-items: center;
1124
+ justify-content: flex-end;
1125
+ gap: 12px;
1126
+ flex-wrap: wrap;
1127
+ }
1128
+
1129
+ .management-center-transfer-filters .gen-ai-filter-field-label {
1130
+ text-align: right;
1131
+ }
1132
+
1133
+ .management-center-transfer-filters .mgmt-toolbar-btn {
1134
+ width: 100px !important;
1135
+ min-width: 100px !important;
1136
+ }
1137
+
1138
+ .management-center-transfer-field__input {
1139
+ width: 160px;
1140
+ }
1141
+
1142
+ .management-center-transfer-field__input--report-name {
1143
+ width: 280px;
1144
+ }
1145
+
1146
+ .management-center-transfer-field__select {
1147
+ width: 200px;
1148
+ }
1149
+
1150
+ .management-center-transfer-field__select :deep(.el-select__wrapper) {
1151
+ min-height: 32px;
1152
+ }
1153
+
1154
+ .management-center-transfer-filters .management-center-field--year {
1155
+ flex-shrink: 0;
1156
+ }
1157
+
1158
+ .management-center-transfer-field__date {
1159
+ width: 360px !important;
1160
+ min-width: 360px;
1161
+ max-width: none;
1162
+ flex: 0 0 360px;
1163
+ --el-date-editor-width: 360px;
1164
+ }
1165
+
1166
+ .management-center-transfer-field__date :deep(.el-input__wrapper),
1167
+ .management-center-transfer-field__date :deep(.el-range-editor.el-input__wrapper) {
1168
+ width: 100%;
1169
+ box-sizing: border-box;
1170
+ }
1171
+
1172
+ .management-center-transfer-field__date :deep(.el-range-input) {
1173
+ font-size: var(--gen-ai-fs-base);
1174
+ flex: 1;
1175
+ width: auto;
1176
+ min-width: 0;
1177
+ }
1178
+
1179
+ .management-center-transfer-field__date :deep(.el-range-separator) {
1180
+ flex: 0 0 auto;
1181
+ padding: 0 6px;
1182
+ }
1183
+
1184
+ .management-center-transfer__divider {
1185
+ margin: 24px 0;
1186
+ border: none;
1187
+ border-top: 1px dashed #dcdfe6;
1188
+ }
1189
+
1190
+ .mgmt-transfer-btn {
1191
+ width: 90px !important;
1192
+ min-width: 90px !important;
1193
+ height: 28px !important;
1194
+ padding: 0 !important;
1195
+ font-size: var(--gen-ai-fs-sm) !important;
1196
+ position: relative;
1197
+ z-index: 1;
1198
+ margin: 0;
1199
+ display: inline-flex;
1200
+ align-items: center;
1201
+ justify-content: center;
1202
+ flex-shrink: 0;
1203
+ }
1204
+
1205
+ .mgmt-transfer-action-label {
1206
+ display: inline-block;
1207
+ width: 90px;
1208
+ text-align: center;
1209
+ box-sizing: border-box;
1210
+ flex-shrink: 0;
1211
+ }
1212
+
1213
+ .mgmt-add-form__input :deep(.el-textarea__inner) {
1214
+ font-size: var(--gen-ai-fs-base);
1215
+ line-height: var(--gen-ai-lh-normal);
1216
+ }
1217
+
1218
+ .mgmt-transfer-form {
1219
+ display: flex;
1220
+ justify-content: center;
1221
+ align-items: center;
1222
+ min-height: 120px;
1223
+ }
1224
+
1225
+ .mgmt-transfer-form__field {
1226
+ display: inline-flex;
1227
+ align-items: center;
1228
+ gap: 12px;
1229
+ margin: 0;
1230
+ }
1231
+
1232
+ .mgmt-transfer-form__label {
1233
+ flex-shrink: 0;
1234
+ font-size: var(--gen-ai-fs-base);
1235
+ color: #303133;
1236
+ white-space: nowrap;
1237
+ }
1238
+
1239
+ .mgmt-transfer-form__select {
1240
+ width: 360px;
1241
+ }
1242
+
1243
+ .mgmt-transfer-form__select :deep(.el-select__wrapper) {
1244
+ min-height: 32px;
1245
+ }
1246
+ </style>
1247
+
1248
+ <style>
1249
+ /* append-to-body 彈窗內表格:非 scoped;樣式對齊報告中心 RecentReportsTable */
1250
+ .management-center-table {
1251
+ --mgmt-table-row-h: 40px;
1252
+ --mgmt-table-header-bg: #f9fafb;
1253
+ --mgmt-table-header-fs: 14px;
1254
+ --mgmt-table-header-color: #666;
1255
+ --mgmt-table-header-fw: 500;
1256
+ --mgmt-table-row-fs: 14px;
1257
+ --mgmt-table-row-color: #111827;
1258
+ --mgmt-table-border: 1px solid rgb(235, 238, 245);
1259
+ --mgmt-font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
1260
+ width: 100%;
1261
+ margin: 0;
1262
+ padding: 0;
1263
+ --el-table-border: none;
1264
+ --el-table-border-color: transparent;
1265
+ font-family: var(--mgmt-font-family);
1266
+ }
1267
+
1268
+ .management-center-table.el-table,
1269
+ .management-center-table .el-table {
1270
+ --el-table-header-text-color: var(--mgmt-table-header-color);
1271
+ --el-table-text-color: var(--mgmt-table-row-color);
1272
+ --el-table-row-hover-bg-color: #f5f7fa;
1273
+ width: 100% !important;
1274
+ font-family: var(--mgmt-font-family);
1275
+ }
1276
+
1277
+ .management-center-table .el-table__header th.el-table__cell,
1278
+ .management-center-table .el-table__header th.el-table__cell .cell {
1279
+ font-size: var(--mgmt-table-header-fs);
1280
+ font-family: var(--mgmt-font-family);
1281
+ font-weight: var(--mgmt-table-header-fw);
1282
+ color: var(--mgmt-table-header-color);
1283
+ height: var(--mgmt-table-row-h);
1284
+ }
1285
+
1286
+ .management-center-table .el-table__body tr.el-table__row,
1287
+ .management-center-table .el-table__body td.el-table__cell,
1288
+ .management-center-table .el-table__body td.el-table__cell .cell {
1289
+ font-size: var(--mgmt-table-row-fs);
1290
+ font-family: var(--mgmt-font-family);
1291
+ font-weight: 400;
1292
+ color: var(--mgmt-table-row-color);
1293
+ }
1294
+
1295
+ .management-center-table .management-center-table__name {
1296
+ color: var(--mgmt-table-row-color);
1297
+ }
1298
+
1299
+ .management-center-table .management-center-table__action-head {
1300
+ color: var(--mgmt-table-header-color);
1301
+ }
1302
+
1303
+ .management-center-table .el-table__inner-wrapper,
1304
+ .management-center-table .el-table__header-wrapper,
1305
+ .management-center-table .el-table__body-wrapper {
1306
+ margin-left: 0;
1307
+ padding-left: 0;
1308
+ }
1309
+
1310
+ .management-center-table .el-table__header table,
1311
+ .management-center-table .el-table__body table {
1312
+ width: 100% !important;
1313
+ table-layout: fixed;
1314
+ }
1315
+
1316
+ .management-center-table .el-table__header .cell,
1317
+ .management-center-table .el-table__body .cell {
1318
+ padding: 0;
1319
+ text-align: left;
1320
+ font-family: var(--mgmt-font-family);
1321
+ }
1322
+
1323
+ .management-center-table .el-table__body-wrapper .cell {
1324
+ padding: 0;
1325
+ }
1326
+
1327
+ .management-center-table .el-table__header th.el-table__cell .cell {
1328
+ white-space: nowrap;
1329
+ overflow: visible;
1330
+ text-overflow: clip;
1331
+ }
1332
+
1333
+ .management-center-table .el-table__inner-wrapper::before,
1334
+ .management-center-table .el-table__inner-wrapper::after {
1335
+ display: none;
1336
+ }
1337
+
1338
+ .management-center-table .el-table__body-wrapper .el-table__body {
1339
+ border-collapse: separate;
1340
+ border-spacing: 0;
1341
+ }
1342
+
1343
+ .management-center-table .el-table__body tr.el-table__row {
1344
+ height: var(--mgmt-table-row-h);
1345
+ background: #fff;
1346
+ cursor: pointer;
1347
+ }
1348
+
1349
+ .management-center-table .el-table__body tr {
1350
+ background: #fff;
1351
+ }
1352
+
1353
+ .management-center-table.el-table .el-table__body tr.el-table__row:hover > td.el-table__cell {
1354
+ background-color: #f5f7fa !important;
1355
+ box-shadow: none !important;
1356
+ }
1357
+
1358
+ .management-center-table .el-table__body tr.el-table__row:hover > td.el-table__cell:first-child {
1359
+ border-radius: 4px 0 0 4px;
1360
+ }
1361
+
1362
+ .management-center-table .el-table__body tr.el-table__row:hover > td.el-table__cell:last-child {
1363
+ border-radius: 0 4px 4px 0;
1364
+ }
1365
+
1366
+ .management-center-table .el-table__header-wrapper {
1367
+ border-radius: 4px;
1368
+ overflow: hidden;
1369
+ }
1370
+
1371
+ .management-center-table .el-table__header th.el-table__cell {
1372
+ font-weight: var(--mgmt-table-header-fw);
1373
+ border-bottom: var(--mgmt-table-border);
1374
+ background: var(--mgmt-table-header-bg);
1375
+ height: var(--mgmt-table-row-h);
1376
+ padding: 0 16px;
1377
+ vertical-align: middle;
1378
+ box-sizing: border-box;
1379
+ }
1380
+
1381
+ .management-center-table .el-table__header th.el-table__cell:first-child {
1382
+ border-radius: 4px 0 0 4px;
1383
+ }
1384
+
1385
+ .management-center-table .el-table__header th.el-table__cell:first-child .cell {
1386
+ padding-left: 10px;
1387
+ }
1388
+
1389
+ .management-center-table .el-table__header th.el-table__cell .cell {
1390
+ display: flex;
1391
+ align-items: center;
1392
+ width: 100%;
1393
+ box-sizing: border-box;
1394
+ }
1395
+
1396
+ /* 待處理選項:表頭與內容左對齊一致(操作列居中) */
1397
+ .management-center-pending-table .el-table__header th.el-table__cell .cell,
1398
+ .management-center-pending-table .el-table__body td.el-table__cell .cell {
1399
+ justify-content: flex-start;
1400
+ text-align: left;
1401
+ }
1402
+
1403
+ .management-center-pending-table .el-table__header th.mgmt-col-action .cell,
1404
+ .management-center-pending-table .el-table__body td.mgmt-col-action .cell {
1405
+ justify-content: center;
1406
+ text-align: center;
1407
+ }
1408
+
1409
+ .management-center-table .el-table__header th.mgmt-col-name.el-table__cell,
1410
+ .management-center-table .el-table__body td.mgmt-col-name.el-table__cell {
1411
+ padding-left: 16px;
1412
+ padding-right: 16px;
1413
+ }
1414
+
1415
+ .management-center-table .el-table__header th.mgmt-col-name .cell,
1416
+ .management-center-table .el-table__body td.mgmt-col-name .cell {
1417
+ padding-left: 10px;
1418
+ padding-right: 0;
1419
+ }
1420
+
1421
+ .management-center-table .el-table__header th.mgmt-col-name .cell {
1422
+ display: flex;
1423
+ align-items: center;
1424
+ justify-content: flex-start;
1425
+ text-align: left;
1426
+ }
1427
+
1428
+ .management-center-table .mgmt-col-name .cell {
1429
+ justify-content: flex-start;
1430
+ text-align: left;
1431
+ }
1432
+
1433
+ .management-center-table .mgmt-col-name .management-center-table__name--pill {
1434
+ padding-left: 0;
1435
+ padding-right: 10px;
1436
+ }
1437
+
1438
+ .management-center-table .el-table__header th.el-table__cell:last-child {
1439
+ border-radius: 0 4px 4px 0;
1440
+ }
1441
+
1442
+ .management-center-table .el-table__body td.el-table__cell {
1443
+ height: var(--mgmt-table-row-h);
1444
+ border-bottom: var(--mgmt-table-border);
1445
+ padding: 0 16px;
1446
+ vertical-align: middle;
1447
+ box-sizing: border-box;
1448
+ }
1449
+
1450
+ .management-center-table .el-table__body td.el-table__cell .cell {
1451
+ display: flex;
1452
+ align-items: center;
1453
+ min-height: var(--mgmt-table-row-h);
1454
+ height: 100%;
1455
+ box-sizing: border-box;
1456
+ }
1457
+
1458
+ .management-center-table .el-table__body td.el-table__cell:first-child:not(.mgmt-col-name) .cell {
1459
+ padding-left: 10px;
1460
+ }
1461
+
1462
+ .management-center-table .mgmt-col-action .cell,
1463
+ .management-center-table .mgmt-col-delete .cell {
1464
+ justify-content: center;
1465
+ align-items: center;
1466
+ width: 100%;
1467
+ }
1468
+
1469
+ .management-center-table .el-table__header th.mgmt-col-delete .cell,
1470
+ .management-center-table .el-table__header th.mgmt-col-action .cell {
1471
+ display: flex;
1472
+ align-items: center;
1473
+ justify-content: center;
1474
+ text-align: center;
1475
+ }
1476
+
1477
+ .management-center-delete-btn {
1478
+ display: inline-flex;
1479
+ align-items: center;
1480
+ justify-content: center;
1481
+ width: 100%;
1482
+ height: 100%;
1483
+ min-height: var(--mgmt-table-row-h);
1484
+ padding: 0;
1485
+ border: none;
1486
+ background: transparent;
1487
+ color: #909399;
1488
+ line-height: 1;
1489
+ cursor: pointer;
1490
+ transition: color 0.2s ease;
1491
+ }
1492
+
1493
+ .management-center-delete-btn .el-icon {
1494
+ display: inline-flex;
1495
+ align-items: center;
1496
+ justify-content: center;
1497
+ font-size: 16px;
1498
+ }
1499
+
1500
+ .management-center-delete-btn:hover {
1501
+ color: #c53355;
1502
+ }
1503
+
1504
+ .management-center-table__name--pill {
1505
+ display: block;
1506
+ width: 100%;
1507
+ max-width: 100%;
1508
+ padding: 6px 10px;
1509
+ border-radius: 4px;
1510
+ background: transparent;
1511
+ overflow: hidden;
1512
+ text-overflow: ellipsis;
1513
+ white-space: nowrap;
1514
+ box-sizing: border-box;
1515
+ line-height: 1.4;
1516
+ }
1517
+
1518
+ .management-center-table__name {
1519
+ color: inherit;
1520
+ }
1521
+
1522
+ /* 可點擊名稱:對齊報告中心鏈接 hover(原色 + 下劃線) */
1523
+ .management-center-table__name--link {
1524
+ color: #409eff;
1525
+ text-decoration: none;
1526
+ cursor: pointer;
1527
+ }
1528
+
1529
+ .management-center-table__name--link:hover {
1530
+ color: #409eff;
1531
+ text-decoration: underline;
1532
+ }
1533
+
1534
+ .mgmt-pending-actions {
1535
+ display: inline-flex;
1536
+ align-items: center;
1537
+ justify-content: center;
1538
+ gap: 8px;
1539
+ }
1540
+
1541
+ .mgmt-pending-action {
1542
+ width: 32px;
1543
+ height: 32px;
1544
+ padding: 0;
1545
+ border: 1px solid #dcdfe6;
1546
+ border-radius: 4px;
1547
+ background: #fff;
1548
+ color: #606266;
1549
+ font-size: 14px;
1550
+ line-height: 1;
1551
+ cursor: pointer;
1552
+ transition: all 0.2s ease;
1553
+ }
1554
+
1555
+ .mgmt-pending-action.is-active,
1556
+ .mgmt-pending-action--reject.is-active {
1557
+ background: #c53355;
1558
+ border-color: #c53355;
1559
+ color: #fff;
1560
+ }
1561
+
1562
+ .management-center-table
1563
+ .el-table__body
1564
+ tr.el-table__row:hover
1565
+ .mgmt-pending-action:not(.is-active) {
1566
+ background: #fff;
1567
+ border-color: #dcdfe6;
1568
+ color: #606266;
1569
+ }
1570
+
1571
+ .management-center-table .el-table__body tr.el-table__row:hover .mgmt-pending-action.is-active,
1572
+ .management-center-table
1573
+ .el-table__body
1574
+ tr.el-table__row:hover
1575
+ .mgmt-pending-action--reject.is-active {
1576
+ background: #c53355;
1577
+ border-color: #c53355;
1578
+ color: #fff;
1579
+ }
1580
+
1581
+ .management-center-panel__card .management-center-table {
1582
+ overflow: visible;
1583
+ }
1584
+
1585
+ .management-center-panel__card .management-center-table .el-table__inner-wrapper,
1586
+ .management-center-panel__card .management-center-table .el-table__body-wrapper,
1587
+ .management-center-panel__card .management-center-table .el-table__header-wrapper,
1588
+ .management-center-panel__card .management-center-table .el-scrollbar__wrap,
1589
+ .management-center-panel__card .management-center-table .el-scrollbar__view {
1590
+ overflow: visible !important;
1591
+ max-height: none !important;
1592
+ height: auto !important;
1593
+ }
1594
+
1595
+ .management-center-transfer-table .el-table__fixed-right,
1596
+ .management-center-transfer-table .el-table__fixed-right .el-table__fixed-header-wrapper,
1597
+ .management-center-transfer-table .el-table__fixed-right .el-table__fixed-body-wrapper {
1598
+ overflow: visible !important;
1599
+ }
1600
+
1601
+ .management-center-transfer-table .el-table__header th.mgmt-col-transfer-action.el-table__cell,
1602
+ .management-center-transfer-table .el-table__body td.mgmt-col-transfer-action.el-table__cell {
1603
+ width: 140px;
1604
+ min-width: 140px;
1605
+ padding-left: 25px;
1606
+ padding-right: 25px;
1607
+ overflow: visible;
1608
+ }
1609
+
1610
+ .management-center-transfer-table .el-table__header th.mgmt-col-transfer-action.el-table__cell {
1611
+ background: var(--gen-ai-table-header-bg, rgb(249, 250, 251)) !important;
1612
+ }
1613
+
1614
+ .management-center-transfer-table .mgmt-col-transfer-action .cell {
1615
+ display: flex;
1616
+ align-items: center;
1617
+ justify-content: center;
1618
+ overflow: visible;
1619
+ text-align: center;
1620
+ padding: 0 !important;
1621
+ width: 90px;
1622
+ margin: 0 auto;
1623
+ }
1624
+
1625
+ /* 固定列右側邊框偏移補償,使「操作」與「轉移」中心對齊 */
1626
+ .management-center-transfer-table .mgmt-transfer-action-label,
1627
+ .management-center-transfer-table .mgmt-transfer-btn {
1628
+ margin-right: -2px;
1629
+ }
1630
+
1631
+ .management-center-pending-badge .el-badge__content.el-badge__content--danger.is-fixed {
1632
+ margin-right: 10px !important;
1633
+ margin-top: -5px !important;
1634
+ }
1635
+
1636
+ .management-center-pending-badge .mgmt-toolbar-btn--outline {
1637
+ border: 1px solid var(--mgmt-accent, #c53355);
1638
+ color: var(--mgmt-accent, #c53355);
1639
+ background: #fff;
1640
+ }
1641
+
1642
+ .management-center-pending-badge .mgmt-toolbar-btn--outline:hover {
1643
+ border-color: var(--mgmt-accent, #c53355);
1644
+ color: var(--mgmt-accent, #c53355);
1645
+ background: var(--mgmt-accent-soft, #fdf0f2);
1646
+ }
1647
+
1648
+ .mgmt-dialog.el-dialog {
1649
+ --el-dialog-padding-primary: 0;
1650
+ border-radius: 4px;
1651
+ overflow: hidden;
1652
+ }
1653
+
1654
+ .mgmt-dialog .el-dialog__header,
1655
+ .mgmt-dialog__header {
1656
+ display: flex;
1657
+ align-items: center;
1658
+ justify-content: space-between;
1659
+ gap: 16px;
1660
+ margin: 0;
1661
+ padding: 14px 16px;
1662
+ background: transparent;
1663
+ border-bottom: 1px solid #ebeef5;
1664
+ }
1665
+
1666
+ .mgmt-dialog__title {
1667
+ flex: 1;
1668
+ min-width: 0;
1669
+ color: #303133;
1670
+ font-size: 18px;
1671
+ font-weight: 600;
1672
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
1673
+ line-height: 1.4;
1674
+ }
1675
+
1676
+ .mgmt-dialog__close {
1677
+ flex-shrink: 0;
1678
+ width: 32px;
1679
+ height: 32px;
1680
+ margin: 0;
1681
+ padding: 0;
1682
+ border: none;
1683
+ background: transparent;
1684
+ color: #303133;
1685
+ font-size: 18px;
1686
+ line-height: 1;
1687
+ cursor: pointer;
1688
+ }
1689
+
1690
+ .mgmt-dialog__close:hover {
1691
+ color: #606266;
1692
+ }
1693
+
1694
+ .mgmt-dialog .el-dialog__body {
1695
+ padding: 20px 16px;
1696
+ background: #fff;
1697
+ }
1698
+
1699
+ .mgmt-dialog .el-dialog__footer {
1700
+ padding: 12px 16px 16px;
1701
+ background: #fff;
1702
+ }
1703
+
1704
+ .mgmt-dialog__footer {
1705
+ display: flex;
1706
+ align-items: center;
1707
+ justify-content: center;
1708
+ gap: 16px;
1709
+ }
1710
+
1711
+ /* 待處理選項彈窗:與章節結構彈窗尺寸一致 */
1712
+ .mgmt-pending-overlay {
1713
+ overflow: auto;
1714
+ }
1715
+
1716
+ .mgmt-dialog--pending.el-dialog {
1717
+ width: 60%;
1718
+ max-width: 80%;
1719
+ height: 70vh;
1720
+ max-height: 70vh;
1721
+ margin-top: 5%;
1722
+ margin-left: 21%;
1723
+ margin-right: auto;
1724
+ display: flex;
1725
+ flex-direction: column;
1726
+ overflow: hidden;
1727
+ }
1728
+
1729
+ .mgmt-dialog--pending .el-dialog__header,
1730
+ .mgmt-dialog--pending .mgmt-dialog__header {
1731
+ flex-shrink: 0;
1732
+ padding: 14px 16px 14px 20px;
1733
+ }
1734
+
1735
+ .mgmt-dialog--pending .el-dialog__body {
1736
+ flex: 1;
1737
+ min-height: 0;
1738
+ padding: 24px 20px;
1739
+ overflow-y: auto;
1740
+ }
1741
+
1742
+ .mgmt-dialog--pending .el-dialog__footer {
1743
+ flex-shrink: 0;
1744
+ padding: 12px 16px 16px;
1745
+ background: #fff;
1746
+ }
1747
+
1748
+ .mgmt-dialog .mgmt-toolbar-btn {
1749
+ display: inline-flex;
1750
+ align-items: center;
1751
+ justify-content: center;
1752
+ box-sizing: border-box;
1753
+ height: 32px;
1754
+ min-width: 120px;
1755
+ padding: 0 14px;
1756
+ border-radius: 4px;
1757
+ line-height: 1;
1758
+ white-space: nowrap;
1759
+ cursor: pointer;
1760
+ transition: all 0.2s ease;
1761
+ }
1762
+
1763
+ .mgmt-dialog .mgmt-toolbar-btn--outline {
1764
+ border: 1px solid #c53355;
1765
+ color: #c53355;
1766
+ background: #fff;
1767
+ }
1768
+
1769
+ .mgmt-dialog .mgmt-toolbar-btn--outline:hover {
1770
+ border-color: #c53355;
1771
+ color: #c53355;
1772
+ background: #fdf0f2;
1773
+ }
1774
+
1775
+ .mgmt-dialog .mgmt-toolbar-btn--primary {
1776
+ border: 1px solid #c53355;
1777
+ background: #c53355;
1778
+ color: #fff;
1779
+ }
1780
+
1781
+ .mgmt-dialog .mgmt-toolbar-btn--primary:hover {
1782
+ background: #fff;
1783
+ border-color: #c53355;
1784
+ color: #c53355;
1785
+ }
1786
+
1787
+ .mgmt-dialog .mgmt-toolbar-btn--ghost {
1788
+ border: 1px solid #dcdfe6;
1789
+ background: #fff;
1790
+ color: #606266;
1791
+ }
1792
+
1793
+ .mgmt-dialog .mgmt-toolbar-btn--ghost:hover {
1794
+ border-color: #c53355;
1795
+ color: #c53355;
1796
+ background: #fdf0f2;
1797
+ }
1798
+
1799
+ .mgmt-dialog--add.el-dialog {
1800
+ width: 840px !important;
1801
+ max-width: 90vw;
1802
+ }
1803
+
1804
+ .mgmt-dialog--add .el-dialog__body {
1805
+ padding: 40px 32px;
1806
+ }
1807
+
1808
+ .mgmt-dialog--add .mgmt-add-form__input .el-textarea__inner {
1809
+ min-height: 240px;
1810
+ }
1811
+
1812
+ .mgmt-dialog--transfer .el-dialog__body {
1813
+ padding: 40px 32px;
1814
+ }
1815
+ </style>