Kaynağa Gözat

build: 区域级看板样式

chaooo 2 yıl önce
ebeveyn
işleme
bf9dd11a53

+ 5 - 0
src/api/dashboard/types.ts

@@ -8,6 +8,11 @@ export interface DashboardCard {
   equipment: number;
   game: number;
 }
+export interface AreaCard {
+	student: number;
+	game: number;
+	school: number;
+}
 /**
  * 初期分期占比分析
  */

BIN
src/assets/areaboard/games.png


BIN
src/assets/areaboard/schools.png


BIN
src/assets/areaboard/students.png


+ 4 - 2
src/layout/components/Navbar/Admin/index.vue

@@ -39,7 +39,9 @@ function toggleSideBar() {
   align-items: center;
   justify-content: space-between;
   height: 70px;
-  background-color: #fff;
-  box-shadow: 0 0 1px #0003;
+  background: #ffffff;
+  box-shadow: 0 0 10px #eee;
+  position: relative;
+  z-index: 10;
 }
 </style>

+ 4 - 2
src/layout/components/Navbar/School/index.vue

@@ -42,7 +42,9 @@ function toggleSideBar() {
   align-items: center;
   justify-content: space-between;
   height: 70px;
-  background-color: #fff;
-  box-shadow: 0 0 1px #0003;
+  background: #ffffff;
+  box-shadow: 0 0 10px #eee;
+  position: relative;
+  z-index: 10;
 }
 </style>

+ 2 - 2
src/store/modules/user.ts

