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.
- package/gen-ai/build-report/chartStreamRefs.ts +227 -0
- package/gen-ai/build-report/coherence-check/index.vue +559 -0
- package/gen-ai/build-report/components/ReportChapterWorkspace.vue +4571 -0
- package/gen-ai/build-report/components/ReportConfigPanel.vue +727 -0
- package/gen-ai/build-report/components/ReportGenerateSettingsPanel.vue +2361 -0
- package/gen-ai/build-report/components/ReportGeneratingPanel.vue +1765 -0
- package/gen-ai/build-report/components/ReportPromptPanel.vue +625 -0
- package/gen-ai/build-report/components/ReportSourceFileSummary.vue +665 -0
- package/gen-ai/build-report/components/ReportSourcesPanel.vue +1327 -0
- package/gen-ai/build-report/components/ReportTemplateDetail.vue +191 -0
- package/gen-ai/build-report/components/ReportTemplateEditorFormPanel.vue +667 -0
- package/gen-ai/build-report/components/ReportTemplatePicker.vue +302 -0
- package/gen-ai/build-report/components/ReportWorkflowSidebar.vue +148 -0
- package/gen-ai/build-report/components/paramsSetting.vue +886 -0
- package/gen-ai/build-report/components/reportEdit.vue +2283 -0
- package/gen-ai/build-report/index.vue +856 -0
- package/gen-ai/build-report/report-domain.ts +1133 -0
- package/gen-ai/build-report/report-template-data.ts +295 -0
- package/gen-ai/build-report/useReportWorkflow.ts +1386 -0
- package/gen-ai/common/assets/NotoSans-SC.woff2 +0 -0
- package/gen-ai/common/assets/NotoSans-TC.woff2 +0 -0
- package/gen-ai/common/assets/NotoSans.woff2 +0 -0
- package/gen-ai/common/assets/fonts.css +278 -0
- package/gen-ai/common/assets/pagination.css +163 -0
- package/gen-ai/common/directives/btnLoading.ts +455 -0
- package/gen-ai/common/directives/debounce.ts +98 -0
- package/gen-ai/common/directives/index.ts +30 -0
- package/gen-ai/common/en.md +417 -0
- package/gen-ai/common/index.ts +36 -0
- package/gen-ai/common/types/component-expose.ts +9 -0
- package/gen-ai/common/utils/api-res-status.ts +7 -0
- package/gen-ai/common/utils/navigation.ts +59 -0
- package/gen-ai/common/utils/remote-request.ts +45 -0
- package/gen-ai/common/utils/scroll.ts +577 -0
- package/gen-ai/components/AIExport/docx-export.ts +950 -0
- package/gen-ai/components/AIExport/docxExport.ts +2566 -0
- package/gen-ai/components/AIExport/index.ts +52 -0
- package/gen-ai/components/AIExport/parseMarkdownHtml.ts +653 -0
- package/gen-ai/components/CapsuleScrollbar.vue +149 -0
- package/gen-ai/components/ChapterTitleScroll.vue +468 -0
- package/gen-ai/components/EntryHeroNavCards.vue +379 -0
- package/gen-ai/components/GenAiPagination.vue +52 -0
- package/gen-ai/components/ModelCard.vue +1114 -0
- package/gen-ai/components/Preview/FilePreviewPage.vue +949 -0
- package/gen-ai/components/Preview/PreviewCapsuleScroll.vue +205 -0
- package/gen-ai/components/Preview/PreviewOutlineTree.vue +148 -0
- package/gen-ai/components/Preview/index.ts +11 -0
- package/gen-ai/components/Preview/preview.ts +724 -0
- package/gen-ai/components/PromptField.vue +505 -0
- package/gen-ai/components/RecentReportsTable.vue +974 -0
- package/gen-ai/components/ReportCollapsibleSection.vue +172 -0
- package/gen-ai/components/ReportHistoryList.vue +637 -0
- package/gen-ai/components/ReportSectionConfirmPopover.vue +251 -0
- package/gen-ai/components/editor/ChapterTiptapEditor.vue +3718 -0
- package/gen-ai/components/editor/StructureBlockView.vue +441 -0
- package/gen-ai/components/editor/TiptapToolbar.vue +1036 -0
- package/gen-ai/components/editor/TypewriterDocEditor.vue +422 -0
- package/gen-ai/components/editor/_StructureOrgNode.vue +326 -0
- package/gen-ai/components/editor/index.ts +28 -0
- package/gen-ai/components/editor/parseMarkdownHtml.ts +754 -0
- package/gen-ai/components/editor/reviewCitationTip.ts +163 -0
- package/gen-ai/gen-entry/api/index.ts +32 -0
- package/gen-ai/gen-entry/index.vue +287 -0
- package/gen-ai/gen-entry/types/index.ts +4 -0
- package/gen-ai/i.mark +2 -0
- package/gen-ai/management-center/index.vue +1815 -0
- package/gen-ai/management-center/mockData.ts +248 -0
- package/gen-ai/management-center/types/index.ts +37 -0
- package/gen-ai/model-manager/TemplateEditorFormPanel.vue +661 -0
- package/gen-ai/model-manager/api/index.ts +32 -0
- package/gen-ai/model-manager/api/mockApi.ts +33 -0
- package/gen-ai/model-manager/index.vue +1583 -0
- package/gen-ai/model-manager/mockData.ts +177 -0
- package/gen-ai/model-manager/template-detail-shared.ts +154 -0
- package/gen-ai/model-manager/template-editor.vue +345 -0
- package/gen-ai/model-manager/types/index.ts +40 -0
- package/gen-ai/model-manager/utils.ts +25 -0
- package/package.json +12 -0
|
@@ -0,0 +1,1386 @@
|
|
|
1
|
+
import { computed, nextTick, reactive, ref, watch } from 'vue'
|
|
2
|
+
//0917-1:英文顯示(順帶清 mock 空導出改名)
|
|
3
|
+
import {
|
|
4
|
+
buildReportHistoryChapterBodies,
|
|
5
|
+
findFinalizedReportById,
|
|
6
|
+
serializeSelectedChapterLabels,
|
|
7
|
+
stripChapterNumberPrefix,
|
|
8
|
+
} from './report-domain'
|
|
9
|
+
//0918:tip / SSERefList 備庫接線
|
|
10
|
+
import {
|
|
11
|
+
clearChartStreamRefs,
|
|
12
|
+
notifyChartStreamRefsUpdated, // 0924
|
|
13
|
+
upsertChartStreamRef,
|
|
14
|
+
upsertChartStreamRefsFromSources,
|
|
15
|
+
} from './chartStreamRefs'
|
|
16
|
+
//0918
|
|
17
|
+
import type { ReportTemplateCard } from './report-template-data'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 只負責流程控制與路由跳轉。
|
|
21
|
+
* 接口類 API 調用寫在當前調用組件內,禁止寫在本文件。
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
export const FILE_SIZE_LIMIT_KB = 100 * 1024
|
|
25
|
+
export const SOURCE_VISIBLE_LIMIT = 20
|
|
26
|
+
|
|
27
|
+
/** 章節歷史刷新:由 ReportChapterWorkspace 註冊 loadGenAiSectionVersionList */
|
|
28
|
+
type ChapterHistoryReloader = () => Promise<boolean>
|
|
29
|
+
let chapterHistoryReloader: ChapterHistoryReloader | null = null
|
|
30
|
+
|
|
31
|
+
export function registerChapterHistoryReloader(fn: ChapterHistoryReloader | null) {
|
|
32
|
+
chapterHistoryReloader = fn
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function reloadActiveChapterHistory() {
|
|
36
|
+
if (!chapterHistoryReloader) return false
|
|
37
|
+
return chapterHistoryReloader()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type SourceFilterTab = 'all' | 'selected' | 'unselected'
|
|
41
|
+
|
|
42
|
+
export interface PendingFile {
|
|
43
|
+
id: string
|
|
44
|
+
name: string
|
|
45
|
+
sizeKb: number
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface SourceFile {
|
|
49
|
+
id: string
|
|
50
|
+
name: string
|
|
51
|
+
selected: boolean
|
|
52
|
+
/** 可選直鏈;預覽/下載優先使用 */
|
|
53
|
+
url?: string
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type ReportEditorRole = 'initiator' | 'editor'
|
|
57
|
+
|
|
58
|
+
export interface ReportEditor {
|
|
59
|
+
id: string
|
|
60
|
+
name: string
|
|
61
|
+
employeeId: string
|
|
62
|
+
department: string
|
|
63
|
+
role: ReportEditorRole
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface UmsEditorCandidate {
|
|
67
|
+
id: string
|
|
68
|
+
name: string
|
|
69
|
+
employeeId: string
|
|
70
|
+
department: string
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const CURRENT_USER_ID = '8000121'
|
|
74
|
+
|
|
75
|
+
//0917-1:原 MOCK_UMS_EDITOR_USERS 改名;空初始,由接口寫入
|
|
76
|
+
/** UMS 候選人由接口寫入;此處僅作空初始值 */
|
|
77
|
+
export const UMS_EDITOR_CANDIDATES: UmsEditorCandidate[] = []
|
|
78
|
+
//0917-1
|
|
79
|
+
|
|
80
|
+
export type ReportModelStep = 'select' | 'detail' | 'generating' | 'prompt'
|
|
81
|
+
export type PromptRightPanel = 'prompt' | 'settings'
|
|
82
|
+
/** Prompt AI+ / 新增章節 AI+ → 打開智能化生成 Prompt 彈窗 */
|
|
83
|
+
export type AiPromptDialogSource = 'prompt-panel' | 'add-chapter'
|
|
84
|
+
|
|
85
|
+
export interface ReportHistoryItem {
|
|
86
|
+
key: string
|
|
87
|
+
title: string
|
|
88
|
+
author: string
|
|
89
|
+
finalizedAt: string
|
|
90
|
+
recordId?: string
|
|
91
|
+
/** 存為草稿後插入章節歷史首行的草稿卡片 */
|
|
92
|
+
kind?: 'draft' | 'history'
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const CHAPTER_DRAFT_HISTORY_KEY = 'chapter-history-draft'
|
|
96
|
+
export const CHAPTER_DRAFT_HISTORY_TITLE = '您有未生成章節內容的草稿...'
|
|
97
|
+
|
|
98
|
+
export interface PromptChapterForm {
|
|
99
|
+
prompt: string
|
|
100
|
+
enableBodyContent: boolean
|
|
101
|
+
bodyWordLimit: number
|
|
102
|
+
enableTable: boolean
|
|
103
|
+
tableTitle: string
|
|
104
|
+
tableRowLabels: string
|
|
105
|
+
tableColumnLabels: string
|
|
106
|
+
tableContent: string
|
|
107
|
+
enableSourceChart: boolean
|
|
108
|
+
chartDescription: string
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function createDefaultPromptChapterForm(): PromptChapterForm {
|
|
112
|
+
return {
|
|
113
|
+
prompt: '',
|
|
114
|
+
enableBodyContent: false,
|
|
115
|
+
bodyWordLimit: 500,
|
|
116
|
+
enableTable: true,
|
|
117
|
+
tableTitle: '',
|
|
118
|
+
tableRowLabels: '',
|
|
119
|
+
tableColumnLabels: '',
|
|
120
|
+
tableContent: '',
|
|
121
|
+
enableSourceChart: true,
|
|
122
|
+
chartDescription: '',
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface ReportDraft {
|
|
127
|
+
industry: string
|
|
128
|
+
researchFocus: string
|
|
129
|
+
year: string
|
|
130
|
+
reportName: string
|
|
131
|
+
language: string
|
|
132
|
+
chapterContentLanguage: 'zh-Hant' | 'zh-Hans'
|
|
133
|
+
wordsPerChapter: string
|
|
134
|
+
model: string
|
|
135
|
+
searchMode: string
|
|
136
|
+
weight: number
|
|
137
|
+
rerank: boolean
|
|
138
|
+
rerankTopN: number
|
|
139
|
+
rerankThreshold: number
|
|
140
|
+
sourceCount: number
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const STORAGE_KEY = 'gen-ai-report-workflow-draft'
|
|
144
|
+
const TEMPLATE_STORAGE_KEY = 'gen-ai-report-workflow-template'
|
|
145
|
+
const SOURCES_STORAGE_KEY = 'gen-ai-report-workflow-sources'
|
|
146
|
+
const EDITORS_STORAGE_KEY = 'gen-ai-report-workflow-editors'
|
|
147
|
+
const BODIES_STORAGE_KEY = 'gen-ai-report-workflow-generated-bodies'
|
|
148
|
+
const TITLES_STORAGE_KEY = 'gen-ai-report-workflow-generated-titles'
|
|
149
|
+
const SETTINGS_STORAGE_KEY = 'gen-ai-report-workflow-settings'
|
|
150
|
+
const PROMPT_FORM_STORAGE_KEY = 'gen-ai-report-workflow-prompt-form'
|
|
151
|
+
/** 一次性清掉舊版表格/圖表預填默認值 */
|
|
152
|
+
const PROMPT_TABLE_DEFAULTS_CLEARED_KEY = 'gen-ai-report-prompt-table-defaults-cleared-v1'
|
|
153
|
+
const GENERATING_STORAGE_KEY = 'gen-ai-report-generating-session'
|
|
154
|
+
|
|
155
|
+
const draft = reactive<ReportDraft>({
|
|
156
|
+
industry: '',
|
|
157
|
+
researchFocus: '',
|
|
158
|
+
year: String(new Date().getFullYear()),
|
|
159
|
+
reportName: '',
|
|
160
|
+
language: 'zh-Hant',
|
|
161
|
+
chapterContentLanguage: 'zh-Hant',
|
|
162
|
+
wordsPerChapter: '',
|
|
163
|
+
model: 'gpt-666',
|
|
164
|
+
searchMode: 'Hybrid Search',
|
|
165
|
+
weight: 0.5,
|
|
166
|
+
rerank: true,
|
|
167
|
+
rerankTopN: 5,
|
|
168
|
+
rerankThreshold: 0.5,
|
|
169
|
+
sourceCount: 0,
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const selectedTemplate = ref<ReportTemplateCard | null>(null)
|
|
173
|
+
const modelStep = ref<ReportModelStep>('select')
|
|
174
|
+
const editViewOpen = ref(false)
|
|
175
|
+
/** 強制重掛整個 ReportEdit 組件(一鍵生成 / 進入編輯) */
|
|
176
|
+
const reportEditMountKey = ref(0)
|
|
177
|
+
/** 主動跳轉鎖:避免路由 watch / sync 在中途把 modelStep 打回舊值 */
|
|
178
|
+
const reportEditNavLocked = ref(false)
|
|
179
|
+
const reportEditNavTarget = ref<ReportModelStep | null>(null)
|
|
180
|
+
/** 鎖定期目標右側欄;解鎖時強制寫回,避免 remount 後丟 settings */
|
|
181
|
+
const reportEditNavRightPanel = ref<PromptRightPanel | null>(null)
|
|
182
|
+
const promptRightPanel = ref<PromptRightPanel>('prompt')
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 右側欄 / 中欄工具列 / 中欄正文 — 各自獨立開關,由 showPrompt*Panel 原子寫入,
|
|
186
|
+
* 避免只改 promptRightPanel 時 Suspense / 路由競態導致「生成報告設置」與按鈕錯位。
|
|
187
|
+
*/
|
|
188
|
+
const uiShowPromptPanel = ref(true)
|
|
189
|
+
const uiShowGenerateSettingsPanel = ref(false)
|
|
190
|
+
const uiShowChapterEditToolbar = ref(true)
|
|
191
|
+
const uiShowFinalizeToolbar = ref(false)
|
|
192
|
+
const uiShowSettingsDoc = ref(false)
|
|
193
|
+
const uiShowEditorSummary = ref(true)
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* reportEdit 流程視圖(模組級,重掛不丟):與路由 step / rightPanel 對齊
|
|
197
|
+
*/
|
|
198
|
+
const flowStep = ref<ReportModelStep>('select')
|
|
199
|
+
const flowRightPanel = ref<'prompt' | 'settings'>('prompt')
|
|
200
|
+
|
|
201
|
+
function applyPromptEditorUiFlags() {
|
|
202
|
+
promptRightPanel.value = 'prompt'
|
|
203
|
+
flowRightPanel.value = 'prompt'
|
|
204
|
+
uiShowPromptPanel.value = true
|
|
205
|
+
uiShowGenerateSettingsPanel.value = false
|
|
206
|
+
uiShowChapterEditToolbar.value = true
|
|
207
|
+
uiShowFinalizeToolbar.value = false
|
|
208
|
+
uiShowSettingsDoc.value = false
|
|
209
|
+
uiShowEditorSummary.value = true
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function applyGenerateSettingsUiFlags() {
|
|
213
|
+
promptRightPanel.value = 'settings'
|
|
214
|
+
flowRightPanel.value = 'settings'
|
|
215
|
+
uiShowPromptPanel.value = false
|
|
216
|
+
uiShowGenerateSettingsPanel.value = true
|
|
217
|
+
uiShowChapterEditToolbar.value = false
|
|
218
|
+
uiShowFinalizeToolbar.value = true
|
|
219
|
+
uiShowSettingsDoc.value = true
|
|
220
|
+
uiShowEditorSummary.value = false
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const generationDone = ref(false)
|
|
224
|
+
const generatedChapterBodies = ref<string[]>([])
|
|
225
|
+
/** 生成接口返回的章節標題(進入編輯後以接口為準) */
|
|
226
|
+
const generatedChapterTitles = ref<string[]>([])
|
|
227
|
+
|
|
228
|
+
/** 生成報告設置:章節勾選 / 排序(與中間主欄多章預覽同步) */
|
|
229
|
+
export type SettingsChapterPreset = { key: string; label: string }
|
|
230
|
+
export type SettingsChapterVersionOption = {
|
|
231
|
+
text: string
|
|
232
|
+
value: string
|
|
233
|
+
selected: boolean
|
|
234
|
+
uploadUsrNameDisplay?: string
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** AdjustInstance / 資訊源 selected 兼容 boolean、Y/N、1/0 */
|
|
238
|
+
function isGenAiSelectedFlag(flag: unknown): boolean {
|
|
239
|
+
if (flag === true || flag === 1) return true
|
|
240
|
+
if (flag === false || flag === 0 || flag == null) return false
|
|
241
|
+
const raw = String(flag).trim().toUpperCase()
|
|
242
|
+
return raw === 'Y' || raw === 'TRUE' || raw === '1'
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const settingsChapterPresets = ref<SettingsChapterPreset[]>([])
|
|
246
|
+
const settingsSelectedChapters = ref<Record<string, boolean>>({})
|
|
247
|
+
const settingsChapterOrder = ref<string[]>([])
|
|
248
|
+
/** 右側章節版本下拉選項(AdjustInstance.instanceSections.versions) */
|
|
249
|
+
const settingsChapterVersionOptions = ref<Record<string, SettingsChapterVersionOption[]>>({})
|
|
250
|
+
/** 右側章節當前選中版本 value(UUID) */
|
|
251
|
+
const settingsChapterVersions = ref<Record<string, string>>({})
|
|
252
|
+
/** 右側章節生成人(AdjustInstance.instanceSections.createUserName) */
|
|
253
|
+
const settingsChapterGenerators = ref<Record<string, string>>({})
|
|
254
|
+
/**
|
|
255
|
+
* AdjustInstance / SimTradTransfer 返回的報告正文(報告頁中欄)
|
|
256
|
+
* 與右側「生成報告設置」語言下拉共享
|
|
257
|
+
*/
|
|
258
|
+
const adjustInstanceContents = ref<
|
|
259
|
+
Array<{ sectionTitle: string; sectionContent: string; refList?: unknown }>
|
|
260
|
+
>([])
|
|
261
|
+
|
|
262
|
+
// 0924:AdjustInstance.instanceContents[].refList → SSERefList
|
|
263
|
+
function setAdjustInstanceContents(
|
|
264
|
+
contents: Array<{
|
|
265
|
+
sectionTitle: string
|
|
266
|
+
sectionContent: string
|
|
267
|
+
refList?: unknown
|
|
268
|
+
Ref?: unknown
|
|
269
|
+
ref?: unknown
|
|
270
|
+
refs?: unknown
|
|
271
|
+
}>
|
|
272
|
+
) {
|
|
273
|
+
const list = Array.isArray(contents) ? contents : []
|
|
274
|
+
// 0924:refList 与 getSectionVersionList 同形,先入库再写正文
|
|
275
|
+
upsertChartStreamRefsFromSources(list)
|
|
276
|
+
adjustInstanceContents.value = list.map((item) => ({
|
|
277
|
+
sectionTitle: String(item.sectionTitle ?? ''),
|
|
278
|
+
sectionContent: String(item.sectionContent ?? ''),
|
|
279
|
+
refList: item.refList,
|
|
280
|
+
}))
|
|
281
|
+
void nextTick(() => notifyChartStreamRefsUpdated())
|
|
282
|
+
// 0924
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function upsertChapterChartStreamRefsFromSources(sources: unknown) {
|
|
286
|
+
return upsertChartStreamRefsFromSources(sources)
|
|
287
|
+
}
|
|
288
|
+
// 0924
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* AdjustInstance / AdjustInstanceSetting 返回的已存在報告版本號(如 V1)
|
|
292
|
+
* 與報告歷史 instanceVersionNo 相等時,對應 card 為選中態(取第一個匹配)
|
|
293
|
+
*/
|
|
294
|
+
const existsInstanceVersionNo = ref<string | null>(null)
|
|
295
|
+
|
|
296
|
+
function setExistsInstanceVersionNo(versionNo: unknown) {
|
|
297
|
+
if (versionNo == null || versionNo === '') {
|
|
298
|
+
existsInstanceVersionNo.value = null
|
|
299
|
+
return
|
|
300
|
+
}
|
|
301
|
+
existsInstanceVersionNo.value = String(versionNo).trim()
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* 連貫性檢查 CoherenceCheck.beenFinalizedFlag=Y 後:
|
|
306
|
+
* 工具列顯示「查看報告連貫性檢查結果」+「導出(Word)」
|
|
307
|
+
*/
|
|
308
|
+
const coherenceResultReady = ref(false)
|
|
309
|
+
const coherenceCheckContentText = ref('')
|
|
310
|
+
|
|
311
|
+
function setCoherenceCheckResultReady(ready: boolean, contentText = '') {
|
|
312
|
+
coherenceResultReady.value = ready
|
|
313
|
+
coherenceCheckContentText.value = ready ? String(contentText ?? '') : ''
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function clearCoherenceCheckResultReady() {
|
|
317
|
+
coherenceResultReady.value = false
|
|
318
|
+
coherenceCheckContentText.value = ''
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* 避免 AdjustInstance / AdjustInstanceSetting 回寫章節後
|
|
323
|
+
* 再次觸發「章節變更 → AdjustInstanceSetting → 打字機重播」死循環閃爍
|
|
324
|
+
*/
|
|
325
|
+
const adjustInstanceSettingSyncKey = ref('')
|
|
326
|
+
|
|
327
|
+
function markAdjustInstanceSettingSynced(versionIdList: string[]) {
|
|
328
|
+
adjustInstanceSettingSyncKey.value = (versionIdList || [])
|
|
329
|
+
.map((id) => String(id ?? '').trim())
|
|
330
|
+
.filter(Boolean)
|
|
331
|
+
.join(',')
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function isAdjustInstanceSettingSynced(versionIdList: string[]) {
|
|
335
|
+
const key = (versionIdList || [])
|
|
336
|
+
.map((id) => String(id ?? '').trim())
|
|
337
|
+
.filter(Boolean)
|
|
338
|
+
.join(',')
|
|
339
|
+
return !!key && key === adjustInstanceSettingSyncKey.value
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** 進頁 / 接口回寫章節時抑制 Settings 的 AdjustInstanceSetting watch */
|
|
343
|
+
const suppressAdjustInstanceSettingWatch = ref(false)
|
|
344
|
+
|
|
345
|
+
function beginSuppressAdjustInstanceSettingWatch() {
|
|
346
|
+
suppressAdjustInstanceSettingWatch.value = true
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function endSuppressAdjustInstanceSettingWatch() {
|
|
350
|
+
void nextTick(() => {
|
|
351
|
+
suppressAdjustInstanceSettingWatch.value = false
|
|
352
|
+
})
|
|
353
|
+
}
|
|
354
|
+
/** 拖拽排序後:通知中欄從該章節起重新打字機列印(key + token 確保可重複觸發) */
|
|
355
|
+
const settingsTypewriterReplayKey = ref<string | null>(null)
|
|
356
|
+
const settingsTypewriterReplayToken = ref(0)
|
|
357
|
+
/** 中欄打字機當前章(供右側列表等鬆耦合滾動,不反向依賴中欄 DOM) */
|
|
358
|
+
const settingsTypingChapterKey = ref<string | null>(null)
|
|
359
|
+
/** 中欄是否正在打字機列印(列印中隱藏滾動條) */
|
|
360
|
+
const settingsTypewriterPrinting = ref(false)
|
|
361
|
+
|
|
362
|
+
function requestSettingsTypewriterReplay(key: string) {
|
|
363
|
+
if (!key) return
|
|
364
|
+
settingsTypewriterReplayKey.value = key
|
|
365
|
+
settingsTypewriterReplayToken.value += 1
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function setSettingsTypingChapterKey(key: string | null) {
|
|
369
|
+
settingsTypingChapterKey.value = key
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function setSettingsTypewriterPrinting(printing: boolean) {
|
|
373
|
+
settingsTypewriterPrinting.value = printing
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** 需求 2026-08-13 16:58:進入編輯 / settings 不打字;點「生成章節內容」才打字 */
|
|
377
|
+
const chapterTypewriterActive = ref(false)
|
|
378
|
+
/** 生成章節:等待首包;中欄 v-loading,首包到達後關閉 */
|
|
379
|
+
const chapterContentAwaitingFirstChunk = ref(false)
|
|
380
|
+
/** // 0924:chartAsStream 还在推时为 true。打字机先追平也不能关 */
|
|
381
|
+
const chapterContentStreamLive = ref(false)
|
|
382
|
+
/** // 0924:当前下拉选中章节的 sectionId */
|
|
383
|
+
const activeChapterSectionId = ref('')
|
|
384
|
+
// 0924
|
|
385
|
+
|
|
386
|
+
function startChapterContentTypewriter() {
|
|
387
|
+
chapterHistoryPreviewBody.value = ''
|
|
388
|
+
chapterTypewriterActive.value = true
|
|
389
|
+
chapterContentAwaitingFirstChunk.value = true
|
|
390
|
+
// 0924:生成开始,流未结束前不关打字机
|
|
391
|
+
chapterContentStreamLive.value = true
|
|
392
|
+
// 0924
|
|
393
|
+
//0918-1:递增 token → Workspace contentIdentity,重启打字机(不 remount)
|
|
394
|
+
chapterHistoryApplyToken.value += 1
|
|
395
|
+
//0918-1
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function markChapterContentFirstChunk() {
|
|
399
|
+
chapterContentAwaitingFirstChunk.value = false
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/** 停止章節打字機;cancelStream 用於切章時中止 chartAsStream */
|
|
403
|
+
const chapterContentStreamEpoch = ref(0)
|
|
404
|
+
|
|
405
|
+
function stopChapterContentTypewriter(options?: { cancelStream?: boolean }) {
|
|
406
|
+
chapterTypewriterActive.value = false
|
|
407
|
+
chapterContentAwaitingFirstChunk.value = false
|
|
408
|
+
chapterContentStreamLive.value = false
|
|
409
|
+
if (options?.cancelStream) chapterContentStreamEpoch.value += 1
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const CHAPTER_CHART_AUDIT_EVENT_STORAGE_KEY = 'gen-ai-chapter-chart-audit-event-id'
|
|
413
|
+
const chapterChartAuditEventId = ref('')
|
|
414
|
+
|
|
415
|
+
function setChapterChartAuditEventId(id: string) {
|
|
416
|
+
const next = String(id ?? '').trim()
|
|
417
|
+
chapterChartAuditEventId.value = next
|
|
418
|
+
try {
|
|
419
|
+
if (next) sessionStorage.setItem(CHAPTER_CHART_AUDIT_EVENT_STORAGE_KEY, next)
|
|
420
|
+
else sessionStorage.removeItem(CHAPTER_CHART_AUDIT_EVENT_STORAGE_KEY)
|
|
421
|
+
} catch {
|
|
422
|
+
/* ignore */
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function clearChapterChartAuditEventId() {
|
|
427
|
+
chapterChartAuditEventId.value = ''
|
|
428
|
+
try {
|
|
429
|
+
sessionStorage.removeItem(CHAPTER_CHART_AUDIT_EVENT_STORAGE_KEY)
|
|
430
|
+
} catch {
|
|
431
|
+
/* ignore */
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
//0918:SSERefList push / clear
|
|
436
|
+
function upsertChapterChartStreamRef(raw: unknown) {
|
|
437
|
+
return upsertChartStreamRef(raw)
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function clearChapterChartStreamRefs() {
|
|
441
|
+
clearChartStreamRefs()
|
|
442
|
+
}
|
|
443
|
+
//0918
|
|
444
|
+
|
|
445
|
+
/** 保存時取值:內存 → sessionStorage 兜底(始終返回 string) */
|
|
446
|
+
function resolveChapterChartAuditEventId() {
|
|
447
|
+
const fromMem = String(chapterChartAuditEventId.value ?? '').trim()
|
|
448
|
+
if (fromMem) return fromMem
|
|
449
|
+
try {
|
|
450
|
+
return String(sessionStorage.getItem(CHAPTER_CHART_AUDIT_EVENT_STORAGE_KEY) ?? '').trim()
|
|
451
|
+
} catch {
|
|
452
|
+
return ''
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function patchGeneratedChapterBody(index: number, body: string) {
|
|
457
|
+
if (index < 0) return
|
|
458
|
+
const next = [...generatedChapterBodies.value]
|
|
459
|
+
while (next.length <= index) next.push('')
|
|
460
|
+
next[index] = body
|
|
461
|
+
generatedChapterBodies.value = next
|
|
462
|
+
saveGeneratedBodiesToSession()
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function clearSettingsTypewriterReplay() {
|
|
466
|
+
settingsTypewriterReplayKey.value = null
|
|
467
|
+
}
|
|
468
|
+
const sourceFiles = ref<SourceFile[]>([])
|
|
469
|
+
const pendingFiles = ref<PendingFile[]>([])
|
|
470
|
+
const reportEditors = ref<ReportEditor[]>([])
|
|
471
|
+
const reportHistoryItems = ref<ReportHistoryItem[]>([])
|
|
472
|
+
const activeHistoryKey = ref('')
|
|
473
|
+
const historyScrollTargetKey = ref<string | null>(null)
|
|
474
|
+
const historyPreviewRecordId = ref<string | null>(null)
|
|
475
|
+
const historyPreviewLayoutBaseline = ref<string | null>(null)
|
|
476
|
+
const historyPreviewDirty = ref(false)
|
|
477
|
+
const historyPreviewApplyToken = ref(0)
|
|
478
|
+
const historyPreviewChapterBodies = ref<string[]>([])
|
|
479
|
+
const chapterHistoryItems = ref<ReportHistoryItem[]>([])
|
|
480
|
+
const activeChapterHistoryKey = ref('')
|
|
481
|
+
const chapterHistoryPreviewBody = ref<string | null>(null)
|
|
482
|
+
const chapterHistoryApplyToken = ref(0)
|
|
483
|
+
/** 章節歷史折疊面板:無內容默認收起;出現「未生成草稿」時展開 */
|
|
484
|
+
const chapterHistoryOpen = ref(false)
|
|
485
|
+
const promptChapterForm = reactive<PromptChapterForm>(createDefaultPromptChapterForm())
|
|
486
|
+
const chapterEditMode = ref(false)
|
|
487
|
+
let chapterEditSnapshot: string | null = null
|
|
488
|
+
|
|
489
|
+
const aiPromptDialogRequestToken = ref(0)
|
|
490
|
+
const aiPromptDialogSource = ref<AiPromptDialogSource>('prompt-panel')
|
|
491
|
+
|
|
492
|
+
function requestOpenAiPromptDialog(source: AiPromptDialogSource = 'prompt-panel') {
|
|
493
|
+
aiPromptDialogSource.value = source
|
|
494
|
+
aiPromptDialogRequestToken.value += 1
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/** 設置頁「生成報告」chartAsStream 緩衝(接口調用寫在 Settings 組件) */
|
|
498
|
+
const reportGenerateStreamText = ref('')
|
|
499
|
+
const reportGenerateStreamActive = ref(false)
|
|
500
|
+
const reportGenerateStreamEnded = ref(false)
|
|
501
|
+
|
|
502
|
+
function beginReportGenerateStream() {
|
|
503
|
+
reportGenerateStreamText.value = ''
|
|
504
|
+
reportGenerateStreamActive.value = true
|
|
505
|
+
reportGenerateStreamEnded.value = false
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function appendReportGenerateStream(chunk: string) {
|
|
509
|
+
reportGenerateStreamText.value += chunk
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function finishReportGenerateStream() {
|
|
513
|
+
reportGenerateStreamEnded.value = true
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function clearReportGenerateStream() {
|
|
517
|
+
reportGenerateStreamText.value = ''
|
|
518
|
+
reportGenerateStreamActive.value = false
|
|
519
|
+
reportGenerateStreamEnded.value = false
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function serializeChapterEditState() {
|
|
523
|
+
return JSON.stringify({
|
|
524
|
+
prompt: { ...promptChapterForm },
|
|
525
|
+
sources: sourceFiles.value.map((file) => ({ id: file.id, selected: file.selected })),
|
|
526
|
+
})
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function restoreChapterEditSnapshot(snapshot: string) {
|
|
530
|
+
const parsed = JSON.parse(snapshot) as {
|
|
531
|
+
prompt: PromptChapterForm
|
|
532
|
+
sources: Array<{ id: string; selected: boolean }>
|
|
533
|
+
}
|
|
534
|
+
Object.assign(promptChapterForm, parsed.prompt)
|
|
535
|
+
for (const file of sourceFiles.value) {
|
|
536
|
+
const saved = parsed.sources.find((item) => item.id === file.id)
|
|
537
|
+
if (saved) file.selected = saved.selected
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function isChapterEditDirty() {
|
|
542
|
+
if (!chapterEditSnapshot) return false
|
|
543
|
+
return serializeChapterEditState() !== chapterEditSnapshot
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function enterChapterEditMode() {
|
|
547
|
+
stopChapterContentTypewriter()
|
|
548
|
+
chapterEditSnapshot = serializeChapterEditState()
|
|
549
|
+
chapterEditMode.value = true
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function commitChapterEdit() {
|
|
553
|
+
chapterEditSnapshot = serializeChapterEditState()
|
|
554
|
+
chapterEditMode.value = false
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function exitChapterEditMode(revert = false) {
|
|
558
|
+
if (revert && chapterEditSnapshot) {
|
|
559
|
+
restoreChapterEditSnapshot(chapterEditSnapshot)
|
|
560
|
+
} else if (chapterEditMode.value) {
|
|
561
|
+
chapterEditSnapshot = serializeChapterEditState()
|
|
562
|
+
}
|
|
563
|
+
chapterEditMode.value = false
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function resetChapterEditMode() {
|
|
567
|
+
chapterEditSnapshot = null
|
|
568
|
+
chapterEditMode.value = false
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function saveDraftToSession() {
|
|
572
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ ...draft }))
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/** 章節歷史首行插入 / 刷新「未生成草稿」卡片;出現時強制展開章節歷史 */
|
|
576
|
+
function upsertChapterDraftHistory() {
|
|
577
|
+
const draftItem: ReportHistoryItem = {
|
|
578
|
+
key: CHAPTER_DRAFT_HISTORY_KEY,
|
|
579
|
+
title: CHAPTER_DRAFT_HISTORY_TITLE,
|
|
580
|
+
author: '',
|
|
581
|
+
finalizedAt: new Date().toISOString(),
|
|
582
|
+
kind: 'draft',
|
|
583
|
+
}
|
|
584
|
+
const rest = chapterHistoryItems.value.filter((item) => item.kind !== 'draft')
|
|
585
|
+
chapterHistoryItems.value = [draftItem, ...rest]
|
|
586
|
+
activeChapterHistoryKey.value = draftItem.key
|
|
587
|
+
chapterHistoryOpen.value = true
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function removeChapterDraftHistory() {
|
|
591
|
+
const index = chapterHistoryItems.value.findIndex((item) => item.kind === 'draft')
|
|
592
|
+
if (index === -1) return false
|
|
593
|
+
chapterHistoryItems.value.splice(index, 1)
|
|
594
|
+
if (activeChapterHistoryKey.value === CHAPTER_DRAFT_HISTORY_KEY) {
|
|
595
|
+
const nextKey = chapterHistoryItems.value[0]?.key ?? ''
|
|
596
|
+
activeChapterHistoryKey.value = nextKey
|
|
597
|
+
}
|
|
598
|
+
return true
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function loadDraftFromSession() {
|
|
602
|
+
const raw = sessionStorage.getItem(STORAGE_KEY)
|
|
603
|
+
if (!raw) return
|
|
604
|
+
try {
|
|
605
|
+
const parsed = JSON.parse(raw) as Record<string, unknown>
|
|
606
|
+
const legacyContentLanguage = parsed.chapterContentLanguage
|
|
607
|
+
if (legacyContentLanguage === 'zh') {
|
|
608
|
+
parsed.chapterContentLanguage = parsed.language === 'zh-Hans' ? 'zh-Hans' : 'zh-Hant'
|
|
609
|
+
} else if (legacyContentLanguage === 'en') {
|
|
610
|
+
parsed.chapterContentLanguage = 'zh-Hant'
|
|
611
|
+
}
|
|
612
|
+
Object.assign(draft, parsed as Partial<ReportDraft>)
|
|
613
|
+
} catch {
|
|
614
|
+
// ignore invalid cache
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function saveTemplateToSession() {
|
|
619
|
+
if (!selectedTemplate.value) {
|
|
620
|
+
sessionStorage.removeItem(TEMPLATE_STORAGE_KEY)
|
|
621
|
+
return
|
|
622
|
+
}
|
|
623
|
+
sessionStorage.setItem(TEMPLATE_STORAGE_KEY, JSON.stringify(selectedTemplate.value))
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function loadTemplateFromSession() {
|
|
627
|
+
const raw = sessionStorage.getItem(TEMPLATE_STORAGE_KEY)
|
|
628
|
+
if (!raw) return
|
|
629
|
+
try {
|
|
630
|
+
selectedTemplate.value = JSON.parse(raw) as ReportTemplateCard
|
|
631
|
+
} catch {
|
|
632
|
+
sessionStorage.removeItem(TEMPLATE_STORAGE_KEY)
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function saveSourcesToSession() {
|
|
637
|
+
sessionStorage.setItem(
|
|
638
|
+
SOURCES_STORAGE_KEY,
|
|
639
|
+
JSON.stringify(
|
|
640
|
+
sourceFiles.value.map((file) => ({ id: file.id, name: file.name, selected: file.selected }))
|
|
641
|
+
)
|
|
642
|
+
)
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function loadSourcesFromSession() {
|
|
646
|
+
const raw = sessionStorage.getItem(SOURCES_STORAGE_KEY)
|
|
647
|
+
if (!raw) return
|
|
648
|
+
try {
|
|
649
|
+
const parsed = JSON.parse(raw) as SourceFile[]
|
|
650
|
+
if (!Array.isArray(parsed) || !parsed.length) return
|
|
651
|
+
sourceFiles.value = parsed.map((file) => ({
|
|
652
|
+
id: file.id,
|
|
653
|
+
name: file.name,
|
|
654
|
+
selected: !!file.selected,
|
|
655
|
+
}))
|
|
656
|
+
} catch {
|
|
657
|
+
sessionStorage.removeItem(SOURCES_STORAGE_KEY)
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function saveEditorsToSession() {
|
|
662
|
+
sessionStorage.setItem(EDITORS_STORAGE_KEY, JSON.stringify(reportEditors.value))
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function loadEditorsFromSession() {
|
|
666
|
+
const raw = sessionStorage.getItem(EDITORS_STORAGE_KEY)
|
|
667
|
+
if (!raw) return
|
|
668
|
+
try {
|
|
669
|
+
const parsed = JSON.parse(raw) as ReportEditor[]
|
|
670
|
+
if (!Array.isArray(parsed) || !parsed.length) return
|
|
671
|
+
// 過濾 session 中歷史測試編輯者,避免兩頁殘留
|
|
672
|
+
const cleaned = parsed
|
|
673
|
+
.map((editor) => ({
|
|
674
|
+
id: String(editor.id),
|
|
675
|
+
name: String(editor.name ?? ''),
|
|
676
|
+
employeeId: String(editor.employeeId ?? editor.id),
|
|
677
|
+
department: String(editor.department ?? ''),
|
|
678
|
+
role: (editor.role === 'initiator' ? 'initiator' : 'editor') as ReportEditorRole,
|
|
679
|
+
}))
|
|
680
|
+
.filter((editor) => {
|
|
681
|
+
const id = editor.employeeId || editor.id
|
|
682
|
+
if (['8000121', '8000122', '8000123', '8808808'].includes(id)) return false
|
|
683
|
+
if (
|
|
684
|
+
editor.name.includes('陳大文') ||
|
|
685
|
+
editor.name.includes('陳大大') ||
|
|
686
|
+
editor.name.includes('張小小')
|
|
687
|
+
)
|
|
688
|
+
return false
|
|
689
|
+
return true
|
|
690
|
+
})
|
|
691
|
+
reportEditors.value = cleaned
|
|
692
|
+
if (cleaned.length !== parsed.length) saveEditorsToSession()
|
|
693
|
+
} catch {
|
|
694
|
+
sessionStorage.removeItem(EDITORS_STORAGE_KEY)
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function saveGeneratedBodiesToSession() {
|
|
699
|
+
sessionStorage.setItem(BODIES_STORAGE_KEY, JSON.stringify(generatedChapterBodies.value))
|
|
700
|
+
sessionStorage.setItem(TITLES_STORAGE_KEY, JSON.stringify(generatedChapterTitles.value))
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function loadGeneratedBodiesFromSession() {
|
|
704
|
+
const raw = sessionStorage.getItem(BODIES_STORAGE_KEY)
|
|
705
|
+
if (raw) {
|
|
706
|
+
try {
|
|
707
|
+
const parsed = JSON.parse(raw) as string[]
|
|
708
|
+
if (Array.isArray(parsed)) generatedChapterBodies.value = parsed
|
|
709
|
+
} catch {
|
|
710
|
+
sessionStorage.removeItem(BODIES_STORAGE_KEY)
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
const rawTitles = sessionStorage.getItem(TITLES_STORAGE_KEY)
|
|
714
|
+
if (!rawTitles) return
|
|
715
|
+
try {
|
|
716
|
+
const parsed = JSON.parse(rawTitles) as string[]
|
|
717
|
+
if (Array.isArray(parsed)) generatedChapterTitles.value = parsed.map(String)
|
|
718
|
+
} catch {
|
|
719
|
+
sessionStorage.removeItem(TITLES_STORAGE_KEY)
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function saveSettingsToSession() {
|
|
724
|
+
sessionStorage.setItem(
|
|
725
|
+
SETTINGS_STORAGE_KEY,
|
|
726
|
+
JSON.stringify({
|
|
727
|
+
presets: settingsChapterPresets.value,
|
|
728
|
+
selected: settingsSelectedChapters.value,
|
|
729
|
+
order: settingsChapterOrder.value,
|
|
730
|
+
})
|
|
731
|
+
)
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function loadSettingsFromSession() {
|
|
735
|
+
const raw = sessionStorage.getItem(SETTINGS_STORAGE_KEY)
|
|
736
|
+
if (!raw) return
|
|
737
|
+
try {
|
|
738
|
+
const parsed = JSON.parse(raw) as {
|
|
739
|
+
presets?: SettingsChapterPreset[]
|
|
740
|
+
selected?: Record<string, boolean>
|
|
741
|
+
order?: string[]
|
|
742
|
+
}
|
|
743
|
+
if (parsed.presets?.length) settingsChapterPresets.value = parsed.presets
|
|
744
|
+
if (parsed.selected) settingsSelectedChapters.value = parsed.selected
|
|
745
|
+
if (parsed.order?.length) settingsChapterOrder.value = parsed.order
|
|
746
|
+
} catch {
|
|
747
|
+
sessionStorage.removeItem(SETTINGS_STORAGE_KEY)
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
function savePromptFormToSession() {
|
|
752
|
+
sessionStorage.setItem(PROMPT_FORM_STORAGE_KEY, JSON.stringify({ ...promptChapterForm }))
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
function clearPromptTableDefaults() {
|
|
756
|
+
promptChapterForm.tableTitle = ''
|
|
757
|
+
promptChapterForm.tableRowLabels = ''
|
|
758
|
+
promptChapterForm.tableColumnLabels = ''
|
|
759
|
+
promptChapterForm.tableContent = ''
|
|
760
|
+
promptChapterForm.chartDescription = ''
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
function loadPromptFormFromSession() {
|
|
764
|
+
const raw = sessionStorage.getItem(PROMPT_FORM_STORAGE_KEY)
|
|
765
|
+
if (!raw) {
|
|
766
|
+
if (!sessionStorage.getItem(PROMPT_TABLE_DEFAULTS_CLEARED_KEY)) {
|
|
767
|
+
sessionStorage.setItem(PROMPT_TABLE_DEFAULTS_CLEARED_KEY, '1')
|
|
768
|
+
}
|
|
769
|
+
return
|
|
770
|
+
}
|
|
771
|
+
try {
|
|
772
|
+
const parsed = JSON.parse(raw) as Partial<PromptChapterForm>
|
|
773
|
+
Object.assign(promptChapterForm, createDefaultPromptChapterForm(), parsed)
|
|
774
|
+
// 舊 session 可能帶有表格預填範例,統一清空為默認空值
|
|
775
|
+
if (!sessionStorage.getItem(PROMPT_TABLE_DEFAULTS_CLEARED_KEY)) {
|
|
776
|
+
clearPromptTableDefaults()
|
|
777
|
+
sessionStorage.setItem(PROMPT_TABLE_DEFAULTS_CLEARED_KEY, '1')
|
|
778
|
+
savePromptFormToSession()
|
|
779
|
+
}
|
|
780
|
+
} catch {
|
|
781
|
+
sessionStorage.removeItem(PROMPT_FORM_STORAGE_KEY)
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
export type GeneratingSessionChapter = {
|
|
786
|
+
id: string
|
|
787
|
+
sectionId: string
|
|
788
|
+
sectionName: string
|
|
789
|
+
orderSeq: number
|
|
790
|
+
status: 'pending' | 'creating' | 'success' | 'failure'
|
|
791
|
+
body?: string
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
export type GeneratingSessionSnapshot = {
|
|
795
|
+
templateId: string
|
|
796
|
+
instanceId: string
|
|
797
|
+
chapters: GeneratingSessionChapter[]
|
|
798
|
+
isComplete: boolean
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
export function saveGeneratingSession(snapshot: GeneratingSessionSnapshot) {
|
|
802
|
+
sessionStorage.setItem(GENERATING_STORAGE_KEY, JSON.stringify(snapshot))
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
export function loadGeneratingSession(): GeneratingSessionSnapshot | null {
|
|
806
|
+
const raw = sessionStorage.getItem(GENERATING_STORAGE_KEY)
|
|
807
|
+
if (!raw) return null
|
|
808
|
+
try {
|
|
809
|
+
return JSON.parse(raw) as GeneratingSessionSnapshot
|
|
810
|
+
} catch {
|
|
811
|
+
sessionStorage.removeItem(GENERATING_STORAGE_KEY)
|
|
812
|
+
return null
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
export function clearGeneratingSession() {
|
|
817
|
+
sessionStorage.removeItem(GENERATING_STORAGE_KEY)
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/** 瀏覽器刷新後從 session 恢復 build-report 編輯態 */
|
|
821
|
+
function restoreReportEditSession() {
|
|
822
|
+
loadDraftFromSession()
|
|
823
|
+
loadTemplateFromSession()
|
|
824
|
+
loadSourcesFromSession()
|
|
825
|
+
loadEditorsFromSession()
|
|
826
|
+
loadGeneratedBodiesFromSession()
|
|
827
|
+
loadSettingsFromSession()
|
|
828
|
+
loadPromptFormFromSession()
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
watch(
|
|
832
|
+
sourceFiles,
|
|
833
|
+
() => {
|
|
834
|
+
draft.sourceCount = sourceFiles.value.filter((file) => file.selected).length
|
|
835
|
+
saveDraftToSession()
|
|
836
|
+
saveSourcesToSession()
|
|
837
|
+
},
|
|
838
|
+
{ deep: true }
|
|
839
|
+
)
|
|
840
|
+
|
|
841
|
+
watch(
|
|
842
|
+
reportEditors,
|
|
843
|
+
() => {
|
|
844
|
+
saveEditorsToSession()
|
|
845
|
+
},
|
|
846
|
+
{ deep: true }
|
|
847
|
+
)
|
|
848
|
+
|
|
849
|
+
watch(
|
|
850
|
+
[settingsChapterPresets, settingsSelectedChapters, settingsChapterOrder],
|
|
851
|
+
() => {
|
|
852
|
+
if (!settingsChapterPresets.value.length) return
|
|
853
|
+
saveSettingsToSession()
|
|
854
|
+
},
|
|
855
|
+
{ deep: true }
|
|
856
|
+
)
|
|
857
|
+
|
|
858
|
+
watch(
|
|
859
|
+
promptChapterForm,
|
|
860
|
+
() => {
|
|
861
|
+
savePromptFormToSession()
|
|
862
|
+
},
|
|
863
|
+
{ deep: true }
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
export function useReportWorkflow() {
|
|
867
|
+
const hasTemplate = computed(() => !!selectedTemplate.value)
|
|
868
|
+
|
|
869
|
+
const currentUserEditor = computed(
|
|
870
|
+
() => reportEditors.value.find((editor) => editor.id === CURRENT_USER_ID) ?? null
|
|
871
|
+
)
|
|
872
|
+
const isReportInitiator = computed(() => currentUserEditor.value?.role === 'initiator')
|
|
873
|
+
const isReportEditor = computed(() => currentUserEditor.value?.role === 'editor')
|
|
874
|
+
const canManageReportEditors = computed(() => isReportInitiator.value)
|
|
875
|
+
const canEditReport = computed(() => isReportInitiator.value || isReportEditor.value)
|
|
876
|
+
|
|
877
|
+
function patchDraft(partial: Partial<ReportDraft>) {
|
|
878
|
+
Object.assign(draft, partial)
|
|
879
|
+
saveDraftToSession()
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
function setSelectedTemplate(template: ReportTemplateCard | null) {
|
|
883
|
+
selectedTemplate.value = template
|
|
884
|
+
saveTemplateToSession()
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
function setModelStep(step: ReportModelStep) {
|
|
888
|
+
modelStep.value = step
|
|
889
|
+
flowStep.value = step
|
|
890
|
+
if (step !== 'prompt') {
|
|
891
|
+
applyPromptEditorUiFlags()
|
|
892
|
+
resetChapterEditMode()
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
function beginReportEditNavigation(step: ReportModelStep, rightPanel?: PromptRightPanel) {
|
|
897
|
+
reportEditNavLocked.value = true
|
|
898
|
+
reportEditNavTarget.value = step
|
|
899
|
+
reportEditNavRightPanel.value =
|
|
900
|
+
rightPanel ?? (step === 'prompt' ? promptRightPanel.value : null)
|
|
901
|
+
editViewOpen.value = true
|
|
902
|
+
// 先寫模組級 flowStep,再 remount 也不會丟(一鍵生成點一次即可)
|
|
903
|
+
setModelStep(step)
|
|
904
|
+
if (step === 'prompt') {
|
|
905
|
+
if (rightPanel === 'settings') applyGenerateSettingsUiFlags()
|
|
906
|
+
else if (rightPanel === 'prompt') applyPromptEditorUiFlags()
|
|
907
|
+
} else {
|
|
908
|
+
flowRightPanel.value = 'prompt'
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
function endReportEditNavigation() {
|
|
913
|
+
const target = reportEditNavTarget.value
|
|
914
|
+
const panel = reportEditNavRightPanel.value
|
|
915
|
+
if (target) {
|
|
916
|
+
setModelStep(target)
|
|
917
|
+
}
|
|
918
|
+
if (target === 'prompt') {
|
|
919
|
+
if (panel === 'settings') applyGenerateSettingsUiFlags()
|
|
920
|
+
else applyPromptEditorUiFlags()
|
|
921
|
+
} else if (target) {
|
|
922
|
+
flowRightPanel.value = 'prompt'
|
|
923
|
+
}
|
|
924
|
+
reportEditNavTarget.value = null
|
|
925
|
+
reportEditNavRightPanel.value = null
|
|
926
|
+
reportEditNavLocked.value = false
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
function isReportEditNavLocked() {
|
|
930
|
+
return reportEditNavLocked.value
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
function remountReportEditPage() {
|
|
934
|
+
reportEditMountKey.value += 1
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
function ensureSettingsChapterLayout(chapters?: string[]) {
|
|
938
|
+
// 僅接受接口章節標題;無數據則空列表
|
|
939
|
+
const source = chapters?.length ? chapters : []
|
|
940
|
+
if (!source.length) {
|
|
941
|
+
settingsChapterPresets.value = []
|
|
942
|
+
settingsChapterOrder.value = []
|
|
943
|
+
settingsSelectedChapters.value = {}
|
|
944
|
+
settingsChapterVersionOptions.value = {}
|
|
945
|
+
settingsChapterVersions.value = {}
|
|
946
|
+
settingsChapterGenerators.value = {}
|
|
947
|
+
return
|
|
948
|
+
}
|
|
949
|
+
const unavailable = new Set(['附件與數'])
|
|
950
|
+
const presets = source.map((title, index) => ({
|
|
951
|
+
key: `chapter-${index}`,
|
|
952
|
+
label: stripChapterNumberPrefix(title),
|
|
953
|
+
}))
|
|
954
|
+
settingsChapterPresets.value = presets
|
|
955
|
+
settingsChapterOrder.value = presets.map((preset) => preset.key)
|
|
956
|
+
settingsSelectedChapters.value = Object.fromEntries(
|
|
957
|
+
presets.map((preset) => [preset.key, !unavailable.has(preset.label)])
|
|
958
|
+
)
|
|
959
|
+
settingsChapterVersionOptions.value = {}
|
|
960
|
+
settingsChapterVersions.value = Object.fromEntries(presets.map((preset) => [preset.key, '']))
|
|
961
|
+
saveSettingsToSession()
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* AdjustInstance.instanceSections → 右側章節列表 + 版本下拉
|
|
966
|
+
* - sectionTitle → 章節名稱
|
|
967
|
+
* - versions[].text → 版本 select 下拉展示
|
|
968
|
+
* - versions[].value → select 綁定值(UUID)
|
|
969
|
+
* - selected → 章節是否勾選(優先 section.selected;否則 versions 任一 selected)
|
|
970
|
+
* - 版本下拉默認項:versions 中 selected 為真的項,否則取第一個
|
|
971
|
+
*/
|
|
972
|
+
function applyAdjustInstanceSections(
|
|
973
|
+
sections: Array<{
|
|
974
|
+
sectionTitle: string
|
|
975
|
+
selected?: boolean | string | number
|
|
976
|
+
createUserName?: string
|
|
977
|
+
versions?: Array<{
|
|
978
|
+
text: string
|
|
979
|
+
value: string
|
|
980
|
+
selected?: boolean | string | number
|
|
981
|
+
uploadUsrNameDisplay?: string
|
|
982
|
+
}>
|
|
983
|
+
}>
|
|
984
|
+
) {
|
|
985
|
+
const list = Array.isArray(sections) ? sections : []
|
|
986
|
+
const presets = list.map((section, index) => ({
|
|
987
|
+
key: `chapter-${index}`,
|
|
988
|
+
label: stripChapterNumberPrefix(section.sectionTitle || `章節${index + 1}`),
|
|
989
|
+
}))
|
|
990
|
+
settingsChapterPresets.value = presets
|
|
991
|
+
settingsChapterOrder.value = presets.map((preset) => preset.key)
|
|
992
|
+
|
|
993
|
+
const optionsMap: Record<string, SettingsChapterVersionOption[]> = {}
|
|
994
|
+
const versionValueMap: Record<string, string> = {}
|
|
995
|
+
const checkedMap: Record<string, boolean> = {}
|
|
996
|
+
const generatorMap: Record<string, string> = {}
|
|
997
|
+
|
|
998
|
+
list.forEach((section, index) => {
|
|
999
|
+
const key = `chapter-${index}`
|
|
1000
|
+
const sectionGenerator = String(section.createUserName ?? '').trim()
|
|
1001
|
+
const versions = (Array.isArray(section.versions) ? section.versions : []).map((item) => ({
|
|
1002
|
+
text: String(item.text ?? ''),
|
|
1003
|
+
value: String(item.value ?? ''),
|
|
1004
|
+
selected: isGenAiSelectedFlag(item.selected),
|
|
1005
|
+
uploadUsrNameDisplay: String(
|
|
1006
|
+
item.uploadUsrNameDisplay ?? sectionGenerator ?? ''
|
|
1007
|
+
).trim(),
|
|
1008
|
+
}))
|
|
1009
|
+
optionsMap[key] = versions
|
|
1010
|
+
|
|
1011
|
+
// 版本下拉默認:selected 的版本,否則第一個(展示 text,如 V3)
|
|
1012
|
+
const picked = versions.find((item) => item.selected) ?? versions[0]
|
|
1013
|
+
versionValueMap[key] = picked?.value ?? ''
|
|
1014
|
+
|
|
1015
|
+
// 章節勾選:優先章節自身 selected;否則看 versions 是否有 selected
|
|
1016
|
+
if (section.selected !== undefined && section.selected !== null && section.selected !== '') {
|
|
1017
|
+
checkedMap[key] = isGenAiSelectedFlag(section.selected)
|
|
1018
|
+
} else {
|
|
1019
|
+
checkedMap[key] = versions.some((item) => item.selected)
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
generatorMap[key] = sectionGenerator || String(picked?.uploadUsrNameDisplay ?? '').trim()
|
|
1023
|
+
})
|
|
1024
|
+
|
|
1025
|
+
settingsChapterVersionOptions.value = optionsMap
|
|
1026
|
+
settingsChapterVersions.value = versionValueMap
|
|
1027
|
+
settingsSelectedChapters.value = checkedMap
|
|
1028
|
+
settingsChapterGenerators.value = generatorMap
|
|
1029
|
+
saveSettingsToSession()
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* AdjustInstance / AdjustInstanceSetting.instanceFileList → 資訊源列表
|
|
1034
|
+
* - text → 檔案名稱
|
|
1035
|
+
* - value → 文件 UUID(id)
|
|
1036
|
+
* - selected → 勾選反顯(報告歷史環節只讀,不可主動改)
|
|
1037
|
+
*/
|
|
1038
|
+
function applyAdjustInstanceFiles(
|
|
1039
|
+
files: Array<{ text: string; value: string; selected?: boolean | string | number }>
|
|
1040
|
+
) {
|
|
1041
|
+
const list = Array.isArray(files) ? files : []
|
|
1042
|
+
sourceFiles.value = list.map((file) => ({
|
|
1043
|
+
id: String(file.value ?? ''),
|
|
1044
|
+
name: String(file.text ?? ''),
|
|
1045
|
+
selected: isGenAiSelectedFlag(file.selected),
|
|
1046
|
+
}))
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
function clearAdjustInstanceBindings() {
|
|
1050
|
+
settingsChapterPresets.value = []
|
|
1051
|
+
settingsChapterOrder.value = []
|
|
1052
|
+
settingsSelectedChapters.value = {}
|
|
1053
|
+
settingsChapterVersionOptions.value = {}
|
|
1054
|
+
settingsChapterVersions.value = {}
|
|
1055
|
+
settingsChapterGenerators.value = {}
|
|
1056
|
+
adjustInstanceContents.value = []
|
|
1057
|
+
existsInstanceVersionNo.value = null
|
|
1058
|
+
clearCoherenceCheckResultReady()
|
|
1059
|
+
adjustInstanceSettingSyncKey.value = ''
|
|
1060
|
+
// 不清空 sourceFiles:返回章節編輯後「生成報告」仍依賴本地資訊源校驗;
|
|
1061
|
+
// 進設置頁時由 AdjustInstance → applyAdjustInstanceFiles 覆蓋即可。
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
function showPromptSettingsPanel() {
|
|
1065
|
+
exitChapterEditMode(true)
|
|
1066
|
+
// 章節佈局等 AdjustInstance
|
|
1067
|
+
applyGenerateSettingsUiFlags()
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
function showPromptEditorPanel() {
|
|
1071
|
+
applyPromptEditorUiFlags()
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
function openReportEdit(step: ReportModelStep = 'select') {
|
|
1075
|
+
// 鎖定期只允許跳到導航目標,避免中間態路由回寫
|
|
1076
|
+
if (
|
|
1077
|
+
reportEditNavLocked.value &&
|
|
1078
|
+
reportEditNavTarget.value &&
|
|
1079
|
+
step !== reportEditNavTarget.value
|
|
1080
|
+
) {
|
|
1081
|
+
return
|
|
1082
|
+
}
|
|
1083
|
+
editViewOpen.value = true
|
|
1084
|
+
setModelStep(step)
|
|
1085
|
+
// 右側欄(prompt / settings)由 showPrompt*Panel 單獨控制,此處不覆寫
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
function closeReportEdit() {
|
|
1089
|
+
if (reportEditNavLocked.value) return
|
|
1090
|
+
editViewOpen.value = false
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
function setGeneratedChapterBodies(bodies: string[]) {
|
|
1094
|
+
generatedChapterBodies.value = bodies
|
|
1095
|
+
saveGeneratedBodiesToSession()
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
/** 進入編輯:以生成接口章節列表為準寫入標題 + 正文 */
|
|
1099
|
+
function setGeneratedChaptersFromApi(chapters: Array<{ title: string; body: string }>) {
|
|
1100
|
+
generatedChapterTitles.value = chapters.map((item) => item.title)
|
|
1101
|
+
generatedChapterBodies.value = chapters.map((item) => item.body)
|
|
1102
|
+
saveGeneratedBodiesToSession()
|
|
1103
|
+
if (generatedChapterTitles.value.length) {
|
|
1104
|
+
ensureSettingsChapterLayout(generatedChapterTitles.value)
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
/**
|
|
1109
|
+
* 每次進入 ReportChapterWorkspace:清除歷史預覽 / 章節歷史 / Prompt,
|
|
1110
|
+
* 待 getSectionVersionList 等接口覆蓋
|
|
1111
|
+
*/
|
|
1112
|
+
function resetWorkspaceHistoryOnEnter() {
|
|
1113
|
+
resetHistoryPreviewState()
|
|
1114
|
+
historyPreviewApplyToken.value += 1
|
|
1115
|
+
chapterHistoryItems.value = []
|
|
1116
|
+
activeChapterHistoryKey.value = ''
|
|
1117
|
+
chapterHistoryPreviewBody.value = null
|
|
1118
|
+
chapterHistoryApplyToken.value += 1
|
|
1119
|
+
Object.assign(promptChapterForm, createDefaultPromptChapterForm())
|
|
1120
|
+
resetChapterEditMode()
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
function markGenerationSuccess() {
|
|
1124
|
+
generationDone.value = true
|
|
1125
|
+
editViewOpen.value = true
|
|
1126
|
+
// 不在此處改 modelStep:由 handleEnterEditing → navigateAndRemount('prompt') 統一切頁
|
|
1127
|
+
// 避免「store 已是 prompt、路由仍是 generating」導致畫面與路由互打
|
|
1128
|
+
clearGeneratingSession()
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
function resetWorkflow() {
|
|
1132
|
+
selectedTemplate.value = null
|
|
1133
|
+
modelStep.value = 'select'
|
|
1134
|
+
generationDone.value = false
|
|
1135
|
+
applyPromptEditorUiFlags()
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
function prependReportHistory(title: string, recordId: string, finalizedAt?: string) {
|
|
1139
|
+
const key = `history-${Date.now()}`
|
|
1140
|
+
const author =
|
|
1141
|
+
reportEditors.value.find((editor) => editor.role === 'initiator')?.name?.trim() ||
|
|
1142
|
+
reportEditors.value.find((editor) => editor.id === CURRENT_USER_ID)?.name?.trim() ||
|
|
1143
|
+
''
|
|
1144
|
+
reportHistoryItems.value.unshift({
|
|
1145
|
+
key,
|
|
1146
|
+
title,
|
|
1147
|
+
author,
|
|
1148
|
+
finalizedAt: finalizedAt ?? new Date().toISOString(),
|
|
1149
|
+
recordId,
|
|
1150
|
+
})
|
|
1151
|
+
activeHistoryKey.value = key
|
|
1152
|
+
historyScrollTargetKey.value = key
|
|
1153
|
+
return key
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
function clearHistoryScrollTarget() {
|
|
1157
|
+
historyScrollTargetKey.value = null
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
function applyHistoryPreviewSources(sourceFileIds: string[]) {
|
|
1161
|
+
const selectedSet = new Set(sourceFileIds)
|
|
1162
|
+
for (const file of sourceFiles.value) {
|
|
1163
|
+
file.selected = selectedSet.has(file.id)
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
function selectHistoryReport(recordId: string) {
|
|
1168
|
+
const record = findFinalizedReportById(recordId)
|
|
1169
|
+
if (!record) return
|
|
1170
|
+
|
|
1171
|
+
const historyItem = reportHistoryItems.value.find((item) => item.recordId === recordId)
|
|
1172
|
+
if (historyItem) {
|
|
1173
|
+
activeHistoryKey.value = historyItem.key
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
historyPreviewRecordId.value = recordId
|
|
1177
|
+
historyPreviewLayoutBaseline.value = serializeSelectedChapterLabels(record.chapters)
|
|
1178
|
+
historyPreviewDirty.value = false
|
|
1179
|
+
historyPreviewChapterBodies.value = buildReportHistoryChapterBodies(record)
|
|
1180
|
+
historyPreviewApplyToken.value += 1
|
|
1181
|
+
chapterHistoryPreviewBody.value = null
|
|
1182
|
+
chapterHistoryApplyToken.value += 1
|
|
1183
|
+
|
|
1184
|
+
applyHistoryPreviewSources(record.sourceFileIds)
|
|
1185
|
+
patchDraft({
|
|
1186
|
+
language: record.reportLanguage,
|
|
1187
|
+
chapterContentLanguage: record.chapterContentLanguage,
|
|
1188
|
+
})
|
|
1189
|
+
showPromptSettingsPanel()
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
function markHistoryPreviewDirty() {
|
|
1193
|
+
if (!historyPreviewRecordId.value || historyPreviewDirty.value) return
|
|
1194
|
+
historyPreviewDirty.value = true
|
|
1195
|
+
historyPreviewRecordId.value = null
|
|
1196
|
+
historyPreviewLayoutBaseline.value = null
|
|
1197
|
+
historyPreviewChapterBodies.value = []
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
function resetHistoryPreviewState() {
|
|
1201
|
+
historyPreviewRecordId.value = null
|
|
1202
|
+
historyPreviewLayoutBaseline.value = null
|
|
1203
|
+
historyPreviewDirty.value = false
|
|
1204
|
+
historyPreviewChapterBodies.value = []
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
function selectChapterHistory(key: string) {
|
|
1208
|
+
const item = chapterHistoryItems.value.find((entry) => entry.key === key)
|
|
1209
|
+
if (!item) return false
|
|
1210
|
+
|
|
1211
|
+
activeChapterHistoryKey.value = key
|
|
1212
|
+
// 正文 / Prompt 由 ReportChapterWorkspace 依接口版本列表 watch 套用
|
|
1213
|
+
return true
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
function renameChapterHistory(key: string, title: string) {
|
|
1217
|
+
const trimmed = title.trim()
|
|
1218
|
+
if (!trimmed) return false
|
|
1219
|
+
|
|
1220
|
+
const item = chapterHistoryItems.value.find((entry) => entry.key === key)
|
|
1221
|
+
if (!item) return false
|
|
1222
|
+
|
|
1223
|
+
item.title = trimmed.slice(0, 50)
|
|
1224
|
+
return true
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
function deleteChapterHistory(key: string) {
|
|
1228
|
+
const index = chapterHistoryItems.value.findIndex((entry) => entry.key === key)
|
|
1229
|
+
if (index === -1) return false
|
|
1230
|
+
|
|
1231
|
+
chapterHistoryItems.value.splice(index, 1)
|
|
1232
|
+
|
|
1233
|
+
if (activeChapterHistoryKey.value === key) {
|
|
1234
|
+
const nextKey = chapterHistoryItems.value[0]?.key ?? ''
|
|
1235
|
+
activeChapterHistoryKey.value = nextKey
|
|
1236
|
+
if (!nextKey) {
|
|
1237
|
+
chapterHistoryPreviewBody.value = null
|
|
1238
|
+
chapterHistoryApplyToken.value += 1
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
return true
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
return {
|
|
1246
|
+
draft,
|
|
1247
|
+
selectedTemplate,
|
|
1248
|
+
modelStep,
|
|
1249
|
+
editViewOpen,
|
|
1250
|
+
reportEditMountKey,
|
|
1251
|
+
remountReportEditPage,
|
|
1252
|
+
beginReportEditNavigation,
|
|
1253
|
+
endReportEditNavigation,
|
|
1254
|
+
isReportEditNavLocked,
|
|
1255
|
+
promptRightPanel,
|
|
1256
|
+
flowStep,
|
|
1257
|
+
flowRightPanel,
|
|
1258
|
+
uiShowPromptPanel,
|
|
1259
|
+
uiShowGenerateSettingsPanel,
|
|
1260
|
+
uiShowChapterEditToolbar,
|
|
1261
|
+
uiShowFinalizeToolbar,
|
|
1262
|
+
uiShowSettingsDoc,
|
|
1263
|
+
uiShowEditorSummary,
|
|
1264
|
+
generationDone,
|
|
1265
|
+
generatedChapterBodies,
|
|
1266
|
+
generatedChapterTitles,
|
|
1267
|
+
settingsChapterPresets,
|
|
1268
|
+
settingsSelectedChapters,
|
|
1269
|
+
settingsChapterOrder,
|
|
1270
|
+
settingsChapterVersionOptions,
|
|
1271
|
+
settingsChapterVersions,
|
|
1272
|
+
settingsChapterGenerators,
|
|
1273
|
+
adjustInstanceContents,
|
|
1274
|
+
setAdjustInstanceContents,
|
|
1275
|
+
existsInstanceVersionNo,
|
|
1276
|
+
setExistsInstanceVersionNo,
|
|
1277
|
+
coherenceResultReady,
|
|
1278
|
+
coherenceCheckContentText,
|
|
1279
|
+
setCoherenceCheckResultReady,
|
|
1280
|
+
clearCoherenceCheckResultReady,
|
|
1281
|
+
adjustInstanceSettingSyncKey,
|
|
1282
|
+
markAdjustInstanceSettingSynced,
|
|
1283
|
+
isAdjustInstanceSettingSynced,
|
|
1284
|
+
suppressAdjustInstanceSettingWatch,
|
|
1285
|
+
beginSuppressAdjustInstanceSettingWatch,
|
|
1286
|
+
endSuppressAdjustInstanceSettingWatch,
|
|
1287
|
+
applyAdjustInstanceSections,
|
|
1288
|
+
applyAdjustInstanceFiles,
|
|
1289
|
+
clearAdjustInstanceBindings,
|
|
1290
|
+
settingsTypewriterReplayKey,
|
|
1291
|
+
settingsTypewriterReplayToken,
|
|
1292
|
+
settingsTypingChapterKey,
|
|
1293
|
+
settingsTypewriterPrinting,
|
|
1294
|
+
requestSettingsTypewriterReplay,
|
|
1295
|
+
setSettingsTypingChapterKey,
|
|
1296
|
+
setSettingsTypewriterPrinting,
|
|
1297
|
+
chapterTypewriterActive,
|
|
1298
|
+
chapterContentAwaitingFirstChunk,
|
|
1299
|
+
chapterContentStreamLive,
|
|
1300
|
+
activeChapterSectionId,
|
|
1301
|
+
chapterContentStreamEpoch,
|
|
1302
|
+
startChapterContentTypewriter,
|
|
1303
|
+
markChapterContentFirstChunk,
|
|
1304
|
+
stopChapterContentTypewriter,
|
|
1305
|
+
chapterChartAuditEventId,
|
|
1306
|
+
setChapterChartAuditEventId,
|
|
1307
|
+
clearChapterChartAuditEventId,
|
|
1308
|
+
resolveChapterChartAuditEventId,
|
|
1309
|
+
//0918
|
|
1310
|
+
upsertChapterChartStreamRef,
|
|
1311
|
+
clearChapterChartStreamRefs,
|
|
1312
|
+
upsertChapterChartStreamRefsFromSources,
|
|
1313
|
+
//0918
|
|
1314
|
+
reportGenerateStreamText,
|
|
1315
|
+
reportGenerateStreamActive,
|
|
1316
|
+
reportGenerateStreamEnded,
|
|
1317
|
+
beginReportGenerateStream,
|
|
1318
|
+
appendReportGenerateStream,
|
|
1319
|
+
finishReportGenerateStream,
|
|
1320
|
+
clearReportGenerateStream,
|
|
1321
|
+
patchGeneratedChapterBody,
|
|
1322
|
+
clearSettingsTypewriterReplay,
|
|
1323
|
+
sourceFiles,
|
|
1324
|
+
pendingFiles,
|
|
1325
|
+
reportEditors,
|
|
1326
|
+
reportHistoryItems,
|
|
1327
|
+
activeHistoryKey,
|
|
1328
|
+
historyScrollTargetKey,
|
|
1329
|
+
historyPreviewRecordId,
|
|
1330
|
+
historyPreviewLayoutBaseline,
|
|
1331
|
+
historyPreviewDirty,
|
|
1332
|
+
historyPreviewApplyToken,
|
|
1333
|
+
historyPreviewChapterBodies,
|
|
1334
|
+
chapterHistoryItems,
|
|
1335
|
+
activeChapterHistoryKey,
|
|
1336
|
+
chapterHistoryPreviewBody,
|
|
1337
|
+
chapterHistoryApplyToken,
|
|
1338
|
+
chapterHistoryOpen,
|
|
1339
|
+
prependReportHistory,
|
|
1340
|
+
clearHistoryScrollTarget,
|
|
1341
|
+
selectHistoryReport,
|
|
1342
|
+
markHistoryPreviewDirty,
|
|
1343
|
+
resetHistoryPreviewState,
|
|
1344
|
+
selectChapterHistory,
|
|
1345
|
+
renameChapterHistory,
|
|
1346
|
+
deleteChapterHistory,
|
|
1347
|
+
currentUserEditor,
|
|
1348
|
+
isReportInitiator,
|
|
1349
|
+
isReportEditor,
|
|
1350
|
+
canManageReportEditors,
|
|
1351
|
+
canEditReport,
|
|
1352
|
+
hasTemplate,
|
|
1353
|
+
promptChapterForm,
|
|
1354
|
+
chapterEditMode,
|
|
1355
|
+
aiPromptDialogRequestToken,
|
|
1356
|
+
aiPromptDialogSource,
|
|
1357
|
+
requestOpenAiPromptDialog,
|
|
1358
|
+
enterChapterEditMode,
|
|
1359
|
+
commitChapterEdit,
|
|
1360
|
+
exitChapterEditMode,
|
|
1361
|
+
isChapterEditDirty,
|
|
1362
|
+
resetChapterEditMode,
|
|
1363
|
+
saveDraftToSession,
|
|
1364
|
+
upsertChapterDraftHistory,
|
|
1365
|
+
removeChapterDraftHistory,
|
|
1366
|
+
loadDraftFromSession,
|
|
1367
|
+
loadTemplateFromSession,
|
|
1368
|
+
restoreReportEditSession,
|
|
1369
|
+
saveGeneratingSession,
|
|
1370
|
+
loadGeneratingSession,
|
|
1371
|
+
clearGeneratingSession,
|
|
1372
|
+
patchDraft,
|
|
1373
|
+
setSelectedTemplate,
|
|
1374
|
+
setModelStep,
|
|
1375
|
+
showPromptSettingsPanel,
|
|
1376
|
+
ensureSettingsChapterLayout,
|
|
1377
|
+
showPromptEditorPanel,
|
|
1378
|
+
openReportEdit,
|
|
1379
|
+
closeReportEdit,
|
|
1380
|
+
setGeneratedChapterBodies,
|
|
1381
|
+
setGeneratedChaptersFromApi,
|
|
1382
|
+
resetWorkspaceHistoryOnEnter,
|
|
1383
|
+
markGenerationSuccess,
|
|
1384
|
+
resetWorkflow,
|
|
1385
|
+
}
|
|
1386
|
+
}
|