aegon-dangerious 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/build-report/components/AiPromptGenerateDialog.vue +349 -0
- package/build-report/components/CapsuleScrollbar.vue +145 -0
- package/build-report/components/ChapterTitleScroll.vue +292 -0
- package/build-report/components/PromptField.vue +314 -0
- package/build-report/components/RecentReportsTable.vue +408 -0
- package/build-report/components/ReportChapterPreview.vue +437 -0
- package/build-report/components/ReportChapterWorkspace.vue +923 -0
- package/build-report/components/ReportCollapsibleSection.vue +132 -0
- package/build-report/components/ReportConfigPanel.vue +609 -0
- package/build-report/components/ReportGenerateSettingsPanel.vue +403 -0
- package/build-report/components/ReportGeneratingPanel.vue +908 -0
- package/build-report/components/ReportHistoryList.vue +157 -0
- package/build-report/components/ReportHistoryPanel.vue +140 -0
- package/build-report/components/ReportPromptPanel.vue +445 -0
- package/build-report/components/ReportSectionConfirmPopover.vue +232 -0
- package/build-report/components/ReportSourceFileSummary.vue +544 -0
- package/build-report/components/ReportSourcesMoreOverlay.vue +279 -0
- package/build-report/components/ReportSourcesPanel.vue +1055 -0
- package/build-report/components/ReportTemplateDetail.vue +176 -0
- package/build-report/components/ReportTemplateEditorFormPanel.vue +602 -0
- package/build-report/components/ReportTemplatePicker.vue +802 -0
- package/build-report/components/ReportTemplatePromptField.vue +314 -0
- package/build-report/components/ReportWorkflowSidebar.vue +104 -0
- package/build-report/components/reportEdit.vue +334 -0
- package/build-report/index.vue +646 -0
- package/build-report/report-list.vue +407 -0
- package/build-report/report-template-data.ts +248 -0
- package/build-report/styles/report-workflow-dialog.css +33 -0
- package/build-report/useReportWorkflow.ts +343 -0
- package/build-report/utils/report-edit-route.ts +109 -0
- package/build-report/utils/scroll.ts +361 -0
- package/package.json +12 -0
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ReportCollapsibleSection ref="sectionRef" v-model:open="panelOpen" title="報告配置"
|
|
3
|
+
@before-collapse="handleBeforeCollapse">
|
|
4
|
+
<div class="report-config-panel">
|
|
5
|
+
<div class="report-config-field">
|
|
6
|
+
<span class="report-config-field__label">名稱</span>
|
|
7
|
+
<span class="report-config-field__value">{{ displayName }}</span>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<div ref="modelFieldRef" class="report-config-field report-config-field--editable">
|
|
11
|
+
<span class="report-config-field__label">模型</span>
|
|
12
|
+
<span class="report-config-field__value">{{ displayModel }}</span>
|
|
13
|
+
<button ref="modelEditBtnRef" type="button" class="report-config-field__edit" aria-label="編輯模型"
|
|
14
|
+
@click.stop="openFieldEdit('model')">
|
|
15
|
+
<el-icon>
|
|
16
|
+
<Edit />
|
|
17
|
+
</el-icon>
|
|
18
|
+
</button>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<div ref="researchFieldRef" class="report-config-field report-config-field--editable">
|
|
22
|
+
<span class="report-config-field__label">調研主要出發點</span>
|
|
23
|
+
<span class="report-config-field__value">{{ draft.researchFocus || '—' }}</span>
|
|
24
|
+
<button ref="researchEditBtnRef" type="button" class="report-config-field__edit" aria-label="編輯調研主要出發點"
|
|
25
|
+
@click.stop="openFieldEdit('research')">
|
|
26
|
+
<el-icon>
|
|
27
|
+
<Edit />
|
|
28
|
+
</el-icon>
|
|
29
|
+
</button>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div class="report-config-field report-config-field--readonly">
|
|
33
|
+
<span class="report-config-field__label">年份</span>
|
|
34
|
+
<span class="report-config-field__value">{{ draft.year || '—' }}</span>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div ref="languageFieldRef" class="report-config-field report-config-field--editable">
|
|
38
|
+
<span class="report-config-field__label">報告語言</span>
|
|
39
|
+
<span class="report-config-field__value">{{ languageLabel }}</span>
|
|
40
|
+
<button ref="languageEditBtnRef" type="button" class="report-config-field__edit" aria-label="編輯報告語言"
|
|
41
|
+
@click.stop="openFieldEdit('language')">
|
|
42
|
+
<el-icon>
|
|
43
|
+
<Edit />
|
|
44
|
+
</el-icon>
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<button type="button" class="report-config-advanced-toggle" @click="advancedOpen = !advancedOpen">
|
|
49
|
+
<span>進階參數</span>
|
|
50
|
+
<span class="report-config-advanced-toggle__arrow" :class="{ 'is-open': advancedOpen }" aria-hidden="true" />
|
|
51
|
+
</button>
|
|
52
|
+
|
|
53
|
+
<div v-show="advancedOpen" class="report-config-advanced">
|
|
54
|
+
<div class="report-config-field report-config-field--stack">
|
|
55
|
+
<span class="report-config-field__label">搜尋模式</span>
|
|
56
|
+
<el-select v-model="advancedBuffers.searchMode" class="report-config-editor__select" size="small"
|
|
57
|
+
@change="applyAdvanced('searchMode', advancedBuffers.searchMode)">
|
|
58
|
+
<el-option v-for="mode in searchModeOptions" :key="mode" :label="mode" :value="mode" />
|
|
59
|
+
</el-select>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="report-config-field report-config-field--stack">
|
|
62
|
+
<span class="report-config-field__label">每章節字數</span>
|
|
63
|
+
<el-input v-model="advancedBuffers.wordsPerChapter" size="small" placeholder="50-5000"
|
|
64
|
+
@blur="applyWordsPerChapter" />
|
|
65
|
+
</div>
|
|
66
|
+
<div class="report-config-field report-config-field--stack report-config-field--slider">
|
|
67
|
+
<span class="report-config-field__label">權重</span>
|
|
68
|
+
<div class="report-config-slider">
|
|
69
|
+
<el-slider v-model="advancedBuffers.weight" :min="0" :max="1" :step="0.1" :show-tooltip="false"
|
|
70
|
+
@change="applyAdvanced('weight', advancedBuffers.weight)" />
|
|
71
|
+
<span class="report-config-slider__num">{{ advancedBuffers.weight }}</span>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="report-config-field report-config-field--stack">
|
|
75
|
+
<span class="report-config-field__label">Rerank</span>
|
|
76
|
+
<el-switch v-model="advancedBuffers.rerank" size="small" class="report-config-switch"
|
|
77
|
+
@change="applyAdvanced('rerank', advancedBuffers.rerank)" />
|
|
78
|
+
</div>
|
|
79
|
+
<p v-if="draft.searchMode === 'Vector Search'" class="report-config-advanced__hint">
|
|
80
|
+
搜尋模式為「Vector Search」時,權重與 Rerank 相關參數將被忽略。
|
|
81
|
+
</p>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</ReportCollapsibleSection>
|
|
85
|
+
|
|
86
|
+
<el-popover :visible="editModelOpen" :virtual-ref="modelFieldRef" placement="bottom-start" :width="popoverWidth"
|
|
87
|
+
:popper-options="popperOptions" :popper-style="modelPopperStyle" popper-class="report-config-popover"
|
|
88
|
+
:transition="popoverInstant ? 'report-config-popover-instant' : 'report-config-popover-fade'"
|
|
89
|
+
:gpu-acceleration="false" :teleported="true">
|
|
90
|
+
<div class="report-config-editor">
|
|
91
|
+
<el-select v-model="editBuffers.model" class="report-config-editor__select" filterable allow-create
|
|
92
|
+
default-first-option placeholder="">
|
|
93
|
+
<el-option v-for="item in modelOptions" :key="item" :label="item" :value="item" />
|
|
94
|
+
</el-select>
|
|
95
|
+
<div class="report-config-editor__actions">
|
|
96
|
+
<button type="button" class="report-config-editor__btn" @click="editModelOpen = false">取消</button>
|
|
97
|
+
<button type="button" class="report-config-editor__btn report-config-editor__btn--primary"
|
|
98
|
+
@click="applyField('model', editBuffers.model, () => (editModelOpen = false))">
|
|
99
|
+
確認
|
|
100
|
+
</button>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</el-popover>
|
|
104
|
+
|
|
105
|
+
<el-popover :visible="editResearchOpen" :virtual-ref="researchFieldRef" placement="bottom-start"
|
|
106
|
+
:width="popoverWidth" :popper-options="popperOptions" :popper-style="researchPopperStyle"
|
|
107
|
+
popper-class="report-config-popover" :transition="popoverInstant ? 'report-config-popover-instant' : 'report-config-popover-fade'"
|
|
108
|
+
:gpu-acceleration="false" :teleported="true">
|
|
109
|
+
<div class="report-config-editor">
|
|
110
|
+
<el-select v-model="editBuffers.researchFocus" class="report-config-editor__select" filterable allow-create
|
|
111
|
+
default-first-option clearable placeholder="">
|
|
112
|
+
<el-option v-for="item in researchFocusOptions" :key="item" :label="item" :value="item" />
|
|
113
|
+
</el-select>
|
|
114
|
+
<div class="report-config-editor__actions">
|
|
115
|
+
<button type="button" class="report-config-editor__btn" @click="editResearchOpen = false">取消</button>
|
|
116
|
+
<button type="button" class="report-config-editor__btn report-config-editor__btn--primary"
|
|
117
|
+
@click="applyField('researchFocus', editBuffers.researchFocus, () => (editResearchOpen = false))">
|
|
118
|
+
確認
|
|
119
|
+
</button>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</el-popover>
|
|
123
|
+
|
|
124
|
+
<el-popover :visible="editLanguageOpen" :virtual-ref="languageFieldRef" placement="bottom-start"
|
|
125
|
+
:width="popoverWidth" :popper-options="popperOptions" :popper-style="languagePopperStyle"
|
|
126
|
+
popper-class="report-config-popover" :transition="popoverInstant ? 'report-config-popover-instant' : 'report-config-popover-fade'"
|
|
127
|
+
:gpu-acceleration="false" :teleported="true">
|
|
128
|
+
<div class="report-config-editor">
|
|
129
|
+
<el-select v-model="editBuffers.language" class="report-config-editor__select" placeholder="">
|
|
130
|
+
<el-option v-for="item in languageOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
131
|
+
</el-select>
|
|
132
|
+
<div class="report-config-editor__actions">
|
|
133
|
+
<button type="button" class="report-config-editor__btn" @click="editLanguageOpen = false">取消</button>
|
|
134
|
+
<button type="button" class="report-config-editor__btn report-config-editor__btn--primary"
|
|
135
|
+
@click="applyField('language', editBuffers.language, () => (editLanguageOpen = false))">
|
|
136
|
+
確認
|
|
137
|
+
</button>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</el-popover>
|
|
141
|
+
</template>
|
|
142
|
+
|
|
143
|
+
<script lang="ts">
|
|
144
|
+
export const DEFAULT_REPORT_MODEL = 'gpt-666'
|
|
145
|
+
|
|
146
|
+
export const MODEL_OPTIONS = ['gpt-666', 'GPT-4o', 'GPT-4.1', 'Claude 3.5 Sonnet', 'Qwen-Max'] as const
|
|
147
|
+
|
|
148
|
+
export const RESEARCH_FOCUS_OPTIONS = ['中國香港', '中國內地', '東南亞', '歐美市場'] as const
|
|
149
|
+
|
|
150
|
+
export const LANGUAGE_OPTIONS = [
|
|
151
|
+
{ value: 'zh-Hant', label: '繁體中文' },
|
|
152
|
+
{ value: 'zh-Hans', label: '簡體中文' },
|
|
153
|
+
{ value: 'en', label: 'English' },
|
|
154
|
+
] as const
|
|
155
|
+
|
|
156
|
+
export const SEARCH_MODE_OPTIONS = ['Hybrid Search', 'Vector Search'] as const
|
|
157
|
+
</script>
|
|
158
|
+
|
|
159
|
+
<script setup lang="ts">
|
|
160
|
+
import { Edit } from '@element-plus/icons-vue'
|
|
161
|
+
import { ElIcon, ElInput, ElOption, ElPopover, ElSelect, ElSlider, ElSwitch } from 'element-plus'
|
|
162
|
+
import { computed, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
|
|
163
|
+
import { useReportWorkflow } from '../useReportWorkflow'
|
|
164
|
+
import ReportCollapsibleSection from './ReportCollapsibleSection.vue'
|
|
165
|
+
|
|
166
|
+
const WORDS_MIN = 50
|
|
167
|
+
const WORDS_MAX = 5000
|
|
168
|
+
|
|
169
|
+
const { draft, patchDraft } = useReportWorkflow()
|
|
170
|
+
|
|
171
|
+
const panelOpen = ref(true)
|
|
172
|
+
const advancedOpen = ref(false)
|
|
173
|
+
const editModelOpen = ref(false)
|
|
174
|
+
const editResearchOpen = ref(false)
|
|
175
|
+
const editLanguageOpen = ref(false)
|
|
176
|
+
const popoverInstant = ref(false)
|
|
177
|
+
|
|
178
|
+
const modelFieldRef = ref<HTMLElement | null>(null)
|
|
179
|
+
const researchFieldRef = ref<HTMLElement | null>(null)
|
|
180
|
+
const languageFieldRef = ref<HTMLElement | null>(null)
|
|
181
|
+
const modelEditBtnRef = ref<HTMLElement | null>(null)
|
|
182
|
+
const researchEditBtnRef = ref<HTMLElement | null>(null)
|
|
183
|
+
const languageEditBtnRef = ref<HTMLElement | null>(null)
|
|
184
|
+
const sectionRef = ref<InstanceType<typeof ReportCollapsibleSection> | null>(null)
|
|
185
|
+
|
|
186
|
+
const popoverWidthPx = ref(240)
|
|
187
|
+
const arrowAlignKey = ref(0)
|
|
188
|
+
const popoverWidth = computed(() => popoverWidthPx.value)
|
|
189
|
+
|
|
190
|
+
function getSectionContentWidth() {
|
|
191
|
+
const sectionEl = sectionRef.value?.rootRef
|
|
192
|
+
if (!sectionEl) return 240
|
|
193
|
+
const styles = getComputedStyle(sectionEl)
|
|
194
|
+
const paddingX = parseFloat(styles.paddingLeft) + parseFloat(styles.paddingRight)
|
|
195
|
+
return Math.round(sectionEl.clientWidth - paddingX)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function getArrowPopperStyle(editBtn: HTMLElement | null): Record<string, string> {
|
|
199
|
+
void arrowAlignKey.value
|
|
200
|
+
void panelOpen.value
|
|
201
|
+
const sectionEl = sectionRef.value?.rootRef
|
|
202
|
+
if (!editBtn || !sectionEl) return {}
|
|
203
|
+
const sectionRect = sectionEl.getBoundingClientRect()
|
|
204
|
+
const paddingLeft = parseFloat(getComputedStyle(sectionEl).paddingLeft)
|
|
205
|
+
const btnRect = editBtn.getBoundingClientRect()
|
|
206
|
+
const left = btnRect.left + btnRect.width / 2 - sectionRect.left - paddingLeft - 8
|
|
207
|
+
return { '--report-config-arrow-left': `${left}px` }
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const modelPopperStyle = computed(() => getArrowPopperStyle(modelEditBtnRef.value))
|
|
211
|
+
|
|
212
|
+
const researchPopperStyle = computed(() => getArrowPopperStyle(researchEditBtnRef.value))
|
|
213
|
+
|
|
214
|
+
const languagePopperStyle = computed(() => getArrowPopperStyle(languageEditBtnRef.value))
|
|
215
|
+
|
|
216
|
+
const popperOptions = computed(() => {
|
|
217
|
+
const boundary = sectionRef.value?.rootRef
|
|
218
|
+
return {
|
|
219
|
+
modifiers: [
|
|
220
|
+
{
|
|
221
|
+
name: 'preventOverflow',
|
|
222
|
+
options: {
|
|
223
|
+
boundary: boundary ?? 'viewport',
|
|
224
|
+
padding: 0,
|
|
225
|
+
altAxis: true,
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
name: 'flip',
|
|
230
|
+
enabled: false,
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
}
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
function syncPopoverWidth() {
|
|
237
|
+
popoverWidthPx.value = getSectionContentWidth()
|
|
238
|
+
arrowAlignKey.value += 1
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function hasOpenFieldEdits() {
|
|
242
|
+
return editModelOpen.value || editResearchOpen.value || editLanguageOpen.value
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function closeAllFieldEdits() {
|
|
246
|
+
editModelOpen.value = false
|
|
247
|
+
editResearchOpen.value = false
|
|
248
|
+
editLanguageOpen.value = false
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function handleBeforeCollapse(done: (hadOpenOverlays: boolean) => void) {
|
|
252
|
+
const hadOpen = hasOpenFieldEdits()
|
|
253
|
+
if (hadOpen) {
|
|
254
|
+
popoverInstant.value = true
|
|
255
|
+
closeAllFieldEdits()
|
|
256
|
+
window.setTimeout(() => {
|
|
257
|
+
popoverInstant.value = false
|
|
258
|
+
}, 320)
|
|
259
|
+
}
|
|
260
|
+
done(hadOpen)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
type EditableField = 'model' | 'research' | 'language'
|
|
264
|
+
|
|
265
|
+
function openFieldEdit(field: EditableField) {
|
|
266
|
+
syncPopoverWidth()
|
|
267
|
+
editModelOpen.value = field === 'model' ? !editModelOpen.value : false
|
|
268
|
+
editResearchOpen.value = field === 'research' ? !editResearchOpen.value : false
|
|
269
|
+
editLanguageOpen.value = field === 'language' ? !editLanguageOpen.value : false
|
|
270
|
+
if (field === 'model' ? editModelOpen.value : field === 'research' ? editResearchOpen.value : editLanguageOpen.value) {
|
|
271
|
+
void nextTick(() => {
|
|
272
|
+
arrowAlignKey.value += 1
|
|
273
|
+
})
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
watch(panelOpen, (open) => {
|
|
278
|
+
if (!open) closeAllFieldEdits()
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
let sectionResizeObserver: ResizeObserver | null = null
|
|
282
|
+
|
|
283
|
+
onMounted(() => {
|
|
284
|
+
void nextTick(() => {
|
|
285
|
+
syncPopoverWidth()
|
|
286
|
+
const sectionEl = sectionRef.value?.rootRef
|
|
287
|
+
if (sectionEl) {
|
|
288
|
+
sectionResizeObserver = new ResizeObserver(() => syncPopoverWidth())
|
|
289
|
+
sectionResizeObserver.observe(sectionEl)
|
|
290
|
+
}
|
|
291
|
+
})
|
|
292
|
+
window.addEventListener('resize', syncPopoverWidth)
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
onUnmounted(() => {
|
|
296
|
+
window.removeEventListener('resize', syncPopoverWidth)
|
|
297
|
+
sectionResizeObserver?.disconnect()
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
const modelOptions = MODEL_OPTIONS
|
|
301
|
+
const researchFocusOptions = RESEARCH_FOCUS_OPTIONS
|
|
302
|
+
const languageOptions = LANGUAGE_OPTIONS
|
|
303
|
+
const searchModeOptions = SEARCH_MODE_OPTIONS
|
|
304
|
+
|
|
305
|
+
const editBuffers = reactive({
|
|
306
|
+
model: DEFAULT_REPORT_MODEL,
|
|
307
|
+
researchFocus: '',
|
|
308
|
+
language: 'zh-Hant',
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
const advancedBuffers = reactive({
|
|
312
|
+
searchMode: 'Hybrid Search',
|
|
313
|
+
wordsPerChapter: '',
|
|
314
|
+
weight: 0.5,
|
|
315
|
+
rerank: true,
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
const displayName = computed(() => draft.reportName || '—')
|
|
319
|
+
const displayModel = computed(() => draft.model || DEFAULT_REPORT_MODEL)
|
|
320
|
+
|
|
321
|
+
const languageLabel = computed(() => {
|
|
322
|
+
const found = LANGUAGE_OPTIONS.find((item) => item.value === draft.language)
|
|
323
|
+
return found?.label ?? '繁體中文'
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
function syncBuffersFromDraft() {
|
|
327
|
+
editBuffers.model = draft.model || DEFAULT_REPORT_MODEL
|
|
328
|
+
editBuffers.researchFocus = draft.researchFocus
|
|
329
|
+
editBuffers.language = draft.language || 'zh-Hant'
|
|
330
|
+
advancedBuffers.searchMode = draft.searchMode
|
|
331
|
+
advancedBuffers.wordsPerChapter = draft.wordsPerChapter
|
|
332
|
+
advancedBuffers.weight = draft.weight
|
|
333
|
+
advancedBuffers.rerank = draft.rerank
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
watch(
|
|
337
|
+
() => [editModelOpen.value, editResearchOpen.value, editLanguageOpen.value] as const,
|
|
338
|
+
([modelOpen, researchOpen, languageOpen]) => {
|
|
339
|
+
if (modelOpen || researchOpen || languageOpen) syncBuffersFromDraft()
|
|
340
|
+
},
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
watch(advancedOpen, (open) => {
|
|
344
|
+
if (open) syncBuffersFromDraft()
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
syncBuffersFromDraft()
|
|
348
|
+
|
|
349
|
+
function applyField<K extends 'model' | 'researchFocus' | 'language'>(
|
|
350
|
+
key: K,
|
|
351
|
+
value: (typeof editBuffers)[K],
|
|
352
|
+
close: () => void,
|
|
353
|
+
) {
|
|
354
|
+
patchDraft({ [key]: value })
|
|
355
|
+
close()
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function applyAdvanced<K extends 'searchMode' | 'weight' | 'rerank'>(
|
|
359
|
+
key: K,
|
|
360
|
+
value: (typeof advancedBuffers)[K],
|
|
361
|
+
) {
|
|
362
|
+
patchDraft({ [key]: value })
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function applyWordsPerChapter() {
|
|
366
|
+
const raw = advancedBuffers.wordsPerChapter.trim()
|
|
367
|
+
if (!raw) {
|
|
368
|
+
patchDraft({ wordsPerChapter: '' })
|
|
369
|
+
return
|
|
370
|
+
}
|
|
371
|
+
const value = Math.round(Number(raw))
|
|
372
|
+
if (Number.isNaN(value)) {
|
|
373
|
+
advancedBuffers.wordsPerChapter = ''
|
|
374
|
+
patchDraft({ wordsPerChapter: '' })
|
|
375
|
+
return
|
|
376
|
+
}
|
|
377
|
+
const clamped = Math.min(WORDS_MAX, Math.max(WORDS_MIN, value))
|
|
378
|
+
advancedBuffers.wordsPerChapter = String(clamped)
|
|
379
|
+
patchDraft({ wordsPerChapter: String(clamped) })
|
|
380
|
+
}
|
|
381
|
+
</script>
|
|
382
|
+
|
|
383
|
+
<style scoped>
|
|
384
|
+
.report-config-panel {
|
|
385
|
+
box-sizing: border-box;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.report-config-panel__title {
|
|
389
|
+
margin: 0 0 4px;
|
|
390
|
+
font-size: 15px;
|
|
391
|
+
font-weight: 600;
|
|
392
|
+
color: #1a1a1a;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.report-config-panel__subtitle {
|
|
396
|
+
margin: 0 0 8px;
|
|
397
|
+
font-size: 13px;
|
|
398
|
+
font-weight: 600;
|
|
399
|
+
color: #303133;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.report-config-field {
|
|
403
|
+
display: grid;
|
|
404
|
+
grid-template-columns: 88px minmax(0, 1fr) auto;
|
|
405
|
+
align-items: center;
|
|
406
|
+
gap: 8px;
|
|
407
|
+
height: 40px;
|
|
408
|
+
padding: 0;
|
|
409
|
+
font-size: 14px;
|
|
410
|
+
font-weight: 400;
|
|
411
|
+
color: auto;
|
|
412
|
+
box-sizing: border-box;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.report-config-field--readonly {
|
|
416
|
+
grid-template-columns: 88px minmax(0, 1fr);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.report-config-field--editable {
|
|
420
|
+
width: 100%;
|
|
421
|
+
cursor: default;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.report-config-field--stack {
|
|
425
|
+
grid-template-columns: 1fr;
|
|
426
|
+
gap: 6px;
|
|
427
|
+
height: auto;
|
|
428
|
+
min-height: 40px;
|
|
429
|
+
padding: 8px 0;
|
|
430
|
+
font-size: 13px;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.report-config-field--slider {
|
|
434
|
+
align-items: flex-start;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.report-config-field__label {
|
|
438
|
+
color: auto;
|
|
439
|
+
font-size: 14px;
|
|
440
|
+
font-weight: 400;
|
|
441
|
+
white-space: nowrap;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.report-config-field__value {
|
|
445
|
+
text-align: right;
|
|
446
|
+
color: auto;
|
|
447
|
+
font-size: 14px;
|
|
448
|
+
font-weight: 400;
|
|
449
|
+
overflow: hidden;
|
|
450
|
+
text-overflow: ellipsis;
|
|
451
|
+
white-space: nowrap;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.report-config-field__edit {
|
|
455
|
+
display: inline-flex;
|
|
456
|
+
align-items: center;
|
|
457
|
+
justify-content: center;
|
|
458
|
+
padding: 0;
|
|
459
|
+
border: 0;
|
|
460
|
+
background: none;
|
|
461
|
+
color: #606266;
|
|
462
|
+
font-size: 14px;
|
|
463
|
+
cursor: pointer;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.report-config-field__edit:hover {
|
|
467
|
+
color: #c53355;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.report-config-advanced-toggle {
|
|
471
|
+
display: flex;
|
|
472
|
+
align-items: center;
|
|
473
|
+
justify-content: space-between;
|
|
474
|
+
width: 100%;
|
|
475
|
+
margin-top: 10px;
|
|
476
|
+
padding: 0;
|
|
477
|
+
border: 0;
|
|
478
|
+
background: none;
|
|
479
|
+
font-size: 13px;
|
|
480
|
+
font-weight: 700;
|
|
481
|
+
color: #1a1a1a;
|
|
482
|
+
cursor: pointer;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.report-config-advanced-toggle__arrow {
|
|
486
|
+
display: inline-block;
|
|
487
|
+
width: 0;
|
|
488
|
+
height: 0;
|
|
489
|
+
border-left: 5px solid transparent;
|
|
490
|
+
border-right: 5px solid transparent;
|
|
491
|
+
border-bottom: 7px solid #c53355;
|
|
492
|
+
transition: transform 0.2s ease;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.report-config-advanced-toggle__arrow.is-open {
|
|
496
|
+
transform: rotate(180deg);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.report-config-advanced {
|
|
500
|
+
padding-top: 4px;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.report-config-advanced__hint {
|
|
504
|
+
margin: 4px 0 0;
|
|
505
|
+
font-size: 12px;
|
|
506
|
+
line-height: 1.5;
|
|
507
|
+
color: #86909c;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.report-config-editor__select {
|
|
511
|
+
width: 100%;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.report-config-editor__select :deep(.el-select__wrapper) {
|
|
515
|
+
min-height: 32px;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.report-config-editor__actions {
|
|
519
|
+
display: flex;
|
|
520
|
+
justify-content: flex-end;
|
|
521
|
+
gap: 8px;
|
|
522
|
+
margin-top: 10px;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.report-config-editor__btn {
|
|
526
|
+
min-width: 72px;
|
|
527
|
+
padding: 6px 10px;
|
|
528
|
+
border: 1px solid #dcdfe6;
|
|
529
|
+
border-radius: 4px;
|
|
530
|
+
background: #fff;
|
|
531
|
+
font-size: 12px;
|
|
532
|
+
color: #606266;
|
|
533
|
+
cursor: pointer;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.report-config-editor__btn--primary {
|
|
537
|
+
border-color: #c53355;
|
|
538
|
+
background: #c53355;
|
|
539
|
+
color: #fff;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.report-config-editor__btn--primary:hover {
|
|
543
|
+
background: #fff;
|
|
544
|
+
color: #c53355;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.report-config-slider {
|
|
548
|
+
display: flex;
|
|
549
|
+
align-items: center;
|
|
550
|
+
gap: 8px;
|
|
551
|
+
width: 100%;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.report-config-slider :deep(.el-slider) {
|
|
555
|
+
flex: 1;
|
|
556
|
+
--el-slider-main-bg-color: #c53355;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.report-config-slider__num {
|
|
560
|
+
width: 28px;
|
|
561
|
+
font-size: 12px;
|
|
562
|
+
color: #606266;
|
|
563
|
+
text-align: right;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
.report-config-switch {
|
|
567
|
+
--el-switch-on-color: #c53355;
|
|
568
|
+
}
|
|
569
|
+
</style>
|
|
570
|
+
|
|
571
|
+
<style>
|
|
572
|
+
.report-config-popover.el-popover.el-popper {
|
|
573
|
+
max-width: 100%;
|
|
574
|
+
padding: 12px;
|
|
575
|
+
box-sizing: border-box;
|
|
576
|
+
overflow: hidden;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
.report-config-popover.el-popper[data-popper-placement^='bottom'] .el-popper__arrow {
|
|
580
|
+
left: var(--report-config-arrow-left) !important;
|
|
581
|
+
right: auto !important;
|
|
582
|
+
top: -6px !important;
|
|
583
|
+
transform: translateX(-50%) !important;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.report-config-popover.el-popper[data-popper-placement^='bottom'] .el-popper__arrow::before {
|
|
587
|
+
bottom: -1px;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.report-config-popover-fade-enter-active,
|
|
591
|
+
.report-config-popover-fade-leave-active {
|
|
592
|
+
transition: opacity 0.12s ease;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
.report-config-popover-fade-enter-from,
|
|
596
|
+
.report-config-popover-fade-leave-to {
|
|
597
|
+
opacity: 0;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
.report-config-popover-instant-enter-active,
|
|
601
|
+
.report-config-popover-instant-leave-active {
|
|
602
|
+
transition: none !important;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.report-config-popover-instant-enter-from,
|
|
606
|
+
.report-config-popover-instant-leave-to {
|
|
607
|
+
opacity: 0;
|
|
608
|
+
}
|
|
609
|
+
</style>
|