Procházet zdrojové kódy

feat: :sparkles: 测评数据看板

chaooo před 2 roky
rodič
revize
0b16483442

+ 46 - 0
src/api/evaluation/index.ts

@@ -0,0 +1,46 @@
+import request from "@/utils/request";
+import {AxiosPromise} from "axios";
+import {ChartBtmData, ChartMidData, TopCard} from "./types";
+import {StudentList} from "@/api/student/types";
+
+/**
+ * 获取首页数据卡片
+ */
+export function getEvaluationTop(id: number): AxiosPromise<TopCard> {
+  return request({
+    url: "/board/v1/eval-top",
+    method: "get",
+    params: {school_id: id},
+  });
+}
+/**
+ * 测评学生列表
+ * ///board/v1/eval-student?school_id=95&search=18770023942
+ */
+export function getEvaluationStudents(school_id: number, search: string): AxiosPromise<StudentList[]> {
+  return request({
+    url: "/board/v1/eval-student",
+    method: "get",
+    params: {school_id: school_id, search: search},
+  });
+}
+/**
+ * 获取首页图表数据
+ */
+export function getEvaluationMid(school_id: number, baby_id: number): AxiosPromise<ChartMidData> {
+  return request({
+    url: "/board/v1/eval-middle",
+    method: "get",
+    params: {school_id: school_id, baby_id: baby_id},
+  });
+}
+/**
+ * 获取首页图表数据
+ */
+export function getEvaluationBtm(school_id: number, baby_id: number): AxiosPromise<ChartBtmData> {
+  return request({
+    url: "/board/v1/eval-bottom",
+    method: "get",
+    params: {school_id: school_id, baby_id: baby_id},
+  });
+}

+ 32 - 0
src/api/evaluation/types.ts

@@ -0,0 +1,32 @@
+/**
+ * 顶部数据
+ */
+export interface TopCard {
+  studentCount: number;
+  brainCount: number; // 儿童脑电专注力测评次数
+  eegCount: number; // 脑电检测次数
+}
+
+/**
+ * 中间数据
+ */
+export interface ChartMidData {
+  // [[60.42,4],[46.28,4],[94.58,5]]
+  firstRadar: number[][];
+  lastRadar: number[][];
+  firstColumnar: number[];
+  lastColumnar: number[];
+}
+
+/**
+ * 底部数据
+ */
+export interface ChartBtmData {
+  // [[60.42,4],[46.28,4],[94.58,5]]
+  firstRadar: number[][];
+  lastRadar: number[][];
+  firstColumn: number[];
+  secondColumn: number[];
+  lastColumn: number[];
+  lastSecondColumn: number[];
+}

+ 53 - 12
src/components/Charts/FocusBarChart.vue

