#36 问题修复

Merged
LiaoYanZhou merged 6 commits from FanYanPeng/fix/lyz_bug_fix_20260112 into FanYanPeng/host-nju 4 months ago

+ 8 - 0
src/apis/behavior.ts

@@ -11,6 +11,14 @@ const bevaviorApis = {
             }
         })
     },
+    selectBehaviorRecordVersions: (studentId: number, assignmentId :number) => {
+        return axiosInstance.get(`${PREFIX}/versions`, {
+            params: {
+                studentId,
+                assignmentId
+            }
+        })
+    },
     addBehaviorRecord: (assignmentId :number, file) => {
         const formData = new FormData();
         formData.append("assignmentId", assignmentId.toString())

+ 1 - 1
src/router/index.ts

@@ -305,4 +305,4 @@ const router = createRouter({
     return true;
   });
 
-export default router
+export default router

+ 5 - 0
src/types/history.ts

@@ -0,0 +1,5 @@
+export interface BehaviorVersionItem{
+    name:string
+    time:string
+    author:string
+}

+ 1 - 1
src/utils/xunfeiUtil.js

@@ -214,4 +214,4 @@ export function useXfAsr() {
         startRecognition,
         stopRecognition,
     };
-}
+}

+ 101 - 14
src/views/BehaviorPage/BehaviorBoard.vue

@@ -5,6 +5,7 @@ import {computed, onMounted, provide, type Ref, ref, watch} from "vue";
 import {ElLoading, ElMessage} from "element-plus";
 import useApis from "@/apis";
 import {useRoute} from "vue-router";
+import {BehaviorVersionItem} from "@/types/history";
 
 const apis = useApis()
 const route = useRoute()
@@ -29,7 +30,7 @@ const loadingStatus = ref({
   total:false
 })
 provide('loadingStatus',loadingStatus)
-
+const activeNames = ref(['1'])
 const selectedCourse  = ref({courseName: "", courseId: -1});
 const selectedClass  = ref({courseName: "", classId: -1});
 const courseList:Ref<CourseVO[]> = ref([]);
@@ -40,7 +41,6 @@ watch(selectedAssignment,()=>{
 })
 const assignmentList:Ref<AssignmentVO[]> = ref([]);
 const dataIsLoading:Ref<boolean> = ref(true);
