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,349 @@
1
+ <template>
2
+ <el-dialog v-model="visible" width="60%" append-to-body :show-close="false"
3
+ modal-class="report-workflow-dialog-overlay" class="report-workflow-dialog ai-prompt-generate-dialog"
4
+ header-class="ai-prompt-generate-dialog__header" @closed="handleClosed">
5
+ <template #header>
6
+ <span class="ai-prompt-generate-dialog__title">{{ dialogTitle }}</span>
7
+ </template>
8
+
9
+ <div v-if="mode === 'input'" class="ai-prompt-generate-dialog__form">
10
+ <label class="ai-prompt-generate-dialog__field ai-prompt-generate-dialog__field--row">
11
+ <span class="ai-prompt-generate-dialog__label">章節標題</span>
12
+ <div class="ai-prompt-generate-dialog__control">
13
+ <el-input v-model="form.title" :maxlength="CHAPTER_TITLE_MAX" show-word-limit
14
+ class="ai-prompt-generate-dialog__input" />
15
+ </div>
16
+ </label>
17
+
18
+ <label
19
+ class="ai-prompt-generate-dialog__field ai-prompt-generate-dialog__field--row ai-prompt-generate-dialog__field--textarea">
20
+ <span class="ai-prompt-generate-dialog__label">章節描述</span>
21
+ <div class="ai-prompt-generate-dialog__control">
22
+ <el-input v-model="form.description" type="textarea" :rows="4" :maxlength="CHAPTER_DESC_MAX" show-word-limit
23
+ resize="none" class="ai-prompt-generate-dialog__textarea" />
24
+ </div>
25
+ </label>
26
+ </div>
27
+
28
+ <div v-else class="ai-prompt-generate-dialog__preview">
29
+ <div class="ai-prompt-generate-dialog__preview-row">
30
+ <span class="ai-prompt-generate-dialog__label">章節標題</span>
31
+ <p class="ai-prompt-generate-dialog__preview-text">{{ form.title }}</p>
32
+ </div>
33
+
34
+ <div class="ai-prompt-generate-dialog__preview-row">
35
+ <span class="ai-prompt-generate-dialog__label">章節描述</span>
36
+ <p class="ai-prompt-generate-dialog__preview-text ai-prompt-generate-dialog__preview-text--multiline">
37
+ {{ form.description }}
38
+ </p>
39
+ </div>
40
+
41
+ <div class="ai-prompt-generate-dialog__divider" aria-hidden="true" />
42
+
43
+ <div class="ai-prompt-generate-dialog__preview-row ai-prompt-generate-dialog__preview-row--prompt">
44
+ <span class="ai-prompt-generate-dialog__label">生成的 prompt</span>
45
+ <p class="ai-prompt-generate-dialog__preview-text ai-prompt-generate-dialog__preview-text--multiline">
46
+ {{ generatedPrompt }}
47
+ </p>
48
+ </div>
49
+ </div>
50
+
51
+ <template #footer>
52
+ <div class="ai-prompt-generate-dialog__footer">
53
+ <button v-if="mode === 'input'" type="button"
54
+ class="template-center-toolbar-btn gen-ai-action-btn template-center-toolbar-btn--outline ai-prompt-generate-dialog__footer-btn"
55
+ @click="handleCancel">
56
+ 取消
57
+ </button>
58
+ <button v-else type="button"
59
+ class="template-center-toolbar-btn gen-ai-action-btn template-center-toolbar-btn--outline ai-prompt-generate-dialog__footer-btn"
60
+ @click="handleBack">
61
+ 退回
62
+ </button>
63
+
64
+ <button v-if="mode === 'input'" type="button"
65
+ class="template-center-toolbar-btn gen-ai-action-btn template-center-toolbar-btn--primary ai-prompt-generate-dialog__footer-btn"
66
+ @click="handleGenerate">
67
+ 生成 Prompt
68
+ </button>
69
+ <button v-else type="button"
70
+ class="template-center-toolbar-btn gen-ai-action-btn template-center-toolbar-btn--primary ai-prompt-generate-dialog__footer-btn"
71
+ @click="handleConfirm">
72
+ 確認
73
+ </button>
74
+ </div>
75
+ </template>
76
+ </el-dialog>
77
+ </template>
78
+
79
+ <script setup lang="ts">
80
+ import { ElDialog, ElInput, ElMessage } from 'element-plus'
81
+ import { computed, reactive, ref, watch } from 'vue'
82
+
83
+ const CHAPTER_TITLE_MAX = 100
84
+ const CHAPTER_DESC_MAX = 1000
85
+
86
+ const visible = defineModel<boolean>({ default: false })
87
+
88
+ const props = withDefaults(
89
+ defineProps<{
90
+ initialTitle?: string
91
+ initialDescription?: string
92
+ }>(),
93
+ {
94
+ initialTitle: '',
95
+ initialDescription: '',
96
+ },
97
+ )
98
+
99
+ const emit = defineEmits<{
100
+ confirm: [prompt: string]
101
+ }>()
102
+
103
+ type DialogMode = 'input' | 'preview'
104
+
105
+ const mode = ref<DialogMode>('input')
106
+ const generatedPrompt = ref('')
107
+
108
+ const form = reactive({
109
+ title: '',
110
+ description: '',
111
+ })
112
+
113
+ const dialogTitle = computed(() =>
114
+ mode.value === 'input' ? '智能化生成 Prompt' : '智能化生成 Prompt 預覽',
115
+ )
116
+
117
+ function resetForm() {
118
+ form.title = props.initialTitle.trim()
119
+ form.description = props.initialDescription.trim()
120
+ generatedPrompt.value = ''
121
+ mode.value = 'input'
122
+ }
123
+
124
+ watch(visible, (open) => {
125
+ if (open) resetForm()
126
+ })
127
+
128
+ watch(
129
+ () => [props.initialTitle, props.initialDescription] as const,
130
+ () => {
131
+ if (visible.value && mode.value === 'input') {
132
+ form.title = props.initialTitle.trim()
133
+ form.description = props.initialDescription.trim()
134
+ }
135
+ },
136
+ )
137
+
138
+ function buildMockPrompt() {
139
+ return [
140
+ `請根據章節「${form.title}」生成分析內容。`,
141
+ `章節描述:${form.description}`,
142
+ '生成的 prompt 內容:請結合資訊源,輸出結構化章節正文,語氣專業、條理清晰,並在必要時補充表格或要點列表。',
143
+ ].join('\n')
144
+ }
145
+
146
+ function handleGenerate() {
147
+ const title = form.title.trim()
148
+ const description = form.description.trim()
149
+
150
+ if (!title) {
151
+ ElMessage.warning('請輸入章節標題')
152
+ return
153
+ }
154
+ if (!description) {
155
+ ElMessage.warning('請輸入章節描述')
156
+ return
157
+ }
158
+
159
+ generatedPrompt.value = buildMockPrompt()
160
+ mode.value = 'preview'
161
+ }
162
+
163
+ function handleBack() {
164
+ mode.value = 'input'
165
+ }
166
+
167
+ function handleCancel() {
168
+ visible.value = false
169
+ }
170
+
171
+ function handleConfirm() {
172
+ emit('confirm', generatedPrompt.value)
173
+ visible.value = false
174
+ }
175
+
176
+ function handleClosed() {
177
+ resetForm()
178
+ }
179
+ </script>
180
+
181
+ <style scoped>
182
+ .ai-prompt-generate-dialog__form,
183
+ .ai-prompt-generate-dialog__preview {
184
+ display: flex;
185
+ flex-direction: column;
186
+ gap: 18px;
187
+ }
188
+
189
+ .ai-prompt-generate-dialog__field {
190
+ margin: 0;
191
+ }
192
+
193
+ .ai-prompt-generate-dialog__field--row {
194
+ display: grid;
195
+ grid-template-columns: 100px minmax(0, 1fr);
196
+ gap: 16px;
197
+ align-items: start;
198
+ }
199
+
200
+ .ai-prompt-generate-dialog__field--textarea {
201
+ align-items: start;
202
+ }
203
+
204
+ .ai-prompt-generate-dialog__label {
205
+ padding-top: 8px;
206
+ font-size: 14px;
207
+ color: #303133;
208
+ line-height: 1.4;
209
+ white-space: nowrap;
210
+ }
211
+
212
+ .ai-prompt-generate-dialog__control {
213
+ min-width: 0;
214
+ }
215
+
216
+ .ai-prompt-generate-dialog__preview-row {
217
+ display: grid;
218
+ grid-template-columns: 100px minmax(0, 1fr);
219
+ gap: 16px;
220
+ align-items: start;
221
+ }
222
+
223
+ .ai-prompt-generate-dialog__preview-row--prompt .ai-prompt-generate-dialog__label {
224
+ padding-top: 0;
225
+ }
226
+
227
+ .ai-prompt-generate-dialog__preview-text {
228
+ margin: 0;
229
+ font-size: 14px;
230
+ line-height: 1.6;
231
+ color: #303133;
232
+ word-break: break-word;
233
+ }
234
+
235
+ .ai-prompt-generate-dialog__preview-text--multiline {
236
+ white-space: pre-wrap;
237
+ }
238
+
239
+ .ai-prompt-generate-dialog__divider {
240
+ height: 0;
241
+ border-top: 1px dashed #dcdfe6;
242
+ }
243
+
244
+ .ai-prompt-generate-dialog__footer {
245
+ display: flex;
246
+ align-items: center;
247
+ justify-content: center;
248
+ gap: 16px;
249
+ width: 100%;
250
+ }
251
+
252
+ .ai-prompt-generate-dialog__footer-btn {
253
+ min-width: 140px;
254
+ }
255
+ </style>
256
+
257
+ <style>
258
+ .ai-prompt-generate-dialog {
259
+ --report-accent: #c53355;
260
+ --report-accent-soft: #fdf0f2;
261
+ }
262
+
263
+ .ai-prompt-generate-dialog .template-center-toolbar-btn {
264
+ display: inline-flex;
265
+ align-items: center;
266
+ justify-content: center;
267
+ gap: 6px;
268
+ box-sizing: border-box;
269
+ height: 32px;
270
+ min-width: 120px;
271
+ padding: 0 14px;
272
+ border-radius: 4px;
273
+ line-height: 1;
274
+ white-space: nowrap;
275
+ cursor: pointer;
276
+ font-size: 15px;
277
+ font-family: Arial, sans-serif;
278
+ font-weight: 400;
279
+ transition: all 0.2s ease;
280
+ }
281
+
282
+ .ai-prompt-generate-dialog .template-center-toolbar-btn--primary {
283
+ border: 1px solid var(--report-accent);
284
+ background: var(--report-accent);
285
+ color: #fff;
286
+ }
287
+
288
+ .ai-prompt-generate-dialog .template-center-toolbar-btn--primary:hover:not(:disabled) {
289
+ background: #fff;
290
+ border-color: var(--report-accent);
291
+ color: var(--report-accent);
292
+ }
293
+
294
+ .ai-prompt-generate-dialog .template-center-toolbar-btn--outline {
295
+ border: 1px solid var(--report-accent);
296
+ color: var(--report-accent);
297
+ background: #fff;
298
+ }
299
+
300
+ .ai-prompt-generate-dialog .template-center-toolbar-btn--outline:hover:not(:disabled) {
301
+ border-color: var(--report-accent);
302
+ color: var(--report-accent);
303
+ background: var(--report-accent-soft);
304
+ }
305
+ </style>
306
+
307
+ <style>
308
+ .ai-prompt-generate-dialog.el-dialog {
309
+ --el-dialog-padding-primary: 0;
310
+ border-radius: 4px;
311
+ overflow: hidden;
312
+ }
313
+ .ai-prompt-generate-dialog .el-dialog__header,
314
+ .ai-prompt-generate-dialog__header {
315
+ display: flex;
316
+ align-items: center;
317
+ justify-content: center;
318
+ margin: 0;
319
+ padding: 14px 20px;
320
+ background: #4a5f7a;
321
+ }
322
+
323
+ .ai-prompt-generate-dialog__title,
324
+ .ai-prompt-generate-dialog .el-dialog__title {
325
+ color: #fff;
326
+ font-size: 16px;
327
+ font-weight: 600;
328
+ line-height: 1.4;
329
+ }
330
+
331
+ .ai-prompt-generate-dialog .el-dialog__body {
332
+ padding: 24px 20px;
333
+ background: #fff;
334
+ }
335
+
336
+ .ai-prompt-generate-dialog .el-dialog__footer {
337
+ padding: 12px 16px 16px;
338
+ background: #fff;
339
+ display: flex;
340
+ justify-content: center;
341
+ }
342
+
343
+ .ai-prompt-generate-dialog__input :deep(.el-input__wrapper),
344
+ .ai-prompt-generate-dialog__textarea :deep(.el-textarea__inner) {
345
+ border-radius: 2px;
346
+ }
347
+ </style>
348
+
349
+ <style src="../styles/report-workflow-dialog.css"></style>
@@ -0,0 +1,145 @@
1
+ <template>
2
+ <div v-if="canScroll" :class="[rootClass, { [`${rootClass}--hidden`]: !shown }]" :style="rootStyle"
3
+ aria-hidden="true">
4
+ <div :class="thumbClass" :style="thumbStyle" @mousedown.stop="onThumbMouseDown" />
5
+ </div>
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ import { ref, toRef, toValue, watch } from 'vue';
10
+ import { useCapsuleScrollbar } from '../utils/scroll'
11
+
12
+ const props = withDefaults(
13
+ defineProps<{
14
+ /** 滾動容器;模板傳 ref 時會自動解包為 HTMLElement */
15
+ scrollEl?: unknown
16
+ anchorEl?: unknown
17
+ active?: boolean
18
+ gutterPx?: number
19
+ thumbWidthPx?: number
20
+ minThumbPx?: number
21
+ visibleRatio?: number
22
+ alwaysVisible?: boolean
23
+ autoHideAfterMs?: number
24
+ rightOffsetPx?: number
25
+ classPrefix?: string
26
+ }>(),
27
+ {
28
+ scrollEl: undefined,
29
+ anchorEl: undefined,
30
+ active: false,
31
+ alwaysVisible: false,
32
+ autoHideAfterMs: 3000,
33
+ visibleRatio: 1,
34
+ rightOffsetPx: 1,
35
+ },
36
+ )
37
+
38
+ const scrollElRef = ref(null as HTMLElement | null)
39
+ const anchorElRef = ref(null as HTMLElement | null)
40
+ const activeRef = toRef(props, 'active')
41
+
42
+ watch(
43
+ () => toValue(props.scrollEl),
44
+ (el) => {
45
+ scrollElRef.value = (el as HTMLElement | null) ?? null
46
+ },
47
+ { immediate: true },
48
+ )
49
+
50
+ watch(
51
+ () => toValue(props.anchorEl),
52
+ (el) => {
53
+ anchorElRef.value = (el as HTMLElement | null) ?? null
54
+ },
55
+ { immediate: true },
56
+ )
57
+
58
+ const {
59
+ canScroll,
60
+ shown,
61
+ rootStyle,
62
+ thumbStyle,
63
+ rootClass,
64
+ thumbClass,
65
+ onThumbMouseDown,
66
+ refresh,
67
+ } = useCapsuleScrollbar({
68
+ scrollEl: scrollElRef,
69
+ anchorEl: anchorElRef,
70
+ active: activeRef,
71
+ gutterPx: props.gutterPx,
72
+ thumbWidthPx: props.thumbWidthPx,
73
+ minThumbPx: props.minThumbPx,
74
+ visibleRatio: props.visibleRatio,
75
+ alwaysVisible: props.alwaysVisible,
76
+ autoHideAfterMs: props.autoHideAfterMs,
77
+ rightOffsetPx: props.rightOffsetPx,
78
+ classPrefix: props.classPrefix,
79
+ })
80
+
81
+ defineExpose({ refresh })
82
+ </script>
83
+
84
+ <style>
85
+ .capsule-scroll-container {
86
+ scrollbar-width: none !important;
87
+ -ms-overflow-style: none !important;
88
+ overflow-y: auto !important;
89
+ box-sizing: border-box !important;
90
+ padding-right: calc(var(--capsule-scroll-padding-right, 0px) + 20px) !important;
91
+ margin-right: -20px !important;
92
+ width: calc(100% + 20px) !important;
93
+ }
94
+
95
+ .capsule-scroll-container::-webkit-scrollbar,
96
+ .capsule-scroll-container::-webkit-scrollbar-thumb,
97
+ .capsule-scroll-container::-webkit-scrollbar-track,
98
+ .capsule-scroll-container::-webkit-scrollbar-corner {
99
+ -webkit-appearance: none !important;
100
+ appearance: none !important;
101
+ width: 0 !important;
102
+ height: 0 !important;
103
+ display: none !important;
104
+ opacity: 0 !important;
105
+ visibility: hidden !important;
106
+ background: transparent !important;
107
+ }
108
+
109
+ .capsule-scrollbar {
110
+ position: fixed;
111
+ z-index: 12;
112
+ box-sizing: border-box;
113
+ padding: 0;
114
+ pointer-events: none;
115
+ transition: opacity 0.25s ease;
116
+ }
117
+
118
+ .capsule-scrollbar--hidden {
119
+ opacity: 0;
120
+ pointer-events: none !important;
121
+ }
122
+
123
+ .capsule-scrollbar__thumb {
124
+ height: 100%;
125
+ background: #999;
126
+ border-radius: 9999px;
127
+ cursor: grab;
128
+ pointer-events: auto;
129
+ box-shadow: none;
130
+ filter: none;
131
+ outline: none;
132
+ border: none;
133
+ backface-visibility: hidden;
134
+ -webkit-backface-visibility: hidden;
135
+ }
136
+
137
+ .capsule-scrollbar__thumb:hover {
138
+ background: #777;
139
+ }
140
+
141
+ .capsule-scrollbar__thumb:active {
142
+ cursor: grabbing;
143
+ background: #666;
144
+ }
145
+ </style>