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,1583 @@
1
+ <template>
2
+ <div class="template-center-page">
3
+ <main class="template-center-page__body">
4
+ <section class="template-center-hero">
5
+ <h2 class="template-center-hero__title">模板中心</h2>
6
+ <p class="template-center-hero__desc gen-ai-page-hero-desc">
7
+ 所有已配置完畢的報告模板將會在此顯示
8
+ </p>
9
+ </section>
10
+
11
+ <section class="template-center-toolbar">
12
+ <div class="template-center-toolbar__tabs">
13
+ <div class="template-center-toolbar__tabs-left">
14
+ <button
15
+ type="button"
16
+ class="template-center-toolbar-btn gen-ai-action-btn template-center-toolbar-btn--primary template-center-btn--create"
17
+ @click="handleCreate"
18
+ >
19
+ 新建模板
20
+ </button>
21
+ <el-badge
22
+ :value="pendingCount"
23
+ :max="99"
24
+ type="danger"
25
+ :badge-style="{ marginRight: '10px', marginTop: '-5px' }"
26
+ class="template-center-tab-badge"
27
+ >
28
+ <button
29
+ type="button"
30
+ class="template-center-toolbar-btn gen-ai-action-btn template-center-toolbar-btn--outline"
31
+ @click="openPendingProcess"
32
+ >
33
+ 待審批模板
34
+ </button>
35
+ </el-badge>
36
+ </div>
37
+ <div class="template-center-toolbar__tabs-right">
38
+ <button
39
+ type="button"
40
+ class="template-center-toolbar-btn gen-ai-action-btn template-center-toolbar-btn--ghost"
41
+ @click="openRecycleBin"
42
+ >
43
+ <el-icon>
44
+ <Delete />
45
+ </el-icon>
46
+ 回收站
47
+ </button>
48
+ </div>
49
+ </div>
50
+
51
+ <div class="template-center-toolbar__controls">
52
+ <div class="template-center-toolbar__right">
53
+ <label class="template-center-field">
54
+ <span class="gen-ai-filter-field-label">語言</span>
55
+ <el-select
56
+ v-model="language"
57
+ class="template-center-field__select template-center-field__select--lang"
58
+ >
59
+ <el-option
60
+ v-for="item in languageOptions"
61
+ :key="item.value"
62
+ :label="item.label"
63
+ :value="item.value"
64
+ />
65
+ </el-select>
66
+ </label>
67
+
68
+ <label class="template-center-field">
69
+ <span class="gen-ai-filter-field-label">單位</span>
70
+ <el-select
71
+ v-model="unitFilter"
72
+ placeholder=""
73
+ clearable
74
+ class="template-center-field__select"
75
+ >
76
+ <el-option v-for="item in unitOptions" :key="item" :label="item" :value="item" />
77
+ </el-select>
78
+ </label>
79
+
80
+ <label class="template-center-field">
81
+ <span class="gen-ai-filter-field-label">Search</span>
82
+ <el-input v-model="keyword" clearable class="template-center-field__input" />
83
+ </label>
84
+
85
+ <button
86
+ type="button"
87
+ class="template-center-toolbar-btn gen-ai-action-btn template-center-toolbar-btn--primary"
88
+ @click="handleRefresh"
89
+ >
90
+ Refresh
91
+ </button>
92
+ </div>
93
+ </div>
94
+ </section>
95
+
96
+ <div class="template-center-card-grid">
97
+ <ModelCard
98
+ v-for="row in paginatedTemplates"
99
+ :key="row.id"
100
+ :template="row"
101
+ @edit="handleEdit"
102
+ @lock="handleToggleLock"
103
+ @delete="handleDelete"
104
+ />
105
+ </div>
106
+
107
+ <GenAiPagination
108
+ v-model:current-page="currentPage"
109
+ v-model:page-size="pageSize"
110
+ :page-sizes="[4]"
111
+ :total="filteredTemplates.length"
112
+ />
113
+
114
+ <!-- //newww dialog — 查看詳情 / 處理(同頁彈出,不另封組件) -->
115
+ <el-dialog
116
+ v-model="detailDialogVisible"
117
+ width="80%"
118
+ append-to-body
119
+ :z-index="10000"
120
+ :show-close="false"
121
+ modal-class="template-detail-overlay"
122
+ class="template-detail-dialog"
123
+ header-class="template-detail-dialog__header"
124
+ @closed="resetDetailForm"
125
+ >
126
+ <template #header="{ close, titleId, titleClass }">
127
+ <span :id="titleId" :class="['template-detail-dialog__title', titleClass]">{{
128
+ detailDialogTitle
129
+ }}</span>
130
+ <button
131
+ type="button"
132
+ class="template-detail-dialog__close"
133
+ :aria-label="TEMPLATE_DETAIL_MSG.closeAriaLabel"
134
+ @click="close"
135
+ >
136
+ ×
137
+ </button>
138
+ </template>
139
+
140
+ <div v-if="templateDetail" class="template-detail-form">
141
+ <label class="template-detail-field template-detail-field--row">
142
+ <span class="template-detail-label">{{ TEMPLATE_DETAIL_MSG.sourceReport }}</span>
143
+ <div class="template-detail-field__control">
144
+ <span class="template-detail-source">{{ templateDetail.sourceReport }}</span>
145
+ </div>
146
+ </label>
147
+
148
+ <label class="template-detail-field template-detail-field--row">
149
+ <span class="template-detail-label">
150
+ {{ TEMPLATE_DETAIL_MSG.primaryLanguage
151
+ }}<span class="template-detail-label__required">*</span>
152
+ </span>
153
+ <div class="template-detail-field__control template-detail-field__control--lang">
154
+ <el-input
155
+ :model-value="detailLanguageDisplay"
156
+ disabled
157
+ class="template-detail-field__input"
158
+ />
159
+ </div>
160
+ </label>
161
+
162
+ <label class="template-detail-field template-detail-field--row">
163
+ <span class="template-detail-label">
164
+ {{ TEMPLATE_DETAIL_MSG.templateTitle
165
+ }}<span class="template-detail-label__required">*</span>
166
+ </span>
167
+ <div class="template-detail-field__control">
168
+ <el-input
169
+ v-model="detailEditableTitle"
170
+ :maxlength="TEMPLATE_DETAIL_LIMITS.titleMax"
171
+ show-word-limit
172
+ :disabled="!isDetailProcessMode"
173
+ class="template-detail-field__input"
174
+ />
175
+ </div>
176
+ </label>
177
+
178
+ <label
179
+ class="template-detail-field template-detail-field--row template-detail-field--textarea-row"
180
+ >
181
+ <span class="template-detail-label">
182
+ {{ TEMPLATE_DETAIL_MSG.templateDescription
183
+ }}<span class="template-detail-label__required">*</span>
184
+ </span>
185
+ <div class="template-detail-field__control">
186
+ <el-input
187
+ v-model="detailEditableDescription"
188
+ type="textarea"
189
+ :rows="4"
190
+ :maxlength="TEMPLATE_DETAIL_LIMITS.descMax"
191
+ show-word-limit
192
+ resize="none"
193
+ :disabled="!isDetailProcessMode"
194
+ class="template-detail-field__textarea"
195
+ />
196
+ </div>
197
+ </label>
198
+
199
+ <div
200
+ v-for="chapter in detailDisplayChapters"
201
+ :key="chapter.id"
202
+ class="template-detail-chapter"
203
+ >
204
+ <h4 class="template-detail-chapter__title">
205
+ {{ chapter.title || TEMPLATE_DETAIL_MSG.chapterTitleFallback }}
206
+ </h4>
207
+
208
+ <label
209
+ class="template-detail-field template-detail-field--row template-detail-field--textarea-row"
210
+ >
211
+ <span class="template-detail-label">{{ TEMPLATE_DETAIL_MSG.chapterPrompt }}</span>
212
+ <div class="template-detail-field__control">
213
+ <el-input
214
+ :model-value="chapter.prompt"
215
+ type="textarea"
216
+ :rows="TEMPLATE_DETAIL_LIMITS.chapterPromptRows"
217
+ :maxlength="TEMPLATE_DETAIL_LIMITS.chapterPromptMax"
218
+ show-word-limit
219
+ resize="none"
220
+ disabled
221
+ class="template-detail-field__textarea"
222
+ />
223
+ </div>
224
+ </label>
225
+
226
+ <label
227
+ class="template-detail-field template-detail-field--row template-detail-field--textarea-row"
228
+ >
229
+ <span class="template-detail-label">{{
230
+ TEMPLATE_DETAIL_MSG.chapterDescription
231
+ }}</span>
232
+ <div class="template-detail-field__control">
233
+ <el-input
234
+ :model-value="chapter.description"
235
+ type="textarea"
236
+ :rows="TEMPLATE_DETAIL_LIMITS.chapterPromptRows"
237
+ :maxlength="TEMPLATE_DETAIL_LIMITS.chapterDescMax"
238
+ show-word-limit
239
+ resize="none"
240
+ disabled
241
+ class="template-detail-field__textarea"
242
+ />
243
+ </div>
244
+ </label>
245
+ </div>
246
+
247
+ <section v-if="isDetailProcessMode" class="template-detail-process">
248
+ <label
249
+ class="template-detail-field template-detail-field--row template-detail-process__row"
250
+ >
251
+ <span class="template-detail-label">
252
+ {{ TEMPLATE_DETAIL_MSG.processAction
253
+ }}<span class="template-detail-label__required">*</span>
254
+ </span>
255
+ <div class="template-detail-field__control">
256
+ <el-radio-group
257
+ v-model="detailProcessAction"
258
+ class="template-detail-process__radios"
259
+ >
260
+ <el-radio value="reject">{{ TEMPLATE_DETAIL_MSG.reject }}</el-radio>
261
+ <el-radio value="approve">{{ TEMPLATE_DETAIL_MSG.approve }}</el-radio>
262
+ </el-radio-group>
263
+ </div>
264
+ </label>
265
+
266
+ <label
267
+ class="template-detail-field template-detail-field--row template-detail-field--textarea-row"
268
+ >
269
+ <span class="template-detail-label">{{ TEMPLATE_DETAIL_MSG.opinion }}</span>
270
+ <div class="template-detail-field__control">
271
+ <el-input
272
+ v-model="detailProcessOpinion"
273
+ type="textarea"
274
+ :rows="4"
275
+ :maxlength="TEMPLATE_DETAIL_LIMITS.opinionMax"
276
+ show-word-limit
277
+ resize="none"
278
+ class="template-detail-field__textarea"
279
+ />
280
+ </div>
281
+ </label>
282
+
283
+ <div class="template-detail-process__actions">
284
+ <button
285
+ type="button"
286
+ class="template-detail-btn template-detail-btn--ghost"
287
+ @click="detailDialogVisible = false"
288
+ >
289
+ {{ TEMPLATE_DETAIL_MSG.cancel }}
290
+ </button>
291
+ <button
292
+ type="button"
293
+ class="template-detail-btn template-detail-btn--primary"
294
+ @click="submitDetailProcess"
295
+ >
296
+ {{ TEMPLATE_DETAIL_MSG.submit }}
297
+ </button>
298
+ </div>
299
+ </section>
300
+
301
+ <section class="template-detail-approval">
302
+ <h4 class="template-detail-approval__title">
303
+ {{ TEMPLATE_DETAIL_MSG.approvalSectionTitle }}
304
+ </h4>
305
+ <el-table
306
+ :data="templateDetail.approvalRecords"
307
+ border
308
+ class="template-detail-approval-table"
309
+ >
310
+ <el-table-column
311
+ :label="TEMPLATE_DETAIL_MSG.approvalOperator"
312
+ min-width="180"
313
+ prop="operator"
314
+ />
315
+ <el-table-column
316
+ :label="TEMPLATE_DETAIL_MSG.approvalStatus"
317
+ width="120"
318
+ prop="status"
319
+ />
320
+ <el-table-column
321
+ :label="TEMPLATE_DETAIL_MSG.approvalCompletedAt"
322
+ min-width="180"
323
+ prop="completedAt"
324
+ />
325
+ <el-table-column
326
+ :label="TEMPLATE_DETAIL_MSG.approvalDetail"
327
+ min-width="200"
328
+ prop="detail"
329
+ show-overflow-tooltip
330
+ />
331
+ </el-table>
332
+ </section>
333
+ </div>
334
+ </el-dialog>
335
+ </main>
336
+ </div>
337
+ </template>
338
+
339
+ <script setup lang="ts">
340
+ import '../common/assets/fonts.css'
341
+ import { Delete } from '@element-plus/icons-vue'
342
+ import { ElMessage, ElMessageBox } from 'element-plus'
343
+ import { computed, onMounted, ref, watch } from 'vue'
344
+ import { useRouter } from 'vue-router'
345
+ import { GenAiPagination, ModelCard, openTemplateEditor } from '../common'
346
+ import modelManagerApi from './api'
347
+ import { TEMPLATE_CATALOG } from './mockData'
348
+ import {
349
+ TEMPLATE_DETAIL_LIMITS,
350
+ TEMPLATE_DETAIL_MSG,
351
+ getDetailDisplayChapters,
352
+ getDetailLanguageDisplay,
353
+ getTemplateDetail,
354
+ type TemplateDetailMode,
355
+ type TemplateProcessAction,
356
+ } from './template-detail-shared'
357
+ import type { TemplateItem } from './types'
358
+
359
+ //newww start — 查看詳情 / 處理(類型、文案、mock、狀態與邏輯)
360
+ const DETAIL_MSG = {
361
+ viewDetails: '查看詳情',
362
+ process: '處理',
363
+ rejectedSuccess: '已拒絕該模板',
364
+ approvedSuccess: '已通過,模板已更新至列表',
365
+ } as const
366
+
367
+ const detailDialogVisible = ref(false)
368
+ const detailTemplateId = ref('')
369
+ const detailDialogMode = ref<TemplateDetailMode>('view')
370
+ const detailEditableTitle = ref('')
371
+ const detailEditableDescription = ref('')
372
+ const detailProcessAction = ref<'' | TemplateProcessAction>('')
373
+ const detailProcessOpinion = ref('')
374
+
375
+ const templateDetail = computed(() => {
376
+ if (!detailTemplateId.value) return null
377
+ return getTemplateDetail(detailTemplateId.value)
378
+ })
379
+
380
+ const isDetailProcessMode = computed(() => detailDialogMode.value === 'process')
381
+ const detailDialogTitle = computed(() =>
382
+ isDetailProcessMode.value
383
+ ? TEMPLATE_DETAIL_MSG.dialogTitleProcess
384
+ : TEMPLATE_DETAIL_MSG.dialogTitleView
385
+ )
386
+ const detailLanguageDisplay = computed(() =>
387
+ templateDetail.value ? getDetailLanguageDisplay(templateDetail.value.language) : ''
388
+ )
389
+ const detailDisplayChapters = computed(() => getDetailDisplayChapters(templateDetail.value))
390
+
391
+ function resetDetailForm() {
392
+ if (!templateDetail.value) return
393
+ detailEditableTitle.value = templateDetail.value.title
394
+ detailEditableDescription.value = templateDetail.value.description
395
+ detailProcessAction.value = ''
396
+ detailProcessOpinion.value = ''
397
+ }
398
+
399
+ function openTemplateDetail(row: TemplateItem, mode: TemplateDetailMode) {
400
+ detailTemplateId.value = row.id
401
+ detailDialogMode.value = mode
402
+ detailDialogVisible.value = true
403
+ resetDetailForm()
404
+ }
405
+
406
+ function handleProcess(row: TemplateItem) {
407
+ openTemplateDetail(row, 'process')
408
+ }
409
+
410
+ function openPendingProcess() {
411
+ const pending = templates.value.find((item) => item.approvalStatus === '待審批')
412
+ if (!pending) {
413
+ ElMessage.info('暫無待審批模板')
414
+ return
415
+ }
416
+ handleProcess(pending)
417
+ }
418
+
419
+ function formatDetailNow() {
420
+ const now = new Date()
421
+ const pad = (n: number) => String(n).padStart(2, '0')
422
+ return `${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()} ${pad(now.getHours())}:${pad(now.getMinutes())}`
423
+ }
424
+
425
+ function applyDetailProcessResult(
426
+ action: TemplateProcessAction,
427
+ title: string,
428
+ description: string
429
+ ) {
430
+ const target = templates.value.find((item) => item.id === detailTemplateId.value)
431
+ if (!target) return
432
+
433
+ target.name = title
434
+ target.description = description
435
+
436
+ if (action === 'reject') {
437
+ target.approvalStatus = '已拒絕'
438
+ ElMessage.success(DETAIL_MSG.rejectedSuccess)
439
+ return
440
+ }
441
+
442
+ target.approvalStatus = '已通過'
443
+ target.updatedAt = formatDetailNow()
444
+ ElMessage.success(DETAIL_MSG.approvedSuccess)
445
+ }
446
+
447
+ async function submitDetailProcess() {
448
+ if (!isDetailProcessMode.value) return
449
+
450
+ if (!detailEditableTitle.value.trim()) {
451
+ ElMessage.warning(TEMPLATE_DETAIL_MSG.warnTitleRequired)
452
+ return
453
+ }
454
+ if (!detailEditableDescription.value.trim()) {
455
+ ElMessage.warning(TEMPLATE_DETAIL_MSG.warnDescriptionRequired)
456
+ return
457
+ }
458
+ if (!detailProcessAction.value) {
459
+ ElMessage.warning(TEMPLATE_DETAIL_MSG.warnActionRequired)
460
+ return
461
+ }
462
+ if (detailProcessAction.value === 'reject' && !detailProcessOpinion.value.trim()) {
463
+ ElMessage.warning(TEMPLATE_DETAIL_MSG.warnOpinionRequired)
464
+ return
465
+ }
466
+
467
+ const isReject = detailProcessAction.value === 'reject'
468
+ try {
469
+ await ElMessageBox.confirm(
470
+ isReject
471
+ ? TEMPLATE_DETAIL_MSG.confirmRejectMessage
472
+ : TEMPLATE_DETAIL_MSG.confirmApproveMessage,
473
+ isReject ? TEMPLATE_DETAIL_MSG.confirmRejectTitle : TEMPLATE_DETAIL_MSG.confirmApproveTitle,
474
+ {
475
+ confirmButtonText: TEMPLATE_DETAIL_MSG.confirmButton,
476
+ cancelButtonText: TEMPLATE_DETAIL_MSG.cancelButton,
477
+ type: 'warning',
478
+ }
479
+ )
480
+ } catch {
481
+ return
482
+ }
483
+
484
+ applyDetailProcessResult(
485
+ detailProcessAction.value,
486
+ detailEditableTitle.value.trim(),
487
+ detailEditableDescription.value.trim()
488
+ )
489
+ detailDialogVisible.value = false
490
+ }
491
+ //newww end
492
+
493
+ const unitOptions = ['風險管理部', '授信審批部', '合規部', '公司金融部']
494
+ const languageOptions = [
495
+ { label: '中文', value: 'zh' },
496
+ { label: 'English', value: 'en' },
497
+ ]
498
+ const language = ref('zh')
499
+ const unitFilter = ref('')
500
+ const keyword = ref('')
501
+ const currentPage = ref(1)
502
+ const pageSize = ref(4)
503
+
504
+ const templates = ref([...TEMPLATE_CATALOG])
505
+ const router = useRouter()
506
+
507
+ function isTemplateList(data: unknown) {
508
+ return Array.isArray(data) && data.length > 0 && typeof data[0]?.id === 'string'
509
+ }
510
+
511
+ async function loadTemplates() {
512
+ const data = await modelManagerApi.fetchTemplateListData({
513
+ language: language.value,
514
+ })
515
+ if (isTemplateList(data)) {
516
+ templates.value = data as TemplateItem[]
517
+ }
518
+ }
519
+
520
+ const filteredTemplates = computed(() => {
521
+ const kw = keyword.value.trim().toLowerCase()
522
+ return templates.value.filter((item) => {
523
+ const unitOk = !unitFilter.value || item.unit === unitFilter.value
524
+ const keywordOk =
525
+ !kw ||
526
+ item.name.toLowerCase().includes(kw) ||
527
+ item.description.toLowerCase().includes(kw) ||
528
+ item.author.toLowerCase().includes(kw)
529
+ return unitOk && keywordOk
530
+ })
531
+ })
532
+
533
+ const pendingCount = computed(
534
+ () => templates.value.filter((item) => item.approvalStatus === '待審批').length
535
+ )
536
+
537
+ const paginatedTemplates = computed(() => {
538
+ const start = (currentPage.value - 1) * pageSize.value
539
+ return filteredTemplates.value.slice(start, start + pageSize.value)
540
+ })
541
+
542
+ watch([keyword, unitFilter], () => {
543
+ currentPage.value = 1
544
+ })
545
+
546
+ watch(filteredTemplates, (list) => {
547
+ const maxPage = Math.max(1, Math.ceil(list.length / pageSize.value))
548
+ if (currentPage.value > maxPage) currentPage.value = maxPage
549
+ })
550
+
551
+ onMounted(() => {
552
+ void loadTemplates()
553
+ })
554
+
555
+ function handleRefresh() {
556
+ keyword.value = ''
557
+ unitFilter.value = ''
558
+ void loadTemplates()
559
+ ElMessage.success('已重新整理')
560
+ }
561
+
562
+ function handleCreate() {
563
+ openTemplateEditor(router, { mode: 'create' })
564
+ }
565
+
566
+ function openRecycleBin() {
567
+ ElMessage.info('回收站(演示)')
568
+ }
569
+
570
+ function handleEdit(row: TemplateItem) {
571
+ openTemplateEditor(router, { mode: 'edit', id: row.id })
572
+ }
573
+
574
+ function handleToggleLock(row: TemplateItem) {
575
+ row.locked = !row.locked
576
+ ElMessage.success(row.locked ? `已鎖定:${row.name}` : `已解鎖:${row.name}`)
577
+ }
578
+
579
+ async function handleDelete(row: TemplateItem) {
580
+ const message = row.locked
581
+ ? `模板「${row.name}」已鎖定,確定仍要刪除嗎?`
582
+ : `確定刪除模板「${row.name}」嗎?`
583
+ try {
584
+ await ElMessageBox.confirm(message, '刪除', {
585
+ confirmButtonText: '確定',
586
+ cancelButtonText: '取消',
587
+ type: 'warning',
588
+ })
589
+ ElMessage.success(`已刪除:${row.name}`)
590
+ } catch {
591
+ // 用戶取消
592
+ }
593
+ }
594
+ </script>
595
+
596
+ <style scoped>
597
+ .template-center-page {
598
+ --gen-ai-design-w: 1920;
599
+ --gen-ai-fs-xs: clamp(11px, calc(12 * 100vw / 1920), 12px);
600
+ --gen-ai-fs-sm: clamp(12px, calc(13 * 100vw / 1920), 13px);
601
+ --gen-ai-fs-base: clamp(13px, calc(14 * 100vw / 1920), 14px);
602
+ --gen-ai-fs-md: clamp(14px, calc(16 * 100vw / 1920), 16px);
603
+ --gen-ai-fs-lg: clamp(16px, calc(18 * 100vw / 1920), 18px);
604
+ --gen-ai-fs-xl: clamp(18px, calc(20 * 100vw / 1920), 20px);
605
+ --gen-ai-fs-2xl: clamp(20px, calc(24 * 100vw / 1920), 24px);
606
+ --gen-ai-fs-hero: clamp(26px, calc(32 * 100vw / 1920), 32px);
607
+ --gen-ai-fs-chapter: clamp(12px, calc(14 * 100vw / 1920), 14px);
608
+ --gen-ai-lh-chapter: clamp(18px, calc(22 * 100vw / 1920), 22px);
609
+ --gen-ai-lh-tight: 1.4;
610
+ --gen-ai-lh-normal: 1.6;
611
+ --gen-ai-lh-relaxed: 1.7;
612
+ --gen-ai-fw-regular: 400;
613
+ --gen-ai-fw-semibold: 600;
614
+ --gen-ai-fw-bold: 700;
615
+ --gen-ai-fw-black: 900;
616
+ --gen-ai-el-table-header-fs: 14px;
617
+ --gen-ai-el-table-header-color: #303133;
618
+ --gen-ai-el-table-row-fs: 14px;
619
+ --gen-ai-el-table-row-color: #606266;
620
+ --report-accent: #c53355;
621
+ --report-accent-soft: #fdf0f2;
622
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
623
+ font-size: var(--gen-ai-fs-base);
624
+ line-height: var(--gen-ai-lh-normal);
625
+ color: #2d3139;
626
+ display: flex;
627
+ flex-direction: column;
628
+ height: 100vh;
629
+ overflow: hidden;
630
+ background: #fff;
631
+ }
632
+
633
+ .template-center-page :deep(.el-table),
634
+ .template-center-page :deep(.el-input),
635
+ .template-center-page :deep(.el-select),
636
+ .template-center-page :deep(.el-pagination) {
637
+ font-size: inherit;
638
+ }
639
+
640
+ .template-center-page :deep(.el-button) {
641
+ font-size: 18px;
642
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
643
+ }
644
+
645
+ .gen-ai-page-hero-desc {
646
+ font-size: 16px;
647
+ font-weight: 600;
648
+ line-height: 1.7;
649
+ color: #666;
650
+ }
651
+
652
+ .gen-ai-filter-field-label {
653
+ font-size: 14px;
654
+ color: #606266;
655
+ font-weight: 500;
656
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
657
+ white-space: nowrap;
658
+ }
659
+
660
+ .gen-ai-action-btn,
661
+ .template-center-toolbar-btn {
662
+ font-size: 15px;
663
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
664
+ font-weight: 400;
665
+ }
666
+
667
+ .template-center-toolbar-btn {
668
+ display: inline-flex;
669
+ align-items: center;
670
+ justify-content: center;
671
+ gap: 6px;
672
+ box-sizing: border-box;
673
+ height: 32px;
674
+ min-width: 120px;
675
+ padding: 0 14px;
676
+ border-radius: 4px;
677
+ line-height: 1;
678
+ white-space: nowrap;
679
+ cursor: pointer;
680
+ transition: all 0.2s ease;
681
+ }
682
+
683
+ .template-center-toolbar-btn:disabled {
684
+ opacity: 0.45;
685
+ cursor: not-allowed;
686
+ }
687
+
688
+ .template-center-toolbar-btn--primary {
689
+ border: 1px solid var(--report-accent);
690
+ background: var(--report-accent);
691
+ color: #fff;
692
+ }
693
+
694
+ .template-center-toolbar-btn--primary:hover:not(:disabled) {
695
+ background: #fff;
696
+ border-color: var(--report-accent);
697
+ color: var(--report-accent);
698
+ }
699
+
700
+ .template-center-toolbar-btn--outline {
701
+ border: 1px solid var(--report-accent);
702
+ color: var(--report-accent);
703
+ background: #fff;
704
+ }
705
+
706
+ .template-center-toolbar-btn--outline:hover:not(:disabled) {
707
+ border-color: var(--report-accent);
708
+ color: var(--report-accent);
709
+ background: var(--report-accent-soft);
710
+ }
711
+
712
+ .template-center-toolbar-btn--delete {
713
+ border-color: var(--report-accent);
714
+ color: var(--report-accent);
715
+ background: #fff;
716
+ }
717
+
718
+ .template-center-toolbar-btn--delete:hover:not(:disabled) {
719
+ border-color: var(--report-accent);
720
+ color: var(--report-accent);
721
+ background: var(--report-accent-soft);
722
+ }
723
+
724
+ .template-center-toolbar-btn--ghost {
725
+ border: 1px solid #dcdfe6;
726
+ background: #fff;
727
+ color: #606266;
728
+ }
729
+
730
+ .template-center-toolbar-btn--ghost:hover:not(:disabled) {
731
+ border-color: var(--report-accent);
732
+ color: var(--report-accent);
733
+ background: var(--report-accent-soft);
734
+ }
735
+
736
+ .template-center-table.gen-ai-template-table-header :deep(.el-table__header th.el-table__cell),
737
+ .template-center-table.gen-ai-template-table-header
738
+ :deep(.el-table__header th.el-table__cell .cell) {
739
+ font-size: 15px;
740
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
741
+ font-weight: 500;
742
+ }
743
+
744
+ .template-center-table.gen-ai-template-table-header :deep(.el-table__body tr.el-table__row),
745
+ .template-center-table.gen-ai-template-table-header :deep(.el-table__body td.el-table__cell),
746
+ .template-center-table.gen-ai-template-table-header :deep(.el-table__body td.el-table__cell .cell),
747
+ .template-center-table.gen-ai-template-table-header :deep(.template-center-name),
748
+ .template-center-table.gen-ai-template-table-header :deep(.template-center-desc),
749
+ .template-center-table.gen-ai-template-table-header :deep(.template-center-updated),
750
+ .template-center-table.gen-ai-template-table-header :deep(.template-center-chapters-col),
751
+ .template-center-table.gen-ai-template-table-header :deep(.chapter-title-scroll),
752
+ .template-center-table.gen-ai-template-table-header
753
+ :deep(.chapter-title-scroll .chapter-title-scroll__text) {
754
+ font-size: 15px;
755
+ font-weight: 600;
756
+ color: #303133;
757
+ }
758
+
759
+ .template-center-page__body {
760
+ flex: 1;
761
+ min-height: 0;
762
+ display: flex;
763
+ flex-direction: column;
764
+ overflow: hidden;
765
+ margin-left: 30px;
766
+ padding: 32px 24px 24px;
767
+ box-sizing: border-box;
768
+ }
769
+
770
+ .template-center-hero {
771
+ flex-shrink: 0;
772
+ text-align: center;
773
+ margin-bottom: 28px;
774
+ }
775
+
776
+ .template-center-hero__title {
777
+ margin: 0 0 10px;
778
+ font-size: var(--gen-ai-fs-hero);
779
+ font-weight: var(--gen-ai-fw-bold);
780
+ color: #1a1a1a;
781
+ }
782
+
783
+ .template-center-hero__desc {
784
+ margin: 0;
785
+ }
786
+
787
+ .template-center-toolbar {
788
+ flex-shrink: 0;
789
+ display: flex;
790
+ flex-direction: column;
791
+ align-items: stretch;
792
+ gap: 12px;
793
+ margin-bottom: 20px;
794
+ }
795
+
796
+ .template-center-card-grid {
797
+ flex: 1;
798
+ min-height: 0;
799
+ display: grid;
800
+ grid-template-columns: repeat(4, minmax(0, 1fr));
801
+ grid-auto-rows: minmax(0, 1fr);
802
+ gap: 20px;
803
+ align-content: stretch;
804
+ align-items: stretch;
805
+ }
806
+
807
+ .template-center-card-grid > :deep(.template-center-card) {
808
+ height: 100%;
809
+ min-height: 0;
810
+ }
811
+
812
+ .template-center-page__body > .gen-ai-pagination {
813
+ flex-shrink: 0;
814
+ margin-top: 16px;
815
+ }
816
+
817
+ .template-center-page__body > .gen-ai-pagination :deep(.el-pagination__sizes) {
818
+ display: none;
819
+ }
820
+
821
+ @media (max-width: 1400px) {
822
+ .template-center-card-grid {
823
+ grid-template-columns: repeat(3, minmax(0, 1fr));
824
+ }
825
+ }
826
+
827
+ @media (max-width: 1100px) {
828
+ .template-center-card-grid {
829
+ grid-template-columns: repeat(2, minmax(0, 1fr));
830
+ }
831
+ }
832
+
833
+ @media (max-width: 720px) {
834
+ .template-center-card-grid {
835
+ grid-template-columns: 1fr;
836
+ }
837
+ }
838
+
839
+ .template-center-toolbar__tabs {
840
+ display: flex;
841
+ align-items: center;
842
+ justify-content: space-between;
843
+ }
844
+
845
+ .template-center-toolbar__tabs-left {
846
+ display: flex;
847
+ align-items: center;
848
+ }
849
+
850
+ .template-center-toolbar__tabs-right {
851
+ display: flex;
852
+ align-items: center;
853
+ }
854
+
855
+ .template-center-btn--create {
856
+ margin-right: 15px;
857
+ }
858
+
859
+ .template-center-toolbar__controls {
860
+ display: flex;
861
+ align-items: center;
862
+ width: 100%;
863
+ }
864
+
865
+ .template-center-toolbar__right {
866
+ display: flex;
867
+ align-items: center;
868
+ justify-content: flex-end;
869
+ gap: 12px;
870
+ flex-wrap: wrap;
871
+ margin-left: auto;
872
+ }
873
+
874
+ .template-center-field {
875
+ display: inline-flex;
876
+ align-items: center;
877
+ gap: 8px;
878
+ margin: 0;
879
+ }
880
+
881
+ .template-center-field__select {
882
+ width: 180px;
883
+ }
884
+
885
+ .template-center-field__select--lang {
886
+ width: 162px;
887
+ }
888
+
889
+ .template-center-field__input {
890
+ width: 270px;
891
+ }
892
+
893
+ .template-center-table {
894
+ width: 100%;
895
+ --el-table-border-color: transparent;
896
+ background: transparent;
897
+ overflow: visible;
898
+ }
899
+
900
+ .template-center-table :deep(.el-table__inner-wrapper),
901
+ .template-center-table :deep(.el-table__body-wrapper),
902
+ .template-center-table :deep(.el-table__header-wrapper),
903
+ .template-center-table :deep(.el-scrollbar__wrap),
904
+ .template-center-table :deep(.el-scrollbar__view),
905
+ .template-center-table :deep(.el-table__body),
906
+ .template-center-table :deep(.el-table__body tbody),
907
+ .template-center-table :deep(.el-table__body tr.el-table__row) {
908
+ overflow: visible !important;
909
+ }
910
+
911
+ .template-center-table :deep(.el-table__inner-wrapper::before) {
912
+ display: none;
913
+ }
914
+
915
+ .template-center-table :deep(.el-table__header th.el-table__cell) {
916
+ border-bottom: none;
917
+ background: transparent;
918
+ padding: 0 16px 12px;
919
+ vertical-align: bottom;
920
+ color: #303133;
921
+ }
922
+
923
+ .template-center-table :deep(.el-table__header th.el-table__cell .cell) {
924
+ padding: 0;
925
+ }
926
+
927
+ .template-center-table :deep(.el-table__header th.el-table__cell + th.el-table__cell .cell) {
928
+ position: relative;
929
+ padding-left: 16px;
930
+ }
931
+
932
+ .template-center-table :deep(.el-table__body-wrapper .el-table__body) {
933
+ border-collapse: separate;
934
+ border-spacing: 0 14px;
935
+ }
936
+
937
+ .template-center-table :deep(.el-table__body td.el-table__cell) {
938
+ vertical-align: top;
939
+ height: 1px;
940
+ padding: 20px 16px;
941
+ border-bottom: none;
942
+ background: #fff;
943
+ transition:
944
+ border-color 0.2s ease,
945
+ box-shadow 0.2s ease,
946
+ background-color 0.2s ease;
947
+ }
948
+
949
+ .template-center-table :deep(.el-table__body tr.el-table__row) {
950
+ position: relative;
951
+ z-index: 0;
952
+ background: #fff;
953
+ border-radius: 8px;
954
+ cursor: pointer;
955
+ transition: filter var(--el-transition-duration, 0.3s);
956
+ filter: drop-shadow(0 1px 6px rgba(0, 0, 0, 0.06));
957
+ }
958
+
959
+ .template-center-table :deep(.el-table__body tr.el-table__row:hover) {
960
+ z-index: 1;
961
+ filter: drop-shadow(0 1px 6px rgba(0, 0, 0, 0.06)) drop-shadow(0 0 12px rgba(0, 0, 0, 0.12));
962
+ }
963
+
964
+ .template-center-table :deep(.el-table__body tr.el-table__row:hover > td.el-table__cell) {
965
+ background-color: #fff !important;
966
+ }
967
+
968
+ .template-center-table :deep(.el-table__body-wrapper .cell) {
969
+ padding: 0;
970
+ }
971
+
972
+ .template-center-table :deep(.el-table__body td.el-table__cell:not(:first-child) > .cell) {
973
+ position: relative;
974
+ height: 100%;
975
+ box-sizing: border-box;
976
+ }
977
+
978
+ .template-center-table :deep(.el-table__body td.el-table__cell:not(:first-child) > .cell)::before {
979
+ content: '';
980
+ position: absolute;
981
+ left: 0;
982
+ top: 0;
983
+ bottom: 0;
984
+ width: 1px;
985
+ background: #f0f2f5;
986
+ }
987
+
988
+ .template-center-cell-body,
989
+ .template-center-cell-divider {
990
+ position: relative;
991
+ box-sizing: border-box;
992
+ }
993
+
994
+ .template-center-cell-divider {
995
+ width: 100%;
996
+ padding-left: 16px;
997
+ }
998
+
999
+ .template-center-cell-divider::before {
1000
+ display: none;
1001
+ }
1002
+
1003
+ .template-center-cell-divider--chapters {
1004
+ height: auto;
1005
+ }
1006
+
1007
+ .template-center-cell-divider--updated {
1008
+ overflow: visible;
1009
+ }
1010
+
1011
+ .template-center-cell-divider--actions {
1012
+ width: 100%;
1013
+ display: flex;
1014
+ align-items: center;
1015
+ justify-content: center;
1016
+ margin-left: 0;
1017
+ }
1018
+
1019
+ .template-center-table
1020
+ :deep(.el-table__body tr.el-table__row td.el-table__cell:last-child > .cell) {
1021
+ display: flex;
1022
+ align-items: center;
1023
+ justify-content: center;
1024
+ height: 100%;
1025
+ }
1026
+
1027
+ .template-center-table :deep(.el-table__body tr.el-table__row td.el-table__cell:first-child) {
1028
+ overflow: visible !important;
1029
+ border-radius: 8px 0 0 8px;
1030
+ border: 1px solid #ebeef5;
1031
+ border-right: none;
1032
+ }
1033
+
1034
+ .template-center-table
1035
+ :deep(.el-table__body tr.el-table__row td.el-table__cell:first-child > .cell) {
1036
+ overflow: visible !important;
1037
+ position: relative;
1038
+ }
1039
+
1040
+ .template-center-cell-body.template-center-cell-body--name {
1041
+ position: relative;
1042
+ min-height: 25px;
1043
+ }
1044
+
1045
+ .template-center-row-lock-anchor {
1046
+ position: absolute;
1047
+ left: 0;
1048
+ top: 0;
1049
+ width: 0;
1050
+ height: 0;
1051
+ pointer-events: none;
1052
+ }
1053
+
1054
+ .template-center-row-lock-trigger {
1055
+ line-height: 0;
1056
+ }
1057
+
1058
+ .template-center-row-lock-trigger .el-icon {
1059
+ width: 25px;
1060
+ height: 25px;
1061
+ }
1062
+
1063
+ .template-center-row-lock-trigger .el-icon svg {
1064
+ width: 25px;
1065
+ height: 25px;
1066
+ }
1067
+
1068
+ .template-center-name {
1069
+ display: block;
1070
+ }
1071
+
1072
+ .template-center-table :deep(.el-table__body tr.el-table__row td.el-table__cell:last-child) {
1073
+ border-radius: 0 8px 8px 0;
1074
+ border: 1px solid #ebeef5;
1075
+ border-left: none;
1076
+ }
1077
+
1078
+ .template-center-table :deep(.el-table__header th.template-col-author),
1079
+ .template-center-table :deep(.el-table__header th.template-col-updated),
1080
+ .template-center-table :deep(.el-table__body td.template-col-author),
1081
+ .template-center-table :deep(.el-table__body td.template-col-updated) {
1082
+ overflow: visible;
1083
+ }
1084
+
1085
+ .template-center-table :deep(.el-table__header th.template-col-author .cell),
1086
+ .template-center-table :deep(.el-table__header th.template-col-updated .cell),
1087
+ .template-center-table :deep(.el-table__body td.template-col-author .cell),
1088
+ .template-center-table :deep(.el-table__body td.template-col-updated .cell) {
1089
+ overflow: visible;
1090
+ }
1091
+
1092
+ .template-center-table :deep(.el-table__header th.template-col-updated .cell),
1093
+ .template-center-table :deep(.el-table__body td.template-col-updated .cell) {
1094
+ white-space: nowrap;
1095
+ }
1096
+
1097
+ .template-center-table
1098
+ :deep(.el-table__body tr.el-table__row td.el-table__cell:not(:first-child):not(:last-child)) {
1099
+ border-top: 1px solid #ebeef5;
1100
+ border-bottom: 1px solid #ebeef5;
1101
+ }
1102
+
1103
+ .template-center-name {
1104
+ display: block;
1105
+ }
1106
+
1107
+ .template-center-updated {
1108
+ display: block;
1109
+ line-height: var(--gen-ai-lh-normal);
1110
+ white-space: nowrap;
1111
+ }
1112
+
1113
+ .template-center-desc {
1114
+ margin: 0;
1115
+ line-height: var(--gen-ai-lh-relaxed);
1116
+ white-space: normal;
1117
+ }
1118
+
1119
+ .template-center-chapters-wrap {
1120
+ position: relative;
1121
+ box-sizing: border-box;
1122
+ overflow: hidden;
1123
+ }
1124
+
1125
+ .template-center-chapters {
1126
+ display: flex;
1127
+ align-items: flex-start;
1128
+ gap: 20px;
1129
+ box-sizing: border-box;
1130
+ height: 100%;
1131
+ }
1132
+
1133
+ .template-center-chapters-col {
1134
+ flex: 1;
1135
+ min-width: 0;
1136
+ margin: 0;
1137
+ padding: 0;
1138
+ list-style: none;
1139
+ }
1140
+
1141
+ .template-center-table
1142
+ :deep(.el-table__body tr.el-table__row:hover .chapter-title-scroll__ellipsis) {
1143
+ background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, #fff 45%);
1144
+ }
1145
+
1146
+ .template-center-chapters-col > li + li,
1147
+ .chapter-title-scroll + li,
1148
+ .chapter-title-scroll + .chapter-title-scroll {
1149
+ margin-top: 4px;
1150
+ }
1151
+
1152
+ .template-center-chapters--dialog {
1153
+ display: grid;
1154
+ grid-template-columns: repeat(3, minmax(0, 1fr));
1155
+ gap: 8px 28px;
1156
+ margin: 0;
1157
+ padding: 0;
1158
+ list-style: none;
1159
+ }
1160
+
1161
+ .template-center-chapters--dialog > li {
1162
+ white-space: normal;
1163
+ overflow: visible;
1164
+ }
1165
+
1166
+ .template-center-chapters-ellipsis {
1167
+ list-style: none;
1168
+ }
1169
+
1170
+ .template-center-chapters-ellipsis-btn {
1171
+ padding: 0;
1172
+ border: none;
1173
+ background: transparent;
1174
+ color: #c53355;
1175
+ font-size: var(--gen-ai-fs-xl);
1176
+ font-weight: var(--gen-ai-fw-black);
1177
+ height: var(--gen-ai-lh-chapter);
1178
+ line-height: var(--gen-ai-lh-chapter);
1179
+ letter-spacing: 0.05em;
1180
+ cursor: pointer;
1181
+ }
1182
+
1183
+ .template-center-chapters-ellipsis-btn:hover {
1184
+ color: #a82d48;
1185
+ text-decoration: underline;
1186
+ }
1187
+
1188
+ .template-center-author {
1189
+ font-size: var(--gen-ai-fs-sm);
1190
+ line-height: var(--gen-ai-lh-relaxed);
1191
+ color: #606266;
1192
+ }
1193
+
1194
+ .template-center-author > div {
1195
+ white-space: normal;
1196
+ word-break: break-word;
1197
+ }
1198
+
1199
+ .template-center-row-actions {
1200
+ display: flex;
1201
+ flex-direction: column;
1202
+ align-items: stretch;
1203
+ gap: 8px;
1204
+ width: 100%;
1205
+ max-width: 132px;
1206
+ }
1207
+
1208
+ /* //newww dialog — 查看詳情 / 處理觸發按鈕 */
1209
+ .template-center-row-actions .template-detail-dialog-trigger {
1210
+ width: 100%;
1211
+ min-width: 0;
1212
+ }
1213
+
1214
+ .template-center-row-actions .template-center-toolbar-btn {
1215
+ width: 100%;
1216
+ min-width: 0;
1217
+ }
1218
+
1219
+ /* //newww dialog — 查看詳情 / 處理彈窗(同頁 el-dialog) */
1220
+ .template-detail-form {
1221
+ --template-detail-label-w: 100px;
1222
+ --template-detail-field-gap: 16px;
1223
+ display: flex;
1224
+ flex-direction: column;
1225
+ gap: 20px;
1226
+ }
1227
+
1228
+ .template-detail-field {
1229
+ margin: 0;
1230
+ }
1231
+
1232
+ .template-detail-field--row {
1233
+ display: grid;
1234
+ grid-template-columns: var(--template-detail-label-w) minmax(0, 1fr);
1235
+ column-gap: var(--template-detail-field-gap);
1236
+ align-items: center;
1237
+ width: 100%;
1238
+ }
1239
+
1240
+ .template-detail-field--textarea-row {
1241
+ align-items: start;
1242
+ }
1243
+
1244
+ .template-detail-field--textarea-row .template-detail-label {
1245
+ padding-top: 8px;
1246
+ }
1247
+
1248
+ .template-detail-label {
1249
+ flex-shrink: 0;
1250
+ font-size: 14px;
1251
+ font-weight: 500;
1252
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
1253
+ color: #303133;
1254
+ white-space: nowrap;
1255
+ }
1256
+
1257
+ .template-detail-label__required {
1258
+ margin-left: 2px;
1259
+ color: #f56c6c;
1260
+ }
1261
+
1262
+ .template-detail-field__control {
1263
+ min-width: 0;
1264
+ width: 100%;
1265
+ }
1266
+
1267
+ .template-detail-field__control--lang {
1268
+ width: auto;
1269
+ max-width: 270px;
1270
+ }
1271
+
1272
+ .template-detail-source {
1273
+ font-size: 14px;
1274
+ color: #303133;
1275
+ }
1276
+
1277
+ .template-detail-field__input,
1278
+ .template-detail-field__textarea {
1279
+ width: 100%;
1280
+ }
1281
+
1282
+ .template-detail-field--textarea-row .template-detail-field__control {
1283
+ position: relative;
1284
+ padding-bottom: 18px;
1285
+ }
1286
+
1287
+ .template-detail-field__input :deep(.el-input__wrapper),
1288
+ .template-detail-field__textarea :deep(.el-textarea__inner) {
1289
+ font-size: clamp(13px, calc(14 * 100vw / 1920), 14px);
1290
+ }
1291
+
1292
+ .template-detail-field__input :deep(.el-input__count),
1293
+ .template-detail-field__textarea :deep(.el-input__count) {
1294
+ right: 0;
1295
+ bottom: -18px;
1296
+ background: transparent;
1297
+ font-size: 12px;
1298
+ color: #909399;
1299
+ }
1300
+
1301
+ .template-detail-chapter {
1302
+ padding: 16px;
1303
+ border: 1px solid #ebeef5;
1304
+ border-radius: 4px;
1305
+ }
1306
+
1307
+ .template-detail-chapter__title {
1308
+ margin: 0 0 16px;
1309
+ font-size: 16px;
1310
+ font-weight: 500;
1311
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
1312
+ color: #606266;
1313
+ }
1314
+
1315
+ .template-detail-process {
1316
+ padding: 20px 16px 16px;
1317
+ border: 1px solid #ebeef5;
1318
+ border-radius: 4px;
1319
+ }
1320
+
1321
+ .template-detail-process__row {
1322
+ align-items: center;
1323
+ }
1324
+
1325
+ .template-detail-process__radios {
1326
+ display: flex;
1327
+ align-items: center;
1328
+ gap: 24px;
1329
+ }
1330
+
1331
+ .template-detail-process__actions {
1332
+ display: flex;
1333
+ align-items: center;
1334
+ justify-content: center;
1335
+ gap: 16px;
1336
+ margin-top: 8px;
1337
+ }
1338
+
1339
+ .template-detail-btn {
1340
+ display: inline-flex;
1341
+ align-items: center;
1342
+ justify-content: center;
1343
+ box-sizing: border-box;
1344
+ min-width: 120px;
1345
+ height: 32px;
1346
+ padding: 0 20px;
1347
+ border-radius: 4px;
1348
+ font-size: 14px;
1349
+ line-height: 1;
1350
+ cursor: pointer;
1351
+ transition: all 0.2s ease;
1352
+ }
1353
+
1354
+ .template-detail-btn--primary {
1355
+ border: 1px solid #c53355;
1356
+ background: #c53355;
1357
+ color: #fff;
1358
+ font-weight: 500;
1359
+ }
1360
+
1361
+ .template-detail-btn--primary:hover {
1362
+ background: #fff;
1363
+ color: #c53355;
1364
+ }
1365
+
1366
+ .template-detail-btn--ghost {
1367
+ border: 1px solid #dcdfe6;
1368
+ background: #fff;
1369
+ color: #606266;
1370
+ }
1371
+
1372
+ .template-detail-btn--ghost:hover {
1373
+ border-color: #c53355;
1374
+ color: #c53355;
1375
+ background: #fdf0f2;
1376
+ }
1377
+
1378
+ .template-detail-approval {
1379
+ margin-top: 24px;
1380
+ border: 1px solid #dcdfe6;
1381
+ border-radius: 4px;
1382
+ overflow: hidden;
1383
+ }
1384
+
1385
+ .template-detail-approval__title {
1386
+ margin: 0;
1387
+ padding: 12px 16px;
1388
+ font-size: 16px;
1389
+ font-weight: 500;
1390
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
1391
+ color: #606266;
1392
+ background: transparent;
1393
+ border-bottom: 1px solid #dcdfe6;
1394
+ }
1395
+
1396
+ .template-detail-approval-table {
1397
+ width: 100%;
1398
+ }
1399
+
1400
+ .template-detail-approval-table :deep(.el-table--border) {
1401
+ border: none;
1402
+ }
1403
+
1404
+ .template-detail-approval-table :deep(.el-table--border::before),
1405
+ .template-detail-approval-table :deep(.el-table--border::after),
1406
+ .template-detail-approval-table :deep(.el-table__inner-wrapper::before),
1407
+ .template-detail-approval-table :deep(.el-table__inner-wrapper::after) {
1408
+ display: none;
1409
+ }
1410
+
1411
+ .template-detail-approval-table :deep(.el-table__border-left-patch) {
1412
+ display: none;
1413
+ }
1414
+
1415
+ .template-detail-approval-table :deep(.el-table__header th.el-table__cell) {
1416
+ background: rgb(249, 250, 251);
1417
+ color: #606266;
1418
+ font-weight: 500;
1419
+ font-size: 16px;
1420
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
1421
+ }
1422
+
1423
+ .template-detail-approval-table :deep(.el-table__body tr.el-table__row) {
1424
+ color: #303133;
1425
+ font-size: 15px;
1426
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
1427
+ }
1428
+ </style>
1429
+
1430
+ <style>
1431
+ .template-center-tab-badge .el-badge__content.el-badge__content--danger.is-fixed {
1432
+ margin-right: 10px !important;
1433
+ margin-top: -5px !important;
1434
+ }
1435
+
1436
+ /* //newww dialog — 查看詳情 / 處理彈窗(append-to-body,需全域樣式) */
1437
+ .template-detail-overlay {
1438
+ overflow: auto;
1439
+ }
1440
+
1441
+ .template-detail-dialog.el-dialog {
1442
+ --el-dialog-padding-primary: 0;
1443
+ width: 80% !important;
1444
+ max-width: 80%;
1445
+ margin-top: 5vh;
1446
+ border-radius: 4px;
1447
+ overflow: hidden;
1448
+ display: flex;
1449
+ flex-direction: column;
1450
+ max-height: 90vh;
1451
+ }
1452
+
1453
+ .template-detail-dialog .el-dialog__header,
1454
+ .template-detail-dialog__header {
1455
+ display: flex;
1456
+ align-items: center;
1457
+ justify-content: space-between;
1458
+ gap: 16px;
1459
+ margin: 0;
1460
+ padding: 14px 16px 14px 20px;
1461
+ background: transparent;
1462
+ border-bottom: 1px solid #ebeef5;
1463
+ flex-shrink: 0;
1464
+ }
1465
+
1466
+ .template-detail-dialog__title,
1467
+ .template-detail-dialog .el-dialog__title {
1468
+ flex: 1;
1469
+ min-width: 0;
1470
+ color: #303133;
1471
+ font-size: 18px;
1472
+ font-weight: 600;
1473
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
1474
+ line-height: 1.4;
1475
+ }
1476
+
1477
+ .template-detail-dialog__close {
1478
+ flex-shrink: 0;
1479
+ width: 32px;
1480
+ height: 32px;
1481
+ margin: 0;
1482
+ padding: 0;
1483
+ border: none;
1484
+ background: transparent;
1485
+ color: #303133;
1486
+ font-size: 18px;
1487
+ line-height: 1;
1488
+ cursor: pointer;
1489
+ }
1490
+
1491
+ .template-detail-dialog__close:hover {
1492
+ color: #606266;
1493
+ }
1494
+
1495
+ .template-detail-dialog .el-dialog__body {
1496
+ flex: 1;
1497
+ min-height: 0;
1498
+ padding: 24px 20px;
1499
+ background: #fff;
1500
+ overflow-y: auto;
1501
+ }
1502
+
1503
+ /* 章節結構彈窗(append-to-body,需全域樣式) */
1504
+ .template-center-chapter-overlay {
1505
+ overflow: auto;
1506
+ }
1507
+
1508
+ .template-center-chapter-dialog.el-dialog {
1509
+ --el-dialog-padding-primary: 0;
1510
+ width: 60%;
1511
+ max-width: 80%;
1512
+ max-height: 70%;
1513
+ margin-top: 5%;
1514
+ margin-left: 21%;
1515
+ margin-right: auto;
1516
+ border-radius: 4px;
1517
+ overflow: hidden;
1518
+ display: flex;
1519
+ flex-direction: column;
1520
+ }
1521
+
1522
+ .template-center-chapter-dialog .el-dialog__header,
1523
+ .template-center-chapter-dialog__header {
1524
+ display: flex;
1525
+ align-items: center;
1526
+ justify-content: space-between;
1527
+ gap: 16px;
1528
+ margin: 0;
1529
+ padding: 14px 16px 14px 20px;
1530
+ background: transparent;
1531
+ border-bottom: 1px solid #ebeef5;
1532
+ flex-shrink: 0;
1533
+ }
1534
+
1535
+ .template-center-chapter-dialog__title,
1536
+ .template-center-chapter-dialog .el-dialog__title {
1537
+ flex: 1;
1538
+ min-width: 0;
1539
+ color: #303133;
1540
+ font-size: 18px;
1541
+ font-weight: 600;
1542
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
1543
+ line-height: 1.4;
1544
+ }
1545
+
1546
+ .template-center-chapter-dialog__close {
1547
+ flex-shrink: 0;
1548
+ width: 32px;
1549
+ height: 32px;
1550
+ margin: 0;
1551
+ padding: 0;
1552
+ border: none;
1553
+ background: transparent;
1554
+ color: #303133;
1555
+ font-size: 18px;
1556
+ font-weight: 400;
1557
+ line-height: 1;
1558
+ cursor: pointer;
1559
+ display: inline-flex;
1560
+ align-items: center;
1561
+ justify-content: center;
1562
+ }
1563
+
1564
+ .template-center-chapter-dialog__close:hover {
1565
+ color: #606266;
1566
+ }
1567
+
1568
+ .template-center-chapter-dialog__close:focus-visible {
1569
+ outline: 2px solid rgba(48, 49, 51, 0.35);
1570
+ outline-offset: 2px;
1571
+ }
1572
+
1573
+ .template-center-chapter-dialog .el-dialog__body {
1574
+ flex: 1;
1575
+ min-height: 60vh;
1576
+ padding: 24px 20px;
1577
+ background: #fff;
1578
+ color: #606266;
1579
+ font-size: var(--gen-ai-fs-chapter);
1580
+ line-height: var(--gen-ai-lh-normal);
1581
+ overflow-y: auto;
1582
+ }
1583
+ </style>