aegon-lv82 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/package.json +12 -0
  2. package/src/App.vue +44 -0
  3. package/src/api/index.ts +19 -0
  4. package/src/api/modules/build-report/reportEditor.ts +526 -0
  5. package/src/api/modules/build-report/reportPermission.ts +64 -0
  6. package/src/api/modules/index.ts +69 -0
  7. package/src/api/modules/industry/green_mortgage.ts +42 -0
  8. package/src/api/modules/user/index.ts +4 -0
  9. package/src/api/request.ts +124 -0
  10. package/src/assets/sample-access-icon.png +0 -0
  11. package/src/assets/sample-pie-chart.png +0 -0
  12. package/src/assets/vue.svg +1 -0
  13. package/src/components/CapsuleScrollbar.vue +7 -0
  14. package/src/components/Export/ExcelExport.vue +592 -0
  15. package/src/components/Export/ExcelExport2.vue +494 -0
  16. package/src/components/Export/ExcelExport3.vue +342 -0
  17. package/src/components/Export/ExcelExport4.vue +665 -0
  18. package/src/components/Export/excelExport.js +547 -0
  19. package/src/components/Export/excelExport.ts +551 -0
  20. package/src/components/GEN-AI/index.vue +140 -0
  21. package/src/components/GEN-AI/index1.vue +422 -0
  22. package/src/components/GEN-AI/index10.vue +5 -0
  23. package/src/components/GEN-AI/index2.vue +548 -0
  24. package/src/components/GEN-AI/index3.vue +605 -0
  25. package/src/components/GEN-AI/index4.vue +609 -0
  26. package/src/components/GEN-AI/index5.vue +558 -0
  27. package/src/components/GEN-AI/index6.vue +634 -0
  28. package/src/components/GEN-AI/index7.vue +688 -0
  29. package/src/components/GEN-AI/index8.vue +458 -0
  30. package/src/components/GEN-AI/index9.vue +1217 -0
  31. package/src/components/GEN-AI/types.ts +12 -0
  32. package/src/components/GEN-AI/utils.ts +42 -0
  33. package/src/components/HelloWorld.vue +41 -0
  34. package/src/components/PageCard.vue +124 -0
  35. package/src/components/PageHeader.vue +32 -0
  36. package/src/components/backup/index5 copy.vue +556 -0
  37. package/src/components/backup/index5.vue +620 -0
  38. package/src/components/backup/index9 copy.vue +1029 -0
  39. package/src/components/backup/index9-pro.vue +1065 -0
  40. package/src/components/backup/index9.vue +1057 -0
  41. package/src/directives/btnLoading.ts +455 -0
  42. package/src/directives/debounce copy.ts +670 -0
  43. package/src/directives/debounce.ts +98 -0
  44. package/src/directives/index.ts +25 -0
  45. package/src/layouts/MainLayout.vue +129 -0
  46. package/src/main.ts +76 -0
  47. package/src/router/index.ts +142 -0
  48. package/src/router/menus.ts +28 -0
  49. package/src/store/modules/gen-ai/index.ts +1 -0
  50. package/src/store/modules/gen-ai/reportBuild.ts +37 -0
  51. package/src/style.css +79 -0
  52. package/src/styles/report-config-popover.css +39 -0
  53. package/src/styles/report-icon-btn.css +19 -0
  54. package/src/test/mock.ts +101 -0
  55. package/src/test/test1.vue +402 -0
  56. package/src/test/test2.vue +1689 -0
  57. package/src/types/gen-ai/build-report/reportEditor.ts +684 -0
  58. package/src/utils/function.ts +46 -0
  59. package/src/utils/gen-ai-navigation.ts +1 -0
  60. package/src/utils/gen-ai-scroll.ts +1 -0
  61. package/src/utils/openDataLoaderWordExport.ts +33 -0
  62. package/src/utils/pageScrollbar.ts +115 -0
  63. package/src/utils/randomTranscode.ts +87 -0
  64. package/src/utils/reportPdfExport.ts +44 -0
  65. package/src/views/Blank.vue +68 -0
  66. package/src/views/Home.vue +29 -0
  67. package/src/views/TemplateCenter/Knowledge.ts +83 -0
  68. package/src/views/TemplateCenter/data.d.ts +10 -0
  69. package/src/views/TemplateCenter/index.vue +1128 -0
  70. package/src/views/TemplateCenter/service.ts +69 -0
  71. package/src/views/gen-ai/build-report/chartStreamRefs.ts +174 -0
  72. package/src/views/gen-ai/build-report/coherence-check/index.vue +559 -0
  73. package/src/views/gen-ai/build-report/components/ReportChapterWorkspace.vue +4258 -0
  74. package/src/views/gen-ai/build-report/components/ReportConfigPanel.vue +727 -0
  75. package/src/views/gen-ai/build-report/components/ReportGenerateSettingsPanel.vue +2361 -0
  76. package/src/views/gen-ai/build-report/components/ReportGeneratingPanel.vue +1765 -0
  77. package/src/views/gen-ai/build-report/components/ReportPromptPanel.vue +614 -0
  78. package/src/views/gen-ai/build-report/components/ReportSourceFileSummary.vue +656 -0
  79. package/src/views/gen-ai/build-report/components/ReportSourcesPanel.vue +1327 -0
  80. package/src/views/gen-ai/build-report/components/ReportTemplateDetail.vue +191 -0
  81. package/src/views/gen-ai/build-report/components/ReportTemplateEditorFormPanel.vue +667 -0
  82. package/src/views/gen-ai/build-report/components/ReportTemplatePicker.vue +302 -0
  83. package/src/views/gen-ai/build-report/components/ReportWorkflowSidebar.vue +148 -0
  84. package/src/views/gen-ai/build-report/components/paramsSetting.vue +886 -0
  85. package/src/views/gen-ai/build-report/components/reportEdit.vue +2283 -0
  86. package/src/views/gen-ai/build-report/index.vue +856 -0
  87. package/src/views/gen-ai/build-report/report-domain.ts +1133 -0
  88. package/src/views/gen-ai/build-report/report-template-data.ts +295 -0
  89. package/src/views/gen-ai/build-report/useReportWorkflow.ts +1408 -0
  90. package/src/views/gen-ai/common/assets/NotoSans-SC.woff2 +0 -0
  91. package/src/views/gen-ai/common/assets/NotoSans-TC.woff2 +0 -0
  92. package/src/views/gen-ai/common/assets/NotoSans.woff2 +0 -0
  93. package/src/views/gen-ai/common/assets/fonts.css +278 -0
  94. package/src/views/gen-ai/common/assets/pagination.css +163 -0
  95. package/src/views/gen-ai/common/directives/btnLoading.ts +455 -0
  96. package/src/views/gen-ai/common/directives/debounce.ts +98 -0
  97. package/src/views/gen-ai/common/directives/index.ts +30 -0
  98. package/src/views/gen-ai/common/en.md +417 -0
  99. package/src/views/gen-ai/common/index.ts +36 -0
  100. package/src/views/gen-ai/common/types/component-expose.ts +9 -0
  101. package/src/views/gen-ai/common/utils/api-res-status.ts +7 -0
  102. package/src/views/gen-ai/common/utils/navigation.ts +59 -0
  103. package/src/views/gen-ai/common/utils/remote-request.ts +45 -0
  104. package/src/views/gen-ai/common/utils/scroll.ts +577 -0
  105. package/src/views/gen-ai/components/AIExport/docx-export.ts +950 -0
  106. package/src/views/gen-ai/components/AIExport/docxExport.ts +2566 -0
  107. package/src/views/gen-ai/components/AIExport/index.ts +52 -0
  108. package/src/views/gen-ai/components/AIExport/parseMarkdownHtml.ts +649 -0
  109. package/src/views/gen-ai/components/CapsuleScrollbar.vue +149 -0
  110. package/src/views/gen-ai/components/ChapterTitleScroll.vue +468 -0
  111. package/src/views/gen-ai/components/EntryHeroNavCards.vue +379 -0
  112. package/src/views/gen-ai/components/GenAiPagination.vue +52 -0
  113. package/src/views/gen-ai/components/ModelCard.vue +1114 -0
  114. package/src/views/gen-ai/components/Preview/FilePreviewPage.vue +947 -0
  115. package/src/views/gen-ai/components/Preview/PreviewCapsuleScroll.vue +205 -0
  116. package/src/views/gen-ai/components/Preview/PreviewOutlineTree.vue +148 -0
  117. package/src/views/gen-ai/components/Preview/index.ts +11 -0
  118. package/src/views/gen-ai/components/Preview/preview.ts +722 -0
  119. package/src/views/gen-ai/components/PromptField.vue +505 -0
  120. package/src/views/gen-ai/components/RecentReportsTable.vue +974 -0
  121. package/src/views/gen-ai/components/ReportCollapsibleSection.vue +172 -0
  122. package/src/views/gen-ai/components/ReportHistoryList.vue +637 -0
  123. package/src/views/gen-ai/components/ReportSectionConfirmPopover.vue +251 -0
  124. package/src/views/gen-ai/components/editor/ChapterTiptapEditor.vue +3139 -0
  125. package/src/views/gen-ai/components/editor/StructureBlockView.vue +441 -0
  126. package/src/views/gen-ai/components/editor/TiptapToolbar.vue +1036 -0
  127. package/src/views/gen-ai/components/editor/TypewriterDocEditor.vue +398 -0
  128. package/src/views/gen-ai/components/editor/_StructureOrgNode.vue +326 -0
  129. package/src/views/gen-ai/components/editor/index.ts +12 -0
  130. package/src/views/gen-ai/components/editor/parseMarkdownHtml.ts +667 -0
  131. package/src/views/gen-ai/gen-entry/api/index.ts +32 -0
  132. package/src/views/gen-ai/gen-entry/index.vue +287 -0
  133. package/src/views/gen-ai/gen-entry/types/index.ts +4 -0
  134. package/src/views/gen-ai/i.mark +2 -0
  135. package/src/views/gen-ai/management-center/index.vue +1815 -0
  136. package/src/views/gen-ai/management-center/mockData.ts +248 -0
  137. package/src/views/gen-ai/management-center/types/index.ts +37 -0
  138. package/src/views/gen-ai/model-manager/TemplateEditorFormPanel.vue +661 -0
  139. package/src/views/gen-ai/model-manager/api/index.ts +32 -0
  140. package/src/views/gen-ai/model-manager/api/mockApi.ts +33 -0
  141. package/src/views/gen-ai/model-manager/index.vue +1583 -0
  142. package/src/views/gen-ai/model-manager/mockData.ts +177 -0
  143. package/src/views/gen-ai/model-manager/template-detail-shared.ts +154 -0
  144. package/src/views/gen-ai/model-manager/template-editor.vue +345 -0
  145. package/src/views/gen-ai/model-manager/types/index.ts +40 -0
  146. package/src/views/gen-ai/model-manager/utils.ts +25 -0
  147. package/src/views/greenMortgage.vue +1834 -0
  148. package/src/vite-env.d.ts +46 -0
@@ -0,0 +1,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>