Просмотр исходного кода

feat: 为数据看板任务表格新增所在页码列并实现动态任务数据加载

新增任务表格的所在页码列,重构任务数据逻辑以动态从PPT幻灯片中提取任务信息,新增getWork方法获取任务详情,修正KPI初始值为0,优化班级名称查找逻辑,同时注释掉旧的静态任务数据便于后续开发。
lsc 1 день назад
Родитель
Сommit
d6305e77bb
1 измененных файлов с 126 добавлено и 82 удалено
  1. 126 82
      src/components/pptEasyClass/dataBoard/teacher.vue

+ 126 - 82
src/components/pptEasyClass/dataBoard/teacher.vue

@@ -165,6 +165,7 @@
                 <tr>
                 <tr>
                   <th class="th-num">#</th>
                   <th class="th-num">#</th>
                   <th>任务与工具</th>
                   <th>任务与工具</th>
+                  <th class="th-center">所在页码</th>
                   <th class="th-center">提交率</th>
                   <th class="th-center">提交率</th>
                   <th class="th-center">正确率</th>
                   <th class="th-center">正确率</th>
                   <th class="th-center">互动次数</th>
                   <th class="th-center">互动次数</th>
@@ -173,31 +174,16 @@
                 </tr>
                 </tr>
               </thead>
               </thead>
               <tbody>
               <tbody>
-                <tr v-for="task in tasks" :key="task.id" class="table-row">
-                  <td class="td-num">{{ task.id }}</td>
+                <tr v-for="(task, idx) in tasks" :key="task.id" class="table-row">
+                  <td class="td-num">{{ idx + 1 }}</td>
                   <td>
                   <td>
                     <div class="task-name">{{ task.name }}</div>
                     <div class="task-name">{{ task.name }}</div>
                     <div class="task-type">
                     <div class="task-type">
-                      <svg
-                        class="task-type-icon"
-                        :style="{ color: task.typeIconColor }"
-                        width="14"
-                        height="14"
-                        viewBox="0 0 24 24"
-                        fill="currentColor"
-                      >
-                        <path :d="task.typeIconPath" />
-                      </svg>
                       {{ task.type }}
                       {{ task.type }}
-                      <!-- <span v-if="task.aiRounds" class="ai-rounds-badge">
-                        <svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor">
-                          <path d="M12 2l1.8 6L20 9.8l-6 1.8L12 18l-1.8-6.4L4 9.8l6.2-1.8z" />
-                        </svg>
-                        {{ task.aiRounds }}
-                      </span> -->
                     </div>
                     </div>
                   </td>
                   </td>
-                  <td class="td-center td-extrabold">{{ task.submitRate }}</td>
+                  <td class="td-center">{{ task.task + 1 }}</td>
+                  <td class="td-center td-extrabold">{{ task.submitRate }}%</td>
                   <td class="td-center">
                   <td class="td-center">
                     <span
                     <span
                       v-if="task.accuracy !== '—'"
                       v-if="task.accuracy !== '—'"
@@ -205,11 +191,11 @@
                     >{{ task.accuracy }}</span>
                     >{{ task.accuracy }}</span>
                     <span v-else class="accuracy-dash">{{ task.accuracy }}</span>
                     <span v-else class="accuracy-dash">{{ task.accuracy }}</span>
                   </td>
                   </td>
-                  <td class="td-center td-bold-700">{{ task.interactions }}</td>
+                  <td class="td-center td-bold-700">{{ task.interactions }}次</td>
                   <!-- <td class="td-center td-medium">{{ task.avgTime }}</td> -->
                   <!-- <td class="td-center td-medium">{{ task.avgTime }}</td> -->
                   <td class="td-insight">
                   <td class="td-insight">
                     <div class="insight-text">
                     <div class="insight-text">
-                      {{ task.insightPrefix }}<strong v-if="task.insightStrong" class="insight-strong">{{ task.insightStrong }}</strong>{{ task.insightSuffix }}
+                      {{ task.aiExplanation }}
                     </div>
                     </div>
                   </td>
                   </td>
                 </tr>
                 </tr>