@@ -36,26 +36,37 @@ const props = defineProps({
   },
 });
 const labelFormatter = (params: any) => {
-  //{ value, dataIndex } = params;
-  const star = (props.star as number[])?.[params.dataIndex] * 1 || 0;
   const label = [];
   label.push(params.value);
-  if (star === 1) {
+  if (params.value <= 2) {
     label.push("重度不足");
   }
-  if (star === 2) {
+  if (params.value > 2 && params.value <= 4) {
     label.push("轻度不足");
   }
-  if (star === 3) {
+  if (params.value > 4 && params.value <= 6) {
     label.push("中等水平");
   }
-  if (star === 4) {
+  if (params.value > 6 && params.value <= 8) {
     label.push("良好水平");
   }
-  if (star === 5) {
+  if (params.value > 8) {
     label.push("优秀水平");
   }
-  return label.join(" ");
+  return label;
+};
+const labelFormatter1 = (params: any) => {
+  const label = labelFormatter(params);
+  return `{a|${label.join(" ")}}`;
+};
+const labelFormatter2 = (params: any) => {
+  const label = labelFormatter(params);
+  const before = props?.dataSets?.[0]?.[params.dataIndex];
+  if (params.value >= before) {
+    return `{a|${label.join(" ")} }{b|}`;
+  } else {
+    return `{a|${label.join(" ")} }{c|}`;
+  }
 };
 /**
  * 配置项
@@ -109,8 +120,14 @@ const options = {
       },
       label: {
         show: true,
-        position: "right",
-        formatter: labelFormatter,
+        position: "insideLeft",
+        formatter: labelFormatter1,
+        rich: {
+          a: {
+            align: "right",
+            width: 240,
+          },
+        },
       },
     },
     {
@@ -123,8 +140,32 @@ const options = {
       },
       label: {
         show: true,
-        position: "right",
-        formatter: labelFormatter,
+        position: "insideLeft",
+        formatter: labelFormatter2,
+        rich: {
+          a: {
+            align: "right",
+            width: 250,
+          },
+          b: {
+            height: 11,
+            width: 16,
+            align: "right",
+            backgroundColor: {
+              image:
+                "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAMAAABBPP0LAAAAV1BMVEUAAAAQzAATzAASzQASzAASzAAA1QATzAASzAASzAASzAASzAASzQATzQATzAAOygAPyQATzAATzAATzAASzAATzAATzAATzAATywAPzgASygARzAATzABrWENcAAAAHHRSTlMAMfP5WlQI9PDRxah9eWA1GvXj2riXh2pENCsPDxV/+QAAAFlJREFUCNdVjlkOgCAQxUZR3EBx33r/cxoCidC/ZjrJk8g1SsY9c6Y+NYD9vShRJRyJUxct7NFbqESeBQYd/2t/cCv0OvQhdR10YnwfeXuU2OABvSkx+cThA9DrBRCLutsZAAAAAElFTkSuQmCC",
+            },
+          },
+          c: {
+            height: 11,
+            width: 16,
+            align: "right",
+            backgroundColor: {
+              image:
+                "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAMAAABBPP0LAAAAWlBMVEUAAADlGhrmGRnnGBjmGRnmFxflGhrlGhrlGhrmGhrlGRnlGRnmGRnlGRnhGRniExPVAADlGRnlGRnlGhrlGhrkGhrmGRnmGhrkGRnnGBjiGRnmGhroFxflGhqoh8GtAAAAHXRSTlMA+ftIREH62tC4mHx4UhQNBvLp5MaqoYxxPz4oIdaT/FMAAABeSURBVAjXVcw3EsNADMVQUDlLzhH3v6Y9O1tIqMhXfKaZfQNTVLv/How2NbmtN3gXRpb1qi2Uf3kBLJ12C1CFUcK31X4FkhT156K3DUjSeDrrQC7t6AMOMnJofubjB6mhBJ/theqpAAAAAElFTkSuQmCC",
+            },
+          },
+        },
       },
     },
   ],

+ 1 - 0
src/views/dashboard/index.vue

@@ -28,6 +28,7 @@ async function getDataCard(schoolId: number) {
       cardStatus.value = true;
     })
     .catch((error) => {
+      cardStatus.value = true;
       console.log(error.message);
     });
 }

+ 2 - 1
src/views/evaluation/components/EvaluateCard.vue

@@ -35,7 +35,8 @@ const focusCountOutput = countNumber(focusCount);
 // 脑电检测次数
 const trainingCount = ref(0);
 const trainingCountOutput = countNumber(trainingCount);
-onMounted(() => {
+// 监听数据变化
+watchEffect(() => {
   studentCount.value = <number>props.students;
   focusCount.value = <number>props.focuses;
   trainingCount.value = <number>props.trainings;

+ 200 - 62
src/views/evaluation/index.vue

@@ -5,94 +5,217 @@ import FocusBarChart from "@/components/Charts/FocusBarChart.vue";
 import RelaxBarChart from "@/components/Charts/RelaxBarChart.vue";
 import IndicatorsBarChart from "@/components/Charts/IndicatorsBarChart.vue";
 import {trimInput} from "@/utils";
+import {ChartBtmData, ChartMidData, TopCard} from "@/api/evaluation/types";
+import {getEvaluationBtm, getEvaluationMid, getEvaluationStudents, getEvaluationTop} from "@/api/evaluation";
+import {useUserStore} from "@/store/modules/user";
+import {StudentList} from "@/api/student/types";
 
 defineOptions({
   name: "EvaluateIndex",
   inheritAttrs: false,
 });
-// const userStore = useUserStore();
-// watch(
-//   () => userStore.schoolId,
-//   (newValue, oldValue) => {
-//     console.log(newValue, oldValue);
-//   }
-// );
+const userStore = useUserStore();
 /**
  * 数据卡片
  */
-// 全部学生
-const studentCount = 0;
-// 设备套数
-const focusCount = 0;
-// 累计训练次数
-const trainingCount = 0;
-
-let studentInfo = ref("");
-
+const cardStatus = ref(false);
+const cards = ref<TopCard>();
+async function getDataCard(schoolId: number) {
+  getEvaluationTop(schoolId)
+    .then(({data}) => {
+      cards.value = data;
+      cardStatus.value = true;
+    })
+    .catch((error) => {
+      cardStatus.value = true;
+      console.log(error.message);
+    });
+}
+/**
+ * 学生数据
+ */
+const studentSearch = ref("");
+const studentStatus = ref(false);
+const studentMessage = ref("加载中...");
+const studentActive = ref(0);
+const studentData = ref<StudentList[]>();
+async function getStudentData(schoolId: number) {
+  getEvaluationStudents(schoolId, studentSearch.value)
+    .then(({data}) => {
+      studentData.value = <StudentList[]>data;
+      if (data && studentData.value?.length > 0) {
+        studentActive.value = studentData.value?.[0].id || 0;
+        studentStatus.value = true;
+        // 获取第一个学生图表
+        changeStudent(studentActive.value);
+      } else {
+        studentStatus.value = studentSearch.value != "";
+        studentMessage.value = "学校学生还未进行过测评,暂无测评数据!";
+      }
+    })
+    .catch((error) => {
+      console.log(error.message);
+      studentStatus.value = studentSearch.value != "";
+    });
+}
+function getStudentSearch() {
+  getStudentData(userStore.schoolId);
+}
+function changeStudent(studentId) {
+  studentActive.value = studentId;
+  // 切换学生图表
+  if (studentId > 0) {
+    // 图表数据
+    getMidChartData(userStore.schoolId, studentId);
+    getBtmChartData(userStore.schoolId, studentId);
+  }
+}
+/**
+ * 图表数据
+ */
 const chartStatus = ref(false);
-const chartMessage = ref("学校学生还未进行过测评,暂无测评数据!");
-const data = ref([78, 88, 65, 82, 65]);
-// 五维雷达图
-const radarData = ref([
-  [78, 88, 65, 82, 65],
-  [28, 38, 45, 32, 25],
-]);
-const radarStar = ref([1, 2, 3, 4, 5]);
-const radarTag = true;
+//中间部分图表
+const midStatus = ref(false);
+const midData = ref<ChartMidData>();
+// 五维雷达图1
+const radarMidData = ref<number[][]>();
+const radarMidStar = ref<number[]>();
 // 专注力四维柱状图
-const focusData = ref([
-  [3.8, 4.7, 2.5, 5.0],
-  [7.4, 8.3, 6.5, 9.0],
-]);
+const focusData = ref<number[][]>();
 const focusStar = ref([1, 2, 3, 4]);
+async function getMidChartData(schoolId: number, babyId: number) {
+  midStatus.value = false;
+  getEvaluationMid(schoolId, babyId)
+    .then(({data}) => {
+      midData.value = data;
+      // 五维雷达图
+      radarMidData.value = [];
+      if (midData.value?.lastRadar[0] && midData.value?.lastRadar[0].length > 0) {
+        radarMidData.value?.push(midData.value?.lastRadar[0] || []);
+        radarMidStar.value = midData.value?.lastRadar[1] || [];
+      } else {
+        radarMidStar.value = midData.value?.firstRadar[1] || [];
+      }
+      radarMidData.value?.push(midData.value?.firstRadar[0] || []);
+      // 专注力四维柱状图
+      focusData.value = [];
+      focusData.value?.push(midData.value?.firstColumnar || []);
+      focusData.value?.push(midData.value?.lastColumnar || []);
+
+      midStatus.value = true;
+      chartStatus.value = true;
+    })
+    .catch((error) => {
+      midStatus.value = false;
+      console.log(error.message);
+    });
+}
+//底部图表、
+const btmStatus = ref(false);
+const btmData = ref<ChartBtmData>();
+// 五维雷达图2
+const radarBtmData = ref<number[][]>();
+const radarBtmStar = ref<number[]>();
 // 3维放松度分析柱状图
-const relaxData = ref([
-  [50, 60, 55],
-  [55, 90, 70],
-]);
+const relaxData = ref<number[][]>();
 // 脑电评估检测指数分析看板
-const indicatorsData = ref([
-  [50, 60, 55, 64.5],
-  [55, 90, 70, 82.5],
-]);
+const indicatorsData = ref<number[][]>();
+async function getBtmChartData(schoolId: number, babyId: number) {
+  btmStatus.value = false;
+  getEvaluationBtm(schoolId, babyId)
+    .then(({data}) => {
+      btmData.value = data;
+      // 五维雷达图2
+      radarBtmData.value = [];
+      if (btmData.value?.lastRadar[0] && btmData.value?.lastRadar[0].length > 0) {
+        radarBtmData.value?.push(btmData.value?.lastRadar[0] || []);
+        radarBtmStar.value = btmData.value?.lastRadar[1] || [];
+      } else {
+        radarBtmStar.value = btmData.value?.firstRadar[1] || [];
+      }
+      radarBtmData.value?.push(btmData.value?.firstRadar[0] || []);
+      // 3维放松度分析柱状图
+      relaxData.value = [];
+      relaxData.value?.push(btmData.value?.firstColumn || []);
+      relaxData.value?.push(btmData.value?.secondColumn || []);
+      // 脑电评估检测指数分析看板
+      indicatorsData.value = [];
+      indicatorsData.value?.push(btmData.value?.lastColumn || []);
+      indicatorsData.value?.push(btmData.value?.lastSecondColumn || []);
+      btmStatus.value = true;
+      chartStatus.value = true;
+    })
+    .catch((error) => {
+      btmStatus.value = false;
+      console.log(error.message);
+    });
+}
+
+onMounted(() => {
+  if (userStore.schoolId > 0) {
+    // 数据卡片
+    getDataCard(userStore.schoolId);
+    // 获取学生
+    getStudentData(userStore.schoolId);
+  }
+});
 </script>
 
 <template>
   <div class="evaluate-container">
     <!-- 数据卡片 -->
-    <EvaluateCard :focuses="focusCount" :students="studentCount" :trainings="trainingCount" />
-    <div v-if="chartStatus" class="evaluate-chart">
+    <!-- 数据卡片 -->
+    <template v-if="cardStatus">
+      <EvaluateCard
+        :key="cards.toString()"
+        :focuses="cards?.brainCount || 0"
+        :students="cards?.studentCount || 0"
+        :trainings="cards?.eegCount || 0" />
+    </template>
+    <div v-if="studentStatus" class="evaluate-chart">
       <!-- 学生查找 -->
       <div class="student-search">
         <div class="search">
           <el-input
-            v-model="studentInfo"
+            v-model="studentSearch"
             placeholder="请输入学生名称或手机号码"
             size="large"
-            @input="(value:string) => (studentInfo = trimInput(value))" />
-          <el-button size="large" type="primary">查找</el-button>
+            @input="(value:string) => (studentSearch = trimInput(value))" />
+          <el-button size="large" type="primary" @click="getStudentSearch()">查找</el-button>
         </div>
-        <div class="result">
+        <div v-if="studentData?.length > 0" class="result">
           <el-scrollbar height="700px">
-            <div v-for="item in 20" :key="item" class="scroll-item">
+            <div
+              v-for="item in studentData"
+              :key="item.id"
+              class="scroll-item"
+              :class="{active: item.id === studentActive}"
+              @click="changeStudent(item.id)">
               <img src="" alt="" />
-              <p>风间彻{{ item }}</p>
-              <p>13726267788</p>
+              <p>{{ item.name }}</p>
+              <p>{{ item.phone }}</p>
             </div>
           </el-scrollbar>
         </div>
+        <div v-else class="result">
+          <div class="empty">
+            <img src="../../assets/empty.png" alt="数据为空" />
+            <p>没有符合搜索条件的记录!</p>
+          </div>
+        </div>
       </div>
       <!-- 图表展示-->
-      <div class="student-chart">
+      <div v-if="chartStatus" class="student-chart">
         <div class="title">儿童脑电专注力测评数据分析</div>
-        <el-row :gutter="10">
+        <el-row v-if="midStatus" :gutter="10">
           <el-col :xs="24" :span="8">
             <div class="box-card">
               <RadarChart
                 id="radarChart1"
-                :data-sets="radarData"
-                :star="radarStar"
-                :tag="radarTag"
+                :key="radarMidData?.toString()"
+                :data-sets="radarMidData"
+                :star="radarMidStar"
+                :tag="Boolean(true)"
                 width="400px"
                 height="300px"
                 class="chart" />
@@ -103,6 +226,7 @@ const indicatorsData = ref([
             <div class="box-card">
               <FocusBarChart
                 id="focusBarChart1"
+                :key="focusData.toString()"
                 :data-sets="focusData"
                 :star="focusStar"
                 width="400px"
@@ -120,14 +244,15 @@ const indicatorsData = ref([
           <el-col :xs="24" :span="8"></el-col>
         </el-row>
         <div class="title">脑电检测分析</div>
-        <el-row :gutter="10">
+        <el-row v-if="btmStatus" :gutter="10">
           <el-col :xs="24" :span="8">
             <div class="box-card">
               <RadarChart
                 id="radarChart2"
-                :data-sets="radarData"
-                :star="radarStar"
-                :tag="radarTag"
+                :key="radarBtmData?.toString()"
+                :data-sets="radarBtmData"
+                :star="radarBtmStar"
+                :tag="Boolean(true)"
                 width="400px"
                 height="300px"
                 class="chart" />
@@ -136,7 +261,13 @@ const indicatorsData = ref([
           </el-col>
           <el-col :xs="24" :span="8">
             <div class="box-card">
-              <RelaxBarChart id="relaxBarChart1" :data-sets="relaxData" class="chart" height="300px" width="400px" />
+              <RelaxBarChart
+                id="relaxBarChart1"
+                :key="relaxData.toString()"
+                :data-sets="relaxData"
+                class="chart"
+                height="300px"
+                width="400px" />
               <div class="info legend">
                 3维放松度分析
                 <div class="tag">
@@ -150,6 +281,7 @@ const indicatorsData = ref([
             <div class="box-card">
               <IndicatorsBarChart
                 id="indicatorsBarChart1"
+                :key="indicatorsData.toString()"
                 :data-sets="indicatorsData"
                 width="400px"
                 height="300px"
@@ -165,10 +297,16 @@ const indicatorsData = ref([
           </el-col>
         </el-row>
       </div>
+      <div v-else class="student-chart">
+        <div class="empty">
+          <img src="../../assets/empty.png" alt="数据为空" />
+          <p>暂无测评记录!</p>
+        </div>
+      </div>
     </div>
     <div v-else class="evaluate-chart empty">
       <img src="../../assets/empty.png" alt="数据为空" />
-      <p>{{ chartMessage }}</p>
+      <p>{{ studentMessage }}</p>
     </div>
   </div>
 </template>
@@ -178,15 +316,17 @@ const indicatorsData = ref([
   position: relative;
   padding: 30px;
 }
-
+.empty {
+  padding: 135px 0;
+}
 .evaluate-chart {
   position: relative;
   box-sizing: border-box;
   width: 100%;
+  min-height: 800px;
   padding: 0 10px 20px 386px;
   background: #fff;
   border-radius: 30px;
-
   &.empty {
     padding: 200px 0;
   }
@@ -239,12 +379,10 @@ const indicatorsData = ref([
       padding: 8px 0 0 90px;
       color: #23283c;
       cursor: pointer;
-
       &:hover {
         background: #e5eefe;
       }
-
-      .active {
+      &.active {
         color: #4284f2;
         background: #e5eefe;
       }