|
|
@@ -1,5 +1,5 @@
|
|
|
<template>
|
|
|
- <div class="teacher-dashboard" v-show="showDataBoard">
|
|
|
+ <div class="teacher-dashboard" v-show="showDataBoard" v-loading="loading">
|
|
|
<!-- 顶部导航栏 -->
|
|
|
<nav class="nav-bar">
|
|
|
<div class="nav-container">
|
|
|
@@ -19,9 +19,9 @@
|
|
|
>
|
|
|
<el-option
|
|
|
v-for="cls in classList"
|
|
|
- :key="cls"
|
|
|
- :label="cls"
|
|
|
- :value="cls"
|
|
|
+ :key="cls.id"
|
|
|
+ :label="cls.name"
|
|
|
+ :value="cls.id"
|
|
|
/>
|
|
|
</el-select>
|
|
|
</div>
|
|
|
@@ -48,21 +48,21 @@
|
|
|
<h2 class="section-heading">课程概况</h2>
|
|
|
</div>
|
|
|
<div class="course-info-block">
|
|
|
- <h1 class="course-name">{{ courseInfo.title }}</h1>
|
|
|
- <div class="course-meta-row">
|
|
|
- <div class="course-meta-item">
|
|
|
+ <h1 class="course-name">{{ courseDetail.title }}</h1>
|
|
|
+ <div class="course-meta-row" v-if="grade || subject">
|
|
|
+ <div class="course-meta-item" v-if="grade">
|
|
|
<svg class="meta-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
|
<path d="M22 10L12 5 2 10l10 5 10-5z" />
|
|
|
<path d="M6 12v5c0 1 2.5 3 6 3s6-2 6-3v-5" />
|
|
|
</svg>
|
|
|
- <span class="meta-label">年级:</span>{{ courseInfo.grade }}
|
|
|
+ <span class="meta-label">年级:</span>{{ grade }}
|
|
|
</div>
|
|
|
<span class="meta-divider">|</span>
|
|
|
- <div class="course-meta-item">
|
|
|
+ <div class="course-meta-item" v-if="subject">
|
|
|
<svg class="meta-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
|
<path d="M4 19.5A2.5 2.5 0 016.5 17H20M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z" />
|
|
|
</svg>
|
|
|
- <span class="meta-label">学科:</span>{{ courseInfo.subject }}
|
|
|
+ <span class="meta-label">学科:</span>{{ subject }}
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="course-extra-row">
|
|
|
@@ -70,14 +70,14 @@
|
|
|
<svg class="extra-icon" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
|
<path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75M13 8a4 4 0 11-8 0 4 4 0 018 0z" />
|
|
|
</svg>
|
|
|
- <span>班级: {{ courseInfo.class }}</span>
|
|
|
+ <span>班级: {{ getClassClassName }}</span>
|
|
|
</div>
|
|
|
<div class="course-extra-item">
|
|
|
<svg class="extra-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
|
<circle cx="12" cy="12" r="10" />
|
|
|
<path d="M12 6v6l4 2" />
|
|
|
</svg>
|
|
|
- <span>上课时间: {{ courseInfo.classTime }}</span>
|
|
|
+ <span>上课时间: {{ classstime }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -335,23 +335,29 @@
|
|
|
<script>
|
|
|
export default {
|
|
|
name: 'TeacherDataBoard',
|
|
|
+ props: {
|
|
|
+ courseDetail: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({})
|
|
|
+ },
|
|
|
+ classList: {
|
|
|
+ type: Array,
|
|
|
+ default: () => []
|
|
|
+ },
|
|
|
+ pptJSON: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({})
|
|
|
+ },
|
|
|
+ },
|
|
|
data() {
|
|
|
return {
|
|
|
- selectedClass: '测试1班',
|
|
|
- classList: ['测试1班', '高一(3)班'],
|
|
|
+ selectedClass: '',
|
|
|
searchKeyword: '',
|
|
|
chartInstance: null,
|
|
|
- courseInfo: {
|
|
|
- title: '人工智能普及课程',
|
|
|
- grade: '高中一年级',
|
|
|
- subject: '信息技术',
|
|
|
- class: '一班',
|
|
|
- classTime: '2026-05-13 17:08'
|
|
|
- },
|
|
|
kpis: [
|
|
|
{ value: '90%', suffix: '', label: '出勤率', sub: '(45/50人)', color: '#F97316' },
|
|
|
{ value: '89%', suffix: '', label: '平均提交率', sub: '(313次提交)', color: '#10B981' },
|
|
|
- { value: '64%', suffix: '', label: '平均正确率', sub: '(最高94%·最低25%)', color: '#10B981' },
|
|
|
+ // { value: '64%', suffix: '', label: '平均正确率', sub: '(最高94%·最低25%)', color: '#10B981' },
|
|
|
{ value: '4', suffix: '轮', label: 'AI 轮次', sub: '(最多7·最少0)', color: '#3B82F6' },
|
|
|
{ value: '47', suffix: '', label: '总互动量', sub: '(总点赞数)', color: '#EC4899' }
|
|
|
],
|
|
|
@@ -435,18 +441,24 @@ export default {
|
|
|
}
|
|
|
],
|
|
|
students: [
|
|
|
- { id: 1, name: '张伟', attention: false, tasksDone: 7, tasksTotal: 7, correctRate: '87%', aiRounds: 6, likes: '5/3' },
|
|
|
- { id: 2, name: '李子涵', attention: false, tasksDone: 7, tasksTotal: 7, correctRate: '92%', aiRounds: 7, likes: '8/5' },
|
|
|
- { id: 3, name: '王芳', attention: false, tasksDone: 7, tasksTotal: 7, correctRate: '86%', aiRounds: 5, likes: '4/2' },
|
|
|
- { id: 4, name: '陈嘉怡', attention: false, tasksDone: 7, tasksTotal: 7, correctRate: '91%', aiRounds: 5, likes: '6/4' },
|
|
|
- { id: 5, name: '杨浩宇', attention: true, tasksDone: 7, tasksTotal: 7, correctRate: '76%', aiRounds: 7, likes: '2/1' }
|
|
|
+ // { id: 1, name: '张伟', attention: false, tasksDone: 7, tasksTotal: 7, correctRate: '87%', aiRounds: 6, likes: '5/3' },
|
|
|
+ // { id: 2, name: '李子涵', attention: false, tasksDone: 7, tasksTotal: 7, correctRate: '92%', aiRounds: 7, likes: '8/5' },
|
|
|
+ // { id: 3, name: '王芳', attention: false, tasksDone: 7, tasksTotal: 7, correctRate: '86%', aiRounds: 5, likes: '4/2' },
|
|
|
+ // { id: 4, name: '陈嘉怡', attention: false, tasksDone: 7, tasksTotal: 7, correctRate: '91%', aiRounds: 5, likes: '6/4' },
|
|
|
+ // { id: 5, name: '杨浩宇', attention: true, tasksDone: 7, tasksTotal: 7, correctRate: '76%', aiRounds: 7, likes: '2/1' }
|
|
|
],
|
|
|
chartData: {
|
|
|
labels: ['高频参与', '中等频率', '极少参与'],
|
|
|
values: [65, 25, 10],
|
|
|
colors: ['#10B981', '#60A5FA', '#F43F5E']
|
|
|
},
|
|
|
- showDataBoard: false
|
|
|
+ showDataBoard: false,
|
|
|
+ classstime: '',
|
|
|
+ loading: false,
|
|
|
+ grade: '',
|
|
|
+ subject: '',
|
|
|
+ works: [],
|
|
|
+ likesArray: []
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
|
@@ -454,6 +466,10 @@ export default {
|
|
|
if (!this.searchKeyword) return this.students;
|
|
|
const kw = this.searchKeyword.trim().toLowerCase();
|
|
|
return this.students.filter(s => s.name.toLowerCase().includes(kw));
|
|
|
+ },
|
|
|
+ getClassClassName() {
|
|
|
+ let name = this.classList.length ? this.classList.filter(item => item.id == this.selectedClass)[0].name || '' : '';
|
|
|
+ return name;
|
|
|
}
|
|
|
},
|
|
|
mounted() {
|
|
|
@@ -531,11 +547,13 @@ export default {
|
|
|
handleResize() {
|
|
|
if (this.chartInstance) this.chartInstance.resize();
|
|
|
},
|
|
|
- open(){
|
|
|
-
|
|
|
+ open(tcid2){
|
|
|
+ this.selectedClass = tcid2;
|
|
|
this.showDataBoard = true;
|
|
|
+ console.log(this.selectedClass);
|
|
|
this.$nextTick(() => {
|
|
|
this.initChart();
|
|
|
+ this.getDataBoard();
|
|
|
});
|
|
|
},
|
|
|
handleBack() {
|
|
|
@@ -552,6 +570,59 @@ export default {
|
|
|
},
|
|
|
handleStudentReportRow(row) {
|
|
|
this.$emit('student-row-report', row);
|
|
|
+ },
|
|
|
+ getDataBoard() {
|
|
|
+ this.loading = true;
|
|
|
+ const params = {
|
|
|
+ cid: this.courseDetail.courseId,
|
|
|
+ classid: this.selectedClass
|
|
|
+ }
|
|
|
+ this.ajax.get(this.$store.state.api + "getPPTDataBoard", params).then(res => {
|
|
|
+ this.classstime = res.data[0][0].time;
|
|
|
+ this.grade = res.data[1].map(item => item.name).join('、');
|
|
|
+ this.subject = res.data[2].map(item => item.name).join('、');
|
|
|
+ this.students = res.data[3];
|
|
|
+ this.works = res.data[4];
|
|
|
+ this.likesArray = res.data[5];
|
|
|
+ // 出勤率计算
|
|
|
+ // 计算works中不重复的userid数量,再除以学生总数得到出勤率
|
|
|
+ const uniqueUserIds = new Set(this.works.map(work => work.userid));
|
|
|
+ const attendanceCount = uniqueUserIds.size;
|
|
|
+ const totalStudents = this.students.length || 1;
|
|
|
+ const attendanceRate = attendanceCount / totalStudents;
|
|
|
+ this.kpis[0].value = Math.round(attendanceRate * 100) + '%';
|
|
|
+ this.kpis[0].sub = `(${attendanceCount}/${this.students.length}人)`;
|
|
|
+
|
|
|
+ // 平均提交率
|
|
|
+ let pptSlides = this.pptJSON.slides || [];
|
|
|
+ let tools = 0;
|
|
|
+ const notTool = [74, 75, 76, 82];
|
|
|
+ for (let i = 0; i < pptSlides.length; i++) {
|
|
|
+ const hasSubmissionTool = pptSlides[i].elements.some((element) => {
|
|
|
+ return element.type === 'frame' && !notTool.includes(element.toolType);
|
|
|
+ });
|
|
|
+ if (hasSubmissionTool) {
|
|
|
+ tools++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 平均提交率 = 作业总数 / (工具数量 × 参与学生数量)
|
|
|
+ const totalExpectedSubmissions = tools * attendanceCount;
|
|
|
+ const totalWorks = this.works.length;
|
|
|
+ const submitRate = totalExpectedSubmissions > 0 ? Math.round((totalWorks / totalExpectedSubmissions) * 100) : 0;
|
|
|
+ this.kpis[1].value = submitRate + '%';
|
|
|
+ this.kpis[1].sub = `(${totalWorks}次提交)`;
|
|
|
+
|
|
|
+ // AI 轮次
|
|
|
+
|
|
|
+ // 总点赞数
|
|
|
+ const totalLikes = this.likesArray.length;
|
|
|
+ this.kpis[3].value = totalLikes;
|
|
|
+ this.kpis[3].sub = `(${totalLikes}人点赞)`;
|
|
|
+
|
|
|
+ this.loading = false;
|
|
|
+ }).catch(() => {
|
|
|
+ this.loading = false;
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
};
|