bohui-vue 1.0.1

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 (104) hide show
  1. package/README.md +121 -0
  2. package/bin/create-vue-template.js +565 -0
  3. package/package.json +28 -0
  4. package/templates/vue-project/.browserslistrc +3 -0
  5. package/templates/vue-project/.editorconfig +28 -0
  6. package/templates/vue-project/.env.development +2 -0
  7. package/templates/vue-project/.env.production +2 -0
  8. package/templates/vue-project/.eslintrc.cjs +76 -0
  9. package/templates/vue-project/.keep +0 -0
  10. package/templates/vue-project/.node-version +1 -0
  11. package/templates/vue-project/.prettierignore +13 -0
  12. package/templates/vue-project/.prettierrc +20 -0
  13. package/templates/vue-project/.prettierrc.txt +130 -0
  14. package/templates/vue-project/.stylelintrc.json +94 -0
  15. package/templates/vue-project/README.md +24 -0
  16. package/templates/vue-project/babel.config.js +5 -0
  17. package/templates/vue-project/index.html +34 -0
  18. package/templates/vue-project/package.json +75 -0
  19. package/templates/vue-project/public/favicon.ico +0 -0
  20. package/templates/vue-project/public/static/img/ai-default.jpg +0 -0
  21. package/templates/vue-project/public/static/img/image.png +0 -0
  22. package/templates/vue-project/public/static/img/ppt1.png +0 -0
  23. package/templates/vue-project/public/static/img/ppt2.png +0 -0
  24. package/templates/vue-project/public/static/img/ppt3.png +0 -0
  25. package/templates/vue-project/public/static/js/config.js +11 -0
  26. package/templates/vue-project/public/static/js/dataConfig.js +1143 -0
  27. package/templates/vue-project/src/App.vue +10 -0
  28. package/templates/vue-project/src/api/error-handler.ts +60 -0
  29. package/templates/vue-project/src/api/http.ts +254 -0
  30. package/templates/vue-project/src/api/services/aicebd.ts +47 -0
  31. package/templates/vue-project/src/api/services/base.ts +18 -0
  32. package/templates/vue-project/src/api/services/umse.ts +17 -0
  33. package/templates/vue-project/src/assets/font/Alibaba-PuHuiTi-Medium.otf +0 -0
  34. package/templates/vue-project/src/assets/font/Alibaba-PuHuiTi-Regular.otf +0 -0
  35. package/templates/vue-project/src/assets/font/DOUYINSANSBOLD.OTF +0 -0
  36. package/templates/vue-project/src/assets/font/Pangmen-Title.TTF +0 -0
  37. package/templates/vue-project/src/assets/font/font.css +25 -0
  38. package/templates/vue-project/src/assets/iconfont/iconfont.css +402 -0
  39. package/templates/vue-project/src/assets/iconfont/iconfont.js +66 -0
  40. package/templates/vue-project/src/assets/iconfont/iconfont.json +688 -0
  41. package/templates/vue-project/src/assets/iconfont/iconfont.ttf +0 -0
  42. package/templates/vue-project/src/assets/iconfont/iconfont.woff +0 -0
  43. package/templates/vue-project/src/assets/iconfont/iconfont.woff2 +0 -0
  44. package/templates/vue-project/src/assets/images/Click-tap.png +0 -0
  45. package/templates/vue-project/src/assets/images/Effects.png +0 -0
  46. package/templates/vue-project/src/assets/images/bg.png +0 -0
  47. package/templates/vue-project/src/assets/images/erCode.png +0 -0
  48. package/templates/vue-project/src/assets/images/header-bg.png +0 -0
  49. package/templates/vue-project/src/assets/images/logo.png +0 -0
  50. package/templates/vue-project/src/assets/scss/common.scss +530 -0
  51. package/templates/vue-project/src/assets/styles/element-overrides.css +53 -0
  52. package/templates/vue-project/src/assets/styles/reset.css +186 -0
  53. package/templates/vue-project/src/assets/styles/theme.css +100 -0
  54. package/templates/vue-project/src/components/BarChart.vue +238 -0
  55. package/templates/vue-project/src/components/echarts/EChart.vue +140 -0
  56. package/templates/vue-project/src/composables/useTheme.ts +84 -0
  57. package/templates/vue-project/src/main.ts +111 -0
  58. package/templates/vue-project/src/mocks/base.ts +37 -0
  59. package/templates/vue-project/src/mocks/umse.ts +31 -0
  60. package/templates/vue-project/src/router/index.ts +32 -0
  61. package/templates/vue-project/src/shims-vue.d.ts +19 -0
  62. package/templates/vue-project/src/store/index.ts +18 -0
  63. package/templates/vue-project/src/store/modules/user.ts +85 -0
  64. package/templates/vue-project/src/types/DTO/aicebd.d.ts +60 -0
  65. package/templates/vue-project/src/types/DTO/base.d.ts +26 -0
  66. package/templates/vue-project/src/types/DTO/global.d.ts +48 -0
  67. package/templates/vue-project/src/types/VO/teachingLog.d.ts +15 -0
  68. package/templates/vue-project/src/types/auto-imports.d.ts +73 -0
  69. package/templates/vue-project/src/types/components.d.ts +17 -0
  70. package/templates/vue-project/src/types/element-plus.d.ts +15 -0
  71. package/templates/vue-project/src/types/js-cookie.d.ts +1 -0
  72. package/templates/vue-project/src/types/unocss.d.ts +2 -0
  73. package/templates/vue-project/src/types/vite-plugins.d.ts +3 -0
  74. package/templates/vue-project/src/types/vue-router.d.ts +1 -0
  75. package/templates/vue-project/src/types/window-config.d.ts +12 -0
  76. package/templates/vue-project/src/utils/com-methods.ts +307 -0
  77. package/templates/vue-project/src/utils/echarts.ts +111 -0
  78. package/templates/vue-project/src/utils/number.ts +99 -0
  79. package/templates/vue-project/src/utils/rem.ts +82 -0
  80. package/templates/vue-project/src/utils/responsive.ts +103 -0
  81. package/templates/vue-project/src/utils/time.ts +314 -0
  82. package/templates/vue-project/src/utils/tracker.ts +527 -0
  83. package/templates/vue-project/src/utils/validators.ts +85 -0
  84. package/templates/vue-project/src/utils/window.ts +132 -0
  85. package/templates/vue-project/src/views/home/Home.vue +60 -0
  86. package/templates/vue-project/src/views/home/composables/useUserAuth.ts +13 -0
  87. package/templates/vue-project/src/views/teachingLog/TeachingLog.vue +40 -0
  88. package/templates/vue-project/src/views/teachingLog/__tests__/TeachingEffect.test.ts +96 -0
  89. package/templates/vue-project/src/views/teachingLog/__tests__/TeachingHighlight.test.ts +66 -0
  90. package/templates/vue-project/src/views/teachingLog/__tests__/TeachingLog.test.ts +34 -0
  91. package/templates/vue-project/src/views/teachingLog/components/TeachingEffect.vue +458 -0
  92. package/templates/vue-project/src/views/teachingLog/components/TeachingHighlight.vue +181 -0
  93. package/templates/vue-project/src/views/teachingLog/composables/useEffectTooltip.ts +88 -0
  94. package/templates/vue-project/src/views/teachingLog/composables/useEffectTrendChart.ts +160 -0
  95. package/templates/vue-project/tests/setup.ts +27 -0
  96. package/templates/vue-project/tsconfig.json +24 -0
  97. package/templates/vue-project/tsconfig.node.json +41 -0
  98. package/templates/vue-project/uno.config.ts +84 -0
  99. package/templates/vue-project/vite.config.ts +216 -0
  100. package/templates/vue-project/vue3_ai_prompt.md +652 -0
  101. package/templates/vue-project/vue3_ai_prompt_basic.md +722 -0
  102. package/templates/vue-project/vue3_ai_prompt_full.md +1021 -0
  103. package/templates/vue-project/vue3_ai_prompt_unocss.md +768 -0
  104. package/templates/vue-project//345/267/245/347/250/213/345/214/226/346/250/241/346/235/277/344/273/213/347/273/215.md +463 -0
