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.
Files changed (32) hide show
  1. package/build-report/components/AiPromptGenerateDialog.vue +349 -0
  2. package/build-report/components/CapsuleScrollbar.vue +145 -0
  3. package/build-report/components/ChapterTitleScroll.vue +292 -0
  4. package/build-report/components/PromptField.vue +314 -0
  5. package/build-report/components/RecentReportsTable.vue +408 -0
  6. package/build-report/components/ReportChapterPreview.vue +437 -0
  7. package/build-report/components/ReportChapterWorkspace.vue +923 -0
  8. package/build-report/components/ReportCollapsibleSection.vue +132 -0
  9. package/build-report/components/ReportConfigPanel.vue +609 -0
  10. package/build-report/components/ReportGenerateSettingsPanel.vue +403 -0
  11. package/build-report/components/ReportGeneratingPanel.vue +908 -0
  12. package/build-report/components/ReportHistoryList.vue +157 -0
  13. package/build-report/components/ReportHistoryPanel.vue +140 -0
  14. package/build-report/components/ReportPromptPanel.vue +445 -0
  15. package/build-report/components/ReportSectionConfirmPopover.vue +232 -0
  16. package/build-report/components/ReportSourceFileSummary.vue +544 -0
  17. package/build-report/components/ReportSourcesMoreOverlay.vue +279 -0
  18. package/build-report/components/ReportSourcesPanel.vue +1055 -0
  19. package/build-report/components/ReportTemplateDetail.vue +176 -0
  20. package/build-report/components/ReportTemplateEditorFormPanel.vue +602 -0
  21. package/build-report/components/ReportTemplatePicker.vue +802 -0
  22. package/build-report/components/ReportTemplatePromptField.vue +314 -0
  23. package/build-report/components/ReportWorkflowSidebar.vue +104 -0
  24. package/build-report/components/reportEdit.vue +334 -0
  25. package/build-report/index.vue +646 -0
  26. package/build-report/report-list.vue +407 -0
  27. package/build-report/report-template-data.ts +248 -0
  28. package/build-report/styles/report-workflow-dialog.css +33 -0
  29. package/build-report/useReportWorkflow.ts +343 -0
  30. package/build-report/utils/report-edit-route.ts +109 -0
  31. package/build-report/utils/scroll.ts +361 -0
  32. package/package.json +12 -0
