aegon-lv97 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/gen-ai/build-report/chartStreamRefs.ts +227 -0
  2. package/gen-ai/build-report/coherence-check/index.vue +559 -0
  3. package/gen-ai/build-report/components/ReportChapterWorkspace.vue +4571 -0
  4. package/gen-ai/build-report/components/ReportConfigPanel.vue +727 -0
  5. package/gen-ai/build-report/components/ReportGenerateSettingsPanel.vue +2361 -0
  6. package/gen-ai/build-report/components/ReportGeneratingPanel.vue +1765 -0
  7. package/gen-ai/build-report/components/ReportPromptPanel.vue +625 -0
  8. package/gen-ai/build-report/components/ReportSourceFileSummary.vue +665 -0
  9. package/gen-ai/build-report/components/ReportSourcesPanel.vue +1327 -0
  10. package/gen-ai/build-report/components/ReportTemplateDetail.vue +191 -0
  11. package/gen-ai/build-report/components/ReportTemplateEditorFormPanel.vue +667 -0
  12. package/gen-ai/build-report/components/ReportTemplatePicker.vue +302 -0
  13. package/gen-ai/build-report/components/ReportWorkflowSidebar.vue +148 -0
  14. package/gen-ai/build-report/components/paramsSetting.vue +886 -0
  15. package/gen-ai/build-report/components/reportEdit.vue +2283 -0
  16. package/gen-ai/build-report/index.vue +856 -0
  17. package/gen-ai/build-report/report-domain.ts +1133 -0
  18. package/gen-ai/build-report/report-template-data.ts +295 -0
  19. package/gen-ai/build-report/useReportWorkflow.ts +1386 -0
  20. package/gen-ai/common/assets/NotoSans-SC.woff2 +0 -0
  21. package/gen-ai/common/assets/NotoSans-TC.woff2 +0 -0
  22. package/gen-ai/common/assets/NotoSans.woff2 +0 -0
  23. package/gen-ai/common/assets/fonts.css +278 -0
  24. package/gen-ai/common/assets/pagination.css +163 -0
  25. package/gen-ai/common/directives/btnLoading.ts +455 -0
  26. package/gen-ai/common/directives/debounce.ts +98 -0
  27. package/gen-ai/common/directives/index.ts +30 -0
  28. package/gen-ai/common/en.md +417 -0
  29. package/gen-ai/common/index.ts +36 -0
  30. package/gen-ai/common/types/component-expose.ts +9 -0
  31. package/gen-ai/common/utils/api-res-status.ts +7 -0
  32. package/gen-ai/common/utils/navigation.ts +59 -0
  33. package/gen-ai/common/utils/remote-request.ts +45 -0
  34. package/gen-ai/common/utils/scroll.ts +577 -0
  35. package/gen-ai/components/AIExport/docx-export.ts +950 -0
  36. package/gen-ai/components/AIExport/docxExport.ts +2566 -0
  37. package/gen-ai/components/AIExport/index.ts +52 -0
  38. package/gen-ai/components/AIExport/parseMarkdownHtml.ts +653 -0
  39. package/gen-ai/components/CapsuleScrollbar.vue +149 -0
  40. package/gen-ai/components/ChapterTitleScroll.vue +468 -0
  41. package/gen-ai/components/EntryHeroNavCards.vue +379 -0
  42. package/gen-ai/components/GenAiPagination.vue +52 -0
  43. package/gen-ai/components/ModelCard.vue +1114 -0
  44. package/gen-ai/components/Preview/FilePreviewPage.vue +949 -0
  45. package/gen-ai/components/Preview/PreviewCapsuleScroll.vue +205 -0
  46. package/gen-ai/components/Preview/PreviewOutlineTree.vue +148 -0
  47. package/gen-ai/components/Preview/index.ts +11 -0
  48. package/gen-ai/components/Preview/preview.ts +724 -0
  49. package/gen-ai/components/PromptField.vue +505 -0
  50. package/gen-ai/components/RecentReportsTable.vue +974 -0
  51. package/gen-ai/components/ReportCollapsibleSection.vue +172 -0
  52. package/gen-ai/components/ReportHistoryList.vue +637 -0
  53. package/gen-ai/components/ReportSectionConfirmPopover.vue +251 -0
  54. package/gen-ai/components/editor/ChapterTiptapEditor.vue +3718 -0
  55. package/gen-ai/components/editor/StructureBlockView.vue +441 -0
  56. package/gen-ai/components/editor/TiptapToolbar.vue +1036 -0
  57. package/gen-ai/components/editor/TypewriterDocEditor.vue +422 -0
  58. package/gen-ai/components/editor/_StructureOrgNode.vue +326 -0
  59. package/gen-ai/components/editor/index.ts +28 -0
  60. package/gen-ai/components/editor/parseMarkdownHtml.ts +754 -0
  61. package/gen-ai/components/editor/reviewCitationTip.ts +163 -0
  62. package/gen-ai/gen-entry/api/index.ts +32 -0
  63. package/gen-ai/gen-entry/index.vue +287 -0
  64. package/gen-ai/gen-entry/types/index.ts +4 -0
  65. package/gen-ai/i.mark +2 -0
  66. package/gen-ai/management-center/index.vue +1815 -0
  67. package/gen-ai/management-center/mockData.ts +248 -0
  68. package/gen-ai/management-center/types/index.ts +37 -0
  69. package/gen-ai/model-manager/TemplateEditorFormPanel.vue +661 -0
  70. package/gen-ai/model-manager/api/index.ts +32 -0
  71. package/gen-ai/model-manager/api/mockApi.ts +33 -0
  72. package/gen-ai/model-manager/index.vue +1583 -0
  73. package/gen-ai/model-manager/mockData.ts +177 -0
  74. package/gen-ai/model-manager/template-detail-shared.ts +154 -0
  75. package/gen-ai/model-manager/template-editor.vue +345 -0
  76. package/gen-ai/model-manager/types/index.ts +40 -0
  77. package/gen-ai/model-manager/utils.ts +25 -0
  78. package/package.json +12 -0
