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,856 @@
1
+ <template>
2
+ <!-- key=reportEditMountKey:一鍵生成 / 進入編輯後強制整組件重掛 -->
3
+ <ReportEdit v-if="isReportEditView" :key="reportEditMountKey" />
4
+ <div v-else class="report-center-page">
5
+ <header class="report-center-hero">
6
+ <h1 class="report-center-hero__title">AI行業分析報告助手</h1>
7
+ <p class="report-center-hero__subtitle">選擇報告生成參數,確定分析視角</p>
8
+ </header>
9
+
10
+ <el-card class="report-center-board" shadow="never">
11
+ <!-- 左:參數 -->
12
+ <section class="report-center-panel report-center-panel--params">
13
+ <h2 class="report-center-panel__title">基本參數</h2>
14
+
15
+ <div class="report-form">
16
+ <div class="report-form__row">
17
+ <label class="report-form__label">行業</label>
18
+ <div class="report-form__control">
19
+ <el-select
20
+ v-model="form.industry"
21
+ class="report-form__select"
22
+ popper-class="report-form-select-dropdown"
23
+ filterable
24
+ allow-create
25
+ default-first-option
26
+ clearable
27
+ placeholder=""
28
+ >
29
+ <el-option
30
+ v-for="item in industryOptions"
31
+ :key="item.id"
32
+ :label="item.name"
33
+ :value="item.name"
34
+ />
35
+ </el-select>
36
+ </div>
37
+ </div>
38
+ <div class="report-form__row">
39
+ <label class="report-form__label">調研主要出發點</label>
40
+ <div class="report-form__control">
41
+ <el-select
42
+ v-model="form.researchFocus"
43
+ class="report-form__select"
44
+ popper-class="report-form-select-dropdown"
45
+ filterable
46
+ allow-create
47
+ default-first-option
48
+ clearable
49
+ placeholder=""
50
+ >
51
+ <el-option
52
+ v-for="item in researchFocusOptions"
53
+ :key="item.id"
54
+ :label="item.name"
55
+ :value="item.name"
56
+ />
57
+ </el-select>
58
+ </div>
59
+ </div>
60
+ <div class="report-form__row">
61
+ <label class="report-form__label">年份</label>
62
+ <div class="report-form__control">
63
+ <el-select
64
+ v-model="form.year"
65
+ class="report-form__select"
66
+ popper-class="report-form-select-dropdown"
67
+ placeholder=""
68
+ >
69
+ <el-option v-for="year in yearOptions" :key="year" :label="year" :value="year" />
70
+ </el-select>
71
+ </div>
72
+ </div>
73
+ <div class="report-form__row">
74
+ <label class="report-form__label">報告名稱</label>
75
+ <div class="report-form__control">
76
+ <input
77
+ v-model="form.reportName"
78
+ type="text"
79
+ class="report-input"
80
+ @input="reportNameTouched = true"
81
+ />
82
+ </div>
83
+ </div>
84
+ <div class="report-form__row">
85
+ <label class="report-form__label">報告語言</label>
86
+ <div class="report-form__control report-form__control--radio">
87
+ <label v-for="lang in languageOptions" :key="lang.value" class="report-radio">
88
+ <input v-model="form.language" type="radio" :value="lang.value" />
89
+ <span>{{ lang.label }}</span>
90
+ </label>
91
+ </div>
92
+ </div>
93
+ <div class="report-form__row report-form__row--textarea">
94
+ <label class="report-form__label">每章節字數</label>
95
+ <div class="report-form__control report-form__textarea-box">
96
+ <el-input
97
+ v-model="form.wordsPerChapter"
98
+ type="textarea"
99
+ :rows="2"
100
+ resize="none"
101
+ class="report-form__textarea"
102
+ placeholder=""
103
+ @blur="clampWordsPerChapter"
104
+ />
105
+ <span class="report-form__textarea-hint">50-5000</span>
106
+ </div>
107
+ </div>
108
+ </div>
109
+
110
+ <button type="button" class="report-advanced-toggle" @click="advancedOpen = !advancedOpen">
111
+ <span>進階參數</span>
112
+ <span
113
+ class="report-advanced-toggle__arrow"
114
+ :class="{ 'is-open': advancedOpen }"
115
+ aria-hidden="true"
116
+ />
117
+ </button>
118
+
119
+ <el-scrollbar
120
+ v-show="advancedOpen"
121
+ class="report-form-advanced-scroll"
122
+ max-height="50vh"
123
+ >
124
+ <div class="report-form report-form--advanced">
125
+ <div class="report-form__row">
126
+ <label class="report-form__label">模型</label>
127
+ <div class="report-form__control">
128
+ <el-select
129
+ v-model="form.model"
130
+ class="report-form__select"
131
+ popper-class="report-form-select-dropdown"
132
+ filterable
133
+ allow-create
134
+ default-first-option
135
+ clearable
136
+ placeholder=""
137
+ >
138
+ <el-option
139
+ v-for="item in modelOptions"
140
+ :key="item.id"
141
+ :label="item.name"
142
+ :value="item.name"
143
+ />
144
+ </el-select>
145
+ </div>
146
+ </div>
147
+ <div class="report-form__row">
148
+ <label class="report-form__label">搜尋模式</label>
149
+ <div class="report-form__control">
150
+ <el-select
151
+ v-model="form.searchMode"
152
+ class="report-form__select"
153
+ popper-class="report-form-select-dropdown"
154
+ placeholder=""
155
+ >
156
+ <el-option
157
+ v-for="mode in searchModeOptions"
158
+ :key="mode"
159
+ :label="mode"
160
+ :value="mode"
161
+ />
162
+ </el-select>
163
+ </div>
164
+ </div>
165
+ <div class="report-form__row report-form__row--slider">
166
+ <label class="report-form__label">權重</label>
167
+ <div class="report-form__control report-form__control--slider">
168
+ <el-slider
169
+ v-model="form.weight"
170
+ :min="0"
171
+ :max="1"
172
+ :step="0.1"
173
+ :show-tooltip="false"
174
+ />
175
+ <input
176
+ v-model.number="form.weight"
177
+ type="number"
178
+ min="0"
179
+ max="1"
180
+ step="0.1"
181
+ class="report-slider-num"
182
+ />
183
+ </div>
184
+ </div>
185
+ <div class="report-form__row">
186
+ <label class="report-form__label">Rerank</label>
187
+ <div class="report-form__control">
188
+ <el-switch v-model="form.rerank" class="report-form__switch" />
189
+ </div>
190
+ </div>
191
+ <div class="report-form__row report-form__row--slider">
192
+ <label class="report-form__label">Rerank top-N</label>
193
+ <div class="report-form__control report-form__control--slider">
194
+ <el-slider
195
+ v-model="form.rerankTopN"
196
+ :min="1"
197
+ :max="20"
198
+ :step="1"
199
+ :show-tooltip="false"
200
+ />
201
+ <input
202
+ v-model.number="form.rerankTopN"
203
+ type="number"
204
+ min="1"
205
+ max="20"
206
+ class="report-slider-num"
207
+ />
208
+ </div>
209
+ </div>
210
+ <div class="report-form__row report-form__row--slider">
211
+ <label class="report-form__label">Rerank threshold</label>
212
+ <div class="report-form__control report-form__control--slider">
213
+ <el-slider
214
+ v-model="form.rerankThreshold"
215
+ :min="0"
216
+ :max="1"
217
+ :step="0.1"
218
+ :show-tooltip="false"
219
+ />
220
+ <input
221
+ v-model.number="form.rerankThreshold"
222
+ type="number"
223
+ min="0"
224
+ max="1"
225
+ step="0.1"
226
+ class="report-slider-num"
227
+ />
228
+ </div>
229
+ </div>
230
+ </div>
231
+ </el-scrollbar>
232
+
233
+ <p v-if="form.searchMode === 'Vector Search'" class="report-form__hint">
234
+ 搜尋模式為「Vector Search」時,權重與 Rerank 相關參數將被忽略。
235
+ </p>
236
+ </section>
237
+
238
+ <ReportSourcesPanel sync-workflow />
239
+ </el-card>
240
+
241
+ <footer class="report-center-footer">
242
+ <button type="button" class="app-btn app-btn--wide" @click="selectTemplate">選擇模板</button>
243
+ </footer>
244
+ </div>
245
+ </template>
246
+
247
+ <script setup lang="ts">
248
+ import { ElInput, ElOption, ElScrollbar, ElSelect, ElSlider, ElSwitch } from 'element-plus'
249
+ import { computed, reactive, ref, watch } from 'vue'
250
+ import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
251
+ import '../common/assets/fonts.css'
252
+ import ReportEdit from './components/reportEdit.vue'
253
+ import ReportSourcesPanel from './components/ReportSourcesPanel.vue'
254
+ import {
255
+ createReportEditRouteSyncActions,
256
+ isReportEditRoute,
257
+ navigateToReportEdit,
258
+ syncWorkflowFromReportEditRoute,
259
+ } from './report-domain'
260
+ import { useReportWorkflow } from './useReportWorkflow'
261
+
262
+ const WORDS_PER_CHAPTER_MIN = 50
263
+ const WORDS_PER_CHAPTER_MAX = 5000
264
+
265
+ interface ManagementOption {
266
+ id: string
267
+ name: string
268
+ }
269
+
270
+ const MOCK_INDUSTRIES: ManagementOption[] = [
271
+ { id: 'ind-1', name: '新能源汽車' },
272
+ { id: 'ind-2', name: '新能源發電' },
273
+ { id: 'ind-3', name: '數據中心' },
274
+ { id: 'ind-4', name: '本港房地產' },
275
+ { id: 'ind-5', name: '內地房地產' },
276
+ { id: 'ind-6', name: '香港股票市場' },
277
+ { id: 'ind-7', name: '戰略新興產業' },
278
+ { id: 'ind-8', name: '半導體' },
279
+ { id: 'ind-9', name: '消費電子' },
280
+ { id: 'ind-10', name: '醫療健康' },
281
+ { id: 'ind-11', name: '生物醫藥' },
282
+ { id: 'ind-12', name: '物流運輸' },
283
+ { id: 'ind-13', name: '航空旅遊' },
284
+ { id: 'ind-14', name: '零售消費' },
285
+ { id: 'ind-15', name: '食品飲料' },
286
+ { id: 'ind-16', name: '化工材料' },
287
+ { id: 'ind-17', name: '機械裝備' },
288
+ { id: 'ind-18', name: '建築工程' },
289
+ { id: 'ind-19', name: '電信運營' },
290
+ { id: 'ind-20', name: '互聯網科技' },
291
+ ]
292
+
293
+ const MOCK_RESEARCH_POINTS: ManagementOption[] = [
294
+ { id: 'res-1', name: '中國香港' },
295
+ { id: 'res-2', name: '中國內地' },
296
+ { id: 'res-3', name: '東南亞' },
297
+ { id: 'res-4', name: '歐美市場' },
298
+ ]
299
+
300
+ const route = useRoute()
301
+ const router = useRouter()
302
+ const workflow = useReportWorkflow()
303
+ workflow.restoreReportEditSession()
304
+ const { reportEditMountKey } = workflow
305
+ const isReportEditView = computed(() => isReportEditRoute(route))
306
+ const advancedOpen = ref(false)
307
+ const reportNameTouched = ref(false)
308
+
309
+ const languageOptions = [
310
+ { value: 'zh-Hant', label: '繁體中文' },
311
+ { value: 'zh-Hans', label: '簡體中文' },
312
+ { value: 'en', label: 'English' },
313
+ ] as const
314
+
315
+ const searchModeOptions = ['Hybrid Search', 'Vector Search'] as const
316
+
317
+ const modelOptions = [
318
+ { id: 'model-1', name: 'GPT-4o' },
319
+ { id: 'model-2', name: 'GPT-4.1' },
320
+ { id: 'model-3', name: 'Claude 3.5 Sonnet' },
321
+ { id: 'model-4', name: 'Qwen-Max' },
322
+ ] as const
323
+
324
+ const industryOptions = MOCK_INDUSTRIES
325
+ const researchFocusOptions = MOCK_RESEARCH_POINTS
326
+
327
+ const yearOptions = computed(() => {
328
+ const currentYear = new Date().getFullYear()
329
+ return [currentYear - 2, currentYear - 1, currentYear].map((year) => String(year))
330
+ })
331
+
332
+ function buildReportName(industry: string, year: string) {
333
+ const name = industry.trim()
334
+ const y = year.trim()
335
+ if (!name || !y) return ''
336
+ return `${name}行業調研報告 (${y}年)`
337
+ }
338
+
339
+ const form = reactive({
340
+ industry: '',
341
+ researchFocus: '',
342
+ year: String(new Date().getFullYear()),
343
+ reportName: '',
344
+ language: 'zh-Hant',
345
+ wordsPerChapter: '',
346
+ model: '',
347
+ searchMode: 'Hybrid Search',
348
+ weight: 0.5,
349
+ rerank: true,
350
+ rerankTopN: 5,
351
+ rerankThreshold: 0.5,
352
+ })
353
+
354
+ function hydrateFormFromDraft() {
355
+ const d = workflow.draft
356
+ if (!d.industry && !d.reportName && !d.researchFocus) return
357
+ form.industry = d.industry || form.industry
358
+ form.researchFocus = d.researchFocus || form.researchFocus
359
+ form.year = d.year || form.year
360
+ form.reportName = d.reportName || form.reportName
361
+ form.language = d.language || form.language
362
+ form.wordsPerChapter = d.wordsPerChapter || form.wordsPerChapter
363
+ form.model = d.model || form.model
364
+ form.searchMode = d.searchMode || form.searchMode
365
+ form.weight = d.weight ?? form.weight
366
+ form.rerank = d.rerank ?? form.rerank
367
+ form.rerankTopN = d.rerankTopN ?? form.rerankTopN
368
+ form.rerankThreshold = d.rerankThreshold ?? form.rerankThreshold
369
+ if (d.reportName) reportNameTouched.value = true
370
+ }
371
+
372
+ hydrateFormFromDraft()
373
+
374
+ watch(
375
+ () => [form.industry, form.year] as const,
376
+ ([industry, year]) => {
377
+ if (reportNameTouched.value) return
378
+ form.reportName = buildReportName(industry, year)
379
+ },
380
+ { immediate: true }
381
+ )
382
+
383
+ function clampWordsPerChapter() {
384
+ const raw = form.wordsPerChapter.trim()
385
+ if (!raw) {
386
+ form.wordsPerChapter = ''
387
+ return
388
+ }
389
+ const value = Math.round(Number(raw))
390
+ if (Number.isNaN(value)) {
391
+ form.wordsPerChapter = ''
392
+ return
393
+ }
394
+ if (value < WORDS_PER_CHAPTER_MIN) {
395
+ form.wordsPerChapter = String(WORDS_PER_CHAPTER_MIN)
396
+ } else if (value > WORDS_PER_CHAPTER_MAX) {
397
+ form.wordsPerChapter = String(WORDS_PER_CHAPTER_MAX)
398
+ } else {
399
+ form.wordsPerChapter = String(value)
400
+ }
401
+ }
402
+
403
+ function getSelectedSourceCount() {
404
+ return workflow.sourceFiles.value.filter((file) => file.selected).length
405
+ }
406
+
407
+ function selectTemplate() {
408
+ workflow.patchDraft({
409
+ industry: form.industry,
410
+ researchFocus: form.researchFocus,
411
+ year: form.year,
412
+ reportName: form.reportName,
413
+ language: form.language,
414
+ wordsPerChapter: form.wordsPerChapter,
415
+ model: form.model,
416
+ searchMode: form.searchMode,
417
+ weight: form.weight,
418
+ rerank: form.rerank,
419
+ rerankTopN: form.rerankTopN,
420
+ rerankThreshold: form.rerankThreshold,
421
+ sourceCount: getSelectedSourceCount(),
422
+ })
423
+ workflow.openReportEdit('select')
424
+ void navigateToReportEdit(router, 'select')
425
+ }
426
+
427
+ watch(
428
+ () => route.fullPath,
429
+ () => {
430
+ if (workflow.isReportEditNavLocked()) return
431
+ syncWorkflowFromReportEditRoute(route, createReportEditRouteSyncActions(workflow))
432
+ },
433
+ { immediate: true, flush: 'sync' }
434
+ )
435
+
436
+ onBeforeRouteUpdate((to) => {
437
+ if (workflow.isReportEditNavLocked()) return
438
+ syncWorkflowFromReportEditRoute(to, createReportEditRouteSyncActions(workflow))
439
+ })
440
+ </script>
441
+
442
+ <style scoped>
443
+ .report-center-page {
444
+ /* 與 model-manager 模板列表卡片邊框一致 */
445
+ --report-center-board-border-color: #ebeef5;
446
+ --app-btn-color: #fff;
447
+ --app-btn-color-hover: #c53355;
448
+ --app-btn-bg: #c53355;
449
+ --app-btn-bg-hover: #fff;
450
+ --app-btn-border: #c53355;
451
+ --app-btn-border-hover: #c53355;
452
+ --app-btn-radius: 5px;
453
+ --app-btn-font-size: clamp(14px, calc(16 * 100vw / 1920), 16px);
454
+ --app-btn-font-weight: 600;
455
+ --app-btn-padding-y: 8px;
456
+ --app-btn-padding-x: 15px;
457
+ --app-btn-min-width: 120px;
458
+ display: flex;
459
+ flex-direction: column;
460
+ gap: 20px;
461
+ box-sizing: border-box;
462
+ min-height: 100dvh;
463
+ padding: 32px 24px 24px 30px;
464
+ color: #1a1a1a;
465
+ background: #fff;
466
+ }
467
+
468
+ .report-center-hero {
469
+ text-align: center;
470
+ padding: 8px 0 4px;
471
+ }
472
+
473
+ .report-center-hero__title {
474
+ margin: 0 0 8px;
475
+ font-size: 22px;
476
+ font-weight: 700;
477
+ color: #1a1a1a;
478
+ letter-spacing: 0.02em;
479
+ }
480
+
481
+ .report-center-hero__subtitle {
482
+ margin: 0;
483
+ font-size: 14px;
484
+ color: #4e5969;
485
+ }
486
+
487
+ .report-center-board {
488
+ flex: 1 0 auto;
489
+ border: none;
490
+ background: transparent;
491
+ --el-card-border-color: transparent;
492
+ }
493
+
494
+ .report-center-board :deep(.el-card__body) {
495
+ display: grid;
496
+ grid-template-columns: 1fr 1fr;
497
+ gap: 20px;
498
+ align-items: stretch;
499
+ box-sizing: border-box;
500
+ min-height: 68dvh;
501
+ height: auto;
502
+ padding: 0;
503
+ background: transparent;
504
+ }
505
+
506
+ .report-center-panel {
507
+ display: flex;
508
+ flex-direction: column;
509
+ height: 100%;
510
+ padding: 20px 24px 24px;
511
+ min-width: 0;
512
+ border: 1px solid var(--report-center-board-border-color);
513
+ border-radius: 8px;
514
+ background: #fff;
515
+ box-sizing: border-box;
516
+ transition: box-shadow var(--el-transition-duration, 0.3s);
517
+ }
518
+
519
+ .report-center-panel:hover {
520
+ box-shadow: var(--el-box-shadow-light, 0 0 12px rgba(0, 0, 0, 0.12));
521
+ }
522
+
523
+ .report-center-panel__title {
524
+ margin: 0 0 16px;
525
+ font-size: 15px;
526
+ font-weight: 700;
527
+ color: #1a1a1a;
528
+ }
529
+
530
+ /* —— 表單 —— */
531
+ .report-form {
532
+ display: flex;
533
+ flex-direction: column;
534
+ gap: 10px;
535
+ }
536
+
537
+ .report-form--advanced {
538
+ margin-top: 4px;
539
+ padding-right: 4px;
540
+ }
541
+
542
+ .report-form-advanced-scroll {
543
+ margin-top: 4px;
544
+ width: 100%;
545
+ max-height: 50vh;
546
+ }
547
+
548
+ .report-form-advanced-scroll :deep(.el-scrollbar__wrap) {
549
+ max-height: 50vh !important;
550
+ scrollbar-width: none !important;
551
+ -ms-overflow-style: none !important;
552
+ }
553
+
554
+ .report-form-advanced-scroll :deep(.el-scrollbar__wrap::-webkit-scrollbar) {
555
+ width: 0 !important;
556
+ height: 0 !important;
557
+ display: none !important;
558
+ }
559
+
560
+ .report-form-advanced-scroll :deep(.el-scrollbar__bar.is-horizontal) {
561
+ display: none !important;
562
+ }
563
+
564
+ .report-form-advanced-scroll :deep(.el-scrollbar__bar.is-vertical) {
565
+ width: 8px !important;
566
+ right: 0 !important;
567
+ top: 2px !important;
568
+ bottom: 2px !important;
569
+ height: auto !important;
570
+ opacity: 1 !important;
571
+ z-index: 10;
572
+ }
573
+
574
+ .report-form-advanced-scroll :deep(.el-scrollbar__bar.is-vertical .el-scrollbar__thumb) {
575
+ display: block !important;
576
+ width: 8px !important;
577
+ height: 50px !important;
578
+ min-height: 50px !important;
579
+ border-radius: 4px !important;
580
+ background-color: var(--report-edit-col-scroll-thumb, #999) !important;
581
+ opacity: 1 !important;
582
+ }
583
+
584
+ .report-form__row {
585
+ display: grid;
586
+ grid-template-columns: 120px 1fr;
587
+ align-items: center;
588
+ gap: 12px;
589
+ }
590
+
591
+ .report-form__row--slider {
592
+ align-items: center;
593
+ }
594
+
595
+ .report-form__row--textarea {
596
+ align-items: flex-start;
597
+ margin-bottom: 18px;
598
+ }
599
+
600
+ .report-form__row--textarea .report-form__label {
601
+ padding-top: 6px;
602
+ }
603
+
604
+ .report-form__label {
605
+ font-size: 13px;
606
+ color: #1a1a1a;
607
+ white-space: nowrap;
608
+ }
609
+
610
+ .report-form__control--radio {
611
+ display: flex;
612
+ flex-wrap: wrap;
613
+ gap: 16px;
614
+ }
615
+
616
+ .report-form__control--slider {
617
+ display: flex;
618
+ align-items: center;
619
+ gap: 12px;
620
+ }
621
+
622
+ .report-form__control--slider :deep(.el-slider) {
623
+ flex: 1;
624
+ min-width: 0;
625
+ --el-slider-main-bg-color: #c53355;
626
+ --el-slider-runway-bg-color: #e8eaec;
627
+ --el-slider-button-size: 14px;
628
+ }
629
+
630
+ .report-radio {
631
+ display: inline-flex;
632
+ align-items: center;
633
+ gap: 6px;
634
+ font-size: 13px;
635
+ cursor: pointer;
636
+ }
637
+
638
+ .report-radio input {
639
+ accent-color: #c53355;
640
+ }
641
+
642
+ .report-input {
643
+ width: 100%;
644
+ height: 32px;
645
+ padding: 4px 10px;
646
+ border: 1px solid #c9cdd4;
647
+ border-radius: 2px;
648
+ font-size: 13px;
649
+ color: #1a1a1a;
650
+ background: #fff;
651
+ box-sizing: border-box;
652
+ outline: none;
653
+ }
654
+
655
+ .report-input:focus {
656
+ border-color: #c53355;
657
+ }
658
+
659
+ .report-form__select {
660
+ width: 100%;
661
+ }
662
+
663
+ .report-form__select :deep(.el-select__wrapper) {
664
+ min-height: 32px;
665
+ padding: 4px 10px;
666
+ border-radius: 2px;
667
+ box-shadow: 0 0 0 1px #c9cdd4 inset;
668
+ font-size: 15px;
669
+ font-weight: 500;
670
+ color: #303133;
671
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
672
+ }
673
+
674
+ .report-form__select :deep(.el-select__selected-item),
675
+ .report-form__select :deep(.el-select__placeholder),
676
+ .report-form__select :deep(.el-select__input) {
677
+ font-size: 15px;
678
+ font-weight: 500;
679
+ color: #303133;
680
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
681
+ }
682
+
683
+ .report-form__select :deep(.el-select__wrapper.is-focused) {
684
+ box-shadow: 0 0 0 1px #c53355 inset;
685
+ }
686
+
687
+ .report-form__textarea-box {
688
+ position: relative;
689
+ width: 100%;
690
+ }
691
+
692
+ .report-form__textarea {
693
+ width: 100%;
694
+ }
695
+
696
+ .report-form__textarea :deep(.el-textarea__inner) {
697
+ min-height: 56px;
698
+ padding: 6px 10px 22px;
699
+ border-radius: 2px;
700
+ box-shadow: 0 0 0 1px #c9cdd4 inset;
701
+ font-size: 13px;
702
+ color: #1a1a1a;
703
+ line-height: 1.5;
704
+ }
705
+
706
+ .report-form__textarea :deep(.el-textarea__inner:focus) {
707
+ box-shadow: 0 0 0 1px #c53355 inset;
708
+ }
709
+
710
+ .report-form__textarea-hint {
711
+ position: absolute;
712
+ right: 0;
713
+ bottom: -18px;
714
+ font-size: 12px;
715
+ line-height: 1;
716
+ color: #909399;
717
+ pointer-events: none;
718
+ user-select: none;
719
+ }
720
+
721
+ .report-form__switch {
722
+ --el-switch-on-color: #c53355;
723
+ --el-switch-off-color: #fff;
724
+ }
725
+
726
+ .report-form__switch :deep(.el-switch__core) {
727
+ border: 1px solid #c9cdd4;
728
+ }
729
+
730
+ .report-form__switch :deep(.el-switch.is-checked .el-switch__core) {
731
+ border-color: #c53355;
732
+ }
733
+
734
+ .report-input--select {
735
+ appearance: auto;
736
+ cursor: pointer;
737
+ }
738
+
739
+ .report-slider-num {
740
+ width: 52px;
741
+ height: 28px;
742
+ padding: 2px 6px;
743
+ border: 1px solid #c9cdd4;
744
+ border-radius: 2px;
745
+ font-size: 13px;
746
+ text-align: center;
747
+ flex-shrink: 0;
748
+ }
749
+
750
+ .report-advanced-toggle {
751
+ display: flex;
752
+ align-items: center;
753
+ justify-content: space-between;
754
+ width: 100%;
755
+ margin: 16px 0 8px;
756
+ padding: 0;
757
+ border: 0;
758
+ background: none;
759
+ font-size: 14px;
760
+ font-weight: 700;
761
+ color: #1a1a1a;
762
+ cursor: pointer;
763
+ }
764
+
765
+ .report-advanced-toggle__arrow {
766
+ display: inline-block;
767
+ width: 0;
768
+ height: 0;
769
+ border-left: 5px solid transparent;
770
+ border-right: 5px solid transparent;
771
+ border-bottom: none;
772
+ /* 收起朝下;展開朝上(與折疊區塊一致) */
773
+ border-top: 7px solid #c53355;
774
+ transition: transform 0.2s ease;
775
+ }
776
+
777
+ .report-advanced-toggle__arrow.is-open {
778
+ transform: rotate(180deg);
779
+ }
780
+
781
+ .report-form__hint {
782
+ margin: 12px 0 0;
783
+ font-size: 12px;
784
+ color: #86909c;
785
+ }
786
+
787
+ .report-center-footer {
788
+ flex-shrink: 0;
789
+ display: flex;
790
+ justify-content: center;
791
+ margin-top: auto;
792
+ padding: 4px 0 8px;
793
+ background: #fff;
794
+ }
795
+
796
+ .app-btn {
797
+ min-width: var(--app-btn-min-width);
798
+ padding: var(--app-btn-padding-y) var(--app-btn-padding-x);
799
+ border: 1px solid var(--app-btn-border);
800
+ border-radius: var(--app-btn-radius);
801
+ background: var(--app-btn-bg);
802
+ color: var(--app-btn-color);
803
+ font-size: var(--app-btn-font-size);
804
+ font-weight: var(--app-btn-font-weight);
805
+ line-height: 1.4;
806
+ font-family: inherit;
807
+ cursor: pointer;
808
+ transition: all 0.2s ease;
809
+ }
810
+
811
+ .app-btn:hover {
812
+ background: var(--app-btn-bg-hover);
813
+ border-color: var(--app-btn-border-hover);
814
+ color: var(--app-btn-color-hover);
815
+ }
816
+
817
+ .app-btn--wide {
818
+ min-width: 148px;
819
+ }
820
+
821
+ @media (max-width: 960px) {
822
+ .report-center-board :deep(.el-card__body) {
823
+ grid-template-columns: 1fr;
824
+ gap: 20px;
825
+ }
826
+
827
+ .report-center-panel {
828
+ height: auto;
829
+ }
830
+
831
+ .report-center-board :deep(.el-card__body) {
832
+ min-height: auto;
833
+ }
834
+
835
+ .report-form__row {
836
+ grid-template-columns: 1fr;
837
+ gap: 4px;
838
+ }
839
+ }
840
+ </style>
841
+
842
+ <style>
843
+ /* teleport 下拉層:選項文字統一 */
844
+ .report-form-select-dropdown.el-select__popper .el-select-dropdown__item {
845
+ font-size: 15px;
846
+ font-weight: 500;
847
+ color: #303133;
848
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
849
+ }
850
+
851
+ .report-form-select-dropdown.el-select__popper .el-select-dropdown__item.is-hovering,
852
+ .report-form-select-dropdown.el-select__popper .el-select-dropdown__item.is-selected {
853
+ color: #303133;
854
+ font-weight: 500;
855
+ }
856
+ </style>