ソースを参照

build: 看板首页样式

chaooo 2 年 前
コミット
9b466a4c44

+ 1 - 0
package.json

@@ -38,6 +38,7 @@
     ]
   },
   "dependencies": {
+    "@element-plus/icons-vue": "^2.1.0",
     "@types/lodash": "^4.14.195",
     "@vitejs/plugin-vue": "^4.2.3",
     "@vueuse/core": "^10.1.2",

BIN
src/assets/index/equipments.png


+ 7 - 1
src/layout/components/Navbar.vue

@@ -8,6 +8,7 @@ import { SchoolList } from "@/api/school/types";
 import { getSchoolList } from "@/api/school";
 import { watch } from "vue";
 import SvgIcon from "@/components/SvgIcon/index.vue";
+import { CaretBottom } from "@element-plus/icons-vue";
 
 const appStore = useAppStore();
 const tagsViewStore = useTagsViewStore();
@@ -105,7 +106,12 @@ watch(
       </div>
       <!-- 学校选择下拉框 -->
       <div class="nav-select">
-        <el-select v-model="schoolNumber" size="large" placeholder="全部学校">
+        <el-select
+          v-model="schoolNumber"
+          size="large"
+          placeholder="全部学校"
+          :suffix-icon="CaretBottom"
+        >
           <el-option
             v-for="item in schoolData"
             :key="item.school_id"

+ 1 - 1
src/layout/components/Sidebar/SidebarItem.vue

@@ -105,7 +105,7 @@ function resolvePath(routePath: string) {
           :icon-class="item.meta.icon"
         />
         <span v-if="item.meta && item.meta.title">{{ item.meta.title }}</span>
-        <el-icon><ArrowRightBold color="#ffffff" /></el-icon>
+        <!--        <el-icon><ArrowRightBold color="#ffffff" /></el-icon>-->
       </template>
 
       <sidebar-item

+ 32 - 17
src/store/modules/permission.ts

@@ -12,12 +12,17 @@ const schoolRoutes: RouteRecordRaw[] = JSON.parse(
   JSON.stringify([
     {
       path: "/",
-      component: "AdminIndex",
       redirect: "/areaboard",
+      meta: { hidden: true },
+    },
+    {
+      path: "/areaboard",
+      component: "AdminIndex",
+      redirect: "/areaboard/index",
       children: [
         {
-          path: "areaboard",
-          component: "areaboard",
+          path: "index",
+          component: "areaboard/index",
           meta: { title: "区域级数据看板", icon: "board", keepAlive: true },
         },
       ],
@@ -29,14 +34,24 @@ const adminRoutes: RouteRecordRaw[] = JSON.parse(
   JSON.stringify([
     {
       path: "/",
-      component: "SchoolIndex",
       redirect: "/dashboard",
+      meta: { hidden: true },
+    },
+    {
+      path: "/dashboard",
+      component: "SchoolIndex",
+      redirect: "/dashboard/index",
       children: [
         {
-          path: "dashboard",
-          component: "dashboard",
+          path: "index",
+          component: "dashboard/index",
           meta: { title: "数据看板", icon: "board", keepAlive: true },
         },
+        {
+          path: "example",
+          component: "dashboard/example",
+          meta: { title: "数据看板示例", hidden: true },
+        },
       ],
     },
     {
@@ -45,8 +60,8 @@ const adminRoutes: RouteRecordRaw[] = JSON.parse(
       redirect: "/class/index",
       children: [
         {
-          path: "class",
-          component: "class",
+          path: "index",
+          component: "class/index",
           meta: { title: "班级管理", icon: "class", keepAlive: true },
         },
       ],
@@ -57,8 +72,8 @@ const adminRoutes: RouteRecordRaw[] = JSON.parse(
       redirect: "/teacher/index",
       children: [
         {
-          path: "teacher",
-          component: "teacher",
+          path: "index",
+          component: "teacher/index",
           meta: { title: "教师管理", icon: "teacher", keepAlive: true },
         },
       ],
@@ -69,8 +84,8 @@ const adminRoutes: RouteRecordRaw[] = JSON.parse(
       redirect: "/student/index",
       children: [
         {
-          path: "student",
-          component: "student",
+          path: "index",
+          component: "student/index",
           meta: { title: "学生管理", icon: "student", keepAlive: true },
         },
       ],
@@ -81,8 +96,8 @@ const adminRoutes: RouteRecordRaw[] = JSON.parse(
       redirect: "/equipment/index",
       children: [
         {
-          path: "equipment",
-          component: "equipment",
+          path: "index",
+          component: "equipment/index",
           meta: { title: "设备管理", icon: "equipment", keepAlive: true },
         },
       ],
@@ -94,7 +109,7 @@ const adminRoutes: RouteRecordRaw[] = JSON.parse(
       children: [
         {
           path: "inxdex",
-          component: "training",
+          component: "training/index",
           meta: { title: "训练管理", icon: "training", keepAlive: true },
         },
       ],
@@ -106,7 +121,7 @@ const adminRoutes: RouteRecordRaw[] = JSON.parse(
       children: [
         {
           path: "index",
-          component: "evaluation",
+          component: "evaluation/index",
           meta: { title: "测评数据看板", icon: "evaluation", keepAlive: true },
         },
       ],
@@ -132,7 +147,7 @@ const filterAsyncRoutes = (routes: RouteRecordRaw[], roles: string[]) => {
     } else if (tmpRoute.component?.toString() == "AdminIndex") {
       console.log("AdminIndex");
     } else {
-      const component = modules[`../../views/${tmpRoute.component}/index.vue`];
+      const component = modules[`../../views/${tmpRoute.component}.vue`];
       if (component) {
         tmpRoute.component = component;
       } else {

+ 5 - 0
src/styles/reset.scss

@@ -74,3 +74,8 @@ a:active,
 div:focus {
   outline: none;
 }
+.clear::after {
+  content: "";
+  display: block;
+  clear: both;
+}

+ 38 - 11
src/styles/sidebar.scss

@@ -62,12 +62,10 @@
       border: none;
     }
 
-    .el-sub-menu__title {
-      display: none;
-    }
-
+    .el-sub-menu__title,
     .el-menu-item {
-      margin: 36px auto 0;
+      position: relative;
+      margin: 36px 0 0 20px;
       width: 210px;
       height: 38px;
       line-height: 38px;
@@ -78,14 +76,43 @@
       span {
         color: $menuText;
       }
-      &.is-active {
-        background: $menuHover;
-        color: #ffffff;
-        span {
-          color: $menuBg;
-        }
+    }
+    .el-sub-menu .el-sub-menu__icon-arrow {
+      width: 20px;
+    }
+    .el-menu-item.is-active {
+      background: $menuHover;
+      color: #ffffff;
+      span {
+        color: $menuBg;
+      }
+      &:after {
+        content: "\276F";
+        position: absolute;
+        right: 20px;
       }
     }
+
+    //.el-menu-item {
+    //  margin: 36px auto 0;
+    //  width: 210px;
+    //  height: 38px;
+    //  line-height: 38px;
+    //  border-radius: 5px;
+    //  color: #657dbc;
+    //  font-size: 16px;
+    //  padding-left: 30px;
+    //  span {
+    //    color: $menuText;
+    //  }
+    //  &.is-active {
+    //    background: $menuHover;
+    //    color: #ffffff;
+    //    span {
+    //      color: $menuBg;
+    //    }
+    //  }
+    //}
   }
 
   .hideSidebar {

+ 141 - 0
src/views/dashboard/components/BarChart.vue

@@ -0,0 +1,141 @@
+<!--  柱状图 -->
+<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: "barChart",
+  },
+  className: {
+    type: String,
+    default: "",
+  },
+  width: {
+    type: String,
+    default: "400px",
+    required: true,
+  },
+  height: {
+    type: String,
+    default: "400px",
+    required: true,
+  },
+  title: {
+    type: String,
+    default: "",
+  },
+  percentData: {
+    type: String,
+    default: "0,0,0,0,0",
+  },
+  numberData: {
+    type: String,
+    default: "0,0,0,0,0",
+  },
+});
+function showText(index) {
+  return (
+    props.numberData.split(",")[index] +
+    "人 (" +
+    props.percentData.split(",")[index] +
+    "%)"
+  );
+}
+const options = {
+  title: {
+    text: props.title,
+    left: "20%",
+    textStyle: {
+      color: "#2e3346",
+      fontWeight: "normal",
+      fontSize: 15,
+    },
+  },
+  grid: {
+    left: "10%",
+    right: "10%",
+    top: "8%",
+    containLabel: true,
+  },
+  xAxis: [
+    //x轴数据设置
+    {
+      type: "value",
+      show: false,
+      min: 0,
+      max: 100,
+    },
+  ],
+  yAxis: [
+    {
+      type: "category",
+      offset: 0,
+      axisLine: {
+        show: false,
+      },
+      data: ["0-20分", "21-40分", "41-60分", "51-60分", "60分以上"],
+      interval: 100,
+      axisPointer: {
+        type: "shadow",
+      },
+      axisTick: {
+        show: false,
+      },
+    },
+  ],
+  series: [
+    {
+      data: props.percentData.split(","),
+      type: "bar",
+      //barWidth: 36,
+      label: {
+        normal: {
+          show: true,
+          position: "right",
+          formatter(params) {
+            return params.value == 0 ? "" : showText(params.dataIndex);
+          },
+        },
+      },
+      showBackground: true,
+      backgroundStyle: {
+        color: "#f2f2fb",
+      },
+      itemStyle: {
+        //通常情况下:
+        normal: {
+          //每个柱子的颜色即为colorList数组里的每一项,如果柱子数目多于colorList的长度,则柱子颜色循环使用该数组
+          color: function (params) {
+            const colorList = [
+              "#68cfff",
+              "#688fff",
+              "#b890ef",
+              "#ee7a66",
+              "#f6c04c",
+            ];
+            return colorList[params.dataIndex];
+          },
+        },
+      },
+      barCategoryGap: "50%",
+    },
+  ],
+};
+
+onMounted(() => {
+  // 图表初始化
+  const chart = echarts.init(
+    document.getElementById(props.id) as HTMLDivElement
+  );
+  chart.setOption(options);
+  // 大小自适应
+  window.addEventListener("resize", () => {
+    chart.resize();
+  });
+});
+</script>

+ 2 - 2
src/views/dashboard/components/CircleChart.vue

@@ -21,12 +21,12 @@ const props = defineProps({
   },
   width: {
     type: String,
-    default: "200px",
+    default: "160px",
     required: true,
   },
   height: {
     type: String,
-    default: "200px",
+    default: "160px",
     required: true,
   },
   color: {

+ 2 - 2
src/views/dashboard/components/liquidChart.vue

@@ -22,12 +22,12 @@ const props = defineProps({
   },
   width: {
     type: String,
-    default: "200px",
+    default: "160px",
     required: true,
   },
   height: {
     type: String,
-    default: "200px",
+    default: "160px",
     required: true,
   },
   color: {

+ 21 - 4
src/views/dashboard/components/ExampleDialog.vue → src/views/dashboard/example.vue

@@ -1,6 +1,23 @@
-<!-- 优秀教学效果示例弹出层 -->
+<script setup lang="ts">
+defineOptions({
+  name: "Example",
+  inheritAttrs: false,
+});
+import { watch } from "vue";
+import { useUserStore } from "@/store/modules/user";
+const userStore = useUserStore();
+watch(
+  () => userStore.schoolId,
+  (newValue, oldValue) => {
+    console.log(newValue, oldValue);
+  }
+);
+</script>
+
 <template>
-  <div>
+  <div class="container">
+    <h1>优秀教学效果示例</h1>
+    <div>{{ userStore.schoolId }}</div>
     <div>
       <h6>示例学校:深圳市福田区惠文幼儿园</h6>
       <el-image src="" fit="contain" />
@@ -19,5 +36,5 @@
     </div>
   </div>
 </template>
-<script setup></script>
-<style lang="scss" scoped></style>
+
+<style scoped lang="scss"></style>

+ 196 - 79
src/views/dashboard/index.vue

@@ -1,15 +1,15 @@
 <script setup lang="ts">
 import DataCard from "@/views/dashboard/components/DataCard.vue";
-import ExampleDialog from "@/views/dashboard/components/ExampleDialog.vue";
 import LiquidChart from "@/views/dashboard/components/liquidChart.vue";
 import CircleChart from "@/views/dashboard/components/CircleChart.vue";
+import BarChart from "@/views/dashboard/components/BarChart.vue";
 defineOptions({
   // eslint-disable-next-line vue/no-reserved-component-names
   name: "Dashboard",
   inheritAttrs: false,
 });
 // import { getClassList, getSchoolList } from "@/api/school";
-import { ClassList } from "@/api/school/types";
+import { ClassList, SchoolList } from "@/api/school/types";
 import { watch } from "vue";
 import { useUserStore } from "@/store/modules/user";
 const userStore = useUserStore();
@@ -38,10 +38,16 @@ const trainingCount = 2000;
  */
 // 班级编号
 let classNumber = ref(0);
-const classData = ref<ClassList[]>([]);
+const classData = ref<ClassList[]>([
+  {
+    id: 0,
+    school_id: 0,
+    name: "全部班级",
+  },
+]);
 // function queryClassList() {
 // 	getClassList().then(({ data }) => {
-// 		classData.value = data;
+//		classData.value.concat(data);
 // 	});
 // }
 watch(
@@ -71,85 +77,124 @@ const dialogVisible = ref(false);
     />
 
     <!-- 班级选择 及 案例展示 -->
-    <el-row class="row-bg">
-      <el-col :span="12">
-        <el-select v-model="classNumber" placeholder="全部班级" size="large">
-          <el-option
-            v-for="item in classData"
-            :key="item.id"
-            :label="item.name"
-            :value="item.id"
-          />
-        </el-select>
-      </el-col>
-      <el-col :span="12">
-        <el-button text @click="dialogVisible = true"
-          >优秀教学效果示例</el-button
-        >
-      </el-col>
-    </el-row>
-
-    <!-- Echarts 图表 -->
-    <el-row :gutter="40">
-      <el-col :sm="24" :lg="6" class="mb-4">
-        <LiquidChart
-          id="liquidChart1"
-          data="37.5"
-          height="200px"
-          width="200px"
-          color="#56a2ff"
-          bg-color="#e6edf9"
-          class="bg-[var(--el-bg-color-overlay)]"
-        />
-      </el-col>
-      <el-col :sm="24" :lg="6" class="mb-4">
-        <LiquidChart
-          id="liquidChart2"
-          data="57.6"
-          height="200px"
-          width="200px"
-          color="#646bfa"
-          bg-color="#e6e8f9"
-          class="bg-[var(--el-bg-color-overlay)]"
+    <div class="class-select clear">
+      <el-select v-model="classNumber" placeholder="全部班级" size="large">
+        <el-option
+          v-for="item in classData"
+          :key="item.id"
+          :label="item.name"
+          :value="item.id"
         />
+      </el-select>
+      <router-link to="/dashboard/example">优秀教学效果示例</router-link>
+    </div>
+
+    <!-- Echarts 图表-->
+    <el-row :gutter="20">
+      <el-col :md="24" :lg="8">
+        <div class="charts-item">
+          <p class="title">学员专注力平均值整体对比分析</p>
+          <el-row justify="space-between">
+            <el-col :span="12">
+              <div class="item">
+                <LiquidChart
+                  id="liquidChart1"
+                  data="37.5"
+                  height="200px"
+                  width="200px"
+                  color="#3a7fc2"
+                  bg-color="#accded"
+                  class="chart"
+                />
+                <p>全体学员初期</p>
+                <p>专注力评估均值</p>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="item">
+                <LiquidChart
+                  id="liquidChart2"
+                  data="57.6"
+                  height="200px"
+                  width="200px"
+                  color="#5563ac"
+                  bg-color="#e4e7f4"
+                  class="chart"
+                />
+                <p>全体学员训练近期</p>
+                <p>专注力评估均值</p>
+              </div>
+            </el-col>
+          </el-row>
+          <el-row justify="space-between">
+            <el-col :span="12">
+              <div class="item">
+                <CircleChart
+                  id="circleChart1"
+                  data="37.5"
+                  height="200px"
+                  width="200px"
+                  color="#3a7fc2"
+                  bg-color="#e4e7f4"
+                  class="chart"
+                />
+                <p>初期训练</p>
+                <p>专注力50以上人数比例</p>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="item">
+                <CircleChart
+                  id="circleChart2"
+                  data="57.6"
+                  height="200px"
+                  width="200px"
+                  color="#5563ac"
+                  bg-color="#e4e7f4"
+                  class="chart"
+                />
+                <p>近期训练</p>
+                <p>专注力50以上人数比例</p>
+              </div>
+            </el-col>
+          </el-row>
+        </div>
       </el-col>
-      <el-col :sm="24" :lg="6" class="mb-4">
-        <CircleChart
-          id="circleChart1"
-          data="37.5"
-          height="200px"
-          width="200px"
-          color="#56a2ff"
-          bg-color="#e6edf9"
-          class="bg-[var(--el-bg-color-overlay)]"
-        />
-      </el-col>
-      <el-col :sm="24" :lg="6" class="mb-4">
-        <CircleChart
-          id="circleChart2"
-          data="57.6"
-          height="200px"
-          width="200px"
-          color="#646bfa"
-          bg-color="#e6e8f9"
-          class="bg-[var(--el-bg-color-overlay)]"
-        />
+      <!-- 学员专注力评分分级占比分析 -->
+      <el-col :md="24" :lg="16">
+        <div class="charts-item">
+          <p class="title">学员专注力评分分级占比分析</p>
+          <el-row justify="space-between">
+            <el-col :span="12">
+              <div class="bar">
+                <BarChart
+                  id="barChart1"
+                  width="460px"
+                  height="500px"
+                  title="全体学员初期训练专注力评分占比"
+                  percent-data="5,5,65,10,10"
+                  number-data="1,2,13,2,2"
+                  class="chart"
+                />
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="bar">
+                <BarChart
+                  id="barChart2"
+                  width="460px"
+                  height="500px"
+                  title="全体学员初期训练专注力评分占比"
+                  percent-data="10,25,65,30,10"
+                  number-data="1,2,13,6,10"
+                  class="chart"
+                />
+              </div>
+            </el-col>
+          </el-row>
+        </div>
       </el-col>
     </el-row>
-
-    <!-- 优秀教学效果示例弹出层 -->
-    <el-dialog
-      v-model="dialogVisible"
-      title="学员每次训练专注力评分均值整体变化曲线"
-      center
-    >
-      <ExampleDialog />
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="dialogVisible = false">关闭</el-button>
-        </span>
-      </template>
-    </el-dialog>
   </div>
 </template>
 
@@ -158,4 +203,76 @@ const dialogVisible = ref(false);
   position: relative;
   padding: 24px;
 }
+.class-select {
+  margin: 30px auto;
+  .el-select {
+    width: 160px;
+    margin: 0 20px 0 0;
+  }
+  a {
+    display: inline-block;
+    height: 38px;
+    line-height: 38px;
+    padding: 0 30px;
+    background: #4284f2;
+    border-radius: 12px;
+    color: #ffffff;
+  }
+}
+/* 自定义 el-select 样式 */
+:deep(.el-input__wrapper) {
+  background: #ffffff;
+  border-radius: 12px;
+}
+
+/* el-select 各种边框线隐藏**/
+:deep(.el-select) {
+  --el-select-input-focus-border-color: none !important;
+}
+:deep(.el-input__wrapper) {
+  box-shadow: none !important;
+}
+:deep(.el-select .el-input.is-focus .el-input__wrapper) {
+  box-shadow: none !important;
+}
+:deep(.el-select .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-item {
+  background: #ffffff;
+  border: 1px solid #e8eaec;
+  border-radius: 24px;
+  text-align: center;
+
+  .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;
+  }
+}
 </style>