-let assignmentData:any[]= []
 
 const emptyText = computed(()=>{
   if (selectedCourse.value.courseId === -1){
@@ -59,11 +59,11 @@ function getCourseList(){
     target: pageHTML,
     background: '#F5F5F59E'
   })
-  
+
   // 从路由参数中获取课程ID和作业ID
   const courseIdFromRoute = route.query.courseId ? Number(route.query.courseId) : null
   const assignmentIdFromRoute = route.query.assignmentId ? Number(route.query.assignmentId) : null
-  
+
   apis.getCourseByTeacherId(0,100)
       .then((res: any)=>{
         if (res&&res.data.code===200){
@@ -91,7 +91,7 @@ function getCourseList(){
             loading.close();
             return;
           }
-          
+
           // 获取选中课程的作业列表
           const targetCourseId = selectedCourse.value.courseId
           apis.getAssignmentByCourseId(targetCourseId,0,1000)
@@ -153,10 +153,10 @@ function getAssignmentList(courseId:number){
     target: pageHTML,
     background: '#F5F5F59E'
   })
-  
+
   // 从路由参数中获取作业ID(仅在用户手动切换课程时不使用)
   const assignmentIdFromRoute = route.query.assignmentId ? Number(route.query.assignmentId) : null
-  
+
   apis.getAssignmentByCourseId(courseId,0,1000)
       .then((res: any)=>{
         if (res&&res.data.code===200){
@@ -241,8 +241,10 @@ watch(currentEngagementIndex,()=>{
   }
 })
 provide('currentEngagement',currentEngagement);
-
-
+watch(currentEngagement,()=>{
+  activeNames.value = []
+  currentVersionIndex.value = 0
+})
 /**
  * 切换作业题目
  * @param value 作业题目
@@ -250,14 +252,15 @@ provide('currentEngagement',currentEngagement);
  */
 function AssignmentChanged(value:AssignmentVO){
   console.log("用户切换了作业")
+  currentVersionIndex.value = 0
   if (value.assignmentId === -1){
     currentEngagementIndex.value = -1;
     return;
   }
-  
+
   // 从路由参数中获取学生ID
   const studentIdFromRoute = route.query.studentId ? Number(route.query.studentId) : null
-  
+
   apis.getEngagementByAssignmentId(value.assignmentId,0,10000)
       .then((res:any) => {
         console.log(res);
@@ -266,7 +269,7 @@ function AssignmentChanged(value:AssignmentVO){
           if (engagements.value&&engagements.value.length>0){
             // 如果路由中有学生ID参数,尝试定位到该学生
             if (studentIdFromRoute) {
-              const targetIndex = engagements.value.findIndex((engagement:any) => 
+              const targetIndex = engagements.value.findIndex((engagement:any) =>
                 engagement.studentId === studentIdFromRoute
               )
               if (targetIndex !== -1) {
@@ -300,10 +303,12 @@ function AssignmentChanged(value:AssignmentVO){
 function assignmentSwitch(direction:number){
   if (direction===1){
     console.log("切换下一个作业")
+    activeNames.value = []
     currentEngagementIndex.value++;
   }
   else if (direction===-1){
     console.log("切换上一个作业")
+    activeNames.value = []
     currentEngagementIndex.value--;
   }
   else{
@@ -417,6 +422,52 @@ function downLoadBehaviorData(){
 const handleClose = (done: () => void) => {
   done()
 }
+
+const dataHistory = ref<BehaviorVersionItem[]>([])
+
+const activeVersion = ref('1')
+function getHistory(array:string[]) {
+  if (array.length == 0)return
+  dataHistory.value = []
+  activeVersion.value = '1'
+  // 生成测试数据
+  getVersionList()
+}
+
+/**
+ * 版本控制
+ */
+const currentVersionIndex:Ref<number> = ref(0)
+provide('currentVersionIndex',currentVersionIndex)
+const versionIsLoading = ref(false)
+const formatDateTime = (isoString: string): string => {
+  let tempDate = new Date(isoString)
+  return `${tempDate.getFullYear()}/${tempDate.getMonth()+1}/${tempDate.getDate()} `+
+  `${tempDate.getHours().toString().padStart(2,'0')}:${tempDate.getMinutes().toString().padStart(2,'0')}:${tempDate.getSeconds().toString().padStart(2,'0')}`
+};
+async function getVersionList(){
+  versionIsLoading.value = true
+  let res = await apis.selectBehaviorRecordVersions(currentEngagement.value.studentId,currentEngagement.value.assignmentId)
+  console.log('版本号',res)
+  if (res && res.data && res.data.code === 200 && Array.isArray(res.data.data?.versions)){
+    let versions = res.data.data.versions as any[]
+    dataHistory.value = versions.map((value)=>{
+      return {
+        name:`版本${value.version}`,
+        time:formatDateTime(value.createdAt),
+        author:currentEngagement.value.studentName
+      }
+    })
+    versionIsLoading.value = false
+  }
+  else{
+    ElMessage.error('获取版本信息失败')
+    versionIsLoading.value = false
+  }
+}
+
+
+
 onMounted(()=>{
   getCourseList();
 })
@@ -499,8 +550,30 @@ onMounted(()=>{
       </div>
       <el-empty v-else :description="emptyText" style="height: 227px" />
     </el-card>
+    <el-card  class="info-card" style="margin-top: 0;max-height: 500px;overflow-y:auto;">
+      <el-collapse v-loading="versionIsLoading" style="border: none;" v-model="activeNames" @change="getHistory">
+        <el-collapse-item id="timeline-collapse" name="1">
+          <template #title>
+            <span  style="font-weight: bold;font-size: 16px">版本选择</span>
+          </template>
+          <el-timeline style="margin-bottom: -25px;margin-left: 10px;margin-right: 10px">
+            <el-timeline-item
+                v-for="(activity, index) in dataHistory"
+                :key="index"
+                :color="index == currentVersionIndex?'#409EFFFF':''"
+                @click="currentVersionIndex = index"
+            >
+              <div :class="{'timeline-item-row':true,'is-active':currentVersionIndex == index}" >
+                <span >{{activity.name}}</span>
+                <span >{{activity.author}}</span>
+                <span>{{activity.time}}</span>
+              </div>
+            </el-timeline-item>
+          </el-timeline>
+        </el-collapse-item>
+      </el-collapse>
+    </el-card>
     <router-view :class="{hidden:!(!dataIsLoading&&currentEngagement)}" >
-
     </router-view>
     <el-dialog
         v-model="dialogVisible"
@@ -536,7 +609,7 @@ onMounted(()=>{
   display: flex;
   flex-direction: column;
   align-items: center;
-  gap: 30px;
+  gap: 15px;
   margin-bottom: 100px;
 }
 .info-card{
@@ -556,4 +629,18 @@ onMounted(()=>{
   gap: 10px;
   margin-top: 50px;
 }
+.timeline-item-row{
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+  & > *{
+    flex: 1;
+  }
+}
+#timeline-collapse > *{
+  border: none;
+}
+.is-active{
+  color: var(--el-color-primary);
+}
 </style>

+ 21 - 193
src/views/BehaviorPage/component/EditCanvas.vue

@@ -4,7 +4,6 @@ import * as echarts from 'echarts';
 import {inject} from "vue-demi";
 import useApis from "@/apis";
 import {type EChartsType} from "echarts";
-import {ElMessage} from "element-plus";
 const props = defineProps<{
   assignmentId:number,
   splitNumber:number,
@@ -16,7 +15,7 @@ let productData = [];
 let cursorData = [];
 let processData = [];
 let noData = ref(false)
-
+let versionIndex = inject('currentVersionIndex') satisfies Ref<number>
 watch(currentEngagement,()=>{
   noData.value = false
   setTimeout(()=>{
@@ -27,8 +26,20 @@ watch(currentEngagement,()=>{
       myChart = echarts.init(chartDom);
     }
     getCanvasData(myChart);
+
   },0)
 });
+
+watch(versionIndex,()=>{
+  let chartDom = document.getElementById('edit-canvas');
+  if (!chartDom)return;
+  let myChart = echarts.getInstanceByDom(chartDom);
+  if (!myChart){
+    myChart = echarts.init(chartDom);
+  }
+  getCanvasData(myChart);
+})
+
 const loadingStatus = inject('loadingStatus') as Ref<{
   edit:boolean,
   pause:boolean,
@@ -40,7 +51,6 @@ function formatTimeWithMilliseconds(stamp:number ){
   const hours = date.getHours().toString().padStart(2, '0');
   const minutes = date.getMinutes().toString().padStart(2, '0');
   const seconds = date.getSeconds().toString().padStart(2, '0');
-  const milliseconds = date.getMilliseconds().toString().padStart(3, '0');
   return `${hours}:${minutes}:${seconds}`;
 }
 function getCanvasData(chart:EChartsType){
@@ -52,10 +62,10 @@ function getCanvasData(chart:EChartsType){
   let assignmentId = currentEngagement.value.assignmentId;
   chart.showLoading({text:'加载中'});
   loadingStatus.value.edit = true;
-  apis.getBehaviorRecordAll(studentId,assignmentId)
+  apis.selectBehaviorRecordVersions(studentId,assignmentId)
   .then((res:any) => {
-    if (res && res.data && res.data.code === 200){
-      let rowData = res.data.data.events as any[];
+    if (res && res.data && res.data.code === 200 && res.data.data && res.data.data.versions&& res.data.data.versions[versionIndex.value]){
+      let rowData = res.data.data.versions[versionIndex.value]?.events satisfies any[];
       console.log("行为数据",rowData);
       cursorData.length=0;
       productData.length = 0;
@@ -186,194 +196,12 @@ function getCanvasData(chart:EChartsType){
       chart.hideLoading();
       loadingStatus.value.edit = false;
     }
+    else{
+      noData.value = true
+      loadingStatus.value.edit = false
+      chart.hideLoading()
+    }
   })
-  //测试用数据
-
-  // productData = [[new Date('2025-07-26 00:0:20'),0],
-  //   [new Date('2025-07-26 00:0:40'),15],
-  //   [new Date('2025-07-26 00:1:0'),15],
-  //   [new Date('2025-07-26 00:1:20'),17],
-  //   [new Date('2025-07-26 00:1:40'),21],
-  //   [new Date('2025-07-26 00:2:0'),39],
-  //   [new Date('2025-07-26 00:2:20'),49],
-  //   [new Date('2025-07-26 00:2:40'),61],
-  //   [new Date('2025-07-26 00:3:0'),71],
-  //   [new Date('2025-07-26 00:3:20'),89],
-  //   [new Date('2025-07-26 00:3:40'),103],
-  //   [new Date('2025-07-26 00:4:0'),109],
-  //   [new Date('2025-07-26 00:4:20'),109],
-  //   [new Date('2025-07-26 00:4:40'),128],
-  //   [new Date('2025-07-26 00:5:0'),143],
-  //   [new Date('2025-07-26 00:5:20'),143],
-  //   [new Date('2025-07-26 00:5:40'),145],
-  //   [new Date('2025-07-26 00:6:0'),160],
-  //   [new Date('2025-07-26 00:6:20'),177],
-  //   [new Date('2025-07-26 00:6:40'),177],
-  //   [new Date('2025-07-26 00:7:0'),196],
-  //   [new Date('2025-07-26 00:7:20'),199],
-  //   [new Date('2025-07-26 00:7:40'),204],
-  //   [new Date('2025-07-26 00:8:0'),209],
-  //   [new Date('2025-07-26 00:8:20'),213],
-  //   [new Date('2025-07-26 00:8:40'),229],
-  //   [new Date('2025-07-26 00:9:0'),245],
-  //   [new Date('2025-07-26 00:9:20'),250],
-  //   [new Date('2025-07-26 00:9:40'),250],
-  //   [new Date('2025-07-26 00:10:0'),265],
-  //   [new Date('2025-07-26 00:10:20'),266],
-  //   [new Date('2025-07-26 00:10:40'),282],
-  //   [new Date('2025-07-26 00:11:0'),291],
-  //   [new Date('2025-07-26 00:11:20'),308],
-  //   [new Date('2025-07-26 00:11:40'),322],
-  //   [new Date('2025-07-26 00:12:0'),335],
-  //   [new Date('2025-07-26 00:12:20'),343],
-  //   [new Date('2025-07-26 00:12:40'),350],
-  //   [new Date('2025-07-26 00:13:0'),354],
-  //   [new Date('2025-07-26 00:13:20'),367],
-  //   [new Date('2025-07-26 00:13:40'),386],
-  //   [new Date('2025-07-26 00:14:0'),392],
-  //   [new Date('2025-07-26 00:14:20'),411],
-  //   [new Date('2025-07-26 00:14:40'),414],
-  //   [new Date('2025-07-26 00:15:0'),431],
-  //   [new Date('2025-07-26 00:15:20'),437],
-  //   [new Date('2025-07-26 00:15:40'),450],
-  //   [new Date('2025-07-26 00:16:0'),453],
-  //   [new Date('2025-07-26 00:16:20'),468],
-  //   [new Date('2025-07-26 00:16:40'),469],
-  //   [new Date('2025-07-26 00:17:0'),486],
-  //   [new Date('2025-07-26 00:17:20'),486],
-  //   [new Date('2025-07-26 00:17:40'),501],
-  //   [new Date('2025-07-26 00:18:0'),508],
-  //   [new Date('2025-07-26 00:18:20'),515],
-  //   [new Date('2025-07-26 00:18:40'),523],
-  //   [new Date('2025-07-26 00:19:0'),524],
-  //   [new Date('2025-07-26 00:19:20'),529],
-  //   [new Date('2025-07-26 00:19:40'),536],
-  //   [new Date('2025-07-26 00:20:0'),554],
-  //   [new Date('2025-07-26 00:20:20'),563],
-  //   [new Date('2025-07-26 00:20:40'),570],
-  //   [new Date('2025-07-26 00:21:0'),589],
-  //   [new Date('2025-07-26 00:21:20'),600],
-  //   [new Date('2025-07-26 00:21:40'),602],
-  //   [new Date('2025-07-26 00:22:0'),602],
-  //   [new Date('2025-07-26 00:22:20'),618],
-  //   [new Date('2025-07-26 00:22:40'),620],
-  //   [new Date('2025-07-26 00:23:0'),636],
-  //   [new Date('2025-07-26 00:23:20'),650],
-  //   [new Date('2025-07-26 00:23:40'),653],
-  //   [new Date('2025-07-26 00:24:0'),668],
-  //   [new Date('2025-07-26 00:24:20'),670],
-  //   [new Date('2025-07-26 00:24:40'),680],
-  //   [new Date('2025-07-26 00:25:0'),680],
-  //   [new Date('2025-07-26 00:25:20'),695],
-  //   [new Date('2025-07-26 00:25:40'),709],
-  //   [new Date('2025-07-26 00:26:0'),714],
-  //   [new Date('2025-07-26 00:26:20'),714],
-  //   [new Date('2025-07-26 00:26:40'),729],
-  //   [new Date('2025-07-26 00:27:0'),743],
-  //   [new Date('2025-07-26 00:27:20'),747],
-  //   [new Date('2025-07-26 00:27:40'),764],
-  //   [new Date('2025-07-26 00:28:0'),783],
-  //   [new Date('2025-07-26 00:28:20'),792],
-  //   [new Date('2025-07-26 00:28:40'),806],
-  //   [new Date('2025-07-26 00:29:0'),817],
-  //   [new Date('2025-07-26 00:29:20'),823],
-  //   [new Date('2025-07-26 00:29:40'),837],
-  //   [new Date('2025-07-26 00:30:0'),854],];
-  // cursorData = [[new Date('2025-07-26 00:0:20'),0],
-  //   [new Date('2025-07-26 00:0:40'),15],
-  //   [new Date('2025-07-26 00:1:0'),15],
-  //   [new Date('2025-07-26 00:1:20'),17],
-  //   [new Date('2025-07-26 00:1:40'),21],
-  //   [new Date('2025-07-26 00:2:0'),39],
-  //   [new Date('2025-07-26 00:2:20'),49],
-  //   [new Date('2025-07-26 00:2:40'),61],
-  //   [new Date('2025-07-26 00:3:0'),71],
-  //   [new Date('2025-07-26 00:3:20'),89],
-  //   [new Date('2025-07-26 00:3:40'),103],
-  //   [new Date('2025-07-26 00:4:0'),109],
-  //   [new Date('2025-07-26 00:4:20'),109],
-  //   [new Date('2025-07-26 00:4:40'),128],
-  //   [new Date('2025-07-26 00:5:0'),143],
-  //   [new Date('2025-07-26 00:5:20'),143],
-  //   [new Date('2025-07-26 00:5:40'),145],
-  //   [new Date('2025-07-26 00:6:0'),160],
-  //   [new Date('2025-07-26 00:6:20'),177],
-  //   [new Date('2025-07-26 00:6:40'),177],
-  //   [new Date('2025-07-26 00:7:0'),196],
-  //   [new Date('2025-07-26 00:7:20'),199],
-  //   [new Date('2025-07-26 00:7:40'),204],
-  //   [new Date('2025-07-26 00:7:41'),150],
-  //   [new Date('2025-07-26 00:7:59'),158],
-  //   [new Date('2025-07-26 00:8:0'),212],
-  //   [new Date('2025-07-26 00:8:20'),213],
-  //   [new Date('2025-07-26 00:8:40'),229],
-  //   [new Date('2025-07-26 00:9:0'),245],
-  //   [new Date('2025-07-26 00:9:20'),250],
-  //   [new Date('2025-07-26 00:9:40'),250],
-  //   [new Date('2025-07-26 00:10:0'),265],
-  //   [new Date('2025-07-26 00:10:20'),266],
-  //   [new Date('2025-07-26 00:10:40'),282],
-  //   [new Date('2025-07-26 00:11:0'),291],
-  //   [new Date('2025-07-26 00:11:20'),308],
-  //   [new Date('2025-07-26 00:11:40'),322],
-  //   [new Date('2025-07-26 00:12:0'),335],
-  //   [new Date('2025-07-26 00:12:20'),343],
-  //   [new Date('2025-07-26 00:12:40'),350],
-  //   [new Date('2025-07-26 00:13:0'),354],
-  //   [new Date('2025-07-26 00:13:20'),367],
-  //   [new Date('2025-07-26 00:13:40'),386],
-  //   [new Date('2025-07-26 00:14:0'),392],
-  //   [new Date('2025-07-26 00:14:20'),411],
-  //   [new Date('2025-07-26 00:14:40'),414],
-  //   [new Date('2025-07-26 00:15:0'),431],
-  //   [new Date('2025-07-26 00:15:20'),437],
-  //   [new Date('2025-07-26 00:15:40'),450],
-  //   [new Date('2025-07-26 00:16:0'),453],
-  //   [new Date('2025-07-26 00:16:20'),468],
-  //   [new Date('2025-07-26 00:16:40'),469],
-  //   [new Date('2025-07-26 00:17:0'),486],
-  //   [new Date('2025-07-26 00:17:20'),486],
-  //   [new Date('2025-07-26 00:17:40'),501],
-  //   [new Date('2025-07-26 00:18:0'),508],
-  //   [new Date('2025-07-26 00:18:20'),515],
-  //   [new Date('2025-07-26 00:18:40'),523],
-  //   [new Date('2025-07-26 00:19:0'),524],
-  //   [new Date('2025-07-26 00:19:20'),529],
-  //   [new Date('2025-07-26 00:19:40'),536],
-  //   [new Date('2025-07-26 00:20:0'),554],
-  //   [new Date('2025-07-26 00:20:20'),563],
-  //   [new Date('2025-07-26 00:20:40'),570],
-  //   [new Date('2025-07-26 00:21:0'),589],
-  //   [new Date('2025-07-26 00:21:20'),600],
-  //   [new Date('2025-07-26 00:21:40'),602],
-  //   [new Date('2025-07-26 00:22:0'),602],
-  //   [new Date('2025-07-26 00:22:20'),618],
-  //   [new Date('2025-07-26 00:22:40'),620],
-  //   [new Date('2025-07-26 00:23:0'),636],
-  //   [new Date('2025-07-26 00:23:20'),650],
-  //   [new Date('2025-07-26 00:23:40'),653],
-  //   [new Date('2025-07-26 00:24:0'),668],
-  //   [new Date('2025-07-26 00:24:20'),670],
-  //   [new Date('2025-07-26 00:24:40'),680],
-  //   [new Date('2025-07-26 00:25:0'),680],
-  //   [new Date('2025-07-26 00:25:20'),695],
-  //   [new Date('2025-07-26 00:25:40'),709],
-  //   [new Date('2025-07-26 00:26:0'),714],
-  //   [new Date('2025-07-26 00:26:20'),714],
-  //   [new Date('2025-07-26 00:26:21'),500],
-  //   [new Date('2025-07-26 00:26:30'),515],
-  //   [new Date('2025-07-26 00:26:39'),515],
-  //   [new Date('2025-07-26 00:26:40'),729],
-  //   [new Date('2025-07-26 00:27:0'),743],
-  //   [new Date('2025-07-26 00:27:20'),747],
-  //   [new Date('2025-07-26 00:27:40'),764],
-  //   [new Date('2025-07-26 00:28:0'),783],
-  //   [new Date('2025-07-26 00:28:20'),792],
-  //   [new Date('2025-07-26 00:28:40'),806],
-  //   [new Date('2025-07-26 00:29:0'),817],
-  //   [new Date('2025-07-26 00:29:20'),823],
-  //   [new Date('2025-07-26 00:29:40'),837],
-  //   [new Date('2025-07-26 00:30:0'),854],];
 }
 
 onMounted(()=>{

+ 27 - 19
src/views/BehaviorPage/component/HeatMap.vue

@@ -85,15 +85,14 @@
 import {ref, computed, onMounted,type Ref, watch} from 'vue'
 import bevaviorApis from '@/apis/behavior'
 import {inject} from "vue-demi";
-import {ElMessage} from "element-plus";
+
 
 // 响应式数据 - 控制UI元素尺寸
 const charWidth = ref(12) // 普通字符宽度(像素)
 const spaceWidth = ref(8) // 空格字符宽度(像素)
 const heatmapRectWidth = ref(8) // 热度图矩形宽度(像素)
 const heatmapRectHeight = ref(12) // 热度图矩形高度(像素)
-const heatmapOffset = ref(5) // 热度图与文本的垂直偏移(像素)
-const lineHeight = ref(30) // 文本行高(像素)
+
 
 // 提示框相关数据
 const tooltipVisible = ref(false)
@@ -132,13 +131,14 @@ const actionData = ref([]);
 // 这里 studentId 和 assignmentId 可根据实际情况传递
 
 const engagement = inject('currentEngagement') as Ref<any>;
+let versionIndex = inject('currentVersionIndex') satisfies Ref<number>
 watch(engagement,async () => {
   try {
     studentId = engagement.value.studentId;
     assignmentId = engagement.value.assignmentId;
-    const res = await bevaviorApis.getBehaviorRecordAll(studentId, assignmentId)
-    if (res.data && res.data.data && Array.isArray(res.data.data.events)) {
-      actionData.value = res.data.data.events
+    const res = await bevaviorApis.selectBehaviorRecordVersions(studentId, assignmentId)
+    if (res.data && res.data.code == 200 && res.data.data && res.data.data.versions && Array.isArray(res.data.data.versions[versionIndex.value].events)) {
+      actionData.value = res.data.data.versions[versionIndex.value].events
     } else {
       actionData.value = []
     }
@@ -150,14 +150,30 @@ watch(engagement,async () => {
 
 let studentId = 16
 let assignmentId = 7
-
+watch(versionIndex,async ()=>{
+  try {
+    studentId = engagement.value.studentId;
+    assignmentId = engagement.value.assignmentId;
+    const res = await bevaviorApis.selectBehaviorRecordVersions(studentId, assignmentId)
+    console.log('热力图',res)
+    if (res.data &&res.data.code == 200 && res.data.data && res.data.data.versions && Array.isArray(res.data.data.versions[versionIndex.value].events)) {
+      actionData.value = res.data.data.versions[versionIndex.value].events
+    } else {
+      actionData.value = []
+    }
+  } catch (e) {
+    actionData.value = []
+    // 可加错误提示
+  }
+})
 onMounted(async () => {
   try {
     studentId = engagement.value.studentId;
     assignmentId = engagement.value.assignmentId;
-    const res = await bevaviorApis.getBehaviorRecordAll(studentId, assignmentId)
-    if (res.data && res.data.data && Array.isArray(res.data.data.events)) {
-      actionData.value = res.data.data.events
+    const res = await bevaviorApis.selectBehaviorRecordVersions(studentId, assignmentId)
+    if (res.data && res.data.code == 200 && res.data.data && res.data.data.versions && res.data.data.versions[versionIndex.value]
+        && Array.isArray(res.data.data.versions[versionIndex.value].events)) {
+      actionData.value = res.data.data.versions[versionIndex.value].events
     } else {
       actionData.value = []
     }
@@ -353,15 +369,7 @@ const hideTooltip = () => {
   border-radius: 2px;
 }
 
-.tooltip {
-  position: absolute;
-  background-color: rgba(0, 0, 0, 0.8);
-  color: white;
-  padding: 5px;
-  border-radius: 3px;
-  font-size: 12px;
-  z-index: 10;
-}
+
 
 .controls {
   font-size: 15px;

+ 32 - 9
src/views/BehaviorPage/component/PauseCanvas.vue

@@ -4,7 +4,6 @@ import * as echarts from 'echarts';
 import {inject} from "vue-demi";
 import useApis from "@/apis";
 import type {EChartsType} from "echarts";
-import {ElMessage} from "element-plus";
 const props = defineProps<{
   assignmentId:number,
   splitNumber:number,
@@ -26,7 +25,6 @@ function formatTimeWithMilliseconds(stamp:number ){
   const hours = date.getHours().toString().padStart(2, '0');
   const minutes = date.getMinutes().toString().padStart(2, '0');
   const seconds = date.getSeconds().toString().padStart(2, '0');
-  const milliseconds = date.getMilliseconds().toString().padStart(3, '0');
   return `${hours}:${minutes}:${seconds}`;
 }
 
@@ -52,6 +50,18 @@ const loadingStatus = inject('loadingStatus') as Ref<{
   process:boolean,
   total:boolean
 }>;
+let versionIndex = inject('currentVersionIndex') satisfies Ref<number>
+watch(versionIndex,()=>{
+  setTimeout(()=>{
+    let chartDom = document.getElementById('pause-canvas');
+    if (!chartDom)return;
+    let myChart = echarts.getInstanceByDom(chartDom);
+    if (!myChart){
+      myChart = echarts.init(chartDom);
+    }
+    getCanvasData(myChart);
+  },0)
+})
 function getCanvasData(chart:EChartsType){
   if (!currentEngagement.value){
     return;
@@ -60,10 +70,10 @@ function getCanvasData(chart:EChartsType){
   let assignmentId = currentEngagement.value.assignmentId;
   chart.showLoading({text:'加载中'});
   loadingStatus.value.pause = true;
-  apis.getBehaviorRecordAll(studentId,assignmentId)
+  apis.selectBehaviorRecordVersions(studentId,assignmentId)
       .then((res:any) => {
-        if (res && res.data && res.data.code === 200){
-          let rowData = res.data.data.events as any[];
+        if (res.data && res.data.code == 200 && res.data.data && res.data.data.versions &&  Array.isArray(res.data.data.versions[versionIndex.value]?.events)){
+          let rowData = res.data.data.versions[versionIndex.value].events as any[];
           console.log("行为数据",rowData);
           let zeroCount =0;
           pauseData.length = 0;
@@ -115,8 +125,12 @@ function getCanvasData(chart:EChartsType){
             },
             yAxis: {
               type:'value',
-              name:'停顿时间(ms)',
-
+              name:'停顿时间(s)',
+              axisLabel:{
+                formatter: function (value:number) {
+                  return (value/1000).toFixed(2) + 's';
+                }
+              }
             },
             tooltip:{
               formatter:(value:any)=>{
@@ -124,7 +138,7 @@ function getCanvasData(chart:EChartsType){
                 let data = value.data[1];
                 return `停顿时刻:${[date.getHours(), date.getMinutes(), date.getSeconds()]
                     .map(num => num.toString().padStart(2, '0'))
-                    .join(':')}\n  停顿时长:${data}ms`
+                    .join(':')}\n  停顿时长:${(data/1000).toFixed(3)}s`
               }
             },
             visualMap: {
@@ -137,8 +151,11 @@ function getCanvasData(chart:EChartsType){
                 color: ['orange', 'red'] // 渐变范围:小值→蓝色,大值→红色
               },
               itemWidth: props.barWidth,
-              bottom:0,
+              bottom:'center',
               right:0,
+              formatter: function (value:number) {
+                return  (value/1000).toFixed(3)+'s'; // 范围标签显示内容。
+              }
             },
             dataZoom: [
               {
@@ -172,6 +189,12 @@ function getCanvasData(chart:EChartsType){
             chart.setOption(option)
           });
         }
+        else{
+          loadingStatus.value.pause = true
+          chart.hideLoading()
+          noData.value = true
+
+        }
       })
 
 }

+ 31 - 4
src/views/BehaviorPage/component/ProcessCanvas.vue

@@ -49,6 +49,18 @@ const limit = {
   max:0,
   min:0
 }
+let versionIndex = inject('currentVersionIndex') satisfies Ref<number>
+watch(versionIndex,()=>{
+  setTimeout(()=>{
+    let chartDom = document.getElementById('process-canvas');
+    if (!chartDom)return;
+    let myChart = echarts.getInstanceByDom(chartDom);
+    if (!myChart){
+      myChart = echarts.init(chartDom);
+    }
+    getCanvasData(myChart);
+  },0)
+})
 function getCanvasData(chart:EChartsType){
   //TODO
   if (!currentEngagement.value){
@@ -58,10 +70,11 @@ function getCanvasData(chart:EChartsType){
   loadingStatus.value.process = true;
   let studentId = currentEngagement.value.studentId;
   let assignmentId = currentEngagement.value.assignmentId;
-  apis.getBehaviorRecordAll(studentId,assignmentId)
+  apis.selectBehaviorRecordVersions(studentId,assignmentId)
       .then((res:any) => {
-        if (res && res.data && res.data.code === 200){
-          let rowData = res.data.data.events as any[];
+        if (res.data && res.data.code == 200 && res.data.data && res.data.data.versions
+            && Array.isArray(res.data.data.versions[versionIndex.value]?.events)){
+          let rowData = res.data.data.versions[versionIndex.value].events as any[];
           console.log("行为数据",rowData);
           //数组初始化
           cursorData.length=0;
@@ -146,8 +159,13 @@ function getCanvasData(chart:EChartsType){
               alignTicks:true
             },{
               type: 'value',
-              name:'停顿时间(ms)',
+              name:'停顿时间(s)',
               position: 'right',
+              axisLabel:{
+                formatter: function (value:number) {
+                  return (value/1000).toFixed(2) + 's';
+                }
+              }
             }],
             tooltip:{
 
@@ -178,6 +196,9 @@ function getCanvasData(chart:EChartsType){
               right:0,
               orient:'horizontal',
               align: 'right',
+              formatter: function (value:number) {
+                return  (value/1000).toFixed(3)+'s'; // 范围标签显示内容。
+              }
             },
             series: [
               {
@@ -234,6 +255,12 @@ function getCanvasData(chart:EChartsType){
             chart.setOption(option)
           });
         }
+        else{
+          loadingStatus.value.process = false
+          chart.hideLoading()
+          noData.value = true
+
+        }
       })
   //测试用数据
 

+ 19 - 6
src/views/BehaviorPage/component/TotalCanvas.vue

@@ -35,6 +35,18 @@ watch(currentEngagement,()=>{
 },{deep:true})
 
 let data = []
+let versionIndex = inject('currentVersionIndex') satisfies Ref<number>
+watch(versionIndex,()=>{
+  setTimeout(()=>{
+    let chartDom = document.getElementById('total-canvas');
+    if (!chartDom)return;
+    let myChart = echarts.getInstanceByDom(chartDom);
+    if (!myChart){
+      myChart = echarts.init(chartDom);
+    }
+    drawChart(myChart);
+  },0)
+})
 function drawChart(chart: EChartsType){
   //TODO
   if (!currentEngagement.value){
@@ -44,21 +56,22 @@ function drawChart(chart: EChartsType){
     text: '加载中'
   });
   loadingStatus.value.total = true;
-  apis.getOverview(currentEngagement.value.studentId,currentEngagement.value.assignmentId)
+  apis.selectBehaviorRecordVersions(currentEngagement.value.studentId,currentEngagement.value.assignmentId)
   .then((res:any) => {
     // console.log(res);
-    if (res&&res.data&&res.data.code === 200){
+    if (res && res.data && res.data.code == 200 && res.data.data && res.data.data.versions&& res.data.data.versions[versionIndex.value]
+        && Array.isArray(res.data.data.versions[versionIndex.value]?.events)){
       noData.value = false
-      if (res.data.data.insertCount == 0 && res.data.data.longPauseCount == 0 && res.data.data.deleteCount == 0){
+      if (!res.data.data.versions[versionIndex.value]){
         noData.value = true;
         chart.hideLoading();
         loadingStatus.value.total = false;
         return
       }
       data = [
-        { value: res.data.data.insertCount, name: '产出行为' },
-        { value: res.data.data.longPauseCount, name: '停顿行为' },
-        { value: res.data.data.deleteCount, name: '修改行为' },
+        { value: res.data.data.versions[versionIndex.value].insertCount, name: '产出行为' },
+        { value: res.data.data.versions[versionIndex.value].longPauseCount, name: '停顿行为' },
+        { value: res.data.data.versions[versionIndex.value].deleteCount, name: '修改行为' },
       ]
       let option = {
         tooltip: {

+ 2 - 2
src/views/BehaviorPage/subPage/BehaviorHome.vue

@@ -78,7 +78,7 @@ function toDetail(index:number){
     </template>
     <div style="width: 100%;height: 350px">
       <EditCanvas
-          :split-number="4"
+          :split-number="3"
           :assignment-id="1"/>
     </div>
   </el-card>
@@ -97,7 +97,7 @@ function toDetail(index:number){
     </template>
     <div style="width: 100%;height: 350px">
       <ProcessCanvas
-          :split-number="4"
+          :split-number="3"
           :assignment-id="1"/>
     </div>
   </el-card>

+ 8 - 54
src/views/BehaviorPage/subPage/EditAnalysis.vue

@@ -3,7 +3,6 @@ import "./TotalAnalysis.scss"
 import EditCanvas from "@/views/BehaviorPage/component/EditCanvas.vue";
 import {onMounted, type Ref, ref, watch} from "vue";
 import {inject} from "vue-demi";
-import * as echarts from "echarts";
 import useApis from "@/apis";
 let tableData = ref([])
 
@@ -23,57 +22,12 @@ function formatTimeWithMilliseconds(stamp:number ){
   const milliseconds = date.getMilliseconds().toString().padStart(3, '0');
   return `${hours}:${minutes}:${seconds}.${milliseconds}`;
 }
-
+let versionIndex = inject('currentVersionIndex') satisfies Ref<number>
+watch(versionIndex,()=>{
+  getTableData();
+})
 function getTableData() {
   tableData.value = [
-    {
-      revision: 0,
-      type: "Normal Production",
-      content: "This is a test",
-      edits: "???",
-      start: "125",
-      end: "145",
-      duration: "10s",
-      beginPos: "0",
-      endPos: "10",
-      length: "10",
-    },
-    {
-      revision: 1,
-      type: "Delete",
-      content: "This is a test",
-      edits: "???",
-      start: "125",
-      end: "145",
-      duration: "10s",
-      beginPos: "0",
-      endPos: "10",
-      length: "10",
-    },
-    {
-      revision: 0,
-      type: "Normal Production",
-      content: "This is a test",
-      edits: "???",
-      start: "125",
-      end: "145",
-      duration: "10s",
-      beginPos: "0",
-      endPos: "10",
-      length: "10",
-    },
-    {
-      revision: 2,
-      type: "Insert",
-      content: "This is a test",
-      edits: "???",
-      start: "125",
-      end: "145",
-      duration: "10s",
-      beginPos: "0",
-      endPos: "10",
-      length: "10",
-    },
 
   ]
   if (!currentEngagement.value){
@@ -81,10 +35,10 @@ function getTableData() {
   }
   let studentId = currentEngagement.value.studentId;
   let assignmentId = currentEngagement.value.assignmentId;
-  apis.getBehaviorRecordAll(studentId,assignmentId)
+  apis.selectBehaviorRecordVersions(studentId,assignmentId)
       .then((res:any) => {
-        if (res && res.data && res.data.code === 200){
-          let rowData = res.data.data.events as any[];
+        if (res && res.data && res.data.code == 200 && res.data.data && res.data.data.versions && Array.isArray(res.data.data.versions[versionIndex.value]?.events)){
+          let rowData = res.data.data.versions[versionIndex.value].events as any[];
           console.log("行为数据",rowData);
           tableData.value.length = 0;
           rowData.forEach((item:any,index:number) => {
@@ -134,7 +88,7 @@ onMounted(()=>{
     </template>
     <div class="total-canvas-container">
       <EditCanvas
-          :split-number="10"
+          :split-number="8"
           :assignment-id="1"/>
     </div>
     <div class="table-container">

+ 6 - 6
src/views/BehaviorPage/subPage/PauseAnalysis.vue

@@ -85,11 +85,11 @@ onMounted(()=>{
     </div>
     <div class="statistic-container" v-loading="dataLoading">
       <el-statistic
-          :value="data.totalTime"
+          :value="(data.totalTime/1000).toFixed(3)"
           suffix="min"
           title="停顿总时长">
         <template #suffix>
-          <span class="statistic-suffix" >ms</span>
+          <span class="statistic-suffix" >s</span>
         </template>
       </el-statistic>
       <el-divider direction="vertical" style="height: 50px;margin: 0"/>
@@ -102,18 +102,18 @@ onMounted(()=>{
       </el-statistic>
       <el-divider direction="vertical" style="height: 50px;margin: 0"/>
       <el-statistic
-          :value="data.averagePauseTime"
+          :value="(data.averagePauseTime/1000).toFixed(3)"
           title="平均停顿时长">
         <template #suffix>
-          <span class="statistic-suffix" >ms</span>
+          <span class="statistic-suffix" >s</span>
         </template>
       </el-statistic>
       <el-divider direction="vertical" style="height: 50px;margin: 0"/>
       <el-statistic
-          :value="data.medianPauseTime"
+          :value="(data.medianPauseTime/1000).toFixed(3)"
           title="停顿时长中位数">
         <template #suffix>
-          <span class="statistic-suffix" >ms</span>
+          <span class="statistic-suffix" >s</span>
         </template>
       </el-statistic>
       <el-divider direction="vertical" style="height: 50px;margin: 0"/>

+ 1 - 1
src/views/BehaviorPage/subPage/ProcessAnalysis.vue

@@ -14,7 +14,7 @@ import ProcessCanvas from "@/views/BehaviorPage/component/ProcessCanvas.vue";
       </div>
     </template>
     <div class="total-canvas-container">
-      <ProcessCanvas assignment-id="-1"  split-number="3"/>
+      <ProcessCanvas :assignment-id="-1"  :split-number="8"/>
     </div>
   </el-card>
 </template>

+ 12 - 17
src/views/BehaviorPage/subPage/TotalAnalysis.vue

@@ -4,8 +4,7 @@ import TotalCanvas from "@/views/BehaviorPage/component/TotalCanvas.vue";
 import {onMounted, type Ref, ref, watch} from "vue";
 import useApis from "@/apis";
 import {inject} from "vue-demi";
-import * as echarts from "echarts";
-import {type TableColumnCtx} from "element-plus";
+import {ElMessage, type TableColumnCtx} from "element-plus";
 
 let tableData = ref([])
 const apis = useApis();
@@ -32,27 +31,20 @@ interface Event {
   Output:string|null|undefined,
   Position:number,
 }
-
+let versionIndex = inject('currentVersionIndex') satisfies Ref<number>
+watch(versionIndex,()=>{
+  getBehaviorData();
+})
 function getBehaviorData(){
   //TODO
-  // tableData.value = [
-  //   {Id:0,EventType:"focus",Output:"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",Position:"12",DocLength:"12",StartTime:"00:00:00",EndTime:"00:01:05"},
-  //   {Id:1,EventType:"mouse",Output:"xxxx",Position:"12",DocLength:"12",StartTime:"00:00:00",EndTime:"00:01:05"},
-  //   {Id:2,EventType:"keyboard",Output:"xxxx",Position:"12",DocLength:"12",StartTime:"00:00:00",EndTime:"00:01:05"},
-  //   {Id:3,EventType:"focus",Output:"xxxx",Position:"12",DocLength:"12",StartTime:"00:00:00",EndTime:"00:01:05"},
-  //   {Id:4,EventType:"focus",Output:"xxxx",Position:"12",DocLength:"12",StartTime:"00:00:00",EndTime:"00:01:05"},
-  //   {Id:5,EventType:"mouse",Output:"xxxx",Position:"12",DocLength:"12",StartTime:"00:00:00",EndTime:"00:01:05"},
-  //   {Id:6,EventType:"keyboard",Output:"xxxx",Position:"12",DocLength:"12",StartTime:"00:00:00",EndTime:"00:01:05"},
-  // ];
   if (!currentEngagement.value){
     return;
   }
-  apis.getBehaviorRecordAll(currentEngagement.value.studentId,currentEngagement.value.assignmentId)
+  apis.selectBehaviorRecordVersions(currentEngagement.value.studentId,currentEngagement.value.assignmentId)
     .then((res: any) => {
-      if (res && res.data && res.data.code === 200){
-        let rowData = res.data.data
+      if (res && res.data && res.data.code == 200 && res.data.data && res.data.data.versions && Array.isArray(res.data.data.versions[versionIndex.value]?.events)){
         console.log("行为数据",res)
-        tableData.value = res.data.data.events.map((item:any,index:number) => {
+        tableData.value = res.data.data.versions[versionIndex.value].events.map((item:any,index:number) => {
           return {
             Id:index,
             EventType:item.type,
@@ -62,6 +54,9 @@ function getBehaviorData(){
         })
         console.log("tableData",tableData)
       }
+      else{
+        ElMessage.error('获取数据失败')
+      }
     })
 }
 const filterHandler = (
@@ -95,7 +90,7 @@ onMounted(()=>{
           :assignment-id="1"/>
     </div>
     <div class="table-container">
-      <el-table :data="tableData" style="width: 90%" max-height="500">
+      <el-table empty-text="暂无数据" :data="tableData" style="width: 90%" max-height="500">
         <el-table-column prop="Id" label="Id" width="120" sortable/>
         <el-table-column prop="EventType"
                          :filters="[

+ 23 - 20
src/views/ClassPage/CourseClass.vue

@@ -2,26 +2,29 @@
     <div class="course-class" v-show="DataLoading">
         <classHeader>{{Coursedata.courseName}}</classHeader>
         <div class="content scroll">
-            <a v-for="(item) in data" :key="item.classId" 
-                class="content-item" 
-                @mouseenter="activeId=item.classId" 
-                @mouseleave="activeId=-1" 
+            <a v-for="(item) in data" :key="item.classId"
+                class="content-item"
+                @mouseenter="activeId=item.classId"
+                @mouseleave="activeId=-1"
                 :class="{'shadow_unactive':item.classId!==activeId,'shadow_active':item.classId===activeId}"
-                @click="{$event.preventDefault();$router.push(`/home/classDetail?classId=${item.classId}&courseId=${item.courseId}`)}"
+                @click="(event)=>{
+                  event.preventDefault();
+                  $router.push(`/home/classDetail?classId=${item.classId}&courseId=${item.courseId}`)
+                }"
             >
                 <h2>{{ item.className }}</h2>
                 <div class="svg-text">
-                    <img src="@/assets/people.svg" width="24px" height="24px"></img> 
+                    <img src="@/assets/people.svg" width="24px" height="24px"/>
                     <span>:</span>
                     <span>{{ item.stuNumber }}</span>
                 </div>
                 <div class="svg-text">
-                    <img src="@/assets/time.svg" width="24px" height="24px"></img> 
+                    <img src="@/assets/time.svg" width="24px" height="24px"/>
                     <span>:</span>
                     <span>{{ item.classTime }}</span>
                 </div>
                 <div class="svg-text">
-                    <img src="@/assets/plus-square.svg" width="24px" height="24px"></img> 
+                    <img src="@/assets/plus-square.svg" width="24px" height="24px"/>
                     <span>:</span>
                     <span>{{item.classCode}}</span>
                 </div>
@@ -35,11 +38,11 @@
                 </div>
             </a>
             <a class="plus"
-                @mouseenter="activeId=-2" 
-                @mouseleave="activeId=-1" 
+                @mouseenter="activeId=-2"
+                @mouseleave="activeId=-1"
                 @click="visible_add=true"
                 :class="{'shadow_unactive':-2!==activeId,'shadow_active':-2===activeId}">
-                <img src="@/assets/add.svg"></img> 
+                <img src="@/assets/add.svg"/>
             </a>
         </div>
     </div>
@@ -59,9 +62,9 @@
     <div v-if="visible_add" class="modal-mask" @click.self="isloading_add || (visible_add=false)" >
         <div class="modal-container">
             <div class="add1" v-show="!isloading_add">
-                <h2>班级名称:</h2> <input type="text" v-model.trim="addClassName" placeholder="班级名称建议小于6个字"></input>
+                <h2>班级名称:</h2> <input type="text" v-model.trim="addClassName" placeholder="班级名称建议小于6个字"/>
             </div>
-            <div class="add1" v-show="isloading_add" 
+            <div class="add1" v-show="isloading_add"
             :style="{'height': '66%', 'margin-left':'210px'}">
                 <h2>上传中...</h2>
             </div>
@@ -83,7 +86,7 @@
                     <option value="08:00">08:00</option>
                     <option value="10:10">10:10</option>
                     <option value="14:00">14:00</option>
-                    <option value="16:10">16:10</option> 
+                    <option value="16:10">16:10</option>
                 </select>
             </div>
             <div class="modal-footer add2">
@@ -160,7 +163,7 @@ const AddClass = async () =>{
         visible_add.value=false
         addClassName.value = ""
         addClassDay.value = ""
-        addClassClock.value = ""    
+        addClassClock.value = ""
         getClass()
     }
     else{
@@ -245,7 +248,7 @@ export default {
         width: 160px;
         height: 260px;
         border-radius: 25px;
-        border: 20px solid #70bbdb; 
+        border: 20px solid #70bbdb;
         cursor: pointer;
         transition: all 0.3s ease;
         margin-left: 20px;
@@ -292,13 +295,13 @@ export default {
     }
     a:active{
         box-shadow: none;
-        border: 25px solid #4a90c4; 
+        border: 25px solid #4a90c4;
     }
     .plus{
         width: 160px;
         height: 260px;
         border-radius: 25px;
-        border: 20px solid #70bbdb; 
+        border: 20px solid #70bbdb;
         display: flex;
         justify-content: center;
         align-items: center;
@@ -416,6 +419,6 @@ export default {
         .add2 {
             height: 33%;
         }
-    }   
+    }
 }
-</style>
+</style>