aegon-gen 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 (86) hide show
  1. package/package.json +12 -0
  2. package/src/App.vue +3 -0
  3. package/src/api/index.ts +19 -0
  4. package/src/api/modules/gen-ai/gen-entry/index.ts +30 -0
  5. package/src/api/modules/gen-ai/model-manager/index.ts +42 -0
  6. package/src/api/modules/gen-ai/model-manager/mockApi.ts +33 -0
  7. package/src/api/modules/index.ts +98 -0
  8. package/src/api/modules/user/index.ts +4 -0
  9. package/src/api/request.ts +102 -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 +93 -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 +142 -0
  21. package/src/components/GEN-AI/index1.vue +456 -0
  22. package/src/components/GEN-AI/index10.vue +5 -0
  23. package/src/components/GEN-AI/index2.vue +568 -0
  24. package/src/components/GEN-AI/index3.vue +623 -0
  25. package/src/components/GEN-AI/index4.vue +629 -0
  26. package/src/components/GEN-AI/index5.vue +578 -0
  27. package/src/components/GEN-AI/index6.vue +656 -0
  28. package/src/components/GEN-AI/index7.vue +717 -0
  29. package/src/components/GEN-AI/index8.vue +405 -0
  30. package/src/components/GEN-AI/index9.vue +1065 -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 +7 -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/components/el-date-picker.vue +64 -0
  42. package/src/directives/btnLoading.ts +427 -0
  43. package/src/directives/debounce copy.ts +670 -0
  44. package/src/directives/debounce.ts +98 -0
  45. package/src/directives/index.ts +25 -0
  46. package/src/layouts/MainLayout.vue +101 -0
  47. package/src/main.ts +85 -0
  48. package/src/router/index.ts +76 -0
  49. package/src/router/menus.ts +28 -0
  50. package/src/style.css +79 -0
  51. package/src/styles/_variables.scss +24 -0
  52. package/src/styles/app-button.css +26 -0
  53. package/src/styles/element-overrides.css +23 -0
  54. package/src/styles/global.css +44 -0
  55. package/src/styles/index.scss +1 -0
  56. package/src/styles/page-card.css +21 -0
  57. package/src/styles/variables.css +26 -0
  58. package/src/test/mock.ts +101 -0
  59. package/src/test/test1.vue +402 -0
  60. package/src/test/test2.vue +1689 -0
  61. package/src/types/gen-ai/gen-entry/index.ts +17 -0
  62. package/src/types/gen-ai/model-manager/index.ts +19 -0
  63. package/src/utils/docxExport.ts +1610 -0
  64. package/src/utils/gen-ai-navigation.ts +37 -0
  65. package/src/utils/gen-ai-scroll.ts +455 -0
  66. package/src/utils/openDataLoaderWordExport.ts +33 -0
  67. package/src/utils/pageScrollbar.ts +115 -0
  68. package/src/utils/randomTranscode.ts +87 -0
  69. package/src/utils/reportPdfExport.ts +44 -0
  70. package/src/views/AdminCenter/index.vue +817 -0
  71. package/src/views/Blank.vue +68 -0
  72. package/src/views/Home.vue +29 -0
  73. package/src/views/ReportCenter/index.vue +1380 -0
  74. package/src/views/TemplateCenter/Knowledge.ts +83 -0
  75. package/src/views/TemplateCenter/data.d.ts +10 -0
  76. package/src/views/TemplateCenter/index.vue +1205 -0
  77. package/src/views/TemplateCenter/service.ts +69 -0
  78. package/src/views/gen-ai/components/RecentReportsTable.vue +193 -0
  79. package/src/views/gen-ai/gen-entry/index.vue +309 -0
  80. package/src/views/gen-ai/gen-entry/mockData.ts +160 -0
  81. package/src/views/gen-ai/management-center/index.vue +53 -0
  82. package/src/views/gen-ai/model-manager/ChapterTitleScroll.vue +275 -0
  83. package/src/views/gen-ai/model-manager/index.vue +1205 -0
  84. package/src/views/gen-ai/model-manager/mockData.ts +122 -0
  85. package/src/views/gen-ai/report-center/index.vue +158 -0
  86. package/src/vite-env.d.ts +38 -0