@@ -0,0 +1,437 @@
1
+ <template>
2
+ <div class="report-chapter-preview">
3
+ <main class="report-chapter-preview__main">
4
+ <div class="report-chapter-preview__inner">
5
+ <div class="report-chapter-preview__card">
6
+ <div class="report-chapter-preview__body doc-sample-root">
7
+ <div class="content-wrapper doc-sample-body">
8
+ <template v-for="(item, idx) in displaySequence" :key="idx">
9
+ <TypewriterText
10
+ v-if="item.type === 'text' && idx < activeIndex"
11
+ :text="item.content!"
12
+ :custom-class="item.styleClass"
13
+ :auto-start="idx === activeIndex - 1 && idx === lastStartedTextIndex"
14
+ @completed="onTextCompleted"
15
+ />
16
+ <component
17
+ v-else-if="item.type === 'component' && idx < activeIndex"
18
+ :is="item.component"
19
+ @ready="onComponentReady"
20
+ />
21
+ </template>
22
+ </div>
23
+ <span v-if="showGlobalCursor" class="typewriter-cursor typewriter-cursor--global">|</span>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ </main>
28
+ </div>
29
+ </template>
30
+
31
+ <script setup lang="ts">
32
+ import {
33
+ computed,
34
+ defineComponent,
35
+ h,
36
+ markRaw,
37
+ nextTick,
38
+ onMounted,
39
+ ref,
40
+ watch,
41
+ type Component,
42
+ } from 'vue'
43
+
44
+ const props = withDefaults(
45
+ defineProps<{
46
+ chapterTitle?: string
47
+ chapterIntro?: string
48
+ }>(),
49
+ {
50
+ chapterTitle: '',
51
+ chapterIntro: '',
52
+ },
53
+ )
54
+
55
+ const TH_GREEN_STYLE = {
56
+ backgroundColor: '#c6efce',
57
+ color: '#000000',
58
+ }
59
+
60
+ const TABLE_CELL_STYLE = {
61
+ border: '1px solid #000000',
62
+ padding: '4pt 6pt',
63
+ fontSize: '11pt',
64
+ color: '#000000',
65
+ }
66
+
67
+ const STYLE_TAG_MAP: Record<string, string> = {
68
+ 'doc-h2': 'h2',
69
+ 'doc-h3': 'h3',
70
+ 'doc-p': 'p',
71
+ }
72
+
73
+ interface DisplayItem {
74
+ type: 'text' | 'component'
75
+ content?: string
76
+ styleClass?: string
77
+ component?: Component
78
+ }
79
+
80
+ const activeIndex = ref(0)
81
+ const lastStartedTextIndex = ref(-1)
82
+
83
+ const tableAttrs = () => ({
84
+ class: 'doc-table',
85
+ style: {
86
+ width: '100%',
87
+ borderCollapse: 'collapse' as const,
88
+ border: '1px solid #000000',
89
+ },
90
+ cellspacing: '0',
91
+ cellpadding: '0',
92
+ border: '1',
93
+ })
94
+
95
+ const thCell = (
96
+ text: string,
97
+ extra: { style?: Record<string, string>; [key: string]: unknown } = {},
98
+ ) => {
99
+ const { style: extraStyle, ...rest } = extra
100
+ return h(
101
+ 'th',
102
+ {
103
+ style: { ...TABLE_CELL_STYLE, fontWeight: 'bold', ...extraStyle },
104
+ ...rest,
105
+ },
106
+ text,
107
+ )
108
+ }
109
+
110
+ const tdCell = (text: string, alignRight = false) =>
111
+ h(
112
+ 'td',
113
+ {
114
+ style: {
115
+ ...TABLE_CELL_STYLE,
116
+ textAlign: alignRight ? 'right' : 'left',
117
+ },
118
+ },
119
+ text,
120
+ )
121
+
122
+ const ReportChapterMockTable = markRaw(
123
+ defineComponent({
124
+ name: 'ReportChapterMockTable',
125
+ emits: ['ready'],
126
+ setup(_, { emit }) {
127
+ const rows = [
128
+ ['不良貸款率', '1.82%', '+0.12pct'],
129
+ ['撥備覆蓋率', '245.6%', '-3.2pct'],
130
+ ['關注類貸款占比', '3.45%', '+0.28pct'],
131
+ ['行業集中度(Top5)', '41.2%', '-1.5pct'],
132
+ ['逾期 90 天以上占比', '0.96%', '+0.05pct'],
133
+ ]
134
+ onMounted(() => emit('ready'))
135
+ return () =>
136
+ h('table', tableAttrs(), [
137
+ h('thead', {}, [
138
+ h('tr', { class: 'doc-table-head-green' }, [
139
+ thCell('風險指標', {
140
+ class: 'doc-th-green',
141
+ style: { ...TABLE_CELL_STYLE, fontWeight: 'bold', ...TH_GREEN_STYLE },
142
+ }),
143
+ thCell('本期值', {
144
+ class: 'doc-th-green',
145
+ style: { ...TABLE_CELL_STYLE, fontWeight: 'bold', textAlign: 'right', ...TH_GREEN_STYLE },
146
+ }),
147
+ thCell('同比變化', {
148
+ class: 'doc-th-green',
149
+ style: { ...TABLE_CELL_STYLE, fontWeight: 'bold', textAlign: 'right', ...TH_GREEN_STYLE },
150
+ }),
151
+ ]),
152
+ ]),
153
+ h(
154
+ 'tbody',
155
+ {},
156
+ rows.map((row) =>
157
+ h('tr', {}, [
158
+ tdCell(row[0]!),
159
+ tdCell(row[1]!, true),
160
+ tdCell(row[2]!, true),
161
+ ]),
162
+ ),
163
+ ),
164
+ ])
165
+ },
166
+ }),
167
+ )
168
+
169
+ function buildDisplaySequence(title: string, intro: string): DisplayItem[] {
170
+ return [
171
+ { type: 'text', content: title || '章節內容', styleClass: 'doc-h2' },
172
+ {
173
+ type: 'text',
174
+ content:
175
+ intro ||
176
+ '本章節為演示 Mock 正文,展示打字機逐字輸出效果。接入生成服務後,內容將由模型根據 Prompt 與資訊源自動產出。',
177
+ styleClass: 'doc-p',
178
+ },
179
+ { type: 'text', content: '核心監測指標', styleClass: 'doc-h3' },
180
+ {
181
+ type: 'text',
182
+ content: '下表列示本年度重點風險指標及同比變化情況,供本章風險研判參考:',
183
+ styleClass: 'doc-p',
184
+ },
185
+ { type: 'component', component: ReportChapterMockTable },
186
+ ]
187
+ }
188
+
189
+ const displaySequence = computed(() =>
190
+ buildDisplaySequence(props.chapterTitle, props.chapterIntro),
191
+ )
192
+
193
+ const showGlobalCursor = computed(() => {
194
+ if (activeIndex.value >= displaySequence.value.length) return false
195
+ const pending = displaySequence.value[activeIndex.value]
196
+ return pending?.type !== 'text'
197
+ })
198
+
199
+ const TypewriterText = defineComponent({
200
+ name: 'TypewriterText',
201
+ props: {
202
+ text: { type: String, required: true },
203
+ autoStart: { type: Boolean, default: false },
204
+ customClass: { type: String, default: '' },
205
+ },
206
+ emits: ['completed'],
207
+ setup(textProps, { emit }) {
208
+ const displayed = ref('')
209
+ const isTyping = ref(false)
210
+ let timer: number | null = null
211
+
212
+ const classList = () =>
213
+ textProps.customClass
214
+ .split(/\s+/)
215
+ .filter(Boolean)
216
+ .concat('typewriter-paragraph')
217
+
218
+ const resolveTag = () => {
219
+ const primary = textProps.customClass.split(/\s+/).find((c) => STYLE_TAG_MAP[c])
220
+ return primary ? (STYLE_TAG_MAP[primary] ?? 'p') : 'p'
221
+ }
222
+
223
+ const startTyping = () => {
224
+ if (timer) return
225
+ displayed.value = ''
226
+ isTyping.value = true
227
+ let i = 0
228
+ timer = window.setInterval(() => {
229
+ if (i < textProps.text.length) {
230
+ displayed.value += textProps.text[i]
231
+ i++
232
+ } else {
233
+ if (timer) clearInterval(timer)
234
+ timer = null
235
+ isTyping.value = false
236
+ emit('completed')
237
+ }
238
+ }, 15)
239
+ }
240
+
241
+ watch(
242
+ () => textProps.autoStart,
243
+ (val) => {
244
+ if (val) startTyping()
245
+ },
246
+ { immediate: true },
247
+ )
248
+
249
+ return () => {
250
+ const tag = resolveTag()
251
+ return h(tag as keyof HTMLElementTagNameMap, { class: classList() }, [
252
+ displayed.value,
253
+ isTyping.value ? h('span', { class: 'typewriter-cursor' }, '|') : null,
254
+ ])
255
+ }
256
+ },
257
+ })
258
+
259
+ function nextItem() {
260
+ if (activeIndex.value < displaySequence.value.length) {
261
+ activeIndex.value++
262
+ const newItem = displaySequence.value[activeIndex.value - 1]
263
+ if (!newItem) return
264
+ if (newItem.type === 'text') {
265
+ lastStartedTextIndex.value = activeIndex.value - 1
266
+ }
267
+ }
268
+ }
269
+
270
+ function onTextCompleted() {
271
+ nextItem()
272
+ }
273
+
274
+ function onComponentReady() {
275
+ nextItem()
276
+ }
277
+
278
+ function startSequence() {
279
+ if (displaySequence.value.length === 0) return
280
+ activeIndex.value = 1
281
+ const firstItem = displaySequence.value[0]
282
+ if (firstItem?.type === 'text') lastStartedTextIndex.value = 0
283
+ }
284
+
285
+ function restartDemo() {
286
+ activeIndex.value = 0
287
+ lastStartedTextIndex.value = -1
288
+ void nextTick(() => startSequence())
289
+ }
290
+
291
+ watch(
292
+ () => [props.chapterTitle, props.chapterIntro] as const,
293
+ () => restartDemo(),
294
+ { immediate: true },
295
+ )
296
+
297
+ defineExpose({ restartDemo })
298
+ </script>
299
+
300
+ <style scoped>
301
+ .report-chapter-preview {
302
+ flex: 1;
303
+ min-height: 0;
304
+ height: 100%;
305
+ display: flex;
306
+ flex-direction: column;
307
+ overflow: hidden;
308
+ background: #fff;
309
+ }
310
+
311
+ .report-chapter-preview__main {
312
+ flex: 1;
313
+ min-height: 0;
314
+ height: 100%;
315
+ overflow-y: auto;
316
+ padding: 0;
317
+ }
318
+
319
+ .report-chapter-preview__inner {
320
+ width: 100%;
321
+ max-width: none;
322
+ min-height: 100%;
323
+ margin: 0;
324
+ display: flex;
325
+ flex-direction: column;
326
+ }
327
+
328
+ .report-chapter-preview__card {
329
+ width: 100%;
330
+ flex: 1;
331
+ min-height: 100%;
332
+ display: flex;
333
+ flex-direction: column;
334
+ border: none;
335
+ border-radius: 0;
336
+ box-shadow: none;
337
+ background: #fff;
338
+ }
339
+
340
+ .report-chapter-preview__body {
341
+ flex: 1;
342
+ width: 100%;
343
+ min-height: 100%;
344
+ padding: 0;
345
+ box-sizing: border-box;
346
+ background: #fff;
347
+ overflow-x: hidden;
348
+ overflow-y: auto;
349
+ }
350
+
351
+ .doc-sample-root,
352
+ .doc-sample-body {
353
+ font-family: Calibri, 'Segoe UI', Arial, Helvetica, sans-serif;
354
+ font-size: 11pt;
355
+ color: #000000;
356
+ line-height: 1.15;
357
+ text-align: left;
358
+ }
359
+
360
+ .doc-h2 {
361
+ font-size: 14pt;
362
+ font-weight: bold;
363
+ color: #000000;
364
+ margin: 12pt 0 6pt;
365
+ padding: 0;
366
+ border: none;
367
+ }
368
+
369
+ .doc-h3 {
370
+ font-size: 12pt;
371
+ font-weight: bold;
372
+ color: #000000;
373
+ margin: 10pt 0 4pt;
374
+ }
375
+
376
+ .doc-p {
377
+ font-size: 11pt;
378
+ line-height: 1.15;
379
+ color: #000000;
380
+ margin: 0 0 10pt;
381
+ text-align: left;
382
+ }
383
+
384
+ :deep(.doc-table) {
385
+ width: 100%;
386
+ max-width: 100%;
387
+ table-layout: fixed;
388
+ border-collapse: collapse;
389
+ margin: 6pt 0 12pt;
390
+ font-size: 11pt;
391
+ color: #000000;
392
+ border: 1px solid #000000;
393
+ box-sizing: border-box;
394
+ word-wrap: break-word;
395
+ }
396
+
397
+ :deep(.doc-table th),
398
+ :deep(.doc-table td) {
399
+ border: 1px solid #000000 !important;
400
+ padding: 4pt 6pt;
401
+ vertical-align: top;
402
+ word-wrap: break-word;
403
+ overflow-wrap: break-word;
404
+ box-sizing: border-box;
405
+ }
406
+
407
+ :deep(.doc-th-green),
408
+ :deep(th.doc-th-green) {
409
+ background-color: #c6efce !important;
410
+ color: #000000 !important;
411
+ }
412
+
413
+ .typewriter-paragraph {
414
+ white-space: pre-wrap;
415
+ word-break: break-word;
416
+ }
417
+
418
+ .typewriter-cursor {
419
+ display: inline-block;
420
+ color: inherit;
421
+ font-weight: normal;
422
+ margin-left: 1px;
423
+ animation: typewriter-blink 1s step-end infinite;
424
+ }
425
+
426
+ .typewriter-cursor--global {
427
+ font-size: 11pt;
428
+ color: #000000;
429
+ margin-top: 4pt;
430
+ }
431
+
432
+ @keyframes typewriter-blink {
433
+ 50% {
434
+ opacity: 0;
435
+ }
436
+ }
437
+ </style>