林旭祥 hai 8 meses
pai
achega
3af6760f5c

+ 1 - 1
src/api/module/common.ts

@@ -61,7 +61,7 @@ export default {
   //获取测试数据列表
   reportList: (data: any) => {
     return req({
-      url: '/report/index',
+      url: '/exam/today/examdata/list',
       method: 'get',
       data: data
     });

+ 6 - 11
src/components/FaceWindow/index.vue

@@ -18,7 +18,7 @@
               <img src="@/assets/images/test/profilePicture.png" />
             </div>
             <div class="name" :class="{ 'name2': faceCheckStu.student_id }">
-              {{ faceCheckStu.student_id ? faceCheckStu.name : parameter.gesture ? "请举手看摄像头人脸识别" : "请看摄像头进行人脸识别" }}
+              {{ faceCheckStu.student_id ? faceCheckStu.name : gesture ? "请举手看摄像头人脸识别" : "请看摄像头进行人脸识别" }}
             </div>
           </div>
         </div>
@@ -38,6 +38,10 @@ const props = defineProps({
     type: Object,
     default: {}
   },
+  gesture: {
+    type: Boolean,
+    default: false
+  },
 });
 
 
@@ -64,15 +68,6 @@ const close = () => {
   faceState.value = false;
 };
 
-onBeforeMount(() => {
-  parameter.value = route.query;
-  if (parameter.value.gesture == 'true') {
-    parameter.value.gesture = true
-  } else {
-    parameter.value.gesture = false
-  }
-})
-
 //暴露给父组件用
 defineExpose({
   faceState,
@@ -155,7 +150,7 @@ defineExpose({
       .name {
         width: 100%;
         color: #1A293A;
-        font-size: 2.21rem;
+        font-size: 2.0rem;
       }
 
       .name2 {

+ 21 - 4
src/components/OptionWindow/index.vue

@@ -12,7 +12,7 @@
           <div class="content">
             <div class="areaBox">
               <div class="content-title content-title2">
-                {{ project.key !== 'skiprope' ? '请选择测试区域' : '请选择设备组' }}
+                {{ project.key !== 'skiprope' ? '测试区域' : '设备组' }}
               </div>
               <div class="testAreaChooseRoll">
                 <div class="li" v-for="(item, index) in areaList"
@@ -83,7 +83,12 @@
             </div>
           </div>
           <div class="bottom">
-            <div class="btn" @click="confirm">确 定</div>
+            <div class="btn" @click="confirm">
+              <el-icon class="is-loading" v-if="loading">
+                <Loading />
+              </el-icon>
+              <div>确 定</div>
+            </div>
           </div>
         </div>
       </div>
@@ -146,9 +151,10 @@ const data = reactive<any>({
   classList: [],
   chooseArea: [],
   chooseAllState: false,
+  loading: false,
 });
 
-const { optionForm, optionWindow, project, musicList, classList, chooseArea, chooseAllState } = toRefs(data);
+const { optionForm, optionWindow, project, musicList, classList, chooseArea, chooseAllState, loading } = toRefs(data);
 
 //打开
 const open = (data: any) => {
@@ -278,6 +284,7 @@ const confirm = (data: any) => {
     ElMessage({ message: message, type: 'error', duration: 3 * 1000 });
     return false;
   }
+  loading.value = true;
   if (chooseArea.value.length > 1) {
     //多区域
     router.push({ path: '/train/multiple', query: optionForm.value });
@@ -289,12 +296,15 @@ const confirm = (data: any) => {
     //单项
     router.push({ path: '/train/test', query: optionForm.value });
   }
-
 };
 
 onMounted(() => {
 })
 
+onUnmounted(() => {
+  loading.value = false;
+})
+
 //暴露给父组件用
 defineExpose({
   open,
@@ -497,6 +507,13 @@ defineExpose({
         background: radial-gradient(141% 126% at 5% 93%, #8EFFA9 0%, #07FFE7 100%);
         box-shadow: inset 0px 1px 0px 2px rgba(255, 255, 255, 0.5577);
         cursor: pointer;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+
+        .is-loading {
+          margin-right: 5px;
+        }
       }
     }
   }

+ 12 - 7
src/components/ReportWindow/index.vue

@@ -63,17 +63,17 @@
             <div class="close" @click="close"></div>
           </div>
           <div class="content">
-            <div class="video" v-show="currentTab == 1 ? true : false">
+            <div class="video" v-if="reportDetails.video_urls?.length" v-show="currentTab == 1 ? true : false">
               <!--视频开始-->
               <video v-for="(item, index) in reportDetails.video_urls" :key="index"
                 style="width: 100%; object-fit: fill" :src="item"
-                :poster="item?.replace('  ', '') + '?x-oss-process=video/snapshot,t_100,f_jpg,w_0,h_0,ar_auto'"
-                controls>
+                :poster="item?.replace('  ', '') + '?x-oss-process=video/snapshot,t_100,f_jpg,w_0,h_0,ar_auto'" controls
+                autoplay>
                 您的浏览器不支持 video 标签。
               </video>
               <!--视频结束-->
             </div>
-            <div class="pic" v-show="currentTab == 2 ? true : false">
+            <div class="pic" v-if="reportDetails.image_urls?.length" v-show="currentTab == 2 ? true : false">
               <!--图片开始-->
               <swiper ref="refSwiper" :slides-per-view="1" :slides-per-group="1" :space-between="0"
                 :modules="[Navigation, Pagination]" :navigation="{
@@ -144,9 +144,14 @@ const getReportDetails = async () => {
   reportDetails.value = {};
   let params: any = {
     exam_name: type.value,
-    student_ids: faceCheckStu.value.student_id,
-    result_id_info: faceCheckStu.value.result_id_info
   };
+  if (faceCheckStu.value.result_id_info) {
+    params.student_ids = faceCheckStu.value.student_id;
+    params.result_id_info = faceCheckStu.value.result_id_info;
+  } else {
+    params.student_id = faceCheckStu.value.student_id;
+    params.result_id = faceCheckStu.value.result_id;
+  }
   await proxy?.$http.common.reportDetails(params).then((res: any) => {
     if (res.data.length > 0) {
       reportDetails.value = res.data[0];
@@ -164,7 +169,7 @@ const open = async (typeData: any, data: any) => {
   console.log("data", data)
   type.value = typeData;
   faceCheckStu.value = data;
-  if (faceCheckStu.value.result_id_info == undefined) {
+  if (faceCheckStu.value.result_id == undefined && faceCheckStu.value.result_id_info == undefined) {
     proxy?.$modal.msgWarning("查询参数错误");
     return false;
   }

+ 2 - 1
src/router/index.ts

@@ -18,7 +18,8 @@ const router = createRouter({
         { path: '/train/multiple', component: () => import('@/views/train/multiple.vue') },
         { path: '/set', component: () => import('@/views/set/index.vue') },
         { path: '/set/config', component: () => import('@/views/set/config.vue') },
-        { path: '/ranking', component: () => import('@/views/ranking/index.vue') }
+        { path: '/ranking', component: () => import('@/views/ranking/index.vue') },
+        { path: '/course', component: () => import('@/views/course/index.vue') }
       ]
     }
   ]

+ 1 - 0
src/types/components.d.ts

@@ -10,6 +10,7 @@ declare module 'vue' {
     ChooseStudent: typeof import('./../components/ChooseStudent/index.vue')['default']
     ElAvatar: typeof import('element-plus/es')['ElAvatar']
     ElButton: typeof import('element-plus/es')['ElButton']
+    ElIcon: typeof import('element-plus/es')['ElIcon']
     ElInput: typeof import('element-plus/es')['ElInput']
     ElOption: typeof import('element-plus/es')['ElOption']
     ElPagination: typeof import('element-plus/es')['ElPagination']

+ 5 - 5
src/utils/speech.ts

@@ -85,11 +85,11 @@ export const speckText = (text: any) => {
     // '各就位,预备': 'Ready.mp3',
     // '还有30秒,加油!': 'countdown30s.mp3',
     // '还有10秒,坚持住!': 'countdown10s.mp3',
-    // '5': '5.mp3',
-    // '4': '4.mp3',
-    // '3': '3.mp3',
-    // '2': '2.mp3',
-    // '1': '1.mp3',
+    '5': '5.mp3',
+    '4': '4.mp3',
+    '3': '3.mp3',
+    '2': '2.mp3',
+    '1': '1.mp3',
     // '预备和哨声': 'PrepareWhistle.mp3',
     跑: 'run.mp3',
     哨声: 'shaosheng.mp3',

+ 274 - 0
src/views/course/index.vue

@@ -0,0 +1,274 @@
+<template>
+  <div class="train">
+    <div class="logo"> <img src="@/assets/images/logo.png">
+      </img></div>
+    <div class="close" @click="getExit"></div>
+    <div class="menu">
+      <div class="box">
+        <div class="left">
+          <ul>
+            <li :class="{ on: currentData.key == item.key }" v-for="(item, index) in courseList " :key="index"
+              @click="clickVideo(item)">
+              <div class="pic"><img :src="'/src/assets/images/train/' + item.key + '.png'">
+              </div>
+              <div class="name">
+                {{ item.title }}
+              </div>
+            </li>
+          </ul>
+        </div>
+        <div class="right">
+          <!--视频开始-->
+          <video style="width: 100%; object-fit: fill" :src="currentData.url"
+            :poster="currentData.url?.replace('  ', '') + '?x-oss-process=video/snapshot,t_100,f_jpg,w_0,h_0,ar_auto'"
+            controls autoplay>
+            您的浏览器不支持 video 标签。
+          </video>
+          <!--视频结束-->
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup name="courseIndex" lang="ts">
+const router = useRouter();
+const { proxy } = getCurrentInstance() as any;
+
+
+const data = reactive<any>({
+  currentData: {}
+});
+
+const { currentData } = toRefs(data);
+
+
+const courseList = [
+  {
+    title: "立定跳远",
+    key: "jump",
+    content:
+      "立定跳远是指不用助跑从立定姿势开始的跳远,是集弹跳、爆发力、身体的协调性和技术等方面的身体素质于一体的运动。",
+    url: "https://aiexam-data.oss-cn-shenzhen.aliyuncs.com/midexam/samples/jump.mp4",
+    img: "../../static/images/analysis/jump.png",
+  },
+  {
+    title: "仰卧起坐",
+    key: "situp",
+    content:
+      "仰卧起坐,一种锻炼身体的方式。仰卧,两腿并拢,两手上举,利用腹肌收缩,两臂向前摆动,迅速成坐姿,上体继续前屈,两手触脚面,低头;然后还原成坐姿,如此连续进行。",
+    url: "https://aiexam-data.oss-cn-shenzhen.aliyuncs.com/midexam/samples/situp.mp4",
+    img: "../../static/images/analysis/sitUp.png",
+  },
+  {
+    title: "引体向上",
+    key: "pullup",
+    content:
+      "引体向上是人体手臂抓握力、上肢力量、背部力量和腰腹肌力量的美与力量的综合体现,这一动作练习对提高上肢悬垂力量、握力以及有着十分关键的作用, [3] 是单杠卷身上等杠上动作的基础。",
+    url: "https://aiexam-data.oss-cn-shenzhen.aliyuncs.com/midexam/samples/pullup.mp4",
+    img: "../../static/images/analysis/pullUp.png",
+  },
+  {
+    title: "实心球",
+    key: "solidball",
+    content:
+      "实心球是一项力量性和动作速度项目,是以力量为基础,以动作速度为核心的投掷项目。影响实心球成绩有三个因素,实心球的出手速度、出手角度及出手高度,其中出手速度是最重要的因素。",
+    url: "https://aiexam-data.oss-cn-shenzhen.aliyuncs.com/midexam/samples/solidball.mp4",
+    img: "../../static/images/analysis/solidBall.png",
+  },
+
+  {
+    title: "斜身引体",
+    key: "sidepullup",
+    content:
+      "斜身引体用高度适宜的低单杠,使杠面高度与受试者胸部齐平。受试者面向单杠,自然站立,两手分开与肩同宽,正握杠,两腿前伸,两脚着地并由同伴压住两脚,保持两臂与躯干呈90°,身体斜向下垂;然后做屈臂引体,当下颌能触到或超过横杠时,伸臂复原,为完成一次。",
+    url: "https://aiexam-data.oss-cn-shenzhen.aliyuncs.com/midexam/samples/sidepullup.mp4",
+    img: "../../static/images/analysis/sidepullup.png",
+  },
+  {
+    title: "三级蛙跳",
+    key: "trijump",
+    content:
+      "三级蛙跳是一种高质量的身体锻炼方式,其动作基本原理为:弹跳、稳定和动态平衡。",
+    url: "https://aiexam-data.oss-cn-shenzhen.aliyuncs.com/midexam/samples/trijump.mp4",
+    img: "../../static/images/analysis/trijump.png",
+  },
+  {
+    title: "铅球",
+    key: "shotput",
+    content:
+      "运动员用手托住铅球,在投掷圈内以滑步等形式用单手将球自肩上方推出,落入规定区域内为有效。",
+    url: "https://aiexam-data.oss-cn-shenzhen.aliyuncs.com/midexam/samples/shotput.mp4",
+    img: "../../static/images/analysis/shotput.png",
+  },
+]
+
+/**
+ * 点击视频
+*/
+const clickVideo = (data: any) => {
+  currentData.value = data;
+};
+
+/**
+ * 退出
+*/
+const getExit = () => {
+  router.go(-1);
+};
+
+onMounted(() => {
+  currentData.value = courseList[0];
+})
+
+</script>
+
+<style lang="scss" scoped>
+$topPadding: 5.19rem;
+$waiPadding: 6.51rem;
+
+.logo {
+  img {
+    width: 14.563rem;
+  }
+
+  position: absolute;
+  left: $waiPadding;
+  top: $topPadding;
+}
+
+.close {
+  width: 3.2rem;
+  height: 3.2rem;
+  position: absolute;
+  right: calc($waiPadding - 3.2rem);
+  top: $topPadding;
+  box-sizing: border-box;
+  border: 1px solid #979797;
+  background-image: url("@/assets/images/common/close.png");
+  background-position: center;
+  background-repeat: no-repeat;
+  background-size: 45% 45%;
+  background-color: rgba(216, 216, 216, 0.8);
+  border-radius: 50%;
+  transition: all 0.5s;
+  cursor: pointer;
+
+  &:hover {
+    transform: rotate(180deg);
+    background-color: rgba(216, 216, 216, 1);
+  }
+}
+
+.menu {
+  width: calc(100% - ($waiPadding * 2));
+  height: 75vh;
+  padding-top: 10rem;
+  margin: 0 auto;
+  display: flex;
+
+
+  .box {
+    background: linear-gradient(64deg, #092941 -85%, #2A484B 96%);
+    box-shadow: inset 0px 1px 0px 1px rgba(255, 255, 255, 0.3);
+    padding: 20px;
+    display: flex;
+    box-sizing: border-box;
+    border-radius: 1.6rem;
+    justify-content: space-between;
+  }
+
+  .left {
+    width: 22.2%;
+    display: flex;
+    border-radius: 1.6rem;
+    background: radial-gradient(212% 126% at 97% 6%, #35FFC6 0%, #00FFE8 100%);
+    overflow: hidden;
+
+    ul {
+      overflow-y: scroll;
+      overflow-x: hidden;
+      width: 100%;
+
+      li {
+        display: flex;
+        align-items: center;
+        border-bottom: 1px solid #ccbdbd;
+        padding: 6px 8%;
+        position: relative;
+        cursor: pointer;
+
+        .pic {
+          width: 4.5vw;
+          height: 4.5vw;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          margin-right: 10px;
+          padding: 5px;
+
+          img {
+            max-width: 100%;
+            max-height: 100%;
+            filter: grayscale(100%);
+          }
+        }
+
+        .name {
+          font-size: 1.82rem;
+        }
+      }
+
+      .on {
+        &::after {
+          content: "";
+          display: block;
+          width: calc(100% - 16%);
+          height: calc(100% - 12px);
+          position: absolute;
+          border-radius: 1.3rem;
+          background: #F7F7F7;
+          box-shadow: 0px 1px 6px 0px rgba(0, 0, 0, 0.5);
+          z-index: 0;
+        }
+
+        .pic {
+
+          z-index: 1;
+
+          img {
+            filter: none;
+          }
+        }
+
+        .name {
+          z-index: 1;
+        }
+      }
+
+      &::-webkit-scrollbar {
+        width: 0px;
+      }
+
+      &::-webkit-scrollbar-thumb {
+        border-width: 2px;
+        border-radius: 4px;
+        border-style: dashed;
+        border-color: transparent;
+        background-color: rgba(26, 62, 78, 0.9);
+        background-clip: padding-box;
+      }
+
+      &::-webkit-scrollbar-button:hover {
+        border-radius: 6px;
+        background: rgba(26, 62, 78, 1);
+      }
+    }
+  }
+
+  .right {
+    width: 75%;
+    display: flex;
+  }
+}
+</style>

+ 0 - 3
src/views/ranking/index.vue

@@ -374,7 +374,6 @@ const getStopPlaying = (myTime?: any) => {
 };
 
 //请求数据
-
 const getData = () => {
   if (pageType.value == 1) {
     gradeData.value.forEach((item: any, index: any) => {
@@ -390,8 +389,6 @@ const getData = () => {
 };
 
 //各项目的学生前三
-
-
 const getGoodStudent = () => {
   studentTop.value = [];
   let params: any = {

+ 70 - 11
src/views/train/run.vue

@@ -4,7 +4,7 @@
     <transition :enter-active-class="proxy?.animate.dialog.enter" :leave-active-class="proxy?.animate.dialog.leave">
       <div class="time" v-show="[42].includes(examState)">{{
         countdownNumFormat
-      }}</div>
+        }}</div>
     </transition>
     <div class="main">
       <template v-if="isLongRun">
@@ -82,14 +82,14 @@
         </div>
         <div class="main-right">
           <div class="title">测试记录</div>
-          <ul>
+          <ul :ref="reportListRef" @scroll="getScroll($event)">
             <li v-for="(item, index) in reportList " :key="index">
               <div class="left">
                 <div class="pic"><img :src="item.face_pic || item.logo_url" /></div>
                 <div class="txt">
                   <div>
                     <div class="name">{{ item.student_name }}</div>
-                    <div class="className"></div>
+                    <div class="className">{{ item.class_name }}</div>
                   </div>
                 </div>
               </div>
@@ -133,6 +133,7 @@ const { proxy } = getCurrentInstance() as any;
 const router = useRouter();
 const route = useRoute();
 const chooseStudentRef = ref();
+const reportListRef = ref();
 const data = reactive<any>({
   timerManager: {},//计时器管理
   parameter: {},//参数
@@ -149,10 +150,16 @@ const data = reactive<any>({
   currentTrack: null,//当前跑道
   showTestAgain: false,//再测一次按钮
   reportList: [],//测试列表
+  studentPage: {
+    current: 1,
+    size: 10,
+    pages: 1,
+  }, //学生分页
+  debounceTime: '', //防抖状态
   isLongRun: false,//是否为长跑项目
   isBackRun: false,//是否为折返跑项目
 });
-const { timerManager, parameter, time, userInfo, examState, resultId, faceStudentList, currentTrack, unit, needStart, showTestAgain, reportList, isLongRun, isBackRun } = toRefs(data);
+const { timerManager, parameter, time, userInfo, examState, resultId, faceStudentList, currentTrack, unit, needStart, showTestAgain, reportList, studentPage, debounceTime, isLongRun, isBackRun } = toRefs(data);
 
 /**
  * 接收消息
@@ -652,23 +659,49 @@ const getAchievement = (data: any) => {
 };
 
 /**
- * 当天成绩列表
+ * 成绩列表
 */
 const getReportList = () => {
   let params: any = {
-    start_date: dayjs().format("YYYY-MM-DD"),
-    end_date: dayjs().format("YYYY-MM-DD"),
     exam_name: parameter.value.project,
-    page: 1,
-    per_page: 9000
+    page: studentPage.value.current,
+    per_page: studentPage.value.size
   };
   proxy?.$http.common.reportList(params).then((res: any) => {
     if (res.data.length > 0) {
-      reportList.value = res.data;
+      let list = res.data.map((item: any) => {
+        let type = parameter.value.project;
+        if (type == 'solidball' || type == 'shotball') {
+          item.result = item.result / 100
+        }
+        let result = null;
+        if (item.result.toString().indexOf(".") != -1) {
+          if (['jump', 'longjump', 'run50', 'run70', 'run100', 'run200', 'run400', 'run800', 'run1000', 'run15x4', 'run50x8', 'run10x4', 'basketballv1', 'basketballv1'].includes(type)) {
+            result = item.result.toFixed(2)
+          } else {
+            result = item.result.toFixed(1);
+          }
+        } else {
+          result = item.result
+        }
+        item.result = result;
+        return item;
+      });
+      studentPage.value.current == 1 ?
+        (reportList.value = list) :
+        reportList.value.push(...list);
+      getPages(res.total);
     }
   });
 };
 
+/**
+ * 计算页码
+*/
+const getPages = (data: any) => {
+  studentPage.value.pages = Math.ceil(data / studentPage.value.size);
+};
+
 /**
  * 当天成绩列表
 */
@@ -678,7 +711,6 @@ const close = (data: any) => {
   })
 };
 
-
 /**
  * 将测试列表分页
 */
@@ -745,6 +777,33 @@ const faceStudentListLongRun: any = computed(() => {
   }
 });
 
+/**
+* 成绩翻页
+*/
+const getScroll = (event?: any) => {
+  if (studentPage.value.current == studentPage.value.pages) {
+    return false;
+  }
+  let obj = event.target;
+  let scrollHeight = obj.scrollHeight;
+  let scrollTop = obj.scrollTop;
+  let clientHeight = obj.clientHeight;
+  //提前100高度加载数据
+  if (scrollTop + clientHeight + 100 >= scrollHeight) {
+    console.log('到底了!')
+    //继续加载下一页
+    if (debounceTime.value) {
+      clearTimeout(debounceTime.value)
+    }
+    debounceTime.value = setTimeout(() => {
+      studentPage.value.current++;
+      getReportList();
+    }, 500)
+  } else {
+    console.log('没到底')
+  }
+};
+
 onBeforeMount(() => {
   parameter.value = route.query;
   let project = parameter.value.project;

+ 83 - 24
src/views/train/test.vue

@@ -21,7 +21,7 @@
               :leave-active-class="proxy?.animate.dialog.leave">
               <div class="time" v-show="needStart && [42].includes(examState)">{{
                 time.countdownNum
-                }}</div>
+              }}</div>
             </transition>
             <div class="tips" v-if="examState == 41">
               <img v-if="parameter.gesture" src="@/assets/images/test/ready1.png" />
@@ -29,7 +29,6 @@
             </div>
             <div class="complete" :class="{ 'complete2': needStart && [42].includes(examState) }"
               v-if="faceCheckStu.student_id && time.ready <= 0 && examState != 43">
-
               <div class="scoreBox" v-if="['basketballv1'].includes(parameter.project)">
                 <div class="score">{{ currentResultObj.count || 0 }}</div>
               </div>
@@ -48,7 +47,6 @@
                 </div>
               </div>
             </div>
-
             <div class="foulBox" v-if="examState == 42 && backReason.length">
               <transition :enter-active-class="proxy?.animate.mask.enter"
                 :leave-active-class="proxy?.animate.mask.leave">
@@ -74,8 +72,8 @@
                     <div>请举手开始</div>
                   </div>
                 </div>
-                <div v-if="!parameter.gesture">
-                  <div class="item"><img src="@/assets/images/test/bujushou.png" /></div>
+                <div class="item" v-else>
+                  <div> <img src="@/assets/images/test/bujushou.png" /></div>
                   <div class="lable">
                     <div>请点击开始</div>
                   </div>
@@ -106,14 +104,14 @@
       </div>
       <div class="main-right">
         <div class="title">测试记录</div>
-        <ul>
-          <li v-for="(item, index) in reportList " :key="index" @click="openReport(item)">
+        <ul :ref="reportListRef" @scroll="getScroll($event)">
+          <li v-for="(item, index) in reportList" :key="index" @click="openReport(item)">
             <div class="left">
               <div class="pic"><img :src="item.face_pic || item.logo_url" /></div>
               <div class="txt">
                 <div>
                   <div class="name">{{ item.student_name }}</div>
-                  <div class="className"></div>
+                  <div class="className">{{ item.class_name }}</div>
                 </div>
               </div>
             </div>
@@ -133,7 +131,7 @@
         </div>
       </div>
     </div>
-    <FaceWindow ref="faceWindowRef" :faceCheckStu="faceCheckStu" />
+    <FaceWindow ref="faceWindowRef" :faceCheckStu="faceCheckStu" :gesture="parameter.gesture" />
     <ChooseStudent ref="chooseStudentRef" @returnData="returnStudent" />
     <ReportWindow ref="reportWindowRef" />
   </div>
@@ -150,6 +148,7 @@ const route = useRoute();
 const faceWindowRef = ref();
 const chooseStudentRef = ref();
 const reportWindowRef = ref();
+const reportListRef = ref();
 const myInfo: any = localStorage.getItem("userInfo");
 const dic: any = dataDictionary;
 const data = reactive<any>({
@@ -171,8 +170,14 @@ const data = reactive<any>({
   needStart: false,//是否需要按钮
   showTestAgain: false,//再测一次按钮
   reportList: [],//测试列表
+  studentPage: {
+    current: 1,
+    size: 10,
+    pages: 1,
+  }, //学生分页
+  debounceTime: '', //防抖状态
 });
-const { timerManager, parameter, time, userInfo, examState, resultId, faceCheckStu, currentResultObj, unit, backReason, backReasonStr, needStart, showTestAgain, reportList } = toRefs(data);
+const { timerManager, parameter, time, userInfo, examState, resultId, faceCheckStu, currentResultObj, unit, backReason, backReasonStr, needStart, showTestAgain, reportList, studentPage, debounceTime } = toRefs(data);
 
 /**
  * 接收消息
@@ -280,7 +285,8 @@ const getStopFace = async () => {
   // }
   getClearTimer("face");
   if (needStart.value) {
-    speckText(faceCheckStu.value.name + parameter.value.gesture ? ",请举手开始测试" : ",请准备");
+    let txt = parameter.value.gesture ? ",请举手开始测试" : ",请准备";
+    speckText(faceCheckStu.value.name + txt);
   }
   await stopFace();
   if (faceCheckStu.value.student_id) {
@@ -344,7 +350,7 @@ const getStartOneTest = () => {
     //显示再测一次按钮
     showTestAgain.value = true;
     //停止播报;
-    speckCancel()
+    speckCancel();
     //计时项目才开
     if (needStart.value == true) {
       //时间为0的为正计时,大于0的为倒计时
@@ -641,7 +647,7 @@ const getAchievement = (data: any) => {
     } else {
       speckText(faceCheckStu?.value.name + "成绩为" + (chineseNumber(count) || 0) + unit.value + ",请下一位准备!" || "");
     }
-
+    studentPage.value.current = 1;
     getReportList();
     faceWindowRef.value.open("right");
     //然后定时自动关闭
@@ -651,38 +657,64 @@ const getAchievement = (data: any) => {
   }
 };
 
-
 /**
- * 当天成绩列表
+ * 成绩列表
 */
 const getReportList = () => {
   let params: any = {
-    start_date: dayjs().format("YYYY-MM-DD"),
-    end_date: dayjs().format("YYYY-MM-DD"),
     exam_name: parameter.value.project,
-    page: 1,
-    per_page: 9000
+    page: studentPage.value.current,
+    per_page: studentPage.value.size
   };
   proxy?.$http.common.reportList(params).then((res: any) => {
     if (res.data.length > 0) {
-      reportList.value = res.data;
+      let list = res.data.map((item: any) => {
+        let type = parameter.value.project;
+        if (type == 'solidball' || type == 'shotball') {
+          item.result = item.result / 100
+        }
+        let result = null;
+        if (item.result.toString().indexOf(".") != -1) {
+          if (['jump', 'longjump', 'run50', 'run70', 'run100', 'run200', 'run400', 'run800', 'run1000', 'run15x4', 'run50x8', 'run10x4', 'basketballv1', 'basketballv1'].includes(type)) {
+            result = item.result.toFixed(2)
+          } else {
+            result = item.result.toFixed(1);
+          }
+        } else {
+          result = item.result
+        }
+        item.result = result;
+        return item;
+      });
+      studentPage.value.current == 1 ?
+        (reportList.value = list) :
+        reportList.value.push(...list);
+      studentPage.value.pages = res.total;
+      getPages(res.total);
     }
   });
 };
 
+/**
+ * 计算页码
+*/
+const getPages = (data: any) => {
+  studentPage.value.pages = Math.ceil(data / studentPage.value.size);
+};
+
 /**
  * 准备开始
 */
 const getReady = () => {
+  speckCancel();
   time.value.ready = 5;
   speckText(time.value.ready);
   timerManager.value.readyTimer = setInterval(() => {
     time.value.ready--;
-    if (time.value.ready < 1) {
+    speckText(time.value.ready);
+    if (time.value.ready == 0) {
       getClearTimer("readyTimer");
       getStartOneTest();
-    } else {
-      speckText(time.value.ready);
     }
   }, 1000);
 };
@@ -694,6 +726,33 @@ const openReport = (data: any) => {
   reportWindowRef.value.open(parameter.value.project, data);
 };
 
+/**
+* 成绩翻页
+*/
+const getScroll = (event?: any) => {
+  if (studentPage.value.current == studentPage.value.pages) {
+    return false;
+  }
+  let obj = event.target;
+  let scrollHeight = obj.scrollHeight;
+  let scrollTop = obj.scrollTop;
+  let clientHeight = obj.clientHeight;
+  //提前100高度加载数据
+  if (scrollTop + clientHeight + 100 >= scrollHeight) {
+    console.log('到底了!')
+    //继续加载下一页
+    if (debounceTime.value) {
+      clearTimeout(debounceTime.value)
+    }
+    debounceTime.value = setTimeout(() => {
+      studentPage.value.current++;
+      getReportList();
+    }, 500)
+  } else {
+    console.log('没到底')
+  }
+};
+
 /**
  * 输出犯规
 */
@@ -860,7 +919,6 @@ $waiPadding: 6.51rem;
           display: flex;
           justify-content: center;
           flex-direction: column;
-          margin-bottom: 3vh;
 
           .scoreBox {
             height: 10vh;
@@ -969,6 +1027,7 @@ $waiPadding: 6.51rem;
           display: flex;
           align-items: center;
           justify-content: center;
+          padding-top: 3vh;
 
           .foul {
             height: 4.2vh;