@@ -0,0 +1,1205 @@
1
+ <template>
2
+ <div class="template-center-page">
3
+ <PageHeader title="IR-DEMO" />
4
+
5
+ <main ref="pageBodyRef" class="template-center-page__body">
6
+ <section class="template-center-hero">
7
+ <h2 class="template-center-hero__title">模板中心</h2>
8
+ <p class="template-center-hero__desc">所有已配置完畢的報告模板將會在此顯示</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 type="button"
15
+ class="template-center-toolbar-btn template-center-toolbar-btn--primary template-center-btn--create"
16
+ @click="handleCreate">
17
+ 新建模板
18
+ </button>
19
+ <el-badge :value="pendingCount" :max="99" type="danger"
20
+ :badge-style="{ marginRight: '10px', marginTop: '-5px' }" class="template-center-tab-badge">
21
+ <button type="button" class="template-center-toolbar-btn template-center-toolbar-btn--outline">
22
+ 待審批模板
23
+ </button>
24
+ </el-badge>
25
+ </div>
26
+ <div class="template-center-toolbar__tabs-right">
27
+ <button type="button" class="template-center-toolbar-btn template-center-toolbar-btn--ghost"
28
+ @click="openRecycleBin">
29
+ <el-icon>
30
+ <Delete />
31
+ </el-icon>
32
+ 回收站
33
+ </button>
34
+ </div>
35
+ </div>
36
+
37
+ <div class="template-center-toolbar__controls">
38
+ <div class="template-center-toolbar__right">
39
+ <label class="template-center-field">
40
+ <span class="template-center-field__label">語言</span>
41
+ <el-select v-model="language" class="template-center-field__select template-center-field__select--lang">
42
+ <el-option v-for="item in languageOptions" :key="item.value" :label="item.label" :value="item.value" />
43
+ </el-select>
44
+ </label>
45
+
46
+ <label class="template-center-field">
47
+ <span class="template-center-field__label">單位</span>
48
+ <el-select v-model="unitFilter" placeholder="" clearable class="template-center-field__select">
49
+ <el-option v-for="item in unitOptions" :key="item" :label="item" :value="item" />
50
+ </el-select>
51
+ </label>
52
+
53
+ <label class="template-center-field">
54
+ <span class="template-center-field__label">Search:</span>
55
+ <el-input v-model="keyword" clearable class="template-center-field__input" />
56
+ </label>
57
+
58
+ <button type="button" class="template-center-toolbar-btn template-center-toolbar-btn--primary"
59
+ @click="handleRefresh">
60
+ Refresh
61
+ </button>
62
+ </div>
63
+ </div>
64
+ </section>
65
+
66
+ <el-table ref="tableRef" :data="paginatedTemplates" class="template-center-table">
67
+ <el-table-column label="模板名稱" min-width="140">
68
+ <template #default="{ row }">
69
+ <div class="template-center-cell-body template-center-cell-body--name">
70
+ <span :ref="(el) => setLockAnchor(row.id, el as HTMLElement | null)"
71
+ class="template-center-row-lock-anchor" aria-hidden="true" />
72
+ <span class="template-center-name">{{ row.name }}</span>
73
+ </div>
74
+ </template>
75
+ </el-table-column>
76
+
77
+ <el-table-column label="模板描述" min-width="280">
78
+ <template #default="{ row }">
79
+ <div class="template-center-cell-divider">
80
+ <p class="template-center-desc">{{ row.description }}</p>
81
+ </div>
82
+ </template>
83
+ </el-table-column>
84
+
85
+ <el-table-column label="章節結構" min-width="320">
86
+ <template #default="{ row }">
87
+ <div class="template-center-cell-divider template-center-cell-divider--chapters">
88
+ <div class="template-center-chapters-wrap" :style="chapterWrapStyle(row.chapters)">
89
+ <div v-for="cols in [splitChapterColumns(row.chapters)]" :key="row.id" class="template-center-chapters">
90
+ <ul class="template-center-chapters-col">
91
+ <ChapterTitleScroll v-for="(chapter, index) in cols.left" :key="`l-${row.id}-${index}`"
92
+ :title="chapter" />
93
+ </ul>
94
+ <ul class="template-center-chapters-col">
95
+ <ChapterTitleScroll v-for="(chapter, index) in cols.right" :key="`r-${row.id}-${index}`"
96
+ :title="chapter" />
97
+ <li v-if="cols.hasMore" class="template-center-chapters-ellipsis">
98
+ <button type="button" class="template-center-chapters-ellipsis-btn" title="查看全部章節"
99
+ @click="openChapterDialog(row)">
100
+ ...
101
+ </button>
102
+ </li>
103
+ </ul>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ </template>
108
+ </el-table-column>
109
+
110
+ <!-- 模板作者 + 最後更新時間等宽;250px 可容納「作者:王明輝(風險管理部)」單行 -->
111
+ <el-table-column label="模板作者" width="250" class-name="template-col-author">
112
+ <template #default="{ row }">
113
+ <div class="template-center-cell-divider">
114
+ <div class="template-center-author">
115
+ <div>作者:{{ row.author }}</div>
116
+ <div>審批:{{ row.approver }}</div>
117
+ </div>
118
+ </div>
119
+ </template>
120
+ </el-table-column>
121
+
122
+ <el-table-column label="最後更新時間" width="250" class-name="template-col-updated">
123
+ <template #default="{ row }">
124
+ <div class="template-center-cell-divider template-center-cell-divider--updated">
125
+ <span class="template-center-updated">{{ row.updatedAt }}</span>
126
+ </div>
127
+ </template>
128
+ </el-table-column>
129
+
130
+ <el-table-column label="" width="132" align="center" class-name="template-col-actions">
131
+ <template #default="{ row }">
132
+ <div class="template-center-cell-divider template-center-cell-divider--actions">
133
+ <div class="template-center-row-actions">
134
+ <button type="button" class="template-center-toolbar-btn template-center-toolbar-btn--ghost"
135
+ @click="handleEdit(row)">
136
+ {{ getEditActionLabel(row) }}
137
+ </button>
138
+ <button type="button" class="template-center-toolbar-btn template-center-toolbar-btn--ghost"
139
+ @click="toggleLock(row)">
140
+ {{ getLockActionLabel(row) }}
141
+ </button>
142
+ <button type="button"
143
+ class="template-center-toolbar-btn template-center-toolbar-btn--outline template-center-toolbar-btn--delete"
144
+ @click="handleDelete(row)">
145
+ 刪除
146
+ </button>
147
+ </div>
148
+ </div>
149
+ </template>
150
+ </el-table-column>
151
+ </el-table>
152
+
153
+ <div class="template-center-pagination">
154
+ <el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[5, 10, 20]"
155
+ layout="prev, pager, next, sizes, ->, jumper, total" :total="filteredTemplates.length" background />
156
+ </div>
157
+
158
+ <el-dialog v-model="chapterDialogVisible" width="60%" append-to-body :z-index="10000" :show-close="false"
159
+ modal-class="template-center-chapter-overlay" class="template-center-chapter-dialog"
160
+ header-class="template-center-chapter-dialog__header">
161
+ <template #header="{ close, titleId, titleClass }">
162
+ <span :id="titleId" :class="['template-center-chapter-dialog__title', titleClass]">章節結構</span>
163
+ <button type="button" class="template-center-chapter-dialog__close" aria-label="關閉" @click="close">
164
+ ×
165
+ </button>
166
+ </template>
167
+ <ul class="template-center-chapters template-center-chapters--dialog">
168
+ <li v-for="(chapter, index) in chapterDialogChapters" :key="`dlg-${index}`">{{ chapter }}</li>
169
+ </ul>
170
+ </el-dialog>
171
+ </main>
172
+
173
+ <Teleport to="body">
174
+ <div v-show="!chapterDialogVisible" class="template-center-row-lock-layer">
175
+ <span v-for="row in paginatedTemplates" v-show="lockPositions[row.id]" :key="`lock-${row.id}`"
176
+ class="template-center-row-lock-trigger template-center-row-lock-trigger--portal"
177
+ :title="row.locked ? `${row.locker} 已鎖定` : '模板未鎖定'" :style="getLockStyle(row)">
178
+ <el-icon :size="25" :color="row.locked ? '#909399' : '#c53355'">
179
+ <Lock v-if="row.locked" />
180
+ <Unlock v-else />
181
+ </el-icon>
182
+ </span>
183
+ </div>
184
+ </Teleport>
185
+ </div>
186
+ </template>
187
+
188
+ <script setup lang="ts">
189
+ import modelManagerApi from '@/api/modules/gen-ai/model-manager/index'
190
+ import PageHeader from '@/components/PageHeader.vue'
191
+ import type { TemplateItem } from '@/types/gen-ai/model-manager/index'
192
+ import { Delete, Lock, Unlock } from '@element-plus/icons-vue'
193
+ import { ElMessage, ElMessageBox, type TableInstance } from 'element-plus'
194
+ import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch, type CSSProperties } from 'vue'
195
+ import ChapterTitleScroll from './ChapterTitleScroll.vue'
196
+ import { MOCK_TEMPLATES } from './mockData'
197
+
198
+ const CHAPTER_DISPLAY_MAX = 14
199
+ const CHAPTER_FIRST_COL_COUNT = 7
200
+ const CHAPTER_LINE_HEIGHT = 19.5
201
+ const CHAPTER_ROW_GAP = 4
202
+
203
+ interface ChapterColumns {
204
+ left: string[]
205
+ right: string[]
206
+ hasMore: boolean
207
+ }
208
+
209
+ function splitChapterColumns(chapters: string[]): ChapterColumns {
210
+ const visible =
211
+ chapters.length > CHAPTER_DISPLAY_MAX ? chapters.slice(0, CHAPTER_DISPLAY_MAX - 1) : chapters
212
+
213
+ return {
214
+ left: visible.slice(0, CHAPTER_FIRST_COL_COUNT),
215
+ right: visible.slice(CHAPTER_FIRST_COL_COUNT),
216
+ hasMore: chapters.length > CHAPTER_DISPLAY_MAX,
217
+ }
218
+ }
219
+
220
+ function chapterColumnRowCount(columns: ChapterColumns) {
221
+ return Math.max(columns.left.length, columns.right.length + (columns.hasMore ? 1 : 0))
222
+ }
223
+
224
+ function chapterBlockHeight(rowCount: number) {
225
+ if (rowCount <= 0) return 0
226
+ return rowCount * CHAPTER_LINE_HEIGHT + Math.max(0, rowCount - 1) * CHAPTER_ROW_GAP
227
+ }
228
+
229
+ function chapterWrapStyle(chapters: string[]) {
230
+ const rowCount = chapterColumnRowCount(splitChapterColumns(chapters))
231
+ const contentHeight = chapterBlockHeight(rowCount)
232
+ return { height: `${contentHeight}px` }
233
+ }
234
+
235
+ const chapterDialogVisible = ref(false)
236
+ const chapterDialogChapters = ref<string[]>([])
237
+
238
+ function openChapterDialog(row: TemplateItem) {
239
+ chapterDialogChapters.value = row.chapters
240
+ chapterDialogVisible.value = true
241
+ }
242
+
243
+ const unitOptions = ['風險管理部', '授信審批部', '合規部', '公司金融部']
244
+ const languageOptions = [
245
+ { label: '中文', value: 'zh' },
246
+ { label: 'English', value: 'en' },
247
+ ] as const
248
+ const language = ref<(typeof languageOptions)[number]['value']>('zh')
249
+ const unitFilter = ref('')
250
+ const keyword = ref('')
251
+ const currentPage = ref(1)
252
+ const pageSize = ref(5)
253
+
254
+ const templates = ref<TemplateItem[]>([...MOCK_TEMPLATES])
255
+
256
+ function isTemplateList(data: unknown): data is TemplateItem[] {
257
+ return Array.isArray(data) && data.length > 0 && typeof data[0]?.id === 'string'
258
+ }
259
+
260
+ async function loadTemplates() {
261
+ try {
262
+ const data = await modelManagerApi.fetchTemplateListData({
263
+ language: language.value,
264
+ })
265
+ if (isTemplateList(data)) {
266
+ templates.value = data
267
+ }
268
+ } catch {
269
+ // 介面不可用時保留 mock 資料
270
+ }
271
+ }
272
+
273
+ const filteredTemplates = computed(() => {
274
+ const kw = keyword.value.trim().toLowerCase()
275
+ return templates.value.filter((item) => {
276
+ const unitOk = !unitFilter.value || item.unit === unitFilter.value
277
+ const keywordOk =
278
+ !kw ||
279
+ item.name.toLowerCase().includes(kw) ||
280
+ item.description.toLowerCase().includes(kw) ||
281
+ item.author.toLowerCase().includes(kw)
282
+ return unitOk && keywordOk
283
+ })
284
+ })
285
+
286
+ const pendingCount = computed(() => filteredTemplates.value.length)
287
+
288
+ const paginatedTemplates = computed(() => {
289
+ const start = (currentPage.value - 1) * pageSize.value
290
+ return filteredTemplates.value.slice(start, start + pageSize.value)
291
+ })
292
+
293
+ const LOCK_OFFSET_LEFT = 65
294
+
295
+ const tableRef = ref<TableInstance>()
296
+ const pageBodyRef = ref<HTMLElement>()
297
+ const lockAnchors = new Map<string, HTMLElement>()
298
+ const lockPositions = reactive<Record<string, { left: number; top: number } | null>>({})
299
+
300
+ function setLockAnchor(id: string, el: HTMLElement | null) {
301
+ if (el) lockAnchors.set(id, el)
302
+ else lockAnchors.delete(id)
303
+ }
304
+
305
+ function getLockStyle(row: TemplateItem): CSSProperties {
306
+ const pos = lockPositions[row.id]
307
+ if (!pos) return { display: 'none' }
308
+
309
+ return {
310
+ position: 'fixed',
311
+ left: `15px`,
312
+ top: `${pos.top}px`,
313
+ transform: 'translateY(-50%)',
314
+ zIndex: 99,
315
+ display: 'inline-flex',
316
+ alignItems: 'center',
317
+ justifyContent: 'center',
318
+ width: '25px',
319
+ height: '25px',
320
+ cursor: 'help',
321
+ pointerEvents: 'auto',
322
+ }
323
+ }
324
+
325
+ function updateLockPositions() {
326
+ const activeIds = new Set(paginatedTemplates.value.map((row) => row.id))
327
+
328
+ for (const id of Object.keys(lockPositions)) {
329
+ if (!activeIds.has(id)) delete lockPositions[id]
330
+ }
331
+
332
+ paginatedTemplates.value.forEach((row) => {
333
+ const anchor = lockAnchors.get(row.id)
334
+ if (!anchor) {
335
+ lockPositions[row.id] = null
336
+ return
337
+ }
338
+
339
+ const rect = anchor.getBoundingClientRect()
340
+ const rowEl = anchor.closest('tr')
341
+ const rowRect = rowEl?.getBoundingClientRect() ?? rect
342
+
343
+ if (rowRect.bottom < 0 || rowRect.top > window.innerHeight) {
344
+ lockPositions[row.id] = null
345
+ return
346
+ }
347
+
348
+ lockPositions[row.id] = {
349
+ left: rect.left - LOCK_OFFSET_LEFT,
350
+ top: rowRect.top + rowRect.height / 2,
351
+ }
352
+ })
353
+ }
354
+
355
+ let lockRafId = 0
356
+ function scheduleLockUpdate() {
357
+ cancelAnimationFrame(lockRafId)
358
+ lockRafId = requestAnimationFrame(updateLockPositions)
359
+ }
360
+
361
+ let tableBodyWrapper: HTMLElement | null = null
362
+ let resizeObserver: ResizeObserver | null = null
363
+
364
+ function bindLockListeners() {
365
+ tableBodyWrapper?.removeEventListener('scroll', scheduleLockUpdate)
366
+ pageBodyRef.value?.removeEventListener('scroll', scheduleLockUpdate)
367
+
368
+ const tableEl = tableRef.value?.$el as HTMLElement | undefined
369
+ if (tableEl) {
370
+ tableBodyWrapper = tableEl.querySelector('.el-table__body-wrapper')
371
+ tableBodyWrapper?.addEventListener('scroll', scheduleLockUpdate, { passive: true })
372
+ resizeObserver?.disconnect()
373
+ resizeObserver = new ResizeObserver(scheduleLockUpdate)
374
+ resizeObserver.observe(tableEl)
375
+ }
376
+
377
+ pageBodyRef.value?.addEventListener('scroll', scheduleLockUpdate, { passive: true })
378
+ scheduleLockUpdate()
379
+ }
380
+
381
+ onMounted(() => {
382
+ void loadTemplates()
383
+ nextTick(() => {
384
+ bindLockListeners()
385
+ nextTick(scheduleLockUpdate)
386
+ })
387
+
388
+ window.addEventListener('resize', scheduleLockUpdate, { passive: true })
389
+ })
390
+
391
+ onBeforeUnmount(() => {
392
+ cancelAnimationFrame(lockRafId)
393
+ window.removeEventListener('resize', scheduleLockUpdate)
394
+ tableBodyWrapper?.removeEventListener('scroll', scheduleLockUpdate)
395
+ pageBodyRef.value?.removeEventListener('scroll', scheduleLockUpdate)
396
+ resizeObserver?.disconnect()
397
+ })
398
+
399
+ watch(paginatedTemplates, () => nextTick(() => {
400
+ bindLockListeners()
401
+ scheduleLockUpdate()
402
+ }), { flush: 'post' })
403
+ watch(currentPage, () => nextTick(scheduleLockUpdate))
404
+ watch(pageSize, () => nextTick(scheduleLockUpdate))
405
+
406
+ watch([keyword, unitFilter], () => {
407
+ currentPage.value = 1
408
+ })
409
+
410
+ watch(filteredTemplates, (list) => {
411
+ const maxPage = Math.max(1, Math.ceil(list.length / pageSize.value))
412
+ if (currentPage.value > maxPage) currentPage.value = maxPage
413
+ })
414
+
415
+ function handleRefresh() {
416
+ keyword.value = ''
417
+ unitFilter.value = ''
418
+ void loadTemplates()
419
+ ElMessage.success('已重新整理')
420
+ }
421
+
422
+ function handleCreate() {
423
+ ElMessage.info('新建模板(演示)')
424
+ }
425
+
426
+ function openRecycleBin() {
427
+ ElMessage.info('回收站(演示)')
428
+ }
429
+
430
+ function getEditActionLabel(row: TemplateItem) {
431
+ return row.locked ? '複製並編輯' : '編輯'
432
+ }
433
+
434
+ function getLockActionLabel(row: TemplateItem) {
435
+ return row.locked ? '解鎖模板' : '鎖定'
436
+ }
437
+
438
+ function handleEdit(row: TemplateItem) {
439
+ if (row.locked) {
440
+ ElMessage.info(`複製並編輯:${row.name}`)
441
+ return
442
+ }
443
+ ElMessage.info(`編輯:${row.name}`)
444
+ }
445
+
446
+ async function handleDelete(row: TemplateItem) {
447
+ const message = row.locked
448
+ ? `模板「${row.name}」已鎖定,確定仍要刪除嗎?`
449
+ : `確定刪除模板「${row.name}」嗎?`
450
+ try {
451
+ await ElMessageBox.confirm(message, '刪除', {
452
+ confirmButtonText: '確定',
453
+ cancelButtonText: '取消',
454
+ type: 'warning',
455
+ })
456
+ ElMessage.success(`已刪除:${row.name}`)
457
+ } catch {
458
+ // 用戶取消
459
+ }
460
+ }
461
+
462
+ async function toggleLock(row: TemplateItem) {
463
+ const willLock = !row.locked
464
+ const title = willLock ? '鎖定模板' : '解鎖模板'
465
+ const message = willLock ? `確定鎖定模板「${row.name}」嗎?` : `確定解鎖模板「${row.name}」嗎?`
466
+ try {
467
+ await ElMessageBox.confirm(message, title, {
468
+ confirmButtonText: '確定',
469
+ cancelButtonText: '取消',
470
+ type: 'warning',
471
+ })
472
+ row.locked = willLock
473
+ ElMessage.success(willLock ? '模板已鎖定' : '模板已解鎖')
474
+ } catch {
475
+ // 用戶取消
476
+ }
477
+ }
478
+ </script>
479
+
480
+ <style scoped>
481
+ .template-center-page {
482
+ --report-accent: #c53355;
483
+ --report-accent-soft: #fdf0f2;
484
+ display: flex;
485
+ flex-direction: column;
486
+ height: 100vh;
487
+ overflow: hidden;
488
+ background: #fff;
489
+ }
490
+
491
+ .template-center-page__body {
492
+ flex: 1;
493
+ min-height: 0;
494
+ overflow: auto;
495
+ margin-left: 30px;
496
+ padding: 32px 24px 40px;
497
+ box-sizing: border-box;
498
+ }
499
+
500
+ .template-center-hero {
501
+ text-align: center;
502
+ margin-bottom: 28px;
503
+ }
504
+
505
+ .template-center-hero__title {
506
+ margin: 0 0 10px;
507
+ font-size: 28px;
508
+ font-weight: 700;
509
+ color: #1a1a1a;
510
+ }
511
+
512
+ .template-center-hero__desc {
513
+ margin: 0;
514
+ font-size: 14px;
515
+ color: #666;
516
+ }
517
+
518
+ .template-center-toolbar {
519
+ display: flex;
520
+ flex-direction: column;
521
+ align-items: stretch;
522
+ gap: 12px;
523
+ margin-bottom: 20px;
524
+ }
525
+
526
+ .template-center-toolbar__tabs {
527
+ display: flex;
528
+ align-items: center;
529
+ justify-content: space-between;
530
+ }
531
+
532
+ .template-center-toolbar__tabs-left {
533
+ display: flex;
534
+ align-items: center;
535
+ }
536
+
537
+ .template-center-toolbar__tabs-right {
538
+ display: flex;
539
+ align-items: center;
540
+ }
541
+
542
+ .template-center-btn--create {
543
+ margin-right: 15px;
544
+ }
545
+
546
+ .template-center-toolbar-btn {
547
+ display: inline-flex;
548
+ align-items: center;
549
+ justify-content: center;
550
+ gap: 6px;
551
+ box-sizing: border-box;
552
+ height: 32px;
553
+ min-width: 120px;
554
+ padding: 0 14px;
555
+ border-radius: 4px;
556
+ font-size: 13px;
557
+ font-weight: 600;
558
+ line-height: 1;
559
+ font-family: inherit;
560
+ white-space: nowrap;
561
+ cursor: pointer;
562
+ transition: all 0.2s ease;
563
+ }
564
+
565
+ .template-center-toolbar-btn--primary {
566
+ border: 1px solid var(--app-btn-border, #c53355);
567
+ background: var(--app-btn-bg, #c53355);
568
+ color: var(--app-btn-color, #fff);
569
+ }
570
+
571
+ .template-center-toolbar-btn--primary:hover {
572
+ background: var(--app-btn-bg-hover, #fff);
573
+ border-color: var(--app-btn-border-hover, #c53355);
574
+ color: var(--app-btn-color-hover, #c53355);
575
+ }
576
+
577
+ .template-center-toolbar-btn--outline {
578
+ border: 1px solid var(--report-accent, #c53355);
579
+ color: var(--report-accent, #c53355);
580
+ background: #fff;
581
+ }
582
+
583
+ .template-center-toolbar-btn--outline:hover {
584
+ border-color: var(--report-accent, #c53355);
585
+ color: var(--report-accent, #c53355);
586
+ background: var(--report-accent-soft, #fdf0f2);
587
+ }
588
+
589
+ /* 刪除:與返回章節編輯一致,默認 opacity 0.8,移入 1 */
590
+ .template-center-toolbar-btn--delete {
591
+ border-color: var(--report-accent, #c53355);
592
+ color: var(--report-accent, #c53355);
593
+ background: #fff;
594
+ opacity: 0.8;
595
+ }
596
+
597
+ .template-center-toolbar-btn--delete:hover {
598
+ border-color: var(--report-accent, #c53355);
599
+ color: var(--report-accent, #c53355);
600
+ background: var(--report-accent-soft, #fdf0f2);
601
+ opacity: 1;
602
+ }
603
+
604
+ .template-center-toolbar-btn--ghost {
605
+ border: 1px solid #dcdfe6;
606
+ background: #fff;
607
+ color: #606266;
608
+ font-weight: 500;
609
+ }
610
+
611
+ .template-center-toolbar-btn--ghost:hover {
612
+ border-color: var(--report-accent, #c53355);
613
+ color: var(--report-accent, #c53355);
614
+ background: var(--report-accent-soft, #fdf0f2);
615
+ }
616
+
617
+ .template-center-toolbar__controls {
618
+ display: flex;
619
+ align-items: center;
620
+ width: 100%;
621
+ }
622
+
623
+ .template-center-toolbar__right {
624
+ display: flex;
625
+ align-items: center;
626
+ justify-content: flex-end;
627
+ gap: 12px;
628
+ flex-wrap: wrap;
629
+ margin-left: auto;
630
+ }
631
+
632
+ .template-center-field {
633
+ display: inline-flex;
634
+ align-items: center;
635
+ gap: 8px;
636
+ margin: 0;
637
+ }
638
+
639
+ .template-center-field__label {
640
+ font-size: 14px;
641
+ color: #606266;
642
+ white-space: nowrap;
643
+ }
644
+
645
+ .template-center-field__select {
646
+ width: 120px;
647
+ }
648
+
649
+ .template-center-field__select--lang {
650
+ width: 108px;
651
+ }
652
+
653
+ .template-center-field__input {
654
+ width: 180px;
655
+ }
656
+
657
+ .template-center-table {
658
+ width: 100%;
659
+ --el-table-border-color: transparent;
660
+ background: transparent;
661
+ overflow: visible;
662
+ }
663
+
664
+ .template-center-table :deep(.el-table__inner-wrapper),
665
+ .template-center-table :deep(.el-table__body-wrapper),
666
+ .template-center-table :deep(.el-table__header-wrapper),
667
+ .template-center-table :deep(.el-scrollbar__wrap),
668
+ .template-center-table :deep(.el-scrollbar__view),
669
+ .template-center-table :deep(.el-table__body),
670
+ .template-center-table :deep(.el-table__body tbody),
671
+ .template-center-table :deep(.el-table__body tr.el-table__row) {
672
+ overflow: visible !important;
673
+ }
674
+
675
+ .template-center-table :deep(.el-table__inner-wrapper::before) {
676
+ display: none;
677
+ }
678
+
679
+ .template-center-table :deep(.el-table__header th.el-table__cell) {
680
+ border-bottom: none;
681
+ background: transparent;
682
+ padding: 0 16px 12px;
683
+ vertical-align: bottom;
684
+ color: #1a1a1a;
685
+ font-weight: 600;
686
+ }
687
+
688
+ .template-center-table :deep(.el-table__header th.el-table__cell .cell) {
689
+ padding: 0;
690
+ }
691
+
692
+ .template-center-table :deep(.el-table__header th.el-table__cell + th.el-table__cell .cell) {
693
+ position: relative;
694
+ padding-left: 16px;
695
+ }
696
+
697
+ .template-center-table :deep(.el-table__body-wrapper .el-table__body) {
698
+ border-collapse: separate;
699
+ border-spacing: 0 14px;
700
+ }
701
+
702
+ .template-center-table :deep(.el-table__body td.el-table__cell) {
703
+ vertical-align: top;
704
+ height: 1px;
705
+ padding: 20px 16px;
706
+ border-bottom: none;
707
+ background: #fff;
708
+ transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
709
+ }
710
+
711
+ .template-center-table :deep(.el-table__body tr.el-table__row) {
712
+ background: #fff;
713
+ box-shadow: 0 1px 6px rgba(0, 0, 0, 0.06);
714
+ }
715
+
716
+ .template-center-table :deep(.el-table__body tr.el-table__row:hover > td.el-table__cell) {
717
+ background-color: #fff !important;
718
+ box-shadow: none;
719
+ }
720
+
721
+ .template-center-table :deep(.el-table__body tr.el-table__row:hover td.el-table__cell:first-child) {
722
+ border-color: rgba(197, 51, 85, 0.5);
723
+ }
724
+
725
+ .template-center-table :deep(.el-table__body tr.el-table__row:hover td.el-table__cell:last-child) {
726
+ border-color: rgba(197, 51, 85, 0.5);
727
+ }
728
+
729
+ .template-center-table :deep(.el-table__body tr.el-table__row:hover td.el-table__cell:not(:first-child):not(:last-child)) {
730
+ border-top-color: rgba(197, 51, 85, 0.5);
731
+ border-bottom-color: rgba(197, 51, 85, 0.5);
732
+ }
733
+
734
+ .template-center-table :deep(.el-table__body-wrapper .cell) {
735
+ padding: 0;
736
+ }
737
+
738
+ .template-center-table :deep(.el-table__body td.el-table__cell:not(:first-child) > .cell) {
739
+ position: relative;
740
+ height: 100%;
741
+ box-sizing: border-box;
742
+ }
743
+
744
+ .template-center-table :deep(.el-table__body td.el-table__cell:not(:first-child) > .cell)::before {
745
+ content: '';
746
+ position: absolute;
747
+ left: 0;
748
+ top: 0;
749
+ bottom: 0;
750
+ width: 1px;
751
+ background: #f0f2f5;
752
+ }
753
+
754
+ .template-center-cell-body,
755
+ .template-center-cell-divider {
756
+ position: relative;
757
+ box-sizing: border-box;
758
+ }
759
+
760
+ .template-center-cell-divider {
761
+ width: 100%;
762
+ padding-left: 16px;
763
+ }
764
+
765
+ .template-center-cell-divider::before {
766
+ display: none;
767
+ }
768
+
769
+ .template-center-cell-divider--chapters {
770
+ height: auto;
771
+ }
772
+
773
+ .template-center-cell-divider--updated {
774
+ overflow: visible;
775
+ }
776
+
777
+ .template-center-cell-divider--actions {
778
+ width: 100%;
779
+ display: flex;
780
+ align-items: center;
781
+ justify-content: center;
782
+ margin-left: 0;
783
+ }
784
+
785
+ .template-center-table :deep(.el-table__body tr.el-table__row td.el-table__cell:last-child > .cell) {
786
+ display: flex;
787
+ align-items: center;
788
+ justify-content: center;
789
+ height: 100%;
790
+ }
791
+
792
+ .template-center-table :deep(.el-table__body tr.el-table__row td.el-table__cell:first-child) {
793
+ overflow: visible !important;
794
+ border-radius: 8px 0 0 8px;
795
+ border: 1px solid #ebeef5;
796
+ border-right: none;
797
+ }
798
+
799
+ .template-center-table :deep(.el-table__body tr.el-table__row td.el-table__cell:first-child > .cell) {
800
+ overflow: visible !important;
801
+ position: relative;
802
+ }
803
+
804
+ .template-center-cell-body.template-center-cell-body--name {
805
+ position: static;
806
+ }
807
+
808
+ .template-center-row-lock-anchor {
809
+ position: absolute;
810
+ left: 0;
811
+ top: 0;
812
+ width: 0;
813
+ height: 0;
814
+ pointer-events: none;
815
+ }
816
+
817
+ .template-center-table :deep(.el-table__body tr.el-table__row td.el-table__cell:last-child) {
818
+ border-radius: 0 8px 8px 0;
819
+ border: 1px solid #ebeef5;
820
+ border-left: none;
821
+ }
822
+
823
+ .template-center-table :deep(.el-table__header th.template-col-author),
824
+ .template-center-table :deep(.el-table__header th.template-col-updated),
825
+ .template-center-table :deep(.el-table__body td.template-col-author),
826
+ .template-center-table :deep(.el-table__body td.template-col-updated) {
827
+ overflow: visible;
828
+ }
829
+
830
+ .template-center-table :deep(.el-table__header th.template-col-author .cell),
831
+ .template-center-table :deep(.el-table__header th.template-col-updated .cell),
832
+ .template-center-table :deep(.el-table__body td.template-col-author .cell),
833
+ .template-center-table :deep(.el-table__body td.template-col-updated .cell) {
834
+ overflow: visible;
835
+ }
836
+
837
+ .template-center-table :deep(.el-table__header th.template-col-updated .cell),
838
+ .template-center-table :deep(.el-table__body td.template-col-updated .cell) {
839
+ white-space: nowrap;
840
+ }
841
+
842
+ .template-center-table :deep(.el-table__body tr.el-table__row td.el-table__cell:not(:first-child):not(:last-child)) {
843
+ border-top: 1px solid #ebeef5;
844
+ border-bottom: 1px solid #ebeef5;
845
+ }
846
+
847
+ .template-center-name {
848
+ display: block;
849
+ font-size: 14px;
850
+ color: #303133;
851
+ font-weight: 500;
852
+ }
853
+
854
+ .template-center-updated {
855
+ display: block;
856
+ font-size: 13px;
857
+ line-height: 1.5;
858
+ color: #606266;
859
+ white-space: nowrap;
860
+ }
861
+
862
+ .template-center-desc {
863
+ margin: 0;
864
+ font-size: 13px;
865
+ line-height: 1.65;
866
+ color: #606266;
867
+ white-space: normal;
868
+ }
869
+
870
+ .template-center-chapters-wrap {
871
+ position: relative;
872
+ box-sizing: border-box;
873
+ overflow: hidden;
874
+ }
875
+
876
+ .template-center-chapters {
877
+ display: flex;
878
+ align-items: flex-start;
879
+ gap: 20px;
880
+ box-sizing: border-box;
881
+ height: 100%;
882
+ }
883
+
884
+ .template-center-chapters-col {
885
+ flex: 1;
886
+ min-width: 0;
887
+ margin: 0;
888
+ padding: 0;
889
+ list-style: none;
890
+ font-size: 13px;
891
+ color: #606266;
892
+ }
893
+
894
+ .template-center-table :deep(.el-table__body tr.el-table__row:hover .chapter-title-scroll__ellipsis) {
895
+ background: linear-gradient(90deg, rgba(245, 247, 250, 0) 0%, #f5f7fa 45%);
896
+ }
897
+
898
+ .template-center-chapters-col>li+li,
899
+ .chapter-title-scroll+li,
900
+ .chapter-title-scroll+.chapter-title-scroll {
901
+ margin-top: 4px;
902
+ }
903
+
904
+ .template-center-chapters--dialog {
905
+ display: grid;
906
+ grid-template-columns: repeat(3, minmax(0, 1fr));
907
+ gap: 8px 28px;
908
+ margin: 0;
909
+ padding: 0;
910
+ list-style: none;
911
+ }
912
+
913
+ .template-center-chapters--dialog>li {
914
+ white-space: normal;
915
+ overflow: visible;
916
+ }
917
+
918
+ .template-center-chapters-ellipsis {
919
+ list-style: none;
920
+ }
921
+
922
+ .template-center-chapters-ellipsis-btn {
923
+ padding: 0;
924
+ border: none;
925
+ background: transparent;
926
+ color: #c53355;
927
+ font-size: 20px;
928
+ font-weight: 900;
929
+ height: 19.5px;
930
+ line-height: 19.5px;
931
+ letter-spacing: 0.05em;
932
+ cursor: pointer;
933
+ }
934
+
935
+ .template-center-chapters-ellipsis-btn:hover {
936
+ color: #a82d48;
937
+ text-decoration: underline;
938
+ }
939
+
940
+ .template-center-author {
941
+ font-size: 13px;
942
+ line-height: 1.7;
943
+ color: #606266;
944
+ }
945
+
946
+ .template-center-author>div {
947
+ white-space: normal;
948
+ word-break: break-word;
949
+ }
950
+
951
+ .template-center-row-actions {
952
+ display: flex;
953
+ flex-direction: column;
954
+ align-items: stretch;
955
+ gap: 8px;
956
+ width: 100%;
957
+ max-width: 132px;
958
+ }
959
+
960
+ .template-center-row-actions .template-center-toolbar-btn {
961
+ width: 100%;
962
+ min-width: 0;
963
+ }
964
+
965
+ .template-center-pagination {
966
+ margin-top: 20px;
967
+ }
968
+
969
+ .template-center-pagination :deep(.el-pagination) {
970
+ width: 100%;
971
+ font-size: 14px;
972
+ font-weight: 400;
973
+ color: #606266;
974
+ }
975
+
976
+ .template-center-pagination :deep(.el-pagination.is-background .btn-prev),
977
+ .template-center-pagination :deep(.el-pagination.is-background .btn-next),
978
+ .template-center-pagination :deep(.el-pagination.is-background .el-pager li) {
979
+ margin: 0 4px;
980
+ min-width: 32px;
981
+ height: 32px;
982
+ line-height: 30px;
983
+ padding: 0;
984
+ background-color: #fff;
985
+ border: 1px solid #dcdfe6;
986
+ border-radius: 4px;
987
+ color: #606266;
988
+ font-weight: 400;
989
+ box-sizing: border-box;
990
+ }
991
+
992
+ .template-center-pagination :deep(.el-pagination.is-background .btn-prev),
993
+ .template-center-pagination :deep(.el-pagination.is-background .btn-next) {
994
+ margin-left: 0;
995
+ }
996
+
997
+ .template-center-pagination :deep(.el-pagination.is-background .btn-prev .el-icon),
998
+ .template-center-pagination :deep(.el-pagination.is-background .btn-next .el-icon) {
999
+ font-size: 12px;
1000
+ color: #909399;
1001
+ }
1002
+
1003
+ .template-center-pagination :deep(.el-pagination.is-background .btn-prev:disabled),
1004
+ .template-center-pagination :deep(.el-pagination.is-background .btn-next:disabled),
1005
+ .template-center-pagination :deep(.el-pagination.is-background .btn-prev.is-disabled),
1006
+ .template-center-pagination :deep(.el-pagination.is-background .btn-next.is-disabled) {
1007
+ color: #c0c4cc;
1008
+ background-color: #fff;
1009
+ border-color: #e4e7ed;
1010
+ }
1011
+
1012
+ .template-center-pagination :deep(.el-pagination.is-background .btn-prev:disabled .el-icon),
1013
+ .template-center-pagination :deep(.el-pagination.is-background .btn-next:disabled .el-icon),
1014
+ .template-center-pagination :deep(.el-pagination.is-background .btn-prev.is-disabled .el-icon),
1015
+ .template-center-pagination :deep(.el-pagination.is-background .btn-next.is-disabled .el-icon) {
1016
+ color: #c0c4cc;
1017
+ }
1018
+
1019
+ .template-center-pagination :deep(.el-pagination.is-background .el-pager li.is-active) {
1020
+ background-color: #c53355;
1021
+ border-color: #c53355;
1022
+ color: #fff;
1023
+ font-weight: 600;
1024
+ }
1025
+
1026
+ .template-center-pagination :deep(.el-pagination.is-background .el-pager li.is-active:hover) {
1027
+ color: #fff;
1028
+ }
1029
+
1030
+ .template-center-pagination :deep(.el-pagination__sizes) {
1031
+ margin-left: 8px;
1032
+ color: #606266;
1033
+ }
1034
+
1035
+ .template-center-pagination :deep(.el-pagination__sizes .el-select) {
1036
+ width: 96px;
1037
+ }
1038
+
1039
+ .template-center-pagination :deep(.el-pagination__sizes .el-select__wrapper) {
1040
+ min-height: 32px;
1041
+ padding: 0 8px;
1042
+ border-radius: 4px;
1043
+ box-shadow: 0 0 0 1px #dcdfe6 inset;
1044
+ background-color: #fff;
1045
+ }
1046
+
1047
+ .template-center-pagination :deep(.el-pagination__sizes .el-select__placeholder),
1048
+ .template-center-pagination :deep(.el-pagination__sizes .el-select__selected-item) {
1049
+ color: #606266;
1050
+ font-size: 14px;
1051
+ }
1052
+
1053
+ .template-center-pagination :deep(.el-pagination__rightwrapper) {
1054
+ flex: 1;
1055
+ justify-content: flex-end;
1056
+ }
1057
+
1058
+ .template-center-pagination :deep(.el-pagination__jump) {
1059
+ margin-left: 0;
1060
+ color: #606266;
1061
+ font-size: 14px;
1062
+ }
1063
+
1064
+ .template-center-pagination :deep(.el-pagination__goto) {
1065
+ margin-right: 8px;
1066
+ }
1067
+
1068
+ .template-center-pagination :deep(.el-pagination__editor.el-input) {
1069
+ width: 48px;
1070
+ margin: 0;
1071
+ }
1072
+
1073
+ .template-center-pagination :deep(.el-pagination__editor .el-input__wrapper) {
1074
+ min-height: 32px;
1075
+ padding: 0 8px;
1076
+ border-radius: 4px;
1077
+ box-shadow: 0 0 0 1px #dcdfe6 inset;
1078
+ background-color: #fff;
1079
+ }
1080
+
1081
+ .template-center-pagination :deep(.el-pagination__editor .el-input__inner) {
1082
+ text-align: center;
1083
+ color: #606266;
1084
+ font-size: 14px;
1085
+ }
1086
+
1087
+ .template-center-pagination :deep(.el-pagination__classifier) {
1088
+ margin-left: 8px;
1089
+ color: #606266;
1090
+ }
1091
+
1092
+ .template-center-pagination :deep(.el-pagination__total) {
1093
+ margin-left: 16px;
1094
+ color: #606266;
1095
+ font-size: 14px;
1096
+ font-weight: 400;
1097
+ }
1098
+ </style>
1099
+
1100
+ <style>
1101
+ .template-center-tab-badge .el-badge__content.el-badge__content--danger.is-fixed {
1102
+ margin-right: 10px !important;
1103
+ margin-top: -5px !important;
1104
+ }
1105
+
1106
+ .template-center-row-lock-layer {
1107
+ position: fixed;
1108
+ inset: 0;
1109
+ z-index: 200;
1110
+ pointer-events: none;
1111
+ }
1112
+
1113
+ .template-center-row-lock-trigger--portal {
1114
+ line-height: 0;
1115
+ }
1116
+
1117
+ .template-center-row-lock-trigger--portal .el-icon {
1118
+ width: 25px;
1119
+ height: 25px;
1120
+ }
1121
+
1122
+ .template-center-row-lock-trigger--portal .el-icon svg {
1123
+ width: 25px;
1124
+ height: 25px;
1125
+ }
1126
+
1127
+ /* 章節結構彈窗(append-to-body,需全局樣式) */
1128
+ .template-center-chapter-overlay {
1129
+ overflow: auto;
1130
+ }
1131
+
1132
+ .template-center-chapter-dialog.el-dialog {
1133
+ --el-dialog-padding-primary: 0;
1134
+ width: 60%;
1135
+ max-width: 80%;
1136
+ max-height: 70%;
1137
+ margin-top: 5%;
1138
+ margin-left: 21%;
1139
+ margin-right: auto;
1140
+ border-radius: 4px;
1141
+ overflow: hidden;
1142
+ display: flex;
1143
+ flex-direction: column;
1144
+ }
1145
+
1146
+ .template-center-chapter-dialog .el-dialog__header,
1147
+ .template-center-chapter-dialog__header {
1148
+ display: flex;
1149
+ align-items: center;
1150
+ justify-content: space-between;
1151
+ gap: 16px;
1152
+ margin: 0;
1153
+ padding: 14px 16px 14px 20px;
1154
+ background: #4a5f7a;
1155
+ flex-shrink: 0;
1156
+ }
1157
+
1158
+ .template-center-chapter-dialog__title,
1159
+ .template-center-chapter-dialog .el-dialog__title {
1160
+ flex: 1;
1161
+ min-width: 0;
1162
+ color: #fff;
1163
+ font-size: 16px;
1164
+ font-weight: 600;
1165
+ line-height: 1.4;
1166
+ }
1167
+
1168
+ .template-center-chapter-dialog__close {
1169
+ flex-shrink: 0;
1170
+ width: 32px;
1171
+ height: 32px;
1172
+ margin: 0;
1173
+ padding: 0;
1174
+ border: none;
1175
+ background: transparent;
1176
+ color: #fff;
1177
+ font-size: 24px;
1178
+ font-weight: 400;
1179
+ line-height: 1;
1180
+ cursor: pointer;
1181
+ display: inline-flex;
1182
+ align-items: center;
1183
+ justify-content: center;
1184
+ }
1185
+
1186
+ .template-center-chapter-dialog__close:hover {
1187
+ color: rgba(255, 255, 255, 0.85);
1188
+ }
1189
+
1190
+ .template-center-chapter-dialog__close:focus-visible {
1191
+ outline: 2px solid rgba(255, 255, 255, 0.8);
1192
+ outline-offset: 2px;
1193
+ }
1194
+
1195
+ .template-center-chapter-dialog .el-dialog__body {
1196
+ flex: 1;
1197
+ min-height: 60vh;
1198
+ padding: 24px 20px;
1199
+ background: #fff;
1200
+ color: #606266;
1201
+ font-size: 13px;
1202
+ line-height: 1.6;
1203
+ overflow-y: auto;
1204
+ }
1205
+ </style>