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,646 @@
1
+ <template>
2
+ <ReportEdit
3
+ v-if="isReportEditView"
4
+ :key="`${String(route.query.step ?? '')}-${String(route.query.templateId ?? '')}-${String(route.query.rightPanel ?? '')}`"
5
+ />
6
+ <div v-else class="report-center-page">
7
+ <header class="report-center-hero">
8
+ <h1 class="report-center-hero__title">AI行業分析報告助手</h1>
9
+ <p class="report-center-hero__subtitle">選擇報告生成參數,確定分析視角</p>
10
+ </header>
11
+
12
+ <el-card class="report-center-board" shadow="never">
13
+ <!-- 左:參數 -->
14
+ <section class="report-center-panel report-center-panel--params">
15
+ <h2 class="report-center-panel__title">基本參數</h2>
16
+
17
+ <div class="report-form">
18
+ <div class="report-form__row">
19
+ <label class="report-form__label">行業</label>
20
+ <div class="report-form__control">
21
+ <el-select v-model="form.industry" class="report-form__select" filterable allow-create
22
+ default-first-option clearable placeholder="">
23
+ <el-option v-for="item in industryOptions" :key="item.id" :label="item.name" :value="item.name" />
24
+ </el-select>
25
+ </div>
26
+ </div>
27
+ <div class="report-form__row">
28
+ <label class="report-form__label">調研主要出發點</label>
29
+ <div class="report-form__control">
30
+ <el-select v-model="form.researchFocus" class="report-form__select" filterable allow-create
31
+ default-first-option clearable placeholder="">
32
+ <el-option v-for="item in researchFocusOptions" :key="item.id" :label="item.name" :value="item.name" />
33
+ </el-select>
34
+ </div>
35
+ </div>
36
+ <div class="report-form__row">
37
+ <label class="report-form__label">年份</label>
38
+ <div class="report-form__control">
39
+ <el-select v-model="form.year" class="report-form__select" placeholder="">
40
+ <el-option v-for="year in yearOptions" :key="year" :label="year" :value="year" />
41
+ </el-select>
42
+ </div>
43
+ </div>
44
+ <div class="report-form__row">
45
+ <label class="report-form__label">報告名稱</label>
46
+ <div class="report-form__control">
47
+ <input v-model="form.reportName" type="text" class="report-input" @input="reportNameTouched = true" />
48
+ </div>
49
+ </div>
50
+ <div class="report-form__row">
51
+ <label class="report-form__label">報告語言</label>
52
+ <div class="report-form__control report-form__control--radio">
53
+ <label v-for="lang in languageOptions" :key="lang.value" class="report-radio">
54
+ <input v-model="form.language" type="radio" :value="lang.value" />
55
+ <span>{{ lang.label }}</span>
56
+ </label>
57
+ </div>
58
+ </div>
59
+ <div class="report-form__row report-form__row--textarea">
60
+ <label class="report-form__label">每章節字數</label>
61
+ <div class="report-form__control report-form__textarea-box">
62
+ <el-input v-model="form.wordsPerChapter" type="textarea" :rows="2" resize="none"
63
+ class="report-form__textarea" placeholder="" @blur="clampWordsPerChapter" />
64
+ <span class="report-form__textarea-hint">50-5000</span>
65
+ </div>
66
+ </div>
67
+ </div>
68
+
69
+ <button type="button" class="report-advanced-toggle" @click="advancedOpen = !advancedOpen">
70
+ <span>進階參數</span>
71
+ <span class="report-advanced-toggle__arrow" :class="{ 'is-open': advancedOpen }" aria-hidden="true" />
72
+ </button>
73
+
74
+ <div v-show="advancedOpen" class="report-form report-form--advanced">
75
+ <div class="report-form__row">
76
+ <label class="report-form__label">模型</label>
77
+ <div class="report-form__control">
78
+ <el-select v-model="form.model" class="report-form__select" filterable allow-create default-first-option
79
+ clearable placeholder="">
80
+ <el-option v-for="item in modelOptions" :key="item.id" :label="item.name" :value="item.name" />
81
+ </el-select>
82
+ </div>
83
+ </div>
84
+ <div class="report-form__row">
85
+ <label class="report-form__label">搜尋模式</label>
86
+ <div class="report-form__control">
87
+ <el-select v-model="form.searchMode" class="report-form__select" placeholder="">
88
+ <el-option v-for="mode in searchModeOptions" :key="mode" :label="mode" :value="mode" />
89
+ </el-select>
90
+ </div>
91
+ </div>
92
+ <div class="report-form__row report-form__row--slider">
93
+ <label class="report-form__label">權重</label>
94
+ <div class="report-form__control report-form__control--slider">
95
+ <el-slider v-model="form.weight" :min="0" :max="1" :step="0.1" :show-tooltip="false" />
96
+ <input v-model.number="form.weight" type="number" min="0" max="1" step="0.1" class="report-slider-num" />
97
+ </div>
98
+ </div>
99
+ <div class="report-form__row">
100
+ <label class="report-form__label">Rerank</label>
101
+ <div class="report-form__control">
102
+ <el-switch v-model="form.rerank" class="report-form__switch" />
103
+ </div>
104
+ </div>
105
+ <div class="report-form__row report-form__row--slider">
106
+ <label class="report-form__label">Rerank top-N</label>
107
+ <div class="report-form__control report-form__control--slider">
108
+ <el-slider v-model="form.rerankTopN" :min="1" :max="20" :step="1" :show-tooltip="false" />
109
+ <input v-model.number="form.rerankTopN" type="number" min="1" max="20" class="report-slider-num" />
110
+ </div>
111
+ </div>
112
+ <div class="report-form__row report-form__row--slider">
113
+ <label class="report-form__label">Rerank threshold</label>
114
+ <div class="report-form__control report-form__control--slider">
115
+ <el-slider v-model="form.rerankThreshold" :min="0" :max="1" :step="0.1" :show-tooltip="false" />
116
+ <input v-model.number="form.rerankThreshold" type="number" min="0" max="1" step="0.1"
117
+ class="report-slider-num" />
118
+ </div>
119
+ </div>
120
+ </div>
121
+
122
+ <p v-if="form.searchMode === 'Vector Search'" class="report-form__hint">
123
+ 搜尋模式為「Vector Search」時,權重與 Rerank 相關參數將被忽略。
124
+ </p>
125
+ </section>
126
+
127
+ <ReportSourcesPanel sync-workflow />
128
+ </el-card>
129
+
130
+ <footer class="report-center-footer">
131
+ <button type="button" class="app-btn app-btn--wide" @click="selectTemplate">選擇模板</button>
132
+ </footer>
133
+ </div>
134
+ </template>
135
+
136
+ <script setup lang="ts">
137
+ import { ElInput, ElOption, ElSelect, ElSlider, ElSwitch } from 'element-plus'
138
+ import { computed, reactive, ref, watch } from 'vue'
139
+ import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
140
+ import ReportEdit from './components/reportEdit.vue'
141
+ import ReportSourcesPanel from './components/ReportSourcesPanel.vue'
142
+ import { useReportWorkflow } from './useReportWorkflow'
143
+ import {
144
+ createReportEditRouteSyncActions,
145
+ isReportEditRoute,
146
+ navigateToReportEdit,
147
+ syncWorkflowFromReportEditRoute,
148
+ } from './utils/report-edit-route'
149
+
150
+ const WORDS_PER_CHAPTER_MIN = 50
151
+ const WORDS_PER_CHAPTER_MAX = 5000
152
+
153
+ interface ManagementOption {
154
+ id: string
155
+ name: string
156
+ }
157
+
158
+ const MOCK_INDUSTRIES: ManagementOption[] = [
159
+ { id: 'ind-1', name: '新能源汽車' },
160
+ { id: 'ind-2', name: '新能源發電' },
161
+ { id: 'ind-3', name: '數據中心' },
162
+ { id: 'ind-4', name: '本港房地產' },
163
+ { id: 'ind-5', name: '內地房地產' },
164
+ { id: 'ind-6', name: '香港股票市場' },
165
+ { id: 'ind-7', name: '戰略新興產業' },
166
+ { id: 'ind-8', name: '半導體' },
167
+ { id: 'ind-9', name: '消費電子' },
168
+ { id: 'ind-10', name: '醫療健康' },
169
+ { id: 'ind-11', name: '生物醫藥' },
170
+ { id: 'ind-12', name: '物流運輸' },
171
+ { id: 'ind-13', name: '航空旅遊' },
172
+ { id: 'ind-14', name: '零售消費' },
173
+ { id: 'ind-15', name: '食品飲料' },
174
+ { id: 'ind-16', name: '化工材料' },
175
+ { id: 'ind-17', name: '機械裝備' },
176
+ { id: 'ind-18', name: '建築工程' },
177
+ { id: 'ind-19', name: '電信運營' },
178
+ { id: 'ind-20', name: '互聯網科技' },
179
+ ]
180
+
181
+ const MOCK_RESEARCH_POINTS: ManagementOption[] = [
182
+ { id: 'res-1', name: '中國香港' },
183
+ { id: 'res-2', name: '中國內地' },
184
+ { id: 'res-3', name: '東南亞' },
185
+ { id: 'res-4', name: '歐美市場' },
186
+ ]
187
+
188
+ const route = useRoute()
189
+ const router = useRouter()
190
+ const workflow = useReportWorkflow()
191
+ const isReportEditView = computed(() => isReportEditRoute(route))
192
+ const advancedOpen = ref(false)
193
+ const reportNameTouched = ref(false)
194
+
195
+ const languageOptions = [
196
+ { value: 'zh-Hant', label: '繁體中文' },
197
+ { value: 'zh-Hans', label: '簡體中文' },
198
+ { value: 'en', label: 'English' },
199
+ ] as const
200
+
201
+ const searchModeOptions = ['Hybrid Search', 'Vector Search'] as const
202
+
203
+ const modelOptions = [
204
+ { id: 'model-1', name: 'GPT-4o' },
205
+ { id: 'model-2', name: 'GPT-4.1' },
206
+ { id: 'model-3', name: 'Claude 3.5 Sonnet' },
207
+ { id: 'model-4', name: 'Qwen-Max' },
208
+ ] as const
209
+
210
+ const industryOptions = MOCK_INDUSTRIES
211
+ const researchFocusOptions = MOCK_RESEARCH_POINTS
212
+
213
+ const yearOptions = computed(() => {
214
+ const currentYear = new Date().getFullYear()
215
+ return [currentYear - 2, currentYear - 1, currentYear].map((year) => String(year))
216
+ })
217
+
218
+ function buildReportName(industry: string, year: string) {
219
+ const name = industry.trim()
220
+ const y = year.trim()
221
+ if (!name || !y) return ''
222
+ return `${name}行業調研報告 (${y}年)`
223
+ }
224
+
225
+ const form = reactive({
226
+ industry: '',
227
+ researchFocus: '',
228
+ year: String(new Date().getFullYear()),
229
+ reportName: '',
230
+ language: 'zh-Hant',
231
+ wordsPerChapter: '',
232
+ model: '',
233
+ searchMode: 'Hybrid Search',
234
+ weight: 0.5,
235
+ rerank: true,
236
+ rerankTopN: 5,
237
+ rerankThreshold: 0.5,
238
+ })
239
+
240
+ watch(
241
+ () => [form.industry, form.year] as const,
242
+ ([industry, year]) => {
243
+ if (reportNameTouched.value) return
244
+ form.reportName = buildReportName(industry, year)
245
+ },
246
+ { immediate: true },
247
+ )
248
+
249
+ function clampWordsPerChapter() {
250
+ const raw = form.wordsPerChapter.trim()
251
+ if (!raw) {
252
+ form.wordsPerChapter = ''
253
+ return
254
+ }
255
+ const value = Math.round(Number(raw))
256
+ if (Number.isNaN(value)) {
257
+ form.wordsPerChapter = ''
258
+ return
259
+ }
260
+ if (value < WORDS_PER_CHAPTER_MIN) {
261
+ form.wordsPerChapter = String(WORDS_PER_CHAPTER_MIN)
262
+ } else if (value > WORDS_PER_CHAPTER_MAX) {
263
+ form.wordsPerChapter = String(WORDS_PER_CHAPTER_MAX)
264
+ } else {
265
+ form.wordsPerChapter = String(value)
266
+ }
267
+ }
268
+
269
+ function getSelectedSourceCount() {
270
+ return workflow.sourceFiles.value.filter((file) => file.selected).length
271
+ }
272
+
273
+ function selectTemplate() {
274
+ workflow.patchDraft({
275
+ industry: form.industry,
276
+ researchFocus: form.researchFocus,
277
+ year: form.year,
278
+ reportName: form.reportName,
279
+ language: form.language,
280
+ wordsPerChapter: form.wordsPerChapter,
281
+ model: form.model,
282
+ searchMode: form.searchMode,
283
+ weight: form.weight,
284
+ rerank: form.rerank,
285
+ rerankTopN: form.rerankTopN,
286
+ rerankThreshold: form.rerankThreshold,
287
+ sourceCount: getSelectedSourceCount(),
288
+ })
289
+ navigateToReportEdit(router, 'select')
290
+ }
291
+
292
+ watch(
293
+ () => [
294
+ route.query.pageName,
295
+ route.query.step,
296
+ route.query.templateId,
297
+ route.query.rightPanel,
298
+ ] as const,
299
+ () => {
300
+ syncWorkflowFromReportEditRoute(route, createReportEditRouteSyncActions(workflow))
301
+ },
302
+ { immediate: true },
303
+ )
304
+
305
+ onBeforeRouteUpdate((to) => {
306
+ syncWorkflowFromReportEditRoute(to, createReportEditRouteSyncActions(workflow))
307
+ })
308
+ </script>
309
+
310
+ <style scoped>
311
+ .report-center-page {
312
+ /* 與 model-manager 模板列表卡片邊框一致 */
313
+ --report-center-board-border-color: #ebeef5;
314
+ --app-btn-color: #fff;
315
+ --app-btn-color-hover: #c53355;
316
+ --app-btn-bg: #c53355;
317
+ --app-btn-bg-hover: #fff;
318
+ --app-btn-border: #c53355;
319
+ --app-btn-border-hover: #c53355;
320
+ --app-btn-radius: 5px;
321
+ --app-btn-font-size: clamp(14px, calc(16 * 100vw / 1920), 16px);
322
+ --app-btn-font-weight: 600;
323
+ --app-btn-padding-y: 8px;
324
+ --app-btn-padding-x: 15px;
325
+ --app-btn-min-width: 120px;
326
+ display: flex;
327
+ flex-direction: column;
328
+ gap: 20px;
329
+ box-sizing: border-box;
330
+ min-height: 100dvh;
331
+ padding: 32px 24px 24px 30px;
332
+ color: #1a1a1a;
333
+ background: #fff;
334
+ }
335
+
336
+ .report-center-hero {
337
+ text-align: center;
338
+ padding: 8px 0 4px;
339
+ }
340
+
341
+ .report-center-hero__title {
342
+ margin: 0 0 8px;
343
+ font-size: 22px;
344
+ font-weight: 700;
345
+ color: #1a1a1a;
346
+ letter-spacing: 0.02em;
347
+ }
348
+
349
+ .report-center-hero__subtitle {
350
+ margin: 0;
351
+ font-size: 14px;
352
+ color: #4e5969;
353
+ }
354
+
355
+ .report-center-board {
356
+ flex: 1 0 auto;
357
+ border: none;
358
+ background: transparent;
359
+ --el-card-border-color: transparent;
360
+ }
361
+
362
+ .report-center-board :deep(.el-card__body) {
363
+ display: grid;
364
+ grid-template-columns: 1fr 1fr;
365
+ gap: 20px;
366
+ align-items: stretch;
367
+ box-sizing: border-box;
368
+ min-height: 68dvh;
369
+ height: auto;
370
+ padding: 0;
371
+ background: transparent;
372
+ }
373
+
374
+ .report-center-panel {
375
+ display: flex;
376
+ flex-direction: column;
377
+ height: 100%;
378
+ padding: 20px 24px 24px;
379
+ min-width: 0;
380
+ border: 1px solid var(--report-center-board-border-color);
381
+ border-radius: 8px;
382
+ background: #fff;
383
+ box-sizing: border-box;
384
+ }
385
+
386
+ .report-center-panel__title {
387
+ margin: 0 0 16px;
388
+ font-size: 15px;
389
+ font-weight: 700;
390
+ color: #1a1a1a;
391
+ }
392
+
393
+ /* —— 表單 —— */
394
+ .report-form {
395
+ display: flex;
396
+ flex-direction: column;
397
+ gap: 10px;
398
+ }
399
+
400
+ .report-form--advanced {
401
+ margin-top: 4px;
402
+ }
403
+
404
+ .report-form__row {
405
+ display: grid;
406
+ grid-template-columns: 120px 1fr;
407
+ align-items: center;
408
+ gap: 12px;
409
+ }
410
+
411
+ .report-form__row--slider {
412
+ align-items: center;
413
+ }
414
+
415
+ .report-form__row--textarea {
416
+ align-items: flex-start;
417
+ margin-bottom: 18px;
418
+ }
419
+
420
+ .report-form__row--textarea .report-form__label {
421
+ padding-top: 6px;
422
+ }
423
+
424
+ .report-form__label {
425
+ font-size: 13px;
426
+ color: #1a1a1a;
427
+ white-space: nowrap;
428
+ }
429
+
430
+ .report-form__control--radio {
431
+ display: flex;
432
+ flex-wrap: wrap;
433
+ gap: 16px;
434
+ }
435
+
436
+ .report-form__control--slider {
437
+ display: flex;
438
+ align-items: center;
439
+ gap: 12px;
440
+ }
441
+
442
+ .report-form__control--slider :deep(.el-slider) {
443
+ flex: 1;
444
+ min-width: 0;
445
+ --el-slider-main-bg-color: #c53355;
446
+ --el-slider-runway-bg-color: #e8eaec;
447
+ --el-slider-button-size: 14px;
448
+ }
449
+
450
+ .report-radio {
451
+ display: inline-flex;
452
+ align-items: center;
453
+ gap: 6px;
454
+ font-size: 13px;
455
+ cursor: pointer;
456
+ }
457
+
458
+ .report-radio input {
459
+ accent-color: #c53355;
460
+ }
461
+
462
+ .report-input {
463
+ width: 100%;
464
+ height: 32px;
465
+ padding: 4px 10px;
466
+ border: 1px solid #c9cdd4;
467
+ border-radius: 2px;
468
+ font-size: 13px;
469
+ color: #1a1a1a;
470
+ background: #fff;
471
+ box-sizing: border-box;
472
+ outline: none;
473
+ }
474
+
475
+ .report-input:focus {
476
+ border-color: #c53355;
477
+ }
478
+
479
+ .report-form__select {
480
+ width: 100%;
481
+ }
482
+
483
+ .report-form__select :deep(.el-select__wrapper) {
484
+ min-height: 32px;
485
+ padding: 4px 10px;
486
+ border-radius: 2px;
487
+ box-shadow: 0 0 0 1px #c9cdd4 inset;
488
+ font-size: 13px;
489
+ }
490
+
491
+ .report-form__select :deep(.el-select__wrapper.is-focused) {
492
+ box-shadow: 0 0 0 1px #c53355 inset;
493
+ }
494
+
495
+ .report-form__textarea-box {
496
+ position: relative;
497
+ width: 100%;
498
+ }
499
+
500
+ .report-form__textarea {
501
+ width: 100%;
502
+ }
503
+
504
+ .report-form__textarea :deep(.el-textarea__inner) {
505
+ min-height: 56px;
506
+ padding: 6px 10px 22px;
507
+ border-radius: 2px;
508
+ box-shadow: 0 0 0 1px #c9cdd4 inset;
509
+ font-size: 13px;
510
+ color: #1a1a1a;
511
+ line-height: 1.5;
512
+ }
513
+
514
+ .report-form__textarea :deep(.el-textarea__inner:focus) {
515
+ box-shadow: 0 0 0 1px #c53355 inset;
516
+ }
517
+
518
+ .report-form__textarea-hint {
519
+ position: absolute;
520
+ right: 0;
521
+ bottom: -18px;
522
+ font-size: 12px;
523
+ line-height: 1;
524
+ color: #909399;
525
+ pointer-events: none;
526
+ user-select: none;
527
+ }
528
+
529
+ .report-form__switch {
530
+ --el-switch-on-color: #c53355;
531
+ --el-switch-off-color: #fff;
532
+ }
533
+
534
+ .report-form__switch :deep(.el-switch__core) {
535
+ border: 1px solid #c9cdd4;
536
+ }
537
+
538
+ .report-form__switch :deep(.el-switch.is-checked .el-switch__core) {
539
+ border-color: #c53355;
540
+ }
541
+
542
+ .report-input--select {
543
+ appearance: auto;
544
+ cursor: pointer;
545
+ }
546
+
547
+ .report-slider-num {
548
+ width: 52px;
549
+ height: 28px;
550
+ padding: 2px 6px;
551
+ border: 1px solid #c9cdd4;
552
+ border-radius: 2px;
553
+ font-size: 13px;
554
+ text-align: center;
555
+ flex-shrink: 0;
556
+ }
557
+
558
+ .report-advanced-toggle {
559
+ display: flex;
560
+ align-items: center;
561
+ justify-content: space-between;
562
+ width: 100%;
563
+ margin: 16px 0 8px;
564
+ padding: 0;
565
+ border: 0;
566
+ background: none;
567
+ font-size: 14px;
568
+ font-weight: 700;
569
+ color: #1a1a1a;
570
+ cursor: pointer;
571
+ }
572
+
573
+ .report-advanced-toggle__arrow {
574
+ display: inline-block;
575
+ width: 0;
576
+ height: 0;
577
+ border-left: 5px solid transparent;
578
+ border-right: 5px solid transparent;
579
+ border-bottom: 7px solid #c53355;
580
+ transition: transform 0.2s ease;
581
+ }
582
+
583
+ .report-advanced-toggle__arrow.is-open {
584
+ transform: rotate(180deg);
585
+ }
586
+
587
+ .report-form__hint {
588
+ margin: 12px 0 0;
589
+ font-size: 12px;
590
+ color: #86909c;
591
+ }
592
+
593
+ .report-center-footer {
594
+ flex-shrink: 0;
595
+ display: flex;
596
+ justify-content: center;
597
+ margin-top: auto;
598
+ padding: 4px 0 8px;
599
+ background: #fff;
600
+ }
601
+
602
+ .app-btn {
603
+ min-width: var(--app-btn-min-width);
604
+ padding: var(--app-btn-padding-y) var(--app-btn-padding-x);
605
+ border: 1px solid var(--app-btn-border);
606
+ border-radius: var(--app-btn-radius);
607
+ background: var(--app-btn-bg);
608
+ color: var(--app-btn-color);
609
+ font-size: var(--app-btn-font-size);
610
+ font-weight: var(--app-btn-font-weight);
611
+ line-height: 1.4;
612
+ font-family: inherit;
613
+ cursor: pointer;
614
+ transition: all 0.2s ease;
615
+ }
616
+
617
+ .app-btn:hover {
618
+ background: var(--app-btn-bg-hover);
619
+ border-color: var(--app-btn-border-hover);
620
+ color: var(--app-btn-color-hover);
621
+ }
622
+
623
+ .app-btn--wide {
624
+ min-width: 148px;
625
+ }
626
+
627
+ @media (max-width: 960px) {
628
+ .report-center-board :deep(.el-card__body) {
629
+ grid-template-columns: 1fr;
630
+ gap: 20px;
631
+ }
632
+
633
+ .report-center-panel {
634
+ height: auto;
635
+ }
636
+
637
+ .report-center-board :deep(.el-card__body) {
638
+ min-height: auto;
639
+ }
640
+
641
+ .report-form__row {
642
+ grid-template-columns: 1fr;
643
+ gap: 4px;
644
+ }
645
+ }
646
+ </style>