@@ -355,11 +341,11 @@ export default {
       searchKeyword: '',
       searchKeyword: '',
       chartInstance: null,
       chartInstance: null,
       kpis: [
       kpis: [
-        { value: '90%', suffix: '', label: '出勤率', sub: '(45/50人)', color: '#F97316' },
-        { value: '89%', suffix: '', label: '平均提交率', sub: '(313次提交)', color: '#10B981' },
+        { value: '0', suffix: '', label: '出勤率', sub: '(0/0人)', color: '#F97316' },
+        { value: '0', suffix: '', label: '平均提交率', sub: '(0次提交)', 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' }
+        { value: '0', suffix: '轮', label: 'AI 轮次', sub: '(最多7·最少0)', color: '#3B82F6' },
+        { value: '0', suffix: '', label: '总互动量', sub: '(总点赞数)', color: '#EC4899' }
       ],
       ],
       courseSummary: {
       courseSummary: {
         theme: '探索人工智能的起源、核心技术与未来趋势,建立对AI基础概念的准确认知。',
         theme: '探索人工智能的起源、核心技术与未来趋势,建立对AI基础概念的准确认知。',
@@ -391,54 +377,39 @@ export default {
         }
         }
       ],
       ],
       tasks: [
       tasks: [
-        {
-          id: 1,
-          name: '以下哪项不属于 AI 应用领域',
-          type: '单选题',
-          typeIconPath: 'M12 2a10 10 0 100 20 10 10 0 000-20zm-3 14l-5-5 1.4-1.4L9 13.2l7.6-7.6L18 7l-9 9z',
-          typeIconColor: '#60A5FA',
-          aiRounds: null,
-          submitRate: '86%',
-          accuracy: '60%',
-          accuracyTone: 'tone-orange',
-          interactions: '43 次',
-          avgTime: '1:14',
-          insightPrefix: '概念边界混淆,',
-          insightStrong: '13人错误',
-          insightSuffix: ',建议优先复讲。'
-        },
-        {
-          id: 2,
-          name: 'AI 应用场景排序',
-          type: '排序题',
-          typeIconPath: 'M3 4h2v2H3V4zm4 0h14v2H7V4zM3 11h2v2H3v-2zm4 0h14v2H7v-2zM3 18h2v2H3v-2zm4 0h14v2H7v-2z',
-          typeIconColor: '#A78BFA',
-          aiRounds: null,
-          submitRate: '90%',
-          accuracy: '70%',
-          accuracyTone: 'tone-emerald',
-          interactions: '45 次',
-          avgTime: '3:43',
-          insightPrefix: '弱AI与强AI基本分清,',
-          insightStrong: '35人完全正确',
-          insightSuffix: '。'
-        },
-        {
-          id: 3,
-          name: '什么是神经网络?',
-          type: '开放问答',
-          typeIconPath: 'M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z',
-          typeIconColor: '#818CF8',
-          aiRounds: '4.0轮',
-          submitRate: '90%',
-          accuracy: '—',
-          accuracyTone: '',
-          interactions: '45 次',
-          avgTime: '6:14',
-          insightPrefix: '优秀回答有类比能力,待提升者多停留在表层定义。',
-          insightStrong: '',
-          insightSuffix: ''
-        }
+        // {
+        //   id: 1,
+        //   name: '以下哪项不属于 AI 应用领域',
+        //   type: '单选题',
+        //   submitRate: '86%',
+        //   accuracy: '60%',
+        //   accuracyTone: 'tone-orange',
+        //   interactions: '43 次',
+        //   avgTime: '1:14',
+        //   aiExplanation: '概念边界混淆,',
+        // },
+        // {
+        //   id: 2,
+        //   name: 'AI 应用场景排序',
+        //   type: '排序题',
+        //   submitRate: '90%',
+        //   accuracy: '70%',
+        //   accuracyTone: 'tone-emerald',
+        //   interactions: '45 次',
+        //   avgTime: '3:43',
+        //   aiExplanation: '弱AI与强AI基本分清,',
+        // },
+        // {
+        //   id: 3,
+        //   name: '什么是神经网络?',
+        //   type: '开放问答',
+        //   submitRate: '90%',
+        //   accuracy: '—',
+        //   accuracyTone: '',
+        //   interactions: '45 次',
+        //   avgTime: '6:14',
+        //   aiExplanation: '优秀回答有类比能力,待提升者多停留在表层定义。',
+        // }
       ],
       ],
       students: [
       students: [
         // { id: 1, name: '张伟', attention: false, tasksDone: 7, tasksTotal: 7, correctRate: '87%', aiRounds: 6, likes: '5/3' },
         // { id: 1, name: '张伟', attention: false, tasksDone: 7, tasksTotal: 7, correctRate: '87%', aiRounds: 6, likes: '5/3' },
@@ -458,7 +429,19 @@ export default {
       grade: '',
       grade: '',
       subject: '',
       subject: '',
       works: [],
       works: [],
-      likesArray: []
+      likesArray: [],
+      taskTool: {
+        45: '选择题', 
+        15: '问答题', 
+        73: 'h5网页', 
+        72: '应用中心', 
+        78: '投票', 
+        79: '拍照', 
+        831: 'Kwl表格', 
+        832: '六顶思考帽', 
+        84: '文件上传'
+      },
+      isAi: [45, 15, 72, 831, 832]
     };
     };
   },
   },
   computed: {
   computed: {
@@ -468,8 +451,8 @@ export default {
       return this.students.filter(s => s.name.toLowerCase().includes(kw));
       return this.students.filter(s => s.name.toLowerCase().includes(kw));
     },
     },
     getClassClassName() {
     getClassClassName() {
-      let name = this.classList.length ? this.classList.filter(item => item.id == this.selectedClass)[0].name || '' : '';
-      return name;  
+      const cls = this.classList.find(item => item.id === this.selectedClass);
+      return cls ? cls.name : '';
     }
     }
   },
   },
   mounted() {
   mounted() {
@@ -577,7 +560,7 @@ export default {
         cid: this.courseDetail.courseId,
         cid: this.courseDetail.courseId,
         classid: this.selectedClass
         classid: this.selectedClass
       }
       }
-      this.ajax.get(this.$store.state.api + "getPPTDataBoard", params).then(res => {
+      this.ajax.get(this.$store.state.api + "getPPTDataBoard", params).then(async res => {
         this.classstime = res.data[0][0].time;
         this.classstime = res.data[0][0].time;
         this.grade = res.data[1].map(item => item.name).join('、');
         this.grade = res.data[1].map(item => item.name).join('、');
         this.subject = res.data[2].map(item => item.name).join('、');  
         this.subject = res.data[2].map(item => item.name).join('、');  
@@ -592,16 +575,49 @@ export default {
         const attendanceRate = attendanceCount / totalStudents;
         const attendanceRate = attendanceCount / totalStudents;
         this.kpis[0].value = Math.round(attendanceRate * 100) + '%';
         this.kpis[0].value = Math.round(attendanceRate * 100) + '%';
         this.kpis[0].sub = `(${attendanceCount}/${this.students.length}人)`;
         this.kpis[0].sub = `(${attendanceCount}/${this.students.length}人)`;
-
+        let tasks = []
         // 平均提交率
         // 平均提交率
         let pptSlides = this.pptJSON.slides || [];
         let pptSlides = this.pptJSON.slides || [];
         let tools = 0;
         let tools = 0;
-        const notTool = [74, 75, 76, 82];
+        let toolNum = Object.keys(this.taskTool);
         for (let i = 0; i < pptSlides.length; i++) {
         for (let i = 0; i < pptSlides.length; i++) {
-          const hasSubmissionTool = pptSlides[i].elements.some((element) => {
-            return element.type === 'frame' && !notTool.includes(element.toolType);
+          const hasSubmissionTool = pptSlides[i].elements.filter((element) => {
+            return element.type === 'frame' && toolNum.includes(element.toolType.toString());
           });
           });
-          if (hasSubmissionTool) {
+
+          if (hasSubmissionTool.length > 0) {
+            const match = hasSubmissionTool[0].url.match(/[?&]id=([^&]+)/)
+            let id = match[1];
+            let content = '';
+            if(hasSubmissionTool[0].toolType == 72){
+              id = hasSubmissionTool[0].id;
+            }else {
+              let work = await this.getWork(id);
+              try {
+                let workContent = JSON.parse(work.data[0][0].json);
+                content = workContent;
+              } catch (e) {
+                content = '';
+              }
+              console.log(work);
+            }
+            tasks.push(
+              {
+                id: id,
+                content: content,
+                name: this.taskTool[hasSubmissionTool[0].toolType],
+                type: this.taskTool[hasSubmissionTool[0].toolType],
+                value: hasSubmissionTool.length,
+                url: hasSubmissionTool[0].url,
+                toolType: hasSubmissionTool[0].toolType,
+                task: i,
+                students: [],
+                work: [],
+                interactions: 0,
+                accuracy: '—'
+              }
+            )
+            console.log(hasSubmissionTool);
             tools++;
             tools++;
           }
           }
         }
         }
@@ -619,10 +635,38 @@ export default {
         this.kpis[3].value = totalLikes;
         this.kpis[3].value = totalLikes;
         this.kpis[3].sub = `(${totalLikes}人点赞)`;
         this.kpis[3].sub = `(${totalLikes}人点赞)`;
         
         
+        // 任务概况统计
+        console.log(tasks);
+        console.log(this.works);
+        for(let work = 0; work < this.works.length; work++){
+          for(let task = 0; task < tasks.length; task++){
+            if(this.works[work].task == tasks[task].task && this.works[work].atool == tasks[task].toolType){
+              tasks[task].students.push(this.works[work].userid);
+              tasks[task].work.push(this.works[work].id);
+            }
+          }
+        }
+        for (let like = 0; like < this.likesArray.length; like++){
+          for(let task = 0; task < tasks.length; task++){
+            if(tasks[task].work.includes(this.likesArray[like].workId)){
+              tasks[task].interactions++;
+            }
+          }
+        }
+        for(let task = 0; task < tasks.length; task++){
+          tasks[task].submitRate = Math.round((tasks[task].students.length / totalStudents) * 100);
+        }
+        this.tasks = tasks;
+
         this.loading = false;
         this.loading = false;
       }).catch(() => {
       }).catch(() => {
         this.loading = false;
         this.loading = false;
       })
       })
+    },
+    getWork(id) {
+      return this.ajax.get(this.$store.state.api + "select_workPageById", {
+        id: id,
+      });
     }
     }
   }
   }
 };
 };