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

feat:行为记录可视化基本完成,需要对接前后端

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

+ 1 - 1
src/views/BehaviorPage/BehaviorBoard.vue

@@ -251,7 +251,7 @@ onMounted(()=>{
           </template>
         </el-page-header>
       </template>
-      <el-descriptions column="2" v-if="currentEngagement">
+      <el-descriptions column="2" v-if="currentEngagement" style="width: 90%;margin-left: 5%">
         <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>

+ 209 - 6
src/views/BehaviorPage/component/EditCanvas.vue

@@ -10,13 +10,15 @@ const props = defineProps<{
 const currentEngagement = inject('currentEngagement') as Ref<any>;
 let productData = [];
 let cursorData = [];
-watch(currentEngagement,getCanvasData);
+let processData = [];
+// 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],
@@ -202,6 +204,12 @@ function getCanvasData(){
     [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 count = 0;
+  processData = [];
+  for (let i = 0; i < productData.length; i++) {
+    if (i%2 == 0)count+=7;
+    processData.push([productData[i][0],productData[i][1]+count]);
+  }
   let chartDom = document.getElementById('edit-canvas');
   let myChart = echarts.getInstanceByDom(chartDom);
   let option = {
@@ -264,14 +272,200 @@ function getCanvasData(){
   myChart.setOption( option);
 }
 
-
-
-
-
-
 onMounted(()=>{
   let chartDom = document.getElementById('edit-canvas');
   let myChart = echarts.init(chartDom);
+  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 count = 0;
+  processData = [];
+  for (let i = 0; i < productData.length; i++) {
+    if (i%2 == 0)count+=7;
+    processData.push([productData[i][0],productData[i][1]+count]);
+  }
   let option = {
     xAxis: {
       type: 'time',
@@ -326,6 +520,15 @@ onMounted(()=>{
         name:'cursor',
         data: cursorData,
         type: 'line'
+      },
+      {
+        itemStyle: {
+          color:'#1d86dc',
+          opacity: 0
+        },
+        name:'process',
+        data: processData,
+        type: 'line'
       }
     ]
   };

+ 376 - 0
src/views/BehaviorPage/component/HeatMap.vue

@@ -0,0 +1,376 @@
+<template>
+  <div id="HeatMap">
+    <div class="container">
+      <!-- 控制区:自定义阈值和差值选择框 -->
+      <div class="controls" style="margin-bottom: 20px;">
+        <label>
+          停顿阈值:
+          <select v-model="customThreshold">
+            <option v-for="v in [1000,2000,3000,4000,5000]" :key="v" :value="v">{{ v }}ms</option>
+          </select>
+        </label>
+        <label style="margin-left: 20px;">
+          区间差值:
+          <select v-model="customStep">
+            <option v-for="v in [500,1000,2000]" :key="v" :value="v">{{ v }}ms</option>
+          </select>
+        </label>
+      </div>
+      <!-- 文本和热度图显示区域 -->
+      <div class="text-container">
+        <div
+            v-for="(line, lineIndex) in formattedLines"
+            :key="lineIndex"
+            class="text-line"
+        >
+          <!-- 显示文本行 -->
+          <span
+              v-for="(char, charIndex) in line.chars"
+              :key="charIndex"
+              class="char"
+          >
+            {{ char }}
+          </span>
+          <!-- 显示热度图 -->
+          <div
+              class="heatmap"
+              :style="{ width: `${line.width}px` }"
+          >
+            <div
+                v-for="(item, itemIndex) in line.pauses"
+                :key="itemIndex"
+                class="heatmap-rect"
+                :style="{
+                backgroundColor: getColor(item.totalPauseTime),
+                left: `${item.position}px`,
+                width: `${heatmapRectWidth}px`,
+                height: `${heatmapRectHeight}px`
+              }"
+                @mouseover="showTooltip(item.totalPauseTime, $event)"
+                @mouseout="hideTooltip"
+            />
+          </div>
+        </div>
+      </div>
+
+      <!-- 颜色图例 -->
+      <div class="legend">
+        <div
+            v-for="(item, index) in colorLegend"
+            :key="index"
+            class="legend-item"
+        >
+          <div
+              class="legend-color"
+              :style="{ backgroundColor: item.color }"
+          />
+          <span>{{ item.label }}</span>
+        </div>
+      </div>
+
+      <!-- 提示框 -->
+      <div
+          v-if="tooltipVisible"
+          class="tooltip"
+          :style="{ left: `${tooltipX}px`, top: `${tooltipY}px` }"
+      >
+        totalPauseTime = {{ tooltipText }}ms
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {ref, computed, onMounted, 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)
+const tooltipText = ref('')
+const tooltipX = ref(0)
+const tooltipY = ref(0)
+
+// 自定义停顿阈值和区间差值
+const customThreshold = ref(2000)
+const customStep = ref(1000)
+
+// 动态生成 colorLegend
+const colorLegend = computed(() => {
+  const arr = []
+  let start = customThreshold.value
+  let colorArr = ['#FFF6CC', '#FFD699', '#FFA64D', '#FF7519', '#CC2900']
+  for (let i = 0; i < colorArr.length - 1; i++) {
+    arr.push({
+      time: start + i * customStep.value,
+      color: colorArr[i],
+      label: i === 0
+          ? `≤${start}ms`
+          : `${start + (i - 1) * customStep.value}-${start + i * customStep.value}ms`
+    })
+  }
+  arr.push({
+    time: Infinity,
+    color: colorArr[colorArr.length - 1],
+    label: `≥${start + (colorArr.length - 2) * customStep.value}ms`
+  })
+  return arr
+})
+
+// 用于存储 events 数据
+const actionData = ref([]);
+// 这里 studentId 和 assignmentId 可根据实际情况传递
+
+const engagement = inject('currentEngagement') as Ref<any>;
+watch(engagement,async () => {
+  try {
+    studentId = engagement.value.studentId;
+    assignmentId = engagement.value.assignmentId;
+    const res = await bevaviorApis.selectAllBehaviorRecord(studentId, assignmentId)
+    if (res.data && res.data.data && Array.isArray(res.data.data.events)) {
+      actionData.value = res.data.data.events
+    } else {
+      actionData.value = []
+    }
+  } catch (e) {
+    actionData.value = []
+    // 可加错误提示
+  }
+})
+
+let studentId = 16
+let assignmentId = 7
+
+onMounted(async () => {
+  try {
+    studentId = engagement.value.studentId;
+    assignmentId = engagement.value.assignmentId;
+    const res = await bevaviorApis.selectAllBehaviorRecord(studentId, assignmentId)
+    if (res.data && res.data.data && Array.isArray(res.data.data.events)) {
+      actionData.value = res.data.data.events
+    } else {
+      actionData.value = []
+    }
+  } catch (e) {
+    actionData.value = []
+    // 可加错误提示
+  }
+})
+
+// 计算属性 - 处理原始数据并格式化为多行显示
+const formattedLines = computed(() => {
+  // 计算每行最大宽度(适应窗口大小)
+  const maxLineWidth = Math.min(1000, window.innerWidth - 40)
+  // 使用Map存储每个位置的字符、第一次操作的startStamp、最后一次操作的startStamp和保留位置
+  const positionMap = new Map()
+  let lastStartStamp = 0;
+
+  actionData.value.forEach(item => {
+    // 只处理 insert、delete、copy 类型的数据条目
+    if (["insert", "delete", "copy"].includes(item.type)) {
+      const {retain, insert, startStamp} = item
+      // 删除操作显示为空格
+      const char = item.type === 'delete' ? ' ' : insert
+
+      if (positionMap.has(retain)) {
+        // 已有记录,更新字符和最后一次操作的startStamp
+        const record = positionMap.get(retain)
+        record.char = char
+        record.lastStartStamp = startStamp
+      } else {
+        // 新增记录
+        positionMap.set(retain, {
+          char,
+          firstStartStamp: lastStartStamp,
+          lastStartStamp: startStamp,
+          retain
+        })
+      }
+      lastStartStamp = startStamp;
+    }
+  })
+
+  // 计算每个位置的totalPauseTime
+  positionMap.forEach(record => {
+    record.totalPauseTime = record.lastStartStamp - record.firstStartStamp
+  })
+
+  // 将Map转换为数组并按位置排序
+  const finalPositions = Array.from(positionMap.values()).sort((a, b) => a.retain - b.retain)
+  const lines = []
+  // 当前行对象,存储字符、停顿信息和宽度
+  let currentLine = {chars: [], pauses: [], width: 0, retainOffset: 0}
+
+  // 处理每个字符,生成多行显示
+  finalPositions.forEach(item => {
+    // 处理单个字符的函数
+    const processCharacter = (char, isFirstChar = false) => {
+      // 计算字符宽度(空格和普通字符不同)
+      const charWidthVal = char === ' ' ? spaceWidth.value : charWidth.value
+      const nextLineWidth = currentLine.width + charWidthVal
+
+      // 如果添加字符后超过最大宽度且当前行已有内容,则换行
+      if (nextLineWidth > maxLineWidth && currentLine.chars.length > 0) {
+        lines.push(currentLine)
+        // 创建新行,计算保留位置偏移量
+        currentLine = {
+          chars: [],
+          pauses: [],
+          width: 0,
+          retainOffset: lines.reduce((sum, line) => sum + line.chars.length, 0)
+        }
+      }
+
+      // 添加字符到当前行
+      currentLine.chars.push(char)
+      currentLine.width += charWidthVal
+
+      // 如果停顿时间超过自定义阈值且是字符的第一个字符,则记录停顿
+      if (item.totalPauseTime > customThreshold.value && isFirstChar) {
+        // 计算热度图矩形位置(字符中心偏左一点)
+        const position = currentLine.chars.length * charWidth.value - (charWidth.value / 1.2)
+        currentLine.pauses.push({
+          retain: item.retain,
+          totalPauseTime: item.totalPauseTime,
+          position
+        })
+      }
+    }
+
+    // 处理多字符插入(如复制粘贴的文本)
+    if (item.char && item.char.length > 1) {
+      item.char.split('').forEach((char, index) => {
+        // 只对第一个字符标记停顿
+        processCharacter(char, index === 0)
+      })
+    } else {
+      // 处理单字符插入
+      processCharacter(item.char, true)
+    }
+  })
+
+  // 添加最后一行(如果有内容)
+  if (currentLine.chars.length > 0) {
+    lines.push(currentLine)
+  }
+
+  return lines
+})
+
+// 根据停顿时间获取对应的颜色
+const getColor = (pauseTime) => {
+  // 查找第一个满足 pauseTime <= item.time 的颜色项
+  const legend = colorLegend.value.find(item => pauseTime <= item.time)
+  // 返回找到的颜色,或默认最深的颜色
+  return legend?.color || colorLegend.value[colorLegend.value.length - 1].color
+}
+
+// 显示提示框
+const showTooltip = (totalPauseTime, event) => {
+  tooltipVisible.value = true
+  tooltipText.value = totalPauseTime
+  tooltipX.value = event.clientX + 10
+  tooltipY.value = event.clientY + 10
+}
+
+// 隐藏提示框
+const hideTooltip = () => {
+  tooltipVisible.value = false
+}
+</script>
+
+<style scoped>
+#HeatMap {
+  display: flex;
+  flex-direction: column;
+  align-items: center;      /* 垂直居中 */
+  height: 500px;            /* 占满整个视口高度 */
+  width: 100%;              /* 占满整个宽度 */
+}
+
+.container {
+  max-width: 1000px;
+  margin: 0 auto;
+  padding: 20px;
+  font-family: Arial, sans-serif;
+}
+
+.text-container {
+  position: relative;
+  margin-bottom: 30px;
+}
+
+.text-line {
+  position: relative;
+  margin-bottom: 25px;
+  white-space: nowrap;
+}
+
+.char {
+  display: inline-block;
+  width: v-bind('charWidth + "px"');
+  text-align: center;
+}
+
+.heatmap {
+  position: absolute;
+  bottom: -20px;
+  left: 0;
+  height: v-bind('heatmapRectHeight + "px"');
+}
+
+.heatmap-rect {
+  position: absolute;
+  border-radius: 2px;
+  cursor: pointer;
+}
+
+.legend {
+  display: flex;
+  gap: 15px;
+  margin-top: 50px;
+}
+
+.legend-item {
+  display: flex;
+  align-items: center;
+  gap: 5px;
+}
+
+.legend-color {
+  width: 20px;
+  height: 20px;
+  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;
+  margin-bottom: 20px;
+}
+.controls label {
+  margin-right: 20px;
+}
+.controls select {
+  margin-left: 5px;
+  padding: 2px 8px;
+}
+</style>

+ 1 - 11
src/views/BehaviorPage/component/PauseCanvas.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import {onMounted} from "vue";
+import {onMounted,type Ref, ref} from "vue";
 import * as echarts from 'echarts';
 const props = defineProps<{
   assignmentId:number,
@@ -13,14 +13,6 @@ onMounted(()=>{
   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: {
@@ -312,8 +304,6 @@ onMounted(()=>{
           [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'
       }

+ 833 - 1
src/views/BehaviorPage/component/ProcessCanvas.vue

@@ -1,11 +1,843 @@
 <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 = [];
+let processData = [];
+// 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 count = 0;
+  processData = [];
+  for (let i = 0; i < productData.length; i++) {
+    if (i%2 == 0)count+=7;
+    processData.push([productData[i][0],productData[i][1]+count]);
+  }
+  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);
+}
+
+//停顿数据
+let pauseData = [
+  [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],
+].filter((item:[Date,number]) => item[0].getMinutes()<=29)
+
+
+
+onMounted(()=>{
+  let chartDom = document.getElementById('process-canvas');
+  let myChart = echarts.init(chartDom);
+  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 count = 0;
+  processData = [];
+  for (let i = 0; i < productData.length; i++) {
+    if (i%2 == 0)count+=7;
+    processData.push([productData[i][0],productData[i][1]+count]);
+  }
+  let option = {
+    xAxis: {
+      type: 'time',
+      axisPointer: {
+        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',
+      position:'left',
+    },{
+      type: 'value',
+      name:'time',
+      position: 'right',
+      axisLabel: {
+        formatter: '{value} ms'
+      }
+    }],
+    tooltip:{
+
+    },
+    dataZoom: [
+      {
+        type: 'inside',
+      },
+      {
+        type: 'slider',
+      }
+    ],
+    legend:{
+      left:0
+    },
+    visualMap: {
+      type: 'continuous', // 连续型映射
+      calculable: true,
+      min: 0,             // 数据最小值
+      max: 150,           // 数据最大值
+      dimension: 1,       // 根据第2个维度(y值)映射颜色
+      inRange: {
+        color: ['yellow', 'red'] // 渐变范围:小值→蓝色,大值→红色
+      },
+      seriesIndex: [3],
+      itemWidth: 10,
+      top:0,
+      right:0,
+      orient:'horizontal',
+      align: 'right',
+    },
+    series: [
+      {
+        itemStyle: {
+          color:'#37ff00',
+          opacity: 0
+        },
+        name:'product',
+        data: productData,
+        type: 'line',
+        yAxisIndex: 0
+      },
+      {
+        itemStyle: {
+          color:'#37ff00',
+          opacity: 0
+        },
+        lineStyle: {
+          type: "dotted"
+        },
+        name:'cursor',
+        data: cursorData,
+        type: 'line',
+        yAxisIndex: 0
+      },
+      {
+        itemStyle: {
+          color:'#1d86dc',
+          opacity: 0
+        },
+        name:'process',
+        data: processData,
+        type: 'line',
+        yAxisIndex: 0
+      },
+      {
+        symbolSize: 10,
+
+        data: pauseData,
+        type: 'scatter',
+        yAxisIndex: 1
+      }
+    ]
+  };
+  myChart.setOption( option);
+  window.addEventListener('resize', ()=>{
+    myChart.resize()
+  })
+})
 </script>
 
 <template>
+  <div id="process-canvas" class="canvas-container">
 
+  </div>
 </template>
 
 <style scoped>
-
+.canvas-container{
+  width: 100%;
+  height: 100%;
+}
 </style>

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

@@ -4,6 +4,7 @@ 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";
+import ProcessCanvas from "@/views/BehaviorPage/component/ProcessCanvas.vue";
 
 function toDetail(index:number){
   if ( index === 1){
@@ -94,8 +95,10 @@ function toDetail(index:number){
         </span>
       </div>
     </template>
-    <div id="process-canvas" class="canvas">
-
+    <div style="width: 100%;height: 350px">
+      <ProcessCanvas
+          :split-number="5"
+          :assignment-id="1"/>
     </div>
   </el-card>
 </div>

+ 75 - 1
src/views/BehaviorPage/subPage/EditAnalysis.vue

@@ -1,7 +1,64 @@
 <script setup lang="ts">
 import "./TotalAnalysis.scss"
-import TotalCanvas from "@/views/BehaviorPage/component/TotalCanvas.vue";
 import EditCanvas from "@/views/BehaviorPage/component/EditCanvas.vue";
+import {onMounted, ref} from "vue";
+let tableData = ref([])
+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",
+    },
+
+  ]
+}
+onMounted(()=>{
+  getTableData()
+})
 </script>
 
 <template>
@@ -18,6 +75,23 @@ import EditCanvas from "@/views/BehaviorPage/component/EditCanvas.vue";
           :split-number="10"
           :assignment-id="1"/>
     </div>
+    <div class="table-container">
+      <el-table
+          empty-text="暂无数据"
+          :data="tableData" style="width: 90%" max-height="500">
+        <el-table-column prop="revision" label="#Revision" width="95"/>
+        <el-table-column prop="type" label="Type" width="100">
+        </el-table-column>
+        <el-table-column prop="content" label="content" show-overflow-tooltip width="110"/>
+        <el-table-column prop="edits" label="Edits"  />
+        <el-table-column prop="start" label="Start" />
+        <el-table-column prop="end" label="End"/>
+        <el-table-column prop="duration" label="Duration" />
+        <el-table-column prop="beginPos" label="BeginPos" width="90"/>
+        <el-table-column prop="endPos" label="EndPos"/>
+        <el-table-column prop="length" label="Length" />
+      </el-table>
+    </div>
   </el-card>
 </template>
 

+ 64 - 1
src/views/BehaviorPage/subPage/PauseAnalysis.vue

@@ -1,6 +1,27 @@
 <script setup lang="ts">
 import "./TotalAnalysis.scss"
 import PauseCanvas from "@/views/BehaviorPage/component/PauseCanvas.vue";
+import {ref,type Ref} from "vue";
+import HeatMap from "@/views/BehaviorPage/component/HeatMap.vue";
+
+interface StatisticData{
+  totalTime:number,
+  pauseTimes:number,
+  averagePauseTime:number,
+  medianPauseTime:number,
+  standardDeviation:number,
+}
+const data:Ref<StatisticData> = ref({
+  totalTime:56,
+  pauseTimes:856,
+  averagePauseTime:3600,
+  medianPauseTime:2500,
+  standardDeviation:1.8
+})
+function getStatisticData(){
+  //TODO
+  // data.value = xxxxx
+}
 </script>
 
 <template>
@@ -12,15 +33,57 @@ import PauseCanvas from "@/views/BehaviorPage/component/PauseCanvas.vue";
         </span>
       </div>
     </template>
+    <div class="statistic-container">
+      <el-statistic
+          :value="data.totalTime"
+          suffix="min"
+          title="停顿总时长">
+        <template #suffix>
+          <span class="statistic-suffix" >min</span>
+        </template>
+      </el-statistic>
+      <el-divider direction="vertical" style="height: 50px;margin: 0"/>
+      <el-statistic
+          :value="data.pauseTimes"
+          title="停顿总次数">
+        <template #suffix>
+          <span class="statistic-suffix" >times</span>
+        </template>
+      </el-statistic>
+      <el-divider direction="vertical" style="height: 50px;margin: 0"/>
+      <el-statistic
+          :value="data.averagePauseTime"
+          title="平均停顿时长">
+        <template #suffix>
+          <span class="statistic-suffix" >ms</span>
+        </template>
+      </el-statistic>
+      <el-divider direction="vertical" style="height: 50px;margin: 0"/>
+      <el-statistic
+          :value="data.medianPauseTime"
+          title="停顿时长中位数">
+        <template #suffix>
+          <span class="statistic-suffix" >ms</span>
+        </template>
+      </el-statistic>
+      <el-divider direction="vertical" style="height: 50px;margin: 0"/>
+      <el-statistic
+          :value="data.standardDeviation"
+          title="停顿时长标准差"/>
+    </div>
     <div class="total-canvas-container">
       <PauseCanvas
           :split-number="10"
           :bar-width="20"
           :assignment-id="1"/>
     </div>
+    <HeatMap/>
   </el-card>
 </template>
 
 <style scoped>
-
+.statistic-suffix{
+  font-size: 12px;
+  color: var(--el-text-color-regular);
+}
 </style>

+ 2 - 3
src/views/BehaviorPage/subPage/ProcessAnalysis.vue

@@ -1,6 +1,7 @@
 <script setup lang="ts">
 import "./TotalAnalysis.scss"
 import TotalCanvas from "@/views/BehaviorPage/component/TotalCanvas.vue";
+import ProcessCanvas from "@/views/BehaviorPage/component/ProcessCanvas.vue";
 </script>
 
 <template>
@@ -13,9 +14,7 @@ import TotalCanvas from "@/views/BehaviorPage/component/TotalCanvas.vue";
       </div>
     </template>
     <div class="total-canvas-container">
-      <span style="font-size: 50px">
-        TODO
-      </span>
+      <ProcessCanvas assignment-id="-1"  split-number="3"/>
     </div>
   </el-card>
 </template>

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

@@ -19,3 +19,11 @@
   flex-direction: column;
   align-items: center;
 }
+.statistic-container{
+  width: 90%;
+  margin-left: 5%;
+  display: flex;
+  flex-direction: row;
+  justify-content: space-around;
+  align-items: center;
+}