@@ -0,0 +1,458 @@
1
+ <template>
2
+ <div class="teaching-effect">
3
+ <div class="effect-header">
4
+ <img
5
+ class="effect-icon"
6
+ src="@/assets/images/Click-tap.png"
7
+ alt=""
8
+ />
9
+ <div class="effect-title">教学效果</div>
10
+ </div>
11
+ <div class="effect-stats">
12
+ <div
13
+ v-for="s in effectStats"
14
+ :key="s.label"
15
+ class="stat"
16
+ >
17
+ <div class="stat-label">{{ s.label }}</div>
18
+ <div class="stat-value">{{ s.value }}</div>
19
+ </div>
20
+ </div>
21
+ <div class="effect-trend">
22
+ <div class="trend-title">
23
+ <span class="bullet-bar"></span>
24
+ <span>学生专注度趋势图</span>
25
+ </div>
26
+ <div
27
+ ref="trendEl"
28
+ class="trend-chart"
29
+ ></div>
30
+ </div>
31
+ <div class="decline-section">
32
+ <div class="decline-title">
33
+ <span class="bullet-bar"></span>
34
+ <span>听课效果不佳的教学环节</span>
35
+ </div>
36
+ <div class="timeline">
37
+ <div
38
+ v-for="p in declinePoints"
39
+ :key="p.time"
40
+ class="timeline-item"
41
+ >
42
+ <div class="timeline-time">{{ p.time }}</div>
43
+ <div class="timeline-dot"></div>
44
+ <img
45
+ :src="p.image"
46
+ alt=""
47
+ class="timeline-image"
48
+ @mouseenter="showTooltip(p.description, $event)"
49
+ @mousemove="moveTooltip($event)"
50
+ @mouseleave="hideTooltip()"
51
+ />
52
+ </div>
53
+ </div>
54
+ </div>
55
+ <div
56
+ v-if="showTooltipContent"
57
+ class="tooltip-popup"
58
+ :style="{ left: tooltipX + 'px', top: tooltipY + 'px' }"
59
+ >
60
+ <div class="tooltip-title">
61
+ <span class="bullet-bar"></span>
62
+ <span>教学内容总结</span>
63
+ </div>
64
+ <div class="tooltip-content">{{ tooltipDescription }}</div>
65
+ </div>
66
+ <div class="qr-code">
67
+ <div
68
+ class="title"
69
+ @click="showQRCodeHandler"
70
+ >
71
+ 扫码获取教学日志
72
+ </div>
73
+ <img
74
+ v-show="isShowQRCode"
75
+ src="@/assets/images/erCode.png"
76
+ alt=""
77
+ />
78
+ </div>
79
+ </div>
80
+ </template>
81
+
82
+ <script setup lang="ts">
83
+ import { ref, computed, onMounted, onUnmounted } from "vue";
84
+ import {
85
+ studentListenFocusListAPI,
86
+ getAILessonAttendAPI,
87
+ getStudentListenOverviewAPI,
88
+ } from "@/api/services/aicebd";
89
+ import { useEffectTooltip } from "../composables/useEffectTooltip";
90
+ import { useEffectTrendChart } from "../composables/useEffectTrendChart";
91
+
92
+ const props = defineProps({
93
+ lessonCode: {
94
+ type: String,
95
+ default: "",
96
+ },
97
+ });
98
+
99
+ // 学生出勤信息数据
100
+ const aiLessonAttendanceData = ref<aicebdDto.AILessonAttendance | null>(null);
101
+ async function getAILessonAttendanceData() {
102
+ if (props.lessonCode) {
103
+ const resData = await getAILessonAttendAPI(props.lessonCode);
104
+ aiLessonAttendanceData.value = resData;
105
+ } else {
106
+ aiLessonAttendanceData.value = window.DataConfig?.teachingEffect?.attendanceData || {
107
+ attendanceStudentNum: 0,
108
+ attendanceStudentRate: 0,
109
+ frontRowSeats: 0,
110
+ frontStudentNum: 0,
111
+ frontStudentRate: 0,
112
+ lateNum: 0,
113
+ leaveEarlyNum: 0,
114
+ lessonCode: props.lessonCode,
115
+ shouldStudentNum: 0,
116
+ totalStudentSeats: 0,
117
+ };
118
+ }
119
+ }
120
+
121
+ // 学生整体专注度数据
122
+ const studentListenOverviewData = ref<aicebdDto.StudentListenOverview | null>(null);
123
+ async function getStudentListenOverviewData() {
124
+ if (props.lessonCode) {
125
+ const resData = await getStudentListenOverviewAPI(props.lessonCode);
126
+ studentListenOverviewData.value = resData;
127
+ } else {
128
+ studentListenOverviewData.value = window.DataConfig?.teachingEffect?.overviewData || {
129
+ attendanceRate: 0,
130
+ focusAvg: 0,
131
+ heighFocus: 0,
132
+ heighPerformanceTeachingTimeRangePercentage: 0,
133
+ lowFocus: 0,
134
+ lowPerformanceTeachingTimeRangePercentage: 0,
135
+ };
136
+ }
137
+ }
138
+
139
+ // 根据接口数据,计算教学效果统计数据
140
+ const effectStats = computed(() => {
141
+ const attendanceRate = aiLessonAttendanceData.value?.attendanceStudentRate ?? 0;
142
+ const frontRate = aiLessonAttendanceData.value?.frontStudentRate ?? 0;
143
+ const focus = studentListenOverviewData.value?.focusAvg ?? 0;
144
+ return [
145
+ {
146
+ label: "学生出勤率",
147
+ value: `${(attendanceRate * 100).toFixed(2)}%`,
148
+ },
149
+ {
150
+ label: "前排就座率",
151
+ value: `${(frontRate * 100).toFixed(2)}%`,
152
+ },
153
+ { label: "平均专注度", value: `${(focus * 100).toFixed(2)}%` },
154
+ ];
155
+ });
156
+
157
+ // 学生专注度趋势数据
158
+ let studentListenFocusListData = ref<TeachingLogVo.FocusTrendItem[] | null>(null);
159
+ // 教学效果趋势图表
160
+ const { initTrendChart } = useEffectTrendChart();
161
+ const trendEl = ref<HTMLDivElement | null>(null);
162
+ let disposer: { dispose: () => void } | null = null;
163
+
164
+ async function getStudentListenFocusListData() {
165
+ if (props.lessonCode) {
166
+ const resData = await studentListenFocusListAPI(props.lessonCode);
167
+ const sorted = (resData || [])
168
+ .slice()
169
+ .sort(
170
+ (a: aicebdDto.StudentListenFocus, b: aicebdDto.StudentListenFocus) =>
171
+ a.order - b.order
172
+ );
173
+ studentListenFocusListData.value = sorted.map((d: aicebdDto.StudentListenFocus) => ({
174
+ time: d.order * 60,
175
+ focus: d.focusAvg * 100,
176
+ }));
177
+ } else {
178
+ const fallback = (window.DataConfig?.teachingEffect?.focusTrendData ||
179
+ []) as aicebdDto.StudentListenFocus[];
180
+ const sorted = fallback
181
+ .slice()
182
+ .sort(
183
+ (a: aicebdDto.StudentListenFocus, b: aicebdDto.StudentListenFocus) =>
184
+ a.order - b.order
185
+ );
186
+ studentListenFocusListData.value = sorted.map((d: aicebdDto.StudentListenFocus) => ({
187
+ time: d.order * 60,
188
+ focus: d.focusAvg * 100,
189
+ }));
190
+ }
191
+
192
+ if (trendEl.value) {
193
+ const trend = (studentListenFocusListData.value || []).map(
194
+ (d: TeachingLogVo.FocusTrendItem) => [d.time, d.focus]
195
+ ) as Array<[number, number]>;
196
+ disposer = initTrendChart(trendEl.value, trend);
197
+ }
198
+ }
199
+
200
+ onMounted(() => {
201
+ getAILessonAttendanceData();
202
+ getStudentListenOverviewData();
203
+ getStudentListenFocusListData();
204
+ });
205
+ onUnmounted(() => {
206
+ disposer?.dispose();
207
+ });
208
+
209
+ // 学生专注度下降点数据
210
+ const focusDecrease = window.DataConfig?.teachingEffect?.focusDecrease || [];
211
+ const declinePoints = computed(() =>
212
+ (focusDecrease || []).map((p: TeachingLogVo.FocusDecreaseItem) => ({
213
+ time: p.time,
214
+ image: p.image || "",
215
+ description: p.description || "",
216
+ }))
217
+ );
218
+
219
+ // 学生专注度下降点提示框
220
+ const {
221
+ showTooltipContent,
222
+ tooltipDescription,
223
+ tooltipX,
224
+ tooltipY,
225
+ showTooltip,
226
+ moveTooltip,
227
+ hideTooltip,
228
+ } = useEffectTooltip();
229
+
230
+ // 二维码是否显示
231
+ const isShowQRCode = ref(true);
232
+ function showQRCodeHandler() {
233
+ isShowQRCode.value = !isShowQRCode.value;
234
+ }
235
+ </script>
236
+
237
+ <style scoped lang="scss">
238
+ .teaching-effect {
239
+ position: relative;
240
+ padding: 2rem;
241
+ background-color: #191b1d;
242
+ border-radius: 1.2rem;
243
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
244
+ .effect-header {
245
+ display: flex;
246
+ align-items: center;
247
+ gap: 0.8rem;
248
+ .effect-icon {
249
+ width: 3rem;
250
+ height: 3rem;
251
+ }
252
+ .effect-title {
253
+ color: #fff;
254
+ font-size: 2.8rem;
255
+ font-weight: 600;
256
+ }
257
+ }
258
+ .effect-stats {
259
+ margin-top: 3rem;
260
+ width: calc(100% - 20rem);
261
+ display: grid;
262
+ grid-template-columns: repeat(3, 1fr);
263
+ gap: 1rem;
264
+ .stat {
265
+ display: grid;
266
+ place-items: center;
267
+ row-gap: 1rem;
268
+ position: relative;
269
+ }
270
+ .stat + .stat::before {
271
+ content: "";
272
+ position: absolute;
273
+ left: -0.5rem;
274
+ top: 10%;
275
+ bottom: 10%;
276
+ width: 1px;
277
+ background: #424242;
278
+ }
279
+ .stat-label {
280
+ color: #fff;
281
+ font-size: 2rem;
282
+ }
283
+ .stat-value {
284
+ color: #2ba9ff;
285
+ font-size: 2.6rem;
286
+ font-weight: 700;
287
+ }
288
+ }
289
+
290
+ @media screen and (max-width: 640px) {
291
+ .effect-stats {
292
+ width: calc(100%);
293
+ }
294
+ }
295
+
296
+ .trend-chart {
297
+ margin-top: 2rem;
298
+ width: 100%;
299
+ height: 26vh;
300
+ min-height: 200px;
301
+ background: #1a1b1e;
302
+ }
303
+ .effect-trend {
304
+ margin-top: 3rem;
305
+ .trend-title {
306
+ display: flex;
307
+ align-items: center;
308
+ gap: 0.8rem;
309
+ color: #fff;
310
+ font-size: 2.4rem;
311
+ }
312
+ .bullet-bar {
313
+ width: 4px;
314
+ height: 14px;
315
+ background: #2ba9ff;
316
+ border-radius: 2px;
317
+ }
318
+ }
319
+ .decline-section {
320
+ margin-top: 4rem;
321
+ .decline-title {
322
+ display: flex;
323
+ align-items: center;
324
+ gap: 0.8rem;
325
+ color: #fff;
326
+ font-size: 2.4rem;
327
+ }
328
+ .bullet-bar {
329
+ width: 4px;
330
+ height: 14px;
331
+ background: #2ba9ff;
332
+ border-radius: 2px;
333
+ }
334
+ .timeline {
335
+ margin-top: 1rem;
336
+ display: flex;
337
+ flex-wrap: nowrap;
338
+ overflow-x: auto;
339
+ padding-bottom: 0.4rem;
340
+ align-items: flex-start;
341
+ }
342
+ .timeline-item {
343
+ position: relative;
344
+ display: grid;
345
+ justify-items: center;
346
+ gap: 0.6rem;
347
+ padding: 1rem;
348
+ background: #1a1b1e;
349
+ }
350
+ .timeline-item::after {
351
+ content: "";
352
+ position: absolute;
353
+ left: 0;
354
+ right: 0;
355
+ top: 5.2rem;
356
+ height: 1px;
357
+ background: rgba(43, 169, 255, 0.5);
358
+ }
359
+ .timeline-time {
360
+ color: #66b4ff;
361
+ font-size: 2rem;
362
+ }
363
+ .timeline-dot {
364
+ width: 1.2rem;
365
+ height: 1.2rem;
366
+ border-radius: 50%;
367
+ background: #2ba9ff;
368
+ }
369
+ .timeline-image {
370
+ width: 26.5vw;
371
+ min-width: 20rem;
372
+ max-width: 250px;
373
+
374
+ // height: 10rem;
375
+ object-fit: cover;
376
+ border-radius: 3px;
377
+ border: 1px solid #414141;
378
+ img {
379
+ border-radius: 3px;
380
+ }
381
+ }
382
+ }
383
+ .tooltip-popup {
384
+ position: absolute;
385
+ min-width: 30rem;
386
+ max-width: 36rem;
387
+ padding: 1.2rem 1.4rem;
388
+ background: #000;
389
+ color: #fff;
390
+ border-radius: 6px;
391
+ box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
392
+ z-index: 1000;
393
+ .tooltip-title {
394
+ display: flex;
395
+ align-items: center;
396
+ gap: 8px;
397
+ font-size: 1.8rem;
398
+ margin-bottom: 0.6rem;
399
+ }
400
+ .bullet-bar {
401
+ width: 4px;
402
+ height: 14px;
403
+ background: #2ba9ff;
404
+ border-radius: 2px;
405
+ }
406
+ .tooltip-content {
407
+ font-size: 1.6rem;
408
+ line-height: 1.8;
409
+ color: #e9edf1;
410
+ }
411
+ }
412
+ .qr-code {
413
+ position: absolute;
414
+ top: 0rem;
415
+ right: 0rem;
416
+ display: flex;
417
+ flex-direction: column;
418
+ align-items: center;
419
+ justify-content: center;
420
+ padding: 2rem 3rem;
421
+ background-color: #101012;
422
+ // border: 1px solid #27272c;
423
+ border-radius: 1rem;
424
+ &::before {
425
+ content: "";
426
+ position: absolute;
427
+ top: 0;
428
+ left: -1rem;
429
+ width: 0;
430
+ height: 0;
431
+ border: 1rem solid transparent;
432
+ border-top-color: #101012;
433
+ border-right-color: #101012;
434
+ }
435
+ &::after {
436
+ content: "";
437
+ position: absolute;
438
+ bottom: -1rem;
439
+ right: 0;
440
+ width: 0;
441
+ height: 0;
442
+ border: 1rem solid transparent;
443
+ border-top-color: #101012;
444
+ border-right-color: #101012;
445
+ }
446
+ .title {
447
+ color: #fff;
448
+ font-size: 2rem;
449
+ text-align: center;
450
+ cursor: pointer;
451
+ }
452
+ img {
453
+ margin-top: 1rem;
454
+ height: 15rem;
455
+ }
456
+ }
457
+ }
458
+ </style>
@@ -0,0 +1,181 @@
1
+ <template>
2
+ <div class="teaching-highlight">
3
+ <div class="highlight-header">
4
+ <img
5
+ class="icon-star"
6
+ src="@/assets/images/Effects.png"
7
+ alt=""
8
+ />
9
+ <div class="highlight-title">教学亮点</div>
10
+ </div>
11
+ <div class="highlight-times">
12
+ <div
13
+ v-for="(t, i) in timeRanges"
14
+ :key="i"
15
+ class="time-chip"
16
+ :class="{ active: i === activeIndex }"
17
+ @click="setActive(i)"
18
+ >
19
+ {{ t }}
20
+ </div>
21
+ </div>
22
+ <div class="highlight-body">
23
+ <div class="bullet">
24
+ <span class="bullet-bar"></span>
25
+ <span class="bullet-text">
26
+ 专注度从
27
+ <span class="accent">{{ currentItem?.startFocus }}%</span>
28
+ 持续提升至
29
+ <span class="accent">{{ currentItem?.endFocus }}%</span>
30
+ </span>
31
+ </div>
32
+ <p class="desc">
33
+ {{ currentItem?.trendReason }}
34
+ </p>
35
+ </div>
36
+ </div>
37
+ </template>
38
+
39
+ <script setup lang="ts">
40
+ import { ref, onMounted, computed, watch } from "vue";
41
+ import { formatSeconds } from "@/utils/time";
42
+ import { studentListenPerformanceAnalyzeAPI } from "@/api/services/aicebd";
43
+
44
+ const props = defineProps({
45
+ lessonCode: {
46
+ type: String,
47
+ default: "",
48
+ },
49
+ });
50
+
51
+ // 教学亮点数据
52
+ const upReasonAnalyzeData = ref<aicebdDto.StudentListenPerformanceAnalyzeItem[]>([]);
53
+ async function getStudentListenPerformanceAnalyze() {
54
+ if (props.lessonCode) {
55
+ const resData = await studentListenPerformanceAnalyzeAPI(props.lessonCode);
56
+ upReasonAnalyzeData.value = resData?.upReasonAnalyze || [];
57
+ } else {
58
+ upReasonAnalyzeData.value =
59
+ window.DataConfig?.performanceAnalyzeData?.upReasonAnalyze || [];
60
+ }
61
+ }
62
+
63
+ // 教学亮点时间段列表
64
+ const timeRanges = computed<string[]>(() => {
65
+ return upReasonAnalyzeData.value?.map((i) => {
66
+ return `${formatSeconds(i.startTime / 1000)} - ${formatSeconds(i.endTime / 1000)}`;
67
+ });
68
+ });
69
+
70
+ // 当前选中的教学亮点时间段索引
71
+ const activeIndex = ref<number>(0);
72
+ watch(
73
+ upReasonAnalyzeData,
74
+ () => {
75
+ if (activeIndex.value >= upReasonAnalyzeData.value?.length) {
76
+ activeIndex.value = 0;
77
+ }
78
+ },
79
+ { immediate: true }
80
+ );
81
+
82
+ // 当前选中的教学亮点时间段数据
83
+ const currentItem = computed<aicebdDto.StudentListenPerformanceAnalyzeItem | null>(() => {
84
+ if (!upReasonAnalyzeData.value?.length) return null;
85
+ const _startFocus = upReasonAnalyzeData.value[activeIndex.value]?.startFocus ?? 0;
86
+ const _endFocus = upReasonAnalyzeData.value[activeIndex.value]?.endFocus ?? 0;
87
+ const _currentItem = {
88
+ ...upReasonAnalyzeData.value[activeIndex.value],
89
+ startFocus: Number((_startFocus * 100).toFixed(2)),
90
+ endFocus: Number((_endFocus * 100).toFixed(2)),
91
+ };
92
+ return _currentItem;
93
+ });
94
+ // 设置当前选中的教学亮点时间段索引
95
+ const setActive = (index: number) => {
96
+ activeIndex.value = index;
97
+ };
98
+
99
+ onMounted(() => {
100
+ getStudentListenPerformanceAnalyze();
101
+ });
102
+ </script>
103
+
104
+ <style scoped lang="scss">
105
+ .teaching-highlight {
106
+ margin-top: 3rem;
107
+ padding: 2rem;
108
+ background-color: #191b1d;
109
+ border-radius: 1.2rem;
110
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
111
+ }
112
+ .highlight-header {
113
+ display: flex;
114
+ align-items: center;
115
+ gap: 8px;
116
+ .icon-star {
117
+ width: 3rem;
118
+ height: 3rem;
119
+ }
120
+ .highlight-title {
121
+ color: #fff;
122
+ font-size: 2.8rem;
123
+ font-weight: 600;
124
+ letter-spacing: 0.5px;
125
+ }
126
+ }
127
+ .highlight-times {
128
+ margin-top: 2rem;
129
+ display: flex;
130
+ flex-wrap: nowrap;
131
+ gap: 1rem;
132
+ overflow-x: auto;
133
+ .time-chip {
134
+ white-space: nowrap;
135
+ text-wrap: nowrap;
136
+ border: 1px solid transparent;
137
+ background-color: #282828;
138
+ color: #bebebe;
139
+ padding: 0.6rem 1.2rem;
140
+ font-size: 2rem;
141
+ line-height: 1;
142
+ border-radius: 2rem;
143
+ cursor: pointer;
144
+ transition: all 0.2s ease;
145
+ &.active {
146
+ color: #fff;
147
+ background: rgba(0, 0, 0, 0.2);
148
+ border-color: #3f7af2;
149
+ }
150
+ }
151
+ }
152
+ .highlight-body {
153
+ margin-top: 2rem;
154
+ display: grid;
155
+ gap: 10px;
156
+ .bullet {
157
+ display: flex;
158
+ align-items: center;
159
+ gap: 8px;
160
+ }
161
+ .bullet-bar {
162
+ width: 4px;
163
+ height: 14px;
164
+ background: #2ba9ff;
165
+ border-radius: 2px;
166
+ }
167
+ .bullet-text {
168
+ color: #fff;
169
+ font-size: 2.4rem;
170
+ }
171
+ .accent {
172
+ color: #2ba9ff;
173
+ }
174
+ .desc {
175
+ margin-top: 0.8rem;
176
+ color: #fff;
177
+ font-size: 2rem;
178
+ line-height: 1.8;
179
+ }
180
+ }
181
+ </style>