@@ -0,0 +1,661 @@
1
+ <template>
2
+ <section class="template-editor-form">
3
+ <div class="template-editor-form__row">
4
+ <label class="template-editor-field template-editor-field--row">
5
+ <span class="template-editor-label"
6
+ >主要語言<span class="template-editor-label__required">*</span></span
7
+ >
8
+ <div class="template-editor-field__control template-editor-field__control--lang">
9
+ <el-select v-model="form.language" class="template-editor-field__lang">
10
+ <el-option
11
+ v-for="item in languageOptions"
12
+ :key="item.value"
13
+ :label="item.label"
14
+ :value="item.value"
15
+ />
16
+ </el-select>
17
+ </div>
18
+ </label>
19
+ </div>
20
+
21
+ <div class="template-editor-form__row">
22
+ <label class="template-editor-field template-editor-field--row">
23
+ <span class="template-editor-label"
24
+ >模板標題<span class="template-editor-label__required">*</span></span
25
+ >
26
+ <div class="template-editor-field__control">
27
+ <el-input
28
+ v-model="form.title"
29
+ :maxlength="TEMPLATE_TITLE_MAX"
30
+ show-word-limit
31
+ class="template-editor-field__input"
32
+ />
33
+ </div>
34
+ </label>
35
+ </div>
36
+
37
+ <div class="template-editor-form__row">
38
+ <label
39
+ class="template-editor-field template-editor-field--row template-editor-field--textarea-row"
40
+ >
41
+ <span class="template-editor-label"
42
+ >模板描述<span class="template-editor-label__required">*</span></span
43
+ >
44
+ <div class="template-editor-field__control">
45
+ <el-input
46
+ v-model="form.description"
47
+ type="textarea"
48
+ :rows="4"
49
+ :maxlength="TEMPLATE_DESC_MAX"
50
+ show-word-limit
51
+ resize="none"
52
+ class="template-editor-field__textarea"
53
+ />
54
+ </div>
55
+ </label>
56
+ </div>
57
+
58
+ <div class="template-editor-chapters">
59
+ <div v-for="chapter in form.chapters" :key="chapter.id" class="template-editor-chapter">
60
+ <button
61
+ type="button"
62
+ class="template-editor-chapter__header"
63
+ @click="toggleChapter(chapter.id)"
64
+ >
65
+ <span class="template-editor-chapter__drag" aria-hidden="true"> <i /><i /><i /> </span>
66
+ <span class="template-editor-chapter__header-title-wrap">
67
+ <span class="template-editor-chapter__header-title">
68
+ {{ getChapterDisplayTitle(chapter) }}
69
+ </span>
70
+ <el-icon
71
+ v-if="isChapterTitleEdited(chapter)"
72
+ class="template-editor-chapter__title-edited"
73
+ aria-label="章節標題已修改"
74
+ >
75
+ <Edit />
76
+ </el-icon>
77
+ </span>
78
+ <el-icon
79
+ class="template-editor-chapter__chevron"
80
+ :class="{ 'template-editor-chapter__chevron--expanded': chapter.expanded }"
81
+ >
82
+ <ArrowRight />
83
+ </el-icon>
84
+ </button>
85
+
86
+ <div v-if="chapter.expanded" class="template-editor-chapter__body">
87
+ <div class="template-editor-chapter__toolbar">
88
+ <button
89
+ v-if="isEditMode"
90
+ type="button"
91
+ class="template-center-toolbar-btn gen-ai-action-btn template-editor-chapter__action-btn"
92
+ :class="
93
+ isChapterEditing(chapter.id)
94
+ ? 'template-center-toolbar-btn--outline template-center-toolbar-btn--delete'
95
+ : 'template-center-toolbar-btn--ghost'
96
+ "
97
+ :disabled="isChapterEditing(chapter.id) && form.chapters.length <= 1"
98
+ @click.stop="handleChapterToolbarAction(chapter)"
99
+ >
100
+ {{ isChapterEditing(chapter.id) ? '刪除' : '編輯' }}
101
+ </button>
102
+ <button
103
+ v-else
104
+ type="button"
105
+ class="template-center-toolbar-btn gen-ai-action-btn template-center-toolbar-btn--outline template-center-toolbar-btn--delete template-editor-chapter__action-btn"
106
+ :disabled="form.chapters.length <= 1"
107
+ @click.stop="handleDeleteChapter(chapter)"
108
+ >
109
+ 刪除
110
+ </button>
111
+ </div>
112
+
113
+ <template v-if="showChapterFields(chapter.id)">
114
+ <label class="template-editor-field template-editor-field--row">
115
+ <span class="template-editor-label">章節標題</span>
116
+ <div class="template-editor-field__control">
117
+ <el-input
118
+ v-model="chapter.title"
119
+ :maxlength="CHAPTER_TITLE_MAX"
120
+ show-word-limit
121
+ class="template-editor-field__input"
122
+ />
123
+ </div>
124
+ </label>
125
+
126
+ <label
127
+ class="template-editor-field template-editor-field--row template-editor-field--textarea-row"
128
+ >
129
+ <span class="template-editor-label">章節描述</span>
130
+ <div class="template-editor-field__control">
131
+ <el-input
132
+ v-model="chapter.description"
133
+ type="textarea"
134
+ :rows="4"
135
+ :maxlength="CHAPTER_DESC_MAX"
136
+ show-word-limit
137
+ resize="none"
138
+ class="template-editor-field__textarea"
139
+ />
140
+ </div>
141
+ </label>
142
+
143
+ <PromptField
144
+ v-model="chapter.prompt"
145
+ :maxlength="CHAPTER_PROMPT_MAX"
146
+ :rows="CHAPTER_PROMPT_ROWS"
147
+ v-bind="resolvePromptFieldProps(chapter)"
148
+ @ai-assist="handleAiAssist(chapter)"
149
+ />
150
+ </template>
151
+ </div>
152
+ </div>
153
+ </div>
154
+
155
+ <div class="template-editor-form__add">
156
+ <button
157
+ type="button"
158
+ class="template-editor-form__add-btn"
159
+ aria-label="新增章節"
160
+ :disabled="form.chapters.length >= CHAPTER_MAX_COUNT"
161
+ @click="addChapter"
162
+ >
163
+ +
164
+ </button>
165
+ </div>
166
+ </section>
167
+ </template>
168
+
169
+ <script setup lang="ts">
170
+ import { ArrowRight, Edit } from '@element-plus/icons-vue'
171
+ import { ElMessage, ElMessageBox } from 'element-plus'
172
+ import { computed, ref, watch } from 'vue'
173
+ import PromptField, { type PromptFieldLayoutBind } from '../components/PromptField.vue'
174
+ import { createEmptyChapter } from './mockData'
175
+ import type { TemplateChapterForm, TemplateEditorForm, TemplateEditorMode } from './types'
176
+
177
+ const TEMPLATE_TITLE_MAX = 100
178
+ const TEMPLATE_DESC_MAX = 3000
179
+ const CHAPTER_TITLE_MAX = 100
180
+ const CHAPTER_DESC_MAX = 1000
181
+ const CHAPTER_PROMPT_MAX = 8000
182
+ const CHAPTER_PROMPT_ROWS = 8
183
+ const CHAPTER_MAX_COUNT = 100
184
+
185
+ const languageOptions = [
186
+ { label: '中文', value: 'zh' },
187
+ { label: 'English', value: 'en' },
188
+ ]
189
+
190
+ const props = defineProps<{
191
+ modelValue: TemplateEditorForm
192
+ editorMode: TemplateEditorMode
193
+ }>()
194
+
195
+ const emit = defineEmits<{
196
+ 'update:modelValue': [value: TemplateEditorForm]
197
+ }>()
198
+
199
+ const form = computed({
200
+ get: () => props.modelValue,
201
+ set: (value) => emit('update:modelValue', value),
202
+ })
203
+
204
+ const editingChapterId = ref<string | null>(null)
205
+ const chapterOriginalTitles = ref<Record<string, string>>({})
206
+
207
+ const isEditMode = computed(() => props.editorMode === 'edit')
208
+ const isCreateMode = computed(() => props.editorMode === 'create')
209
+
210
+ function syncChapterOriginalTitles() {
211
+ const map: Record<string, string> = {}
212
+ for (const chapter of form.value.chapters) {
213
+ map[chapter.id] = chapter.title
214
+ }
215
+ chapterOriginalTitles.value = map
216
+ }
217
+
218
+ watch(
219
+ () => props.modelValue,
220
+ () => syncChapterOriginalTitles(),
221
+ { deep: true, immediate: true }
222
+ )
223
+
224
+ function isChapterEditing(chapterId: string) {
225
+ return isEditMode.value && editingChapterId.value === chapterId
226
+ }
227
+
228
+ function showChapterFields(chapterId: string) {
229
+ return !isEditMode.value || isChapterEditing(chapterId)
230
+ }
231
+
232
+ function isChapterTitleEdited(chapter: TemplateChapterForm) {
233
+ const original = chapterOriginalTitles.value[chapter.id] ?? ''
234
+ return chapter.title.trim() !== original.trim()
235
+ }
236
+
237
+ function expandChapter(id: string) {
238
+ form.value = {
239
+ ...form.value,
240
+ chapters: form.value.chapters.map((chapter) => ({
241
+ ...chapter,
242
+ expanded: chapter.id === id,
243
+ })),
244
+ }
245
+ }
246
+
247
+ function toggleChapter(id: string) {
248
+ const target = form.value.chapters.find((chapter) => chapter.id === id)
249
+ if (!target) return
250
+
251
+ if (target.expanded) {
252
+ if (editingChapterId.value === id) {
253
+ editingChapterId.value = null
254
+ }
255
+ form.value = {
256
+ ...form.value,
257
+ chapters: form.value.chapters.map((chapter) =>
258
+ chapter.id === id ? { ...chapter, expanded: false } : chapter
259
+ ),
260
+ }
261
+ return
262
+ }
263
+
264
+ expandChapter(id)
265
+ }
266
+
267
+ function handleStartEditChapter(chapter: TemplateChapterForm) {
268
+ if (editingChapterId.value && editingChapterId.value !== chapter.id) {
269
+ ElMessage.warning('請先保存當前章節,再編輯其他章節')
270
+ return
271
+ }
272
+
273
+ editingChapterId.value = chapter.id
274
+ expandChapter(chapter.id)
275
+ }
276
+
277
+ function handleChapterToolbarAction(chapter: TemplateChapterForm) {
278
+ if (isChapterEditing(chapter.id)) {
279
+ void handleDeleteChapter(chapter)
280
+ return
281
+ }
282
+ handleStartEditChapter(chapter)
283
+ }
284
+
285
+ function addChapter() {
286
+ if (form.value.chapters.length >= CHAPTER_MAX_COUNT) {
287
+ ElMessage.warning(`章節數量已達上限 ${CHAPTER_MAX_COUNT} 個`)
288
+ return
289
+ }
290
+
291
+ const collapsed = form.value.chapters.map((chapter) => ({ ...chapter, expanded: false }))
292
+ const newChapter = createEmptyChapter(true)
293
+ form.value = {
294
+ ...form.value,
295
+ chapters: [...collapsed, newChapter],
296
+ }
297
+ chapterOriginalTitles.value = {
298
+ ...chapterOriginalTitles.value,
299
+ [newChapter.id]: '',
300
+ }
301
+ }
302
+
303
+ function removeChapter(id: string) {
304
+ if (form.value.chapters.length <= 1) return
305
+ if (editingChapterId.value === id) {
306
+ editingChapterId.value = null
307
+ }
308
+ const next = form.value.chapters.filter((chapter) => chapter.id !== id)
309
+ if (!next.some((chapter) => chapter.expanded) && next[0]) {
310
+ next[0] = { ...next[0], expanded: true }
311
+ }
312
+ form.value = { ...form.value, chapters: next }
313
+ const { [id]: _removed, ...rest } = chapterOriginalTitles.value
314
+ chapterOriginalTitles.value = rest
315
+ }
316
+
317
+ async function handleDeleteChapter(chapter: TemplateChapterForm) {
318
+ if (form.value.chapters.length <= 1) return
319
+
320
+ const chapterName = getChapterDisplayTitle(chapter)
321
+ try {
322
+ await ElMessageBox.confirm(`確定刪除章節「${chapterName}」嗎?`, '刪除', {
323
+ confirmButtonText: '確定',
324
+ cancelButtonText: '取消',
325
+ type: 'warning',
326
+ })
327
+ removeChapter(chapter.id)
328
+ ElMessage.success(`已刪除:${chapterName}`)
329
+ } catch {
330
+ // 用戶取消
331
+ }
332
+ }
333
+
334
+ function getChapterDisplayTitle(chapter: TemplateChapterForm) {
335
+ return chapter.title.trim() || '章節標題'
336
+ }
337
+
338
+ function resolvePromptFieldProps(_chapter: TemplateChapterForm): PromptFieldLayoutBind {
339
+ return {
340
+ labelWidth: '100px',
341
+ fieldGap: '16px',
342
+ width: '100%',
343
+ }
344
+ }
345
+
346
+ function handleAiAssist(chapter: TemplateChapterForm) {
347
+ if (!chapter.prompt.trim()) {
348
+ chapter.prompt = '請根據本章節描述與行業背景,生成結構化分析內容。'
349
+ }
350
+ ElMessage.info('AI 輔助生成(演示)')
351
+ }
352
+
353
+ function resetEditingState() {
354
+ editingChapterId.value = null
355
+ }
356
+
357
+ defineExpose({
358
+ resetEditingState,
359
+ isCreateMode,
360
+ })
361
+ </script>
362
+
363
+ <style scoped>
364
+ .template-editor-form {
365
+ --gen-ai-fs-base: clamp(13px, calc(14 * 100vw / 1920), 14px);
366
+ --template-editor-label-w: 100px;
367
+ --template-editor-field-gap: 16px;
368
+ --report-accent: #c53355;
369
+ --report-accent-soft: #fdf0f2;
370
+ display: flex;
371
+ flex-direction: column;
372
+ gap: 20px;
373
+ font-size: var(--gen-ai-fs-base);
374
+ }
375
+
376
+ .template-editor-form__row {
377
+ width: 100%;
378
+ }
379
+
380
+ .template-editor-field {
381
+ margin: 0;
382
+ }
383
+
384
+ .template-editor-field--row {
385
+ display: grid;
386
+ grid-template-columns: var(--template-editor-label-w) minmax(0, 1fr);
387
+ column-gap: var(--template-editor-field-gap);
388
+ align-items: center;
389
+ width: 100%;
390
+ }
391
+
392
+ .template-editor-field--textarea-row {
393
+ align-items: start;
394
+ }
395
+
396
+ .template-editor-field--textarea-row .template-editor-label {
397
+ padding-top: 8px;
398
+ }
399
+
400
+ .template-editor-label {
401
+ flex-shrink: 0;
402
+ font-size: 14px;
403
+ font-weight: 500;
404
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
405
+ color: #303133;
406
+ white-space: nowrap;
407
+ }
408
+
409
+ .template-editor-label__required {
410
+ margin-left: 2px;
411
+ color: #f56c6c;
412
+ }
413
+
414
+ .template-editor-field__control {
415
+ min-width: 0;
416
+ width: 100%;
417
+ }
418
+
419
+ .template-editor-field__control--lang {
420
+ width: auto;
421
+ max-width: 270px;
422
+ }
423
+
424
+ .template-editor-field__lang {
425
+ width: 270px;
426
+ }
427
+
428
+ .template-editor-field__input,
429
+ .template-editor-field__textarea {
430
+ width: 100%;
431
+ }
432
+
433
+ .template-editor-field__input :deep(.el-input__wrapper),
434
+ .template-editor-field__textarea :deep(.el-textarea__inner) {
435
+ font-size: var(--gen-ai-fs-base);
436
+ }
437
+
438
+ .template-editor-field__lang :deep(.el-select__wrapper) {
439
+ min-height: 32px;
440
+ }
441
+
442
+ .template-editor-field__input :deep(.el-input__count),
443
+ .template-editor-field__textarea :deep(.el-input__count) {
444
+ right: 0;
445
+ bottom: -18px;
446
+ background: transparent;
447
+ font-size: 12px;
448
+ color: #909399;
449
+ }
450
+
451
+ .template-editor-chapters {
452
+ display: flex;
453
+ flex-direction: column;
454
+ gap: 0;
455
+ margin-top: 15px;
456
+ padding-top: 15px;
457
+ border-top: 1px solid #ebeef5;
458
+ }
459
+
460
+ .template-editor-chapter {
461
+ border-bottom: 1px solid #ebeef5;
462
+ }
463
+
464
+ .template-editor-chapter:first-child {
465
+ margin-top: 16px;
466
+ }
467
+
468
+ .template-editor-chapter__header {
469
+ display: grid;
470
+ grid-template-columns: 20px var(--template-editor-label-w) minmax(0, 1fr) 20px;
471
+ column-gap: var(--template-editor-field-gap);
472
+ align-items: center;
473
+ width: 100%;
474
+ min-height: 48px;
475
+ padding: 0 4px;
476
+ border: none;
477
+ background: #fff;
478
+ text-align: left;
479
+ cursor: pointer;
480
+ transition: background-color 0.2s ease;
481
+ }
482
+
483
+ .template-editor-chapter__header:hover {
484
+ background: #f5f7fa;
485
+ }
486
+
487
+ .template-editor-chapter__body {
488
+ display: flex;
489
+ flex-direction: column;
490
+ gap: 20px;
491
+ padding: 0 4px 16px;
492
+ }
493
+
494
+ .template-editor-chapter__toolbar {
495
+ display: grid;
496
+ grid-template-columns: var(--template-editor-label-w) minmax(0, 1fr);
497
+ column-gap: var(--template-editor-field-gap);
498
+ margin-top: 20px;
499
+ }
500
+
501
+ .template-editor-chapter__action-btn {
502
+ grid-column: 2;
503
+ justify-self: end;
504
+ width: 83px;
505
+ min-width: 83px;
506
+ height: 32px;
507
+ padding: 0;
508
+ }
509
+
510
+ .template-center-toolbar-btn {
511
+ display: inline-flex;
512
+ align-items: center;
513
+ justify-content: center;
514
+ gap: 6px;
515
+ box-sizing: border-box;
516
+ height: 32px;
517
+ min-width: 83px;
518
+ padding: 0 14px;
519
+ border-radius: 4px;
520
+ border: 1px solid #dcdfe6;
521
+ background: #fff;
522
+ color: #606266;
523
+ font-size: 15px;
524
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
525
+ font-weight: 400;
526
+ line-height: 1;
527
+ white-space: nowrap;
528
+ cursor: pointer;
529
+ transition: all 0.2s ease;
530
+ }
531
+
532
+ .template-center-toolbar-btn--outline {
533
+ border-color: var(--report-accent);
534
+ color: var(--report-accent);
535
+ background: #fff;
536
+ }
537
+
538
+ .template-center-toolbar-btn--outline:hover:not(:disabled) {
539
+ border-color: var(--report-accent);
540
+ color: var(--report-accent);
541
+ background: var(--report-accent-soft);
542
+ }
543
+
544
+ .template-center-toolbar-btn--delete {
545
+ border-color: var(--report-accent);
546
+ color: var(--report-accent);
547
+ background: #fff;
548
+ }
549
+
550
+ .template-center-toolbar-btn--delete:hover:not(:disabled) {
551
+ border-color: var(--report-accent);
552
+ color: var(--report-accent);
553
+ background: var(--report-accent-soft);
554
+ }
555
+
556
+ .template-center-toolbar-btn--ghost {
557
+ border: 1px solid #dcdfe6;
558
+ background: #fff;
559
+ color: #606266;
560
+ }
561
+
562
+ .template-center-toolbar-btn--ghost:hover:not(:disabled) {
563
+ border-color: var(--report-accent);
564
+ color: var(--report-accent);
565
+ background: var(--report-accent-soft);
566
+ }
567
+
568
+ .template-editor-chapter__drag {
569
+ grid-column: 1;
570
+ display: inline-flex;
571
+ flex-direction: column;
572
+ justify-content: center;
573
+ gap: 3px;
574
+ width: 12px;
575
+ flex-shrink: 0;
576
+ }
577
+
578
+ .template-editor-chapter__drag i {
579
+ display: block;
580
+ width: 4px;
581
+ height: 4px;
582
+ border-radius: 50%;
583
+ background: #909399;
584
+ }
585
+
586
+ .template-editor-chapter__header-title-wrap {
587
+ grid-column: 2 / 4;
588
+ display: inline-flex;
589
+ align-items: center;
590
+ gap: 4px;
591
+ min-width: 0;
592
+ }
593
+
594
+ .template-editor-chapter__header-title {
595
+ min-width: 0;
596
+ font-size: 14px;
597
+ font-weight: 500;
598
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
599
+ color: #303133;
600
+ overflow: hidden;
601
+ text-overflow: ellipsis;
602
+ white-space: nowrap;
603
+ }
604
+
605
+ .template-editor-chapter__title-edited {
606
+ flex-shrink: 0;
607
+ font-size: 14px;
608
+ color: #c53355;
609
+ }
610
+
611
+ .template-editor-chapter__chevron {
612
+ grid-column: 4;
613
+ justify-self: end;
614
+ flex-shrink: 0;
615
+ font-size: 16px;
616
+ color: #dcdfe6;
617
+ transition: transform 0.2s ease;
618
+ }
619
+
620
+ .template-editor-chapter__chevron--expanded {
621
+ transform: rotate(90deg);
622
+ }
623
+
624
+ .template-editor-field--textarea-row .template-editor-field__control {
625
+ position: relative;
626
+ padding-bottom: 18px;
627
+ }
628
+
629
+ .template-editor-form__add {
630
+ display: flex;
631
+ justify-content: center;
632
+ padding: 8px 0 4px;
633
+ }
634
+
635
+ .template-editor-form__add-btn {
636
+ display: inline-flex;
637
+ align-items: center;
638
+ justify-content: center;
639
+ box-sizing: border-box;
640
+ width: 160px;
641
+ height: 39px;
642
+ border: 1px solid #dcdfe6;
643
+ border-radius: 4px;
644
+ background: #fff;
645
+ color: #606266;
646
+ font-size: 24px;
647
+ line-height: 1;
648
+ cursor: pointer;
649
+ transition: all 0.2s ease;
650
+ }
651
+
652
+ .template-editor-form__add-btn:hover:not(:disabled) {
653
+ border-color: #c53355;
654
+ color: #c53355;
655
+ }
656
+
657
+ .template-editor-form__add-btn:disabled {
658
+ opacity: 0.45;
659
+ cursor: not-allowed;
660
+ }
661
+ </style>
@@ -0,0 +1,32 @@
1
+ import type { TemplateItem, TemplateListParams } from '../types'
2
+ import { postGenAiRequest } from '../../common/utils/remote-request'
3
+ import modelManagerMockApi from './mockApi'
4
+
5
+ /** VITE_GEN_AI_MODEL_MANAGER_MOCK=true 時僅走 mock;默認先請求後端,失敗/超時回退 mock */
6
+ const FORCE_MOCK = import.meta.env.VITE_GEN_AI_MODEL_MANAGER_MOCK === 'true'
7
+
8
+ function isTemplateList(data: unknown) {
9
+ return Array.isArray(data) && data.length > 0 && typeof data[0]?.id === 'string'
10
+ }
11
+
12
+ const modelManagerApi = {
13
+ /** model-manager: 模板列表;網絡失敗或超時時回退 mock */
14
+ async fetchTemplateListData(params: TemplateListParams = {}) {
15
+ if (FORCE_MOCK) {
16
+ return modelManagerMockApi.fetchTemplateListData(params)
17
+ }
18
+
19
+ try {
20
+ const data = await postGenAiRequest('/template/list', params)
21
+ if (isTemplateList(data)) {
22
+ return data as TemplateItem[]
23
+ }
24
+ } catch (error) {
25
+ console.error('獲取 fetchTemplateListData 失敗', error)
26
+ }
27
+
28
+ return modelManagerMockApi.fetchTemplateListData(params)
29
+ },
30
+ }
31
+
32
+ export default modelManagerApi