Преглед изворни кода

feat:行为记录可视化框架完成,修改了导航栏与当前页面不匹配的问题

廖彦舟 пре 1 година
родитељ
комит
0c394e3fde

+ 2 - 1
package-lock.json

@@ -2152,8 +2152,9 @@
     },
     "node_modules/echarts": {
       "version": "5.6.0",
-      "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz",
+      "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.6.0.tgz",
       "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
+      "license": "Apache-2.0",
       "dependencies": {
         "tslib": "2.3.0",
         "zrender": "5.6.1"

+ 2 - 2
src/apis/axios.config.ts

@@ -1,9 +1,9 @@
 import axios from "axios";
 
 // const BASEURL = 'http://47.111.23.171:8081'
-// const BASEURL = 'http://localhost:8082'
+const BASEURL = 'http://localhost:8082'
 // const BASEURL = 'http://8.130.126.86:8080'
-const BASEURL = 'https://air-nju.seec.seecoder.cn/'
+// const BASEURL = 'https://air-nju.seec.seecoder.cn/'
 
 const axiosInstance = axios.create({
     baseURL: BASEURL,

+ 18 - 13
src/layout/Header/Header.vue

@@ -5,15 +5,19 @@
       <img src="../Header/seecoderLogo_wide.png" alt="logo">
     </div>
     <!-- 选择菜单 -->
+
+
     <div class="header-menu">
-      <el-menu mode="horizontal" :default-active="activePage" @select="handleSelect" :ellipsis="false" class="menu">
+      <el-menu mode="horizontal"
+               router
+               :default-active="useRoute().path" @select="handleSelect" :ellipsis="false" class="menu">
         <template v-if="role == 'student'">
-          <el-menu-item v-for="item in studentMenuItems" :key="item.key" :index="item.key" class="header-menu-item">
+          <el-menu-item v-for="item in studentMenuItems" :key="item.key" :index="item.path" class="header-menu-item">
             <div>{{ item.value }}</div>
           </el-menu-item>
         </template>
         <template v-else>
-          <el-menu-item v-for="item in teacherMenuItems" :key="item.key" :index="item.key" class="header-menu-item">
+          <el-menu-item v-for="item in teacherMenuItems" :key="item.key" :index="item.path" class="header-menu-item">
             <div>{{ item.value }}</div>
           </el-menu-item>
         </template>
@@ -32,7 +36,7 @@
             </template>
             <div class="nickName">{{ store.user.name }}</div>
           </template>
-          <template v-else>            
+          <template v-else>
             <el-avatar :size="32" :shape="'circle'">U</el-avatar>
             <div class="nickName">user</div>
           </template> -->
@@ -63,19 +67,20 @@ const store = useStore();
 const role: 'teachers' | 'student' = store.user.role === 'STUDENT' ? 'student' : 'teachers';
 
 const studentMenuItems = [
-  { value: "课程广场", key: "CourseSquare" },
-  { value: "我的课程", key: "MyCourse" },
-  { value: "我的作业", key: "MyWork" },
+  { value: "课程广场", key: "CourseSquare",path:"/home/courseSquare" },
+  { value: "我的课程", key: "MyCourse",path:"/home/myCourse" },
+  { value: "我的作业", key: "MyWork",path:"/home/myHomework" },
 ];
 
 const teacherMenuItems = [
-  { value: "课程广场", key: "CourseSquare" },
-  { value: "我的课程", key: "MyCourse" },
-  { value: "我的批改", key: "MyWork" },
+  { value: "课程广场", key: "CourseSquare" ,path:"/home/courseSquare"},
+  { value: "我的课程", key: "MyCourse",path:"/home/myCourse" },
+  { value: "我的批改", key: "MyWork",path:"/home/myCorrect/course" },
+    { value: "行为数据", key: "Behavior",path:"/home/behavior/main" },
 ];
 
 const handleSelect = (index: string) => {
-  emitter.emit('change-page', index);
+  // emitter.emit('change-page', index);
 };
 
 const handleCommand = (command: string) => {
@@ -83,7 +88,7 @@ const handleCommand = (command: string) => {
     store.setUser(null);
     store.setToken(null);
     localStorage.setItem('Authorization', null);
-    localStorage.setItem('User', null); 
+    localStorage.setItem('User', null);
     router.push({ path: "/" });
   } else if (command === "profile") {
     router.push({ path: "/profile" });
@@ -114,4 +119,4 @@ onMounted(() => {
 export default {
   name: "Layout"
 };
-</script>
+</script>

+ 2 - 2
src/layout/Layout/Layout.vue

@@ -16,7 +16,7 @@ import Role from '@/views/Home/Role.vue'
         <Header></Header>
       </el-header>
     </el-container>
-    <el-container>
+    <el-container >
       <el-main style="height: 100%;;padding: 0 0 0 0;margin-top: 40px">
         <Role></Role>
       </el-main>
@@ -39,4 +39,4 @@ import Role from '@/views/Home/Role.vue'
   padding: 0;
   height: 40px;
 }
-</style>
+</style>

+ 35 - 2
src/router/index.ts

@@ -19,8 +19,8 @@ const router = createRouter({
     {
       path: "/",
       redirect: () => {
-        window.location.href = "https://p-nju.seec.seecoder.cn/login?from=https://air-nju.seec.seecoder.cn";
-        // window.location.href = "http://localhost:8000/login?from=http://localhost:4000";
+        // window.location.href = "https://p-nju.seec.seecoder.cn/login?from=https://air-nju.seec.seecoder.cn";
+        window.location.href = "http://localhost:8000/login?from=http://localhost:4000";
         // {本地门户首页地址}?from={本地eai首页地址}
         return "/"; // 占位返回值,实际不会执行
     },
@@ -137,6 +137,39 @@ const router = createRouter({
           name: 'myCorrectWithAssignmentId',
           component: () => import("@/views/CorrectPage/CorrectAssignmentPage.vue")
         },
+        {
+          path:"behavior",
+          name: "behavior",
+          redirect: 'home/behavior/main',
+          component: () => import("../views/BehaviorPage/BehaviorBoard.vue"),
+          children:[
+              {
+                path: 'main',
+                name: 'behaviorHome',
+                component: () => import("../views/BehaviorPage/subPage/BehaviorHome.vue")
+              },
+            {
+              path: 'total',
+              name: 'total',
+              component: () => import("../views/BehaviorPage/subPage/TotalAnalysis.vue")
+            },
+            {
+              path: 'edit',
+              name: 'edit',
+              component: () => import("../views/BehaviorPage/subPage/EditAnalysis.vue")
+            },
+            {
+              path: 'pause',
+              name: 'pause',
+              component: () => import("../views/BehaviorPage/subPage/PauseAnalysis.vue")
+            },
+            {
+              path: 'process',
+              name: 'process',
+              component: () => import("../views/BehaviorPage/subPage/ProcessAnalysis.vue")
+            },
+          ]
+        },
       ]
     },
 

+ 312 - 0
src/views/BehaviorPage/BehaviorBoard.vue

@@ -0,0 +1,312 @@
+<script setup lang="ts">
+import router from "@/router";
+import {Back} from "@element-plus/icons-vue";
+import {computed, onMounted, provide, type Ref, ref, watch} from "vue";
+import {ElLoading, ElMessage} from "element-plus";
+import useApis from "@/apis";
+
+const apis = useApis()
+//pageHeader
+interface CourseVO{
+  courseName: string;
+  courseId: number;
+}
+interface AssignmentVO{
+  assignmentName: string;
+  assignmentId: number;
+}
+const selectedCourse  = ref({courseName: "", courseId: -1});
+const courseList:Ref<CourseVO[]> = ref([]);
+let courseData:any[]= []
+const selectedAssignment  = ref({assignmentName: "", assignmentId: -1});
+watch(selectedAssignment,()=>{
+  AssignmentChanged(selectedAssignment.value);
+})
+const assignmentList:Ref<AssignmentVO[]> = ref([]);
+const dataIsLoading:Ref<boolean> = ref(true);
+let assignmentData:any[]= []
+function getCourseList(){
+  console.log("获取课程列表")
+  let pageHTML = document.getElementById("page")
+  console.log(pageHTML)
+  const loading = ElLoading.service({
+    text: '获取数据中',
+    target: pageHTML,
+    background: '#F5F5F59E'
+  })
+  apis.getCourseByTeacherId(0,100)
+      .then((res: any)=>{
+        if (res&&res.data.code===200){
+          courseData = res.data.data.records as [any];
+          console.log(courseData)
+          courseList.value = courseData.map((item:any)=>{
+            return {courseName: item.courseName, courseId: item.courseId}
+          })
+          if (courseList.value&&courseList.value.length>0){
+            selectedCourse.value = courseList.value[0];
+          }
+          else {
+            selectedCourse.value = {courseName: "", courseId: -1}
+          }
+          apis.getAssignmentByCourseId(courseData[0].courseId,0,1000)
+            .then((res: any)=>{
+              if (res&&res.data.code===200){
+                loading.close();
+                dataIsLoading.value = false;
+                let temp = res.data.data.records.filter((item:any)=>{
+                  return item.type === 'writing' || item.type === '写作';
+                })
+                console.log("temp:",temp)
+                assignmentList.value = temp.map((item:any)=>{
+                    return {assignmentName: item.assignmentName, assignmentId: item.assignmentId}
+                })
+                console.log(assignmentList.value);
+                // assignmentData = temp as any[];
+                if (assignmentList.value&&assignmentList.value.length>0){
+                  selectedAssignment.value = assignmentList.value[0];
+                }
+                else{
+                  selectedAssignment.value = {assignmentName: "", assignmentId: -1}
+                }
+              }
+              else{
+                ElMessage.error("获取作业列表失败")
+              }
+            })
+            .catch((err: any) => {
+              console.log(err);
+              ElMessage.error("获取作业列表失败")
+            })
+        }
+        else{
+          ElMessage.error("获取课程列表失败")
+        }
+        console.log(res);
+      })
+      .catch((err: any) => {
+        console.error(err);
+      })
+}
+
+function getAssignmentList(courseId:number){
+  let pageHTML = document.getElementById("info-card")
+  // console.log(pageHTML)
+  const loading = ElLoading.service({
+    text: '获取作业列表中',
+    target: pageHTML,
+    background: '#F5F5F59E'
+  })
+  apis.getAssignmentByCourseId(courseId,0,1000)
+      .then((res: any)=>{
+        if (res&&res.data.code===200){
+          let temp = res.data.data.records.filter((item:any)=>{
+            return item.type === 'writing' || item.type === '写作';
+          })
+          assignmentList.value = temp.map((item:any)=>{
+            return {assignmentName: item.assignmentName, assignmentId: item.assignmentId}
+          })
+          // assignmentData = temp as any[];
+          if (assignmentList.value&&assignmentList.value.length>0){
+            selectedAssignment.value = assignmentList.value[0];
+          }
+          else{
+            selectedAssignment.value = {assignmentName: "", assignmentId: -1}
+          }
+          loading.close();
+        }
+      })
+}
+
+
+const courseSelected = computed(()=>{
+  return selectedCourse.value&&selectedCourse.value.courseName !== "";
+});
+function goBack() {
+  router.go(-1);
+}
+
+/**
+ * 课程切换
+ * @param value 课程名
+ */
+function courseChanged(value:CourseVO){
+  console.log("用户切换了课程")
+  //TODO
+  getAssignmentList(value.courseId);
+}
+
+
+const engagements = ref([]);
+const currentEngagementIndex = ref(-1);
+const preButtonDisabled = computed(()=>{
+  return currentEngagementIndex.value<=0;
+});
+const nextButtonDisabled = computed(()=>{
+  return currentEngagementIndex.value>=engagements.value.length-1;
+})
+const currentEngagement = ref()
+watch(currentEngagementIndex,()=>{
+  if (currentEngagementIndex.value>=0&&currentEngagementIndex.value<engagements.value.length){
+    // console.log(engagements)
+    currentEngagement.value =  engagements.value[currentEngagementIndex.value];
+  }
+  else{
+    currentEngagement.value = null;
+  }
+})
+provide('currentEngagement',currentEngagement);
+
+
+/**
+ * 切换作业题目
+ * @param value 作业题目
+ * @constructor
+ */
+function AssignmentChanged(value:AssignmentVO){
+  console.log("用户切换了作业")
+  if (value.assignmentId === -1){
+    currentEngagementIndex.value = -1;
+    return;
+  }
+  apis.getEngagementByAssignmentId(value.assignmentId,0,10000)
+      .then((res:any) => {
+        console.log(res);
+        if (res.data.code === 200){
+          engagements.value = res.data.data.records;
+          if (engagements.value&&engagements.value.length>0){
+            currentEngagementIndex.value = 0;
+          }
+          else{
+            currentEngagementIndex.value = -1;
+          }
+        }
+      })
+      .catch((err:any) => {
+        console.log("错误信息", err)
+      })
+}
+
+function assignmentSwitch(direction:number){
+  if (direction===1){
+    console.log("切换下一个作业")
+    currentEngagementIndex.value++;
+  }
+  else if (direction===-1){
+    console.log("切换上一个作业")
+    currentEngagementIndex.value--;
+  }
+  else{
+    ElMessage.error("切换错误")
+  }
+}
+onMounted(()=>{
+  getCourseList();
+})
+</script>
+
+<template>
+  <div id="page" class="page">
+    <el-card class="info-card" id="info-card">
+      <template #header>
+        <el-page-header @back="goBack">
+          <template #icon>
+            <el-icon color="#909399"><back/></el-icon>
+          </template>
+          <template #title>
+            <span style="font-size: 18px;color: var(--el-text-color-secondary)" > 返回 </span>
+          </template>
+          <template #content>
+            <span > 行为数据记录 </span>
+          </template>
+          <template #extra>
+            <el-select v-model="selectedCourse"
+                       placeholder="请选择课程"
+                       value-key="courseId"
+                       :title="selectedCourse.courseName"
+                       @change="courseChanged"
+                       style="width: 130px;">
+              <el-option
+                  style="color: var(--el-text-color-regular);font-weight: unset"
+                  v-for="item in courseList"
+                  :key="item.courseId"
+                  :label="item.courseName"
+                  :value="item"
+              />
+            </el-select>
+            <el-select v-model="selectedAssignment"
+                       :disabled="!courseSelected"
+                       value-key="assignmentId"
+                       :class="{'disabled':!courseSelected}"
+                       placeholder="请选择作业"
+                       :title="courseSelected?selectedAssignment.assignmentName:'请先选择课程'"
+                       style="width: 130px;margin-left: 10px">
+              <el-option
+                  style="color: var(--el-text-color-regular);font-weight: unset"
+                  v-for="item in assignmentList"
+                  :key="item.assignmentId"
+                  :label="item.assignmentName"
+                  :value="item"
+              />
+            </el-select>
+          </template>
+        </el-page-header>
+      </template>
+      <el-descriptions column="2" v-if="currentEngagement">
+        <el-descriptions-item label="课程名">{{selectedCourse.courseName}}</el-descriptions-item>
+        <el-descriptions-item label="作业题目">{{selectedAssignment.assignmentName}}</el-descriptions-item>
+        <el-descriptions-item label="学生姓名">{{currentEngagement.studentName}}</el-descriptions-item>
+        <el-descriptions-item label="学号">
+          {{currentEngagement.studentId}}
+        </el-descriptions-item>
+        <el-descriptions-item label="提交时间">
+          2025-07-30 09:30:00
+        </el-descriptions-item>
+      </el-descriptions>
+      <div v-if="currentEngagement" id="info-footer">
+        <el-button
+            :disabled="preButtonDisabled"
+            @change="assignmentSwitch(-1)" type="primary">
+          上一个
+        </el-button>
+        <el-button
+            :disabled="nextButtonDisabled"
+            @change="assignmentSwitch(1)" type="primary">
+          下一个
+        </el-button>
+      </div>
+      <el-empty v-else description="该作业暂无学生提交" style="height: 227px" />
+    </el-card>
+    <router-view v-if="!dataIsLoading&&currentEngagement">
+
+    </router-view>
+  </div>
+</template>
+
+<style scoped>
+.page{
+  height: 100%;
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 30px;
+  margin-bottom: 100px;
+}
+.info-card{
+  width: 70%;
+  margin-top: 2%;
+  border-radius: 16px;
+  min-width: 700px;
+}
+.disabled{
+  cursor: not-allowed;
+}
+#info-footer{
+  display: flex;
+  width: 100%;
+  flex-direction: row;
+  justify-content: end;
+  gap: 10px;
+  margin-top: 50px;
+}
+</style>

+ 350 - 0
src/views/BehaviorPage/component/EditCanvas.vue

@@ -0,0 +1,350 @@
+<script setup lang="ts">
+import {onMounted, type Ref, watch} from "vue";
+import * as echarts from 'echarts';
+import {inject} from "vue-demi";
+const props = defineProps<{
+  assignmentId:number,
+  splitNumber:number,
+}>()
+
+const currentEngagement = inject('currentEngagement') as Ref<any>;
+let productData = [];
+let cursorData = [];
+watch(currentEngagement,getCanvasData);
+function getCanvasData(){
+  //TODO
+  let studentId = currentEngagement.value.studentId;
+  let assignmentId = currentEngagement.value.assignmentId;
+
+  //测试用数据
+  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],];
+  let chartDom = document.getElementById('edit-canvas');
+  let myChart = echarts.getInstanceByDom(chartDom);
+  let option = {
+    xAxis: {
+      type: 'time',
+      axisPointer: {
+        value: "2016-10-7",
+        snap: true,
+        label: {
+          show: true,
+          formatter: function(params) {
+            return echarts.format.formatTime('hh:mm:ss', params.value);
+          }
+        },
+        show: true,
+        type: "line"
+      },
+      splitNumber: props.splitNumber
+    },
+    yAxis: {
+      type: 'value',
+      name:'characters'
+    },
+    tooltip:{
+
+    },
+    dataZoom: [
+      {
+        type: 'inside',
+      },
+      {
+        type: 'slider',
+      }
+    ],
+    legend:{},
+    series: [
+      {
+        itemStyle: {
+          color:'#ff9300',
+          opacity: 0
+        },
+        name:'product',
+        data: productData,
+        type: 'line'
+      },
+      {
+        itemStyle: {
+          color:'#ff9300',
+          opacity: 0
+        },
+        lineStyle: {
+          type: "dotted"
+        },
+        name:'cursor',
+        data: cursorData,
+        type: 'line'
+      }
+    ]
+  };
+  myChart.setOption( option);
+}
+
+
+
+
+
+
+onMounted(()=>{
+  let chartDom = document.getElementById('edit-canvas');
+  let myChart = echarts.init(chartDom);
+  let option = {
+    xAxis: {
+      type: 'time',
+      axisPointer: {
+        value: "2016-10-7",
+        snap: true,
+        label: {
+          show: true,
+          formatter: function(params) {
+            return echarts.format.formatTime('hh:mm:ss', params.value);
+          }
+        },
+        show: true,
+        type: "line"
+      },
+      splitNumber: props.splitNumber
+    },
+    yAxis: {
+      type: 'value',
+      name:'characters'
+    },
+    tooltip:{
+
+    },
+    dataZoom: [
+      {
+        type: 'inside',
+      },
+      {
+        type: 'slider',
+      }
+    ],
+    legend:{},
+    series: [
+      {
+        itemStyle: {
+          color:'#ff9300',
+          opacity: 0
+        },
+        name:'product',
+        data: productData,
+        type: 'line'
+      },
+      {
+        itemStyle: {
+          color:'#ff9300',
+          opacity: 0
+        },
+        lineStyle: {
+          type: "dotted"
+        },
+        name:'cursor',
+        data: cursorData,
+        type: 'line'
+      }
+    ]
+  };
+  myChart.setOption( option);
+  window.addEventListener('resize', ()=>{
+    myChart.resize()
+  })
+})
+</script>
+
+<template>
+  <div id="edit-canvas" class="canvas-container">
+
+  </div>
+</template>
+
+<style scoped>
+.canvas-container{
+  width: 100%;
+  height: 100%;
+}
+</style>

+ 340 - 0
src/views/BehaviorPage/component/PauseCanvas.vue

@@ -0,0 +1,340 @@
+<script setup lang="ts">
+import {onMounted} from "vue";
+import * as echarts from 'echarts';
+const props = defineProps<{
+  assignmentId:number,
+  splitNumber:number,
+  barWidth:number,
+}>()
+
+onMounted(()=>{
+  let chartDom = document.getElementById('pause-canvas');
+  let myChart = echarts.init(chartDom);
+  let option = {
+    xAxis: {
+      type:'time',
+      axisLabel: {
+        formatter: (value) => {
+          const date = new Date(value);
+          return [date.getHours(), date.getMinutes(), date.getSeconds()]
+              .map(num => num.toString().padStart(2, '0'))
+              .join(':');
+        },
+      },
+      splitNumber: props.splitNumber
+    },
+    yAxis: {
+      type:'value',
+      name:'停顿时间(ms)'
+    },
+    tooltip:{
+      formatter:(value:any)=>{
+        let date = value.data[0];
+        let data = value.data[1];
+        return `停顿时刻:${[date.getHours(), date.getMinutes(), date.getSeconds()]
+            .map(num => num.toString().padStart(2, '0'))
+            .join(':')}\n  停顿时长:${data}ms`
+      }
+    },
+    visualMap: {
+      type: 'continuous', // 连续型映射
+      calculable: true,
+      min: 0,             // 数据最小值
+      max: 150,           // 数据最大值
+      dimension: 1,       // 根据第2个维度(y值)映射颜色
+      inRange: {
+        color: ['yellow', 'red'] // 渐变范围:小值→蓝色,大值→红色
+      },
+      itemWidth: props.barWidth,
+      bottom:0,
+      right:0,
+    },
+    dataZoom: [
+      {
+        type: 'inside',
+      },
+      {
+        type: 'slider',
+      }
+    ],
+    series: [
+      {
+        symbolSize: 10,
+
+        data: [
+          [new Date('2025-07-26 00:33:42'),44],
+          [new Date('2025-07-26 00:52:57'),39],
+          [new Date('2025-07-26 00:46:26'),14],
+          [new Date('2025-07-26 00:34:36'),28],
+          [new Date('2025-07-26 00:17:17'),43],
+          [new Date('2025-07-26 00:50:39'),23],
+          [new Date('2025-07-26 00:42:20'),13],
+          [new Date('2025-07-26 00:33:5'),44],
+          [new Date('2025-07-26 00:42:31'),32],
+          [new Date('2025-07-26 00:13:16'),48],
+          [new Date('2025-07-26 00:0:23'),40],
+          [new Date('2025-07-26 00:24:46'),39],
+          [new Date('2025-07-26 00:4:44'),49],
+          [new Date('2025-07-26 00:2:4'),43],
+          [new Date('2025-07-26 00:25:6'),21],
+          [new Date('2025-07-26 00:18:38'),26],
+          [new Date('2025-07-26 00:16:40'),43],
+          [new Date('2025-07-26 00:37:45'),5],
+          [new Date('2025-07-26 00:49:42'),27],
+          [new Date('2025-07-26 00:45:31'),34],
+          [new Date('2025-07-26 00:51:19'),25],
+          [new Date('2025-07-26 00:43:16'),42],
+          [new Date('2025-07-26 00:16:39'),7],
+          [new Date('2025-07-26 00:38:48'),32],
+          [new Date('2025-07-26 00:34:43'),33],
+          [new Date('2025-07-26 00:6:43'),48],
+          [new Date('2025-07-26 00:45:28'),30],
+          [new Date('2025-07-26 00:4:29'),48],
+          [new Date('2025-07-26 00:35:21'),41],
+          [new Date('2025-07-26 00:23:57'),44],
+          [new Date('2025-07-26 00:8:1'),29],
+          [new Date('2025-07-26 00:7:4'),40],
+          [new Date('2025-07-26 00:3:43'),11],
+          [new Date('2025-07-26 00:40:12'),12],
+          [new Date('2025-07-26 00:23:35'),32],
+          [new Date('2025-07-26 00:9:28'),16],
+          [new Date('2025-07-26 00:7:45'),30],
+          [new Date('2025-07-26 00:31:11'),24],
+          [new Date('2025-07-26 00:46:4'),41],
+          [new Date('2025-07-26 00:34:29'),49],
+          [new Date('2025-07-26 00:42:9'),39],
+          [new Date('2025-07-26 00:35:46'),20],
+          [new Date('2025-07-26 00:17:23'),48],
+          [new Date('2025-07-26 00:19:46'),6],
+          [new Date('2025-07-26 00:8:6'),41],
+          [new Date('2025-07-26 00:55:38'),27],
+          [new Date('2025-07-26 00:33:10'),41],
+          [new Date('2025-07-26 00:32:12'),26],
+          [new Date('2025-07-26 00:58:44'),43],
+          [new Date('2025-07-26 00:30:21'),19],
+          [new Date('2025-07-26 00:46:38'),34],
+          [new Date('2025-07-26 00:20:2'),22],
+          [new Date('2025-07-26 00:44:57'),33],
+          [new Date('2025-07-26 00:11:49'),37],
+          [new Date('2025-07-26 00:24:43'),15],
+          [new Date('2025-07-26 00:30:2'),42],
+          [new Date('2025-07-26 00:57:17'),40],
+          [new Date('2025-07-26 00:6:29'),27],
+          [new Date('2025-07-26 00:15:9'),27],
+          [new Date('2025-07-26 00:13:57'),22],
+          [new Date('2025-07-26 00:54:8'),36],
+          [new Date('2025-07-26 00:5:29'),31],
+          [new Date('2025-07-26 00:38:36'),29],
+          [new Date('2025-07-26 00:6:1'),37],
+          [new Date('2025-07-26 00:11:22'),5],
+          [new Date('2025-07-26 00:10:8'),25],
+          [new Date('2025-07-26 00:43:29'),22],
+          [new Date('2025-07-26 00:9:30'),14],
+          [new Date('2025-07-26 00:44:39'),41],
+          [new Date('2025-07-26 00:48:17'),36],
+          [new Date('2025-07-26 00:6:5'),48],
+          [new Date('2025-07-26 00:13:31'),13],
+          [new Date('2025-07-26 00:12:37'),28],
+          [new Date('2025-07-26 00:1:4'),49],
+          [new Date('2025-07-26 00:48:20'),39],
+          [new Date('2025-07-26 00:52:32'),46],
+          [new Date('2025-07-26 00:35:0'),48],
+          [new Date('2025-07-26 00:28:2'),13],
+          [new Date('2025-07-26 00:19:11'),41],
+          [new Date('2025-07-26 00:8:48'),21],
+          [new Date('2025-07-26 00:46:2'),34],
+          [new Date('2025-07-26 00:49:29'),16],
+          [new Date('2025-07-26 00:24:22'),17],
+          [new Date('2025-07-26 00:10:11'),5],
+          [new Date('2025-07-26 00:14:10'),47],
+          [new Date('2025-07-26 00:18:44'),19],
+          [new Date('2025-07-26 00:18:15'),31],
+          [new Date('2025-07-26 00:16:10'),15],
+          [new Date('2025-07-26 00:37:51'),25],
+          [new Date('2025-07-26 00:30:13'),40],
+          [new Date('2025-07-26 00:33:35'),24],
+          [new Date('2025-07-26 00:19:29'),47],
+          [new Date('2025-07-26 00:29:23'),40],
+          [new Date('2025-07-26 00:19:10'),12],
+          [new Date('2025-07-26 00:4:37'),8],
+          [new Date('2025-07-26 00:38:0'),22],
+          [new Date('2025-07-26 00:36:24'),7],
+          [new Date('2025-07-26 00:4:22'),29],
+          [new Date('2025-07-26 00:39:22'),32],
+          [new Date('2025-07-26 00:40:33'),48],
+          [new Date('2025-07-26 00:33:14'),10],
+          [new Date('2025-07-26 00:20:18'),22],
+          [new Date('2025-07-26 00:39:52'),31],
+          [new Date('2025-07-26 00:21:40'),16],
+          [new Date('2025-07-26 00:56:33'),46],
+          [new Date('2025-07-26 00:56:50'),43],
+          [new Date('2025-07-26 00:16:15'),39],
+          [new Date('2025-07-26 00:30:44'),48],
+          [new Date('2025-07-26 00:35:56'),14],
+          [new Date('2025-07-26 00:57:33'),9],
+          [new Date('2025-07-26 00:15:23'),21],
+          [new Date('2025-07-26 00:26:0'),7],
+          [new Date('2025-07-26 00:50:23'),10],
+          [new Date('2025-07-26 00:32:23'),10],
+          [new Date('2025-07-26 00:3:21'),18],
+          [new Date('2025-07-26 00:27:34'),23],
+          [new Date('2025-07-26 00:54:37'),40],
+          [new Date('2025-07-26 00:49:34'),23],
+          [new Date('2025-07-26 00:29:30'),41],
+          [new Date('2025-07-26 00:24:49'),37],
+          [new Date('2025-07-26 00:18:20'),34],
+          [new Date('2025-07-26 00:13:55'),36],
+          [new Date('2025-07-26 00:51:10'),42],
+          [new Date('2025-07-26 00:32:49'),31],
+          [new Date('2025-07-26 00:0:17'),22],
+          [new Date('2025-07-26 00:39:7'),30],
+          [new Date('2025-07-26 00:52:49'),17],
+          [new Date('2025-07-26 00:49:12'),42],
+          [new Date('2025-07-26 00:23:26'),17],
+          [new Date('2025-07-26 00:29:12'),19],
+          [new Date('2025-07-26 00:21:26'),28],
+          [new Date('2025-07-26 00:25:41'),14],
+          [new Date('2025-07-26 00:23:17'),38],
+          [new Date('2025-07-26 00:29:42'),16],
+          [new Date('2025-07-26 00:53:5'),13],
+          [new Date('2025-07-26 00:31:51'),21],
+          [new Date('2025-07-26 00:31:46'),8],
+          [new Date('2025-07-26 00:48:2'),41],
+          [new Date('2025-07-26 00:25:58'),47],
+          [new Date('2025-07-26 00:18:54'),30],
+          [new Date('2025-07-26 00:50:19'),40],
+          [new Date('2025-07-26 00:16:11'),13],
+          [new Date('2025-07-26 00:5:12'),42],
+          [new Date('2025-07-26 00:48:55'),12],
+          [new Date('2025-07-26 00:29:53'),31],
+          [new Date('2025-07-26 00:40:43'),33],
+          [new Date('2025-07-26 00:28:7'),43],
+          [new Date('2025-07-26 00:43:15'),37],
+          [new Date('2025-07-26 00:47:37'),6],
+          [new Date('2025-07-26 00:46:47'),41],
+          [new Date('2025-07-26 00:6:34'),40],
+          [new Date('2025-07-26 00:16:57'),6],
+          [new Date('2025-07-26 00:29:39'),48],
+          [new Date('2025-07-26 00:6:41'),10],
+          [new Date('2025-07-26 00:24:14'),45],
+          [new Date('2025-07-26 00:51:21'),15],
+          [new Date('2025-07-26 00:2:46'),26],
+          [new Date('2025-07-26 00:19:15'),49],
+          [new Date('2025-07-26 00:44:5'),7],
+          [new Date('2025-07-26 00:23:41'),17],
+          [new Date('2025-07-26 00:24:55'),38],
+          [new Date('2025-07-26 00:28:22'),21],
+          [new Date('2025-07-26 00:41:10'),15],
+          [new Date('2025-07-26 00:51:40'),25],
+          [new Date('2025-07-26 00:54:14'),6],
+          [new Date('2025-07-26 00:57:26'),43],
+          [new Date('2025-07-26 00:17:54'),48],
+          [new Date('2025-07-26 00:51:3'),49],
+          [new Date('2025-07-26 00:42:33'),27],
+          [new Date('2025-07-26 00:14:44'),18],
+          [new Date('2025-07-26 00:35:55'),46],
+          [new Date('2025-07-26 00:0:25'),33],
+          [new Date('2025-07-26 00:46:10'),37],
+          [new Date('2025-07-26 00:50:2'),5],
+          [new Date('2025-07-26 00:28:45'),48],
+          [new Date('2025-07-26 00:57:25'),36],
+          [new Date('2025-07-26 00:11:4'),46],
+          [new Date('2025-07-26 00:37:9'),48],
+          [new Date('2025-07-26 00:51:16'),19],
+          [new Date('2025-07-26 00:55:51'),27],
+          [new Date('2025-07-26 00:22:33'),20],
+          [new Date('2025-07-26 00:0:49'),13],
+          [new Date('2025-07-26 00:44:33'),17],
+          [new Date('2025-07-26 00:26:58'),33],
+          [new Date('2025-07-26 00:30:40'),46],
+          [new Date('2025-07-26 00:28:24'),28],
+          [new Date('2025-07-26 00:28:48'),18],
+          [new Date('2025-07-26 00:7:5'),24],
+          [new Date('2025-07-26 00:8:26'),6],
+          [new Date('2025-07-26 00:20:40'),49],
+          [new Date('2025-07-26 00:34:17'),22],
+          [new Date('2025-07-26 00:15:13'),32],
+          [new Date('2025-07-26 00:38:33'),42],
+          [new Date('2025-07-26 00:55:56'),60],
+          [new Date('2025-07-26 00:26:51'),77],
+          [new Date('2025-07-26 00:32:55'),90],
+          [new Date('2025-07-26 00:4:36'),71],
+          [new Date('2025-07-26 00:45:7'),65],
+          [new Date('2025-07-26 00:26:15'),58],
+          [new Date('2025-07-26 00:9:48'),82],
+          [new Date('2025-07-26 00:21:47'),88],
+          [new Date('2025-07-26 00:30:41'),74],
+          [new Date('2025-07-26 00:19:53'),78],
+          [new Date('2025-07-26 00:28:32'),94],
+          [new Date('2025-07-26 00:37:43'),80],
+          [new Date('2025-07-26 00:24:35'),86],
+          [new Date('2025-07-26 00:19:57'),92],
+          [new Date('2025-07-26 00:5:41'),77],
+          [new Date('2025-07-26 00:11:40'),79],
+          [new Date('2025-07-26 00:30:56'),84],
+          [new Date('2025-07-26 00:34:23'),68],
+          [new Date('2025-07-26 00:23:46'),74],
+          [new Date('2025-07-26 00:8:4'),55],
+          [new Date('2025-07-26 00:9:19'),66],
+          [new Date('2025-07-26 00:20:0'),58],
+          [new Date('2025-07-26 00:43:9'),77],
+          [new Date('2025-07-26 00:4:2'),64],
+          [new Date('2025-07-26 00:41:28'),84],
+          [new Date('2025-07-26 00:20:3'),62],
+          [new Date('2025-07-26 00:27:26'),72],
+          [new Date('2025-07-26 00:9:47'),80],
+          [new Date('2025-07-26 00:21:15'),77],
+          [new Date('2025-07-26 00:30:14'),66],
+          [new Date('2025-07-26 00:22:41'),98],
+          [new Date('2025-07-26 00:2:24'),64],
+          [new Date('2025-07-26 00:44:31'),69],
+          [new Date('2025-07-26 00:25:10'),85],
+          [new Date('2025-07-26 00:1:35'),73],
+          [new Date('2025-07-26 00:13:6'),72],
+          [new Date('2025-07-26 00:40:34'),80],
+          [new Date('2025-07-26 00:52:25'),53],
+          [new Date('2025-07-26 00:58:24'),73],
+          [new Date('2025-07-26 00:18:10'),74],
+          [new Date('2025-07-26 00:4:48'),50],
+          [new Date('2025-07-26 00:27:15'),79],
+          [new Date('2025-07-26 00:2:47'),90],
+          [new Date('2025-07-26 00:38:51'),95],
+          [new Date('2025-07-26 00:19:13'),76],
+          [new Date('2025-07-26 00:15:56'),52],
+          [new Date('2025-07-26 00:23:37'),52],
+          [new Date('2025-07-26 00:23:38'),59],
+          [new Date('2025-07-26 00:39:32'),110],
+          [new Date('2025-07-26 00:11:9'),119],
+          [new Date('2025-07-26 00:19:22'),119],
+          [new Date('2025-07-26 00:57:14'),133],
+          [new Date('2025-07-26 00:9:24'),140],
+          [new Date('2025-07-26 00:44:21'),134],
+          [new Date('2025-07-26 00:54:0'),114],
+          [new Date('2025-07-26 00:32:17'),128],
+          [new Date('2025-07-26 00:17:42'),147],
+
+
+        ],
+        type: 'scatter'
+      }
+    ]
+  };
+  myChart.setOption( option);
+  window.addEventListener('resize', ()=>{
+    myChart.resize()
+  })
+})
+</script>
+
+<template>
+  <div id="pause-canvas" class="canvas-container">
+
+  </div>
+</template>
+
+<style scoped>
+.canvas-container{
+  width: 100%;
+  height: 100%;
+}
+</style>

+ 11 - 0
src/views/BehaviorPage/component/ProcessCanvas.vue

@@ -0,0 +1,11 @@
+<script setup lang="ts">
+
+</script>
+
+<template>
+
+</template>
+
+<style scoped>
+
+</style>

+ 77 - 0
src/views/BehaviorPage/component/TotalCanvas.vue

@@ -0,0 +1,77 @@
+<script setup lang="ts">
+import {onMounted} from "vue";
+import * as echarts from 'echarts';
+defineProps<{
+  assignmentId:number
+}>()
+let data = []
+function dataInit(){
+  //TODO
+  data = [
+    { value: 50, name: '产出行为' },
+    { value: 25, name: '停顿行为' },
+    { value: 15, name: '修改行为' },
+    { value: 10, name: '窗口行为' },
+  ]
+}
+onMounted(()=>{
+  let chartDom = document.getElementById('total-canvas');
+  let myChart = echarts.init(chartDom);
+  dataInit()
+  let option = {
+    tooltip: {
+      trigger: 'item'
+    },
+    legend: {
+      top: '5%',
+      left: 'center'
+    },
+    series: [
+      {
+        name: '总体分析',
+        type: 'pie',
+        radius: ['40%', '70%'],
+        avoidLabelOverlap: false,
+        bottom:-50,
+        itemStyle: {
+          borderRadius: 10,
+          borderColor: '#fff',
+          borderWidth: 2
+        },
+        label: {
+          show: false,
+          position: 'center'
+        },
+        emphasis: {
+          label: {
+            show: true,
+            fontSize: 20,
+            fontWeight: 'bold'
+          }
+        },
+        labelLine: {
+          show: false
+        },
+        data: data
+      }
+    ]
+  };
+  myChart.setOption( option);
+  window.addEventListener('resize', ()=>{
+    myChart.resize()
+  })
+})
+</script>
+
+<template>
+<div id="total-canvas" class="canvas-container">
+
+</div>
+</template>
+
+<style scoped>
+.canvas-container{
+  width: 100%;
+  height: 100%;
+}
+</style>

+ 137 - 0
src/views/BehaviorPage/subPage/BehaviorHome.vue

@@ -0,0 +1,137 @@
+<script setup lang="ts">
+
+import router from "@/router";
+import TotalCanvas from "@/views/BehaviorPage/component/TotalCanvas.vue";
+import PauseCanvas from "@/views/BehaviorPage/component/PauseCanvas.vue";
+import EditCanvas from "@/views/BehaviorPage/component/EditCanvas.vue";
+
+function toDetail(index:number){
+  if ( index === 1){
+    router.push("/home/behavior/total")
+  }
+  else if ( index === 2){
+    router.push("/home/behavior/pause")
+  }
+  else if ( index === 3){
+    router.push("/home/behavior/edit")
+  }
+  else{
+    router.push("/home/behavior/process")
+  }
+}
+
+</script>
+
+<template>
+<div class="behavior-main">
+  <el-card class="small-card">
+    <template #header>
+      <div class="small-card-header">
+        <span class="card-title">
+          总体分析
+        </span>
+        <span
+            @click="toDetail(1)"
+            class="card-text-button">
+          查看详情
+        </span>
+      </div>
+    </template>
+    <div style="width: 100%;height: 350px">
+      <TotalCanvas
+          :assignment-id="1"/>
+    </div>
+  </el-card>
+  <el-card class="small-card">
+    <template #header>
+      <div class="small-card-header">
+        <span class="card-title">
+          停顿分析
+        </span>
+        <span
+            @click="toDetail(2)"
+            class="card-text-button">
+          查看详情
+        </span>
+      </div>
+    </template>
+    <div style="width: 100%;height: 350px">
+      <PauseCanvas
+          :split-number="3"
+          :bar-width="10"
+          :assignment-id="1"/>
+    </div>
+  </el-card>
+  <el-card class="small-card">
+    <template #header>
+      <div class="small-card-header">
+        <span class="card-title">
+          修改分析
+        </span>
+        <span
+            @click="toDetail(3)"
+            class="card-text-button">
+          查看详情
+        </span>
+      </div>
+    </template>
+    <div style="width: 100%;height: 350px">
+      <EditCanvas
+          :split-number="5"
+          :assignment-id="1"/>
+    </div>
+  </el-card>
+  <el-card class="small-card">
+    <template #header>
+      <div class="small-card-header">
+        <span class="card-title">
+          写作过程
+        </span>
+        <span
+            @click="toDetail(4)"
+            class="card-text-button">
+          查看详情
+        </span>
+      </div>
+    </template>
+    <div id="process-canvas" class="canvas">
+
+    </div>
+  </el-card>
+</div>
+</template>
+
+<style scoped>
+.behavior-main{
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  grid-gap: 16px;
+  width: 70%;
+  min-width: 700px;
+}
+.small-card{
+  width: 100%;
+  border-radius: 16px;
+}
+.small-card-header{
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  width: 100%;
+}
+.canvas{
+  width: 100%;
+  height: 300px;
+}
+.card-title{
+  font-size: 16px;
+  font-weight: bold;
+}
+.card-text-button{
+  font-size: 16px;
+}
+.card-text-button:hover{
+  color: var(--main-color);
+  cursor: pointer;
+}
+</style>

+ 1 - 0
src/views/BehaviorPage/subPage/EditAnalysis.scss

@@ -0,0 +1 @@
+

+ 26 - 0
src/views/BehaviorPage/subPage/EditAnalysis.vue

@@ -0,0 +1,26 @@
+<script setup lang="ts">
+import "./TotalAnalysis.scss"
+import TotalCanvas from "@/views/BehaviorPage/component/TotalCanvas.vue";
+import EditCanvas from "@/views/BehaviorPage/component/EditCanvas.vue";
+</script>
+
+<template>
+  <el-card class="detail-card">
+    <template #header>
+      <div>
+        <span class="card-title">
+          修改分析
+        </span>
+      </div>
+    </template>
+    <div class="total-canvas-container">
+      <EditCanvas
+          :split-number="10"
+          :assignment-id="1"/>
+    </div>
+  </el-card>
+</template>
+
+<style scoped>
+
+</style>

+ 26 - 0
src/views/BehaviorPage/subPage/PauseAnalysis.vue

@@ -0,0 +1,26 @@
+<script setup lang="ts">
+import "./TotalAnalysis.scss"
+import PauseCanvas from "@/views/BehaviorPage/component/PauseCanvas.vue";
+</script>
+
+<template>
+  <el-card class="detail-card">
+    <template #header>
+      <div>
+        <span class="card-title">
+          停顿分析
+        </span>
+      </div>
+    </template>
+    <div class="total-canvas-container">
+      <PauseCanvas
+          :split-number="10"
+          :bar-width="20"
+          :assignment-id="1"/>
+    </div>
+  </el-card>
+</template>
+
+<style scoped>
+
+</style>

+ 25 - 0
src/views/BehaviorPage/subPage/ProcessAnalysis.vue

@@ -0,0 +1,25 @@
+<script setup lang="ts">
+import "./TotalAnalysis.scss"
+import TotalCanvas from "@/views/BehaviorPage/component/TotalCanvas.vue";
+</script>
+
+<template>
+  <el-card class="detail-card">
+    <template #header>
+      <div>
+        <span class="card-title">
+          写作过程
+        </span>
+      </div>
+    </template>
+    <div class="total-canvas-container">
+      <span style="font-size: 50px">
+        TODO
+      </span>
+    </div>
+  </el-card>
+</template>
+
+<style scoped>
+
+</style>

+ 21 - 0
src/views/BehaviorPage/subPage/TotalAnalysis.scss

@@ -0,0 +1,21 @@
+.detail-card{
+  width: 70%;
+  height: auto;
+  border-radius: 16px;
+  margin-bottom: 50px;
+  min-width: 700px;
+}
+.card-title{
+  font-size: 16px;
+  font-weight: bold;
+}
+.total-canvas-container{
+  width: 100%;
+  height: 400px;
+}
+.table-container{
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}

+ 71 - 0
src/views/BehaviorPage/subPage/TotalAnalysis.vue

@@ -0,0 +1,71 @@
+<script setup lang="ts">
+import "./TotalAnalysis.scss"
+import TotalCanvas from "@/views/BehaviorPage/component/TotalCanvas.vue";
+import {onMounted, ref} from "vue";
+
+let tableData = ref([])
+function getTagType(type:string){
+  if (type === "focus"){
+    return "primary"
+  }
+  else if (type === "mouse"){
+    return "success"
+  }
+  else if (type === "keyboard"){
+    return "error"
+  }
+}
+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"},
+  ];
+}
+
+onMounted(()=>{
+  getBehaviorData();
+})
+
+
+</script>
+
+<template>
+  <el-card class="detail-card">
+    <template #header>
+      <div>
+        <span class="card-title">
+          总体分析
+        </span>
+      </div>
+    </template>
+    <div class="total-canvas-container">
+      <TotalCanvas
+          :assignment-id="1"/>
+    </div>
+    <div class="table-container">
+      <el-table :data="tableData" style="width: 90%" max-height="500">
+        <el-table-column prop="Id" label="Id" width="40"/>
+        <el-table-column prop="EventType" label="Event Type" >
+          <template #default="scope">
+            <el-tag :type="getTagType(scope.row.EventType)">{{ scope.row.EventType }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="Output" label="Output" show-overflow-tooltip width="160"/>
+        <el-table-column prop="Position" label="Position"  />
+        <el-table-column prop="DocLength" label="DocLength" />
+        <el-table-column prop="StartTime" label="StartTime"/>
+        <el-table-column prop="EndTime" label="EndTime" />
+      </el-table>
+    </div>
+  </el-card>
+</template>
+
+<style scoped>
+
+</style>

+ 4 - 1
src/views/Home/Role.vue

@@ -4,7 +4,7 @@
   Created Date: 2024-6-30
 -->
 <template>
-  <div class="home-container">
+  <div id="home-container" class="home-container">
     <router-view v-if="isRouterAlive"></router-view>
   </div>
 </template>
@@ -61,6 +61,9 @@
         case 'MyWork':
           router.push("/home/myCorrect/course");
           break;
+        case 'Behavior':
+          router.push("/home/behavior/main");
+          break;
       }
     }
   }