@@ -53,8 +53,8 @@ export const useUserStore = defineStore("user", () => {
     nickname.value = data.name;
     phone.value = data.phone;
     schoolNum.value = data.num;
-    role.value = data.role;
-    //role.value = "ADMIN";
+    //role.value = data.role;
+    role.value = "ADMIN";
     perms.value = data.perms;
     return role.value;
   }

+ 1 - 2
src/styles/sidebar.scss

@@ -15,10 +15,9 @@
     width: $sideBarWidth !important;
     height: 100%;
     overflow: hidden;
-    background-color: $menuBg;
+    background: #ffffff;
     box-shadow: 0 0 10px #eeeeee;
     transition: width 0.28s;
-
     // reset element-ui css
     .horizontal-collapse-transition {
       transition: 0s width ease-in-out, 0s padding-left ease-in-out,

+ 25 - 24
src/views/areaboard/components/AreaCard.vue → src/views/areaboard/components/AreaDataCard.vue

@@ -8,12 +8,12 @@ const props = defineProps({
     default: 0,
     required: true,
   },
-  focuses: {
+  games: {
     type: Number,
     default: 0,
     required: true,
   },
-  trainings: {
+  schools: {
     type: Number,
     default: 0,
     required: true,
@@ -26,34 +26,35 @@ function countNumber(number: any) {
     transition: TransitionPresets.easeOutExpo,
   });
 }
-// 学生
-const studentCount = ref(0);
-const studentCountOutput = countNumber(studentCount);
-// 脑电专注力测评次数
-const focusCount = ref(0);
-const focusCountOutput = countNumber(focusCount);
-// 脑电检测次数
-const trainingCount = ref(0);
-const trainingCountOutput = countNumber(trainingCount);
-onMounted(() => {
-  studentCount.value = <number>props.students;
-  focusCount.value = <number>props.focuses;
-  trainingCount.value = <number>props.trainings;
+// 样本人数
+const studentsCount = ref(0);
+const studentsCountOutput = countNumber(studentsCount);
+// 累计训练次数
+const gamesCount = ref(0);
+const gamesCountOutput = countNumber(gamesCount);
+// 全部学校
+const schoolsCount = ref(0);
+const schoolsCountOutput = countNumber(schoolsCount);
+// 监听数据变化
+watchEffect(() => {
+	studentsCount.value = <number>props.students;
+	gamesCount.value = <number>props.games;
+	schoolsCount.value = <number>props.schools;
 });
 </script>
 <template>
   <div class="clear">
     <div class="data-card c1">
-      <span class="n">{{ Math.round(Number(studentCountOutput)) }}</span
-      ><span>参与测评学生总计</span>
+      <span class="n">{{ Math.round(Number(studentsCountOutput)) }}</span
+      ><span>样本人数</span>
     </div>
     <div class="data-card c2">
-      <span class="n">{{ Math.round(Number(focusCountOutput)) }}</span
-      ><span>脑电专注力测评次数</span>
+      <span class="n">{{ Math.round(Number(gamesCountOutput)) }}</span
+      ><span>累计训练次数</span>
     </div>
     <div class="data-card c3">
-      <span class="n">{{ Math.round(Number(trainingCountOutput)) }}</span
-      ><span>脑电检测次数</span>
+      <span class="n">{{ Math.round(Number(schoolsCountOutput)) }}</span
+      ><span>全部学校</span>
     </div>
   </div>
 </template>
@@ -79,21 +80,21 @@ onMounted(() => {
     }
   }
   &.c1 {
-    background: #fef6e9 url("../../../assets/evaluate/student.png") 90% 50%
+    background: #fef6e9 url("../../../assets/areaboard/students.png") 90% 50%
       no-repeat;
     span.n {
       color: #e08e0a;
     }
   }
   &.c2 {
-    background: #feeeee url("../../../assets/evaluate/focus.png") 90% 50%
+    background: #feeeee url("../../../assets/areaboard/games.png") 90% 50%
       no-repeat;
     span.n {
       color: #ca7676;
     }
   }
   &.c3 {
-    background: #d0f6f1 url("../../../assets/evaluate/training.png") 90% 50%
+    background: #d0f6f1 url("../../../assets/areaboard/schools.png") 90% 50%
       no-repeat;
     span.n {
       color: #45a498;

+ 337 - 12
src/views/areaboard/index.vue

@@ -1,6 +1,14 @@
 <script setup lang="ts">
-import { watch } from "vue";
-import { useUserStore } from "@/store/modules/user";
+import {watch} from "vue";
+import {useUserStore} from "@/store/modules/user";
+import {AreaCard, DashboardData} from "@/api/dashboard/types";
+import AreaDataCard from "@/views/areaboard/components/AreaDataCard.vue";
+import LiquidChart from "@/views/charts-components/LiquidChart.vue";
+import PercentBarChart from "@/views/charts-components/PercentBarChart.vue";
+import CircleChart from "@/views/charts-components/CircleChart.vue";
+import {getDashboardData} from "@/api/dashboard";
+import LineChart from "@/views/charts-components/LineChart.vue";
+import AverageBarChart from "@/views/charts-components/AverageBarChart.vue";
 
 defineOptions({
   name: "DashboardArea",
@@ -15,28 +23,345 @@ watch(
 );
 
 const date = ref("");
+/**
+ * 数据卡片
+ */
+let cards: AreaCard = reactive({
+	student: 0, // 样本人数
+	game: 0, // 累计训练次数
+	school: 10, // 全部班级
+});
+/**
+ * 图表数据
+ */
+const chartStatus = ref(false);
+let chartData: DashboardData = reactive({
+	frontAverage: 0, // 初期专注力估值
+	afterAverage: 0, // 近期专注力估值
+	front: 0, // 初期50分以上的占比
+	after: 0, // 近期50分以上的占比
+	frontProportion: { num: [], percentage: [] }, // 初期分期占比分析
+	afterProportion: { num: [], percentage: [] }, // 近期分期占比分析
+});
+async function getChartData(gradeId: number) {
+	getDashboardData(gradeId)
+		.then(({ data }) => {
+			chartData = <DashboardData>{ ...data };
+			if (chartData.frontAverage > 0) {
+				chartStatus.value = true;
+				dataStatus.value = 1;
+			} else {
+				dataStatus.value = 2;
+			}
+		})
+		.catch((error) => {
+			console.log(error.toString());
+			dataStatus.value = 2;
+		});
+}
+// 数据状态:1正常,2训练次数小于3,3过期,4缺省
+const dataStatus = ref(5);
+onMounted(() => {
+	// 图表数据
+	getChartData(0);
+});
+const indicatorsData = ref([
+	[50, 60, 55, 64.5],
+	[55, 90, 70, 82.5],
+]);
 </script>
 
 <template>
   <div class="area-container">
     <div class="area-top">
-      <el-date-picker
-        v-model="date"
-        type="daterange"
-        start-placeholder="开始日期"
-        end-placeholder="结束日期"
-        :default-value="[new Date(2023, 6, 1), new Date(2030, 10, 1)]"
-      />
+			<div class="search-box">
+				<el-select placeholder="全部省" size="large">
+					<el-option key="0" label="全部省" :value="Number(0)" />
+					<el-option key="1" label="省1" :value="Number(1)" />
+					<el-option key="1" label="省2" :value="Number(2)" />
+				</el-select>
+				<el-select placeholder="全部市" size="large">
+					<el-option key="0" label="全部市" :value="Number(0)" />
+					<el-option key="1" label="市1" :value="Number(1)" />
+					<el-option key="1" label="市2" :value="Number(2)" />
+				</el-select>
+				<el-select placeholder="全龄" size="large">
+					<el-option key="0" label="全龄" :value="Number(0)" />
+					<el-option key="1" label="1岁" :value="Number(1)" />
+					<el-option key="1" label="2岁" :value="Number(2)" />
+				</el-select>
+				<div>
+					<el-date-picker
+							v-model="date"
+							type="daterange"
+							size="large"
+							start-placeholder="开始日期"
+							end-placeholder="结束日期"
+							:default-value="[new Date(2023, 6, 1), new Date(2030, 10, 1)]"
+					/>
+				</div>
+				<el-button size="large" type="primary">查找</el-button>
+			</div>
+			<div class="card-box">
+				<AreaDataCard :schools="cards.school" :games="cards.game" :students="cards.student"/>
+			</div>
     </div>
+		<div class="search-box s2">
+			<el-select placeholder="全部省" size="large">
+				<el-option key="0" label="全部省" :value="Number(0)" />
+				<el-option key="1" label="省1" :value="Number(1)" />
+				<el-option key="1" label="省2" :value="Number(2)" />
+			</el-select>
+			<el-select placeholder="全部市" size="large">
+				<el-option key="0" label="全部市" :value="Number(0)" />
+				<el-option key="1" label="市1" :value="Number(1)" />
+				<el-option key="1" label="市2" :value="Number(2)" />
+			</el-select>
+			<el-select placeholder="全龄" size="large">
+				<el-option key="0" label="全龄" :value="Number(0)" />
+				<el-option key="1" label="1岁" :value="Number(1)" />
+				<el-option key="1" label="2岁" :value="Number(2)" />
+			</el-select>
+			<el-button size="large" type="primary">查找</el-button>
+		</div>
+		<!-- Echarts 图表 -->
+		<div class="charts-container">
+			<el-row :gutter="20">
+				<el-col :md="24" :lg="12" :xl="8">
+					<div class="charts-item">
+						<p class="title">学员专注力平均值整体对比分析</p>
+						<el-row justify="space-between">
+							<el-col :xs="24" :sm="12">
+								<div v-if="chartStatus" class="item">
+									<LiquidChart
+											id="liquidChart1"
+											:key="chartData.frontAverage"
+											:data="chartData.frontAverage ? chartData.frontAverage : 0"
+											height="200px"
+											width="200px"
+											color="#3a7fc2"
+											bg-color="#accded"
+											class="chart"
+									/>
+									<p>全体学员初期</p>
+									<p>专注力评估均值</p>
+								</div>
+							</el-col>
+							<el-col :xs="24" :sm="12">
+								<div v-if="chartStatus" class="item">
+									<LiquidChart
+											id="liquidChart2"
+											:key="chartData.afterAverage"
+											:data="chartData.afterAverage ? chartData.afterAverage : 0"
+											height="200px"
+											width="200px"
+											color="#5563ac"
+											bg-color="#cacce6"
+											class="chart"
+									/>
+									<p>全体学员训练近期</p>
+									<p>专注力评估均值</p>
+								</div>
+							</el-col>
+						</el-row>
+						<el-row justify="space-between">
+							<el-col :xs="24" :sm="12">
+								<div v-if="chartStatus" class="item">
+									<CircleChart
+											id="circleChart1"
+											:key="chartData.front"
+											:data="chartData.front ? chartData.front : 0"
+											height="200px"
+											width="200px"
+											color="#3a7fc2"
+											bg-color="#e4e7f4"
+											font-color="#3a7fc2"
+											font-size="30px"
+											:round-cap="Boolean(true)"
+									/>
+									<p>初期训练</p>
+									<p>专注力50以上人数比例</p>
+								</div>
+							</el-col>
+							<el-col :xs="24" :sm="12">
+								<div v-if="chartStatus" class="item">
+									<CircleChart
+											id="circleChart2"
+											:key="chartData.after.toString()"
+											:data="chartData.after ? chartData.after : 0"
+											height="200px"
+											width="200px"
+											color="#5563ac"
+											bg-color="#e4e7f4"
+											font-color="#5563ac"
+											font-size="30px"
+											:round-cap="Boolean(true)"
+									/>
+									<p>近期训练</p>
+									<p>专注力50以上人数比例</p>
+								</div>
+							</el-col>
+						</el-row>
+					</div>
+				</el-col>
+				<!-- 学员专注力评分分级占比分析 -->
+				<el-col :md="24" :lg="12" :xl="16">
+					<div class="charts-item">
+						<p class="title">样本每次训练专注力评分均值整体变化曲线</p>
+						<line-chart id="lineChart1" width="1000px" height="558px"/>
+					</div>
+				</el-col>
+			</el-row>
+		</div>
+		<div class="charts-container">
+			<el-row :gutter="20">
+				<el-col :md="24" :lg="12" :xl="8">
+					<div class="charts-item">
+						<p class="title">学员专注力训练高专注占比分析</p>
+						<AverageBarChart
+								id="averageBarChart1"
+								:data-sets="indicatorsData"
+								width="400px"
+								height="300px"
+								class="chart"
+						/>
+					</div>
+				</el-col>
+				<!-- 学员专注力评分分级占比分析 -->
+				<el-col :md="24" :lg="12" :xl="16">
+					<div class="charts-item">
+						<p class="title">学员专注力评分分级占比分析</p>
+						<el-row justify="space-between">
+							<el-col :xs="24" :sm="24" :md="12" :lg="24" :xl="12">
+								<div v-if="chartStatus" class="bar">
+									<PercentBarChart
+											id="barChart1"
+											:key="chartData.frontProportion.toString()"
+											width="400px"
+											height="500px"
+											title="全体学员初期训练专注力评分占比"
+											:percent="chartData.frontProportion.percentage"
+											:data="chartData.frontProportion.num"
+											class="chart"
+									/>
+								</div>
+							</el-col>
+							<el-col :xs="24" :sm="24" :md="12" :lg="24" :xl="12">
+								<div v-if="chartStatus" class="bar">
+									<PercentBarChart
+											id="barChart2"
+											:key="chartData.afterProportion.toString()"
+											width="400px"
+											height="500px"
+											title="全体学员训练近期专注力评分平均占比"
+											:percent="chartData.afterProportion.percentage"
+											:data="chartData.afterProportion.num"
+											class="chart"
+									/>
+								</div>
+							</el-col>
+						</el-row>
+					</div>
+				</el-col>
+			</el-row>
+		</div>
   </div>
 </template>
 
 <style lang="scss" scoped>
-.area-container {
+.area-top {
+  background: #ffffff;
+}
+.card-box{
+  padding:0 30px 20px 30px;
+}
+.search-box {
+	display: flex;
+	line-height: 40px;
+	padding: 20px 55px 20px 55px;
+  .el-select{
+		width: 140px;
+		margin-right: 10px;
+	}
+	.el-date-editor{
+		width:300px !important;
+		margin: 0;
+	}
+  .el-button {
+		font-size: 16px;
+		padding: 0 26px;
+		margin: 0 20px;
+		border-radius: 10px;
+  }
+}
+:deep(.el-select),:deep(.el-date-editor) {
+  --el-select-input-focus-border-color: none !important;
+  border: 1px solid #dddddd;
+  border-radius: 10px;
+  overflow: hidden;
+}
+:deep(.search-box.s2 .el-select){
+	border: none;
+}
+:deep(.el-date-editor) {
+  --el-select-input-focus-border-color: none !important;
+}
+:deep(.el-input__wrapper) {
+  box-shadow: none !important;
+}
+:deep(.el-input__wrapper.is-focus) {
+  box-shadow: none !important;
+}
+:deep(.el-select:hover:not(.el-select--disabled) .el-input__wrapper) {
+  box-shadow: none !important;
+}
+.charts-container{
   position: relative;
-  padding: 30px;
+  padding:0 30px 20px 30px;
 }
-.area-top {
+.charts-item {
   background: #ffffff;
+  border: 1px solid #e8eaec;
+  border-radius: 24px;
+  text-align: center;
+  position: relative;
+  .title {
+	margin: 0;
+	height: 78px;
+	line-height: 78px;
+	text-align: left;
+	text-indent: 2em;
+	font-size: 18px;
+  }
+  .item {
+	padding-bottom: 30px;
+  }
+  .chart {
+	margin: 0 auto;
+  }
+  p {
+	margin: 0;
+	line-height: 24px;
+	font-size: 16px;
+  }
+  .bar {
+	margin-top: 60px;
+  }
+  .empty {
+	box-sizing: border-box;
+	height: 400px;
+	padding-top: 200px;
+	text-align: center;
+	background-image: url("../../assets/empty.png");
+	background-repeat: no-repeat;
+	background-position: center 30%;
+	p {
+	  font-size: 14px;
+	  color: #a1a1a1;
+	  &.red {
+		color: #e04962;
+	  }
+	}
+  }
 }
 </style>

+ 113 - 0
src/views/charts-components/AverageBarChart.vue

@@ -0,0 +1,113 @@
+<!--  脑电评估检测指数分析看板 柱状图 -->
+<template>
+	<div :id="id" :class="className" :style="{ height, width }" />
+</template>
+
+<script setup lang="ts">
+import * as echarts from "echarts";
+
+const props = defineProps({
+	id: {
+		type: String,
+		default: "averageBarChart",
+		required: true,
+	},
+	className: {
+		type: String,
+		default: "chart",
+	},
+	width: {
+		type: String,
+		default: "400px",
+	},
+	height: {
+		type: String,
+		default: "400px",
+	},
+	// dataSets:[[50, 60, 55, 64.5],[55, 90, 70, 82.5]]
+	dataSets: {
+		type: Array,
+		default: [] as Array<number>,
+	},
+});
+/**
+ * 配置项
+ */
+const options = {
+	color: ["#f8b865", "#8877ef"],
+	grid: {
+		left: "10%",
+		right: "8%",
+		top: "8%",
+		bottom: "15%",
+	},
+	tooltip: {
+		show: true,
+	},
+	xAxis: [
+		{
+			type: "category",
+			data: ["专注指数", "放松指数", "认知执行指数", "心情指数"],
+			axisLabel: {
+				fontSize: 14,
+				color: "#00449b",
+				interval: 0,
+			},
+			axisLine: {
+				lineStyle: {
+					color: "#4284f2",
+					width: 2,
+				},
+			},
+			axisTick: {
+				show: false,
+			},
+		},
+	],
+	yAxis: [
+		{
+			type: "value",
+			show: true,
+			min: 0,
+			max: 100,
+			offset: -5,
+			axisLine: {
+				show: true,
+				lineStyle: {
+					color: "#4284f2",
+					width: 1,
+				},
+			},
+			splitLine: {
+				show: false,
+			},
+		},
+	],
+	series: [
+		{
+			name: "首次检测",
+			type: "bar",
+			data: props.dataSets?.[0],
+			barGap: 0,
+			barCategoryGap: "50%",
+		},
+		{
+			name: "最近检测",
+			type: "bar",
+			data: props.dataSets?.[1],
+		},
+	],
+};
+
+onMounted(() => {
+	// 图表初始化
+	const chart = echarts.init(
+		document.getElementById(<string>props.id) as HTMLDivElement
+	);
+	chart.setOption(options);
+	// 大小自适应
+	window.addEventListener("resize", () => {
+		chart.resize();
+	});
+});
+</script>

+ 98 - 0
src/views/charts-components/LineChart.vue

@@ -0,0 +1,98 @@
+<!-- 圆饼图 -->
+<template>
+	<div :id="id" :class="className" :style="{ height, width }" />
+</template>
+
+<script setup lang="ts">
+import * as echarts from "echarts";
+
+const props = defineProps({
+	id: {
+		type: String,
+		default: "lineChart",
+		required: true,
+	},
+	className: {
+		type: String,
+		default: "chart",
+	},
+	width: {
+		type: String,
+		default: "400px",
+	},
+	height: {
+		type: String,
+		default: "300px",
+	},
+	title: {
+		type: String,
+		default: "",
+	},
+	data: {
+		type: Array,
+		default: [] as Array<number>,
+	},
+});
+/**
+ * 配置项
+ */
+const options = {
+	grid: {
+		x: "5%",
+		y: "5%",
+		x2: "8%",
+		y2: "10%",
+	},
+	xAxis: {
+		type: 'category',
+		data: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],
+		axisLabel: {
+			interval: (value, index) => { return (index%4)==0;}
+		},
+	},
+	yAxis: {
+		type: 'value',
+		max: 100,
+		interval: 10
+	},
+	series: [
+		{
+			data: [20, 30, 50, 78, 53, 47, 60,30, 50, 78, 53, 47, 60,30, 50, 78, 53, 47, 60],
+			type: 'line',
+			lineStyle:{
+				normal:{
+					color:{
+						type: "linear",
+						x:0,
+						y:0,
+						x2:0,
+						y2:1,
+						colorStops:[
+							{
+								offset:0,
+								color:"#ffd223",
+							},
+							{
+								offset:1,
+								color:"#9685fb",
+							},
+						]
+					}
+				}
+			}
+		}
+	]
+};
+
+onMounted(() => {
+	// 图表初始化
+	const chart = echarts.init(
+		document.getElementById(<string>props.id) as HTMLDivElement
+	);
+	chart.setOption(options);
+	// 大小自适应
+	window.addEventListener("resize", () => {
+		chart.resize();
+	});
+});
+</script>

+ 1 - 1
src/views/student/result.vue

@@ -64,7 +64,7 @@ const pieData = ref([12, 18, 20, 28, 22]);
       </el-col>
       <el-col :xs="24" :sm="17" :md="17">
         <div class="box-card">
-          <div class="chart-title">专注力脑电维度数据分析</div>
+          <div class="chart-title">专注力区间分布统计</div>
           <el-row>
             <el-col :xs="24" :sm="7" :md="7">
               <div class="charts">