|
@@ -98,7 +98,7 @@
|
|
|
<el-divider direction="vertical" border-style="dashed" />
|
|
|
<div class="px-8px text-center" style="margin: auto;">
|
|
|
<div class="mb-16px text-18px text-white ">请假人数</div>
|
|
|
- <CountTo class="text-28px" :start-val="0" :end-val="totalSate.ExcuNum" :duration="2600" />
|
|
|
+ <CountTo class="text-28px" :start-val="0" :end-val="totalSate.excuseNum" :duration="2600" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-card>
|
|
@@ -123,7 +123,7 @@
|
|
|
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
|
|
<el-card shadow="never" class=" mb-10px h-480px ">
|
|
|
<template #header>
|
|
|
- <div class="h-5 flex justify-between fw-800 text-20px">
|
|
|
+ <div class="h-7 flex justify-between fw-800 text-20px">
|
|
|
<span>周出勤情况</span>
|
|
|
</div>
|
|
|
</template>
|
|
@@ -138,30 +138,51 @@
|
|
|
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
|
|
<el-card shadow="never" class="mb-10px ">
|
|
|
<template #header>
|
|
|
- <div class="h-5 flex justify-between fw-800 text-20px">
|
|
|
+ <div class="h-7 flex justify-between fw-800 text-20px">
|
|
|
<span>实时打卡状态</span>
|
|
|
</div>
|
|
|
</template>
|
|
|
<div class="demo">
|
|
|
+ <div class="table-header">
|
|
|
+ <div class="header">
|
|
|
+ <el-row class="shouye" style="border: none;">
|
|
|
+ <el-col :span="6" class="center text-18px" style="border: none;">
|
|
|
+ <div>姓名</div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6" class="center text-18px" style="border: none;">
|
|
|
+ <div>学号</div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6" class="center text-18px" style="border: none;">
|
|
|
+ <div>工作间</div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6" class="center text-18px" style="border: none;">
|
|
|
+ <div>打卡时间</div>
|
|
|
+ </el-col>
|
|
|
+ <!-- <el-col :span="10" class="center text-18px" style="border: none;">
|
|
|
+ <div>缺勤时间段</div>
|
|
|
+ </el-col> -->
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
<vue3ScrollSeamless class="scroll-wrap text-color" :classOptions="classOptions" :dataList="list">
|
|
|
<div v-if="list.length > 0">
|
|
|
<el-row v-for="(item, i) of list" :key="i" class="shouye"
|
|
|
style="margin-bottom: 10px; display: flex; justify-content: center;">
|
|
|
- <el-col :span="7" class="center"
|
|
|
+ <el-col :span="6" class="center"
|
|
|
style="display: flex; justify-content: center; align-items: center; padding: 6px;">
|
|
|
- <div>{{ item.trainNumber }}</div>
|
|
|
+ <div>{{ item.studentName }}</div>
|
|
|
</el-col>
|
|
|
<el-col :span="6" class="center"
|
|
|
style="display: flex; justify-content: center; align-items: center; padding: 6px;">
|
|
|
- <div>{{ item.destination }}</div>
|
|
|
+ <div>{{ item.userNumber }}</div>
|
|
|
</el-col>
|
|
|
<el-col :span="6" class="center"
|
|
|
style="display: flex; justify-content: center; align-items: center; padding: 6px;">
|
|
|
- <div>{{ item.departureTime }}</div>
|
|
|
+ <div>{{ item.daptName }}</div>
|
|
|
</el-col>
|
|
|
- <el-col :span="5" class="center"
|
|
|
+ <el-col :span="6" class="center"
|
|
|
style="display: flex; justify-content: center; align-items: center; padding: 6px;">
|
|
|
- <div>{{ item.status }}</div>
|
|
|
+ <div>{{ item.clockInTime }}</div>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
</div>
|
|
@@ -188,39 +209,45 @@
|
|
|
</template>
|
|
|
|
|
|
<div class="demos">
|
|
|
- <!-- <div class="table-header">
|
|
|
+ <div class="table-header">
|
|
|
<div class="header">
|
|
|
<el-row class="shouye" style="border: none;">
|
|
|
- <el-col :span="8" class="center text-18px" style="border: none;">
|
|
|
- <div>学生学号</div>
|
|
|
+ <el-col :span="6" class="center text-18px" style="border: none;">
|
|
|
+ <div>姓名</div>
|
|
|
</el-col>
|
|
|
- <el-col :span="10" class="center text-18px" style="border: none;">
|
|
|
+ <el-col :span="6" class="center text-18px" style="border: none;">
|
|
|
+ <div>学号</div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6" class="center text-18px" style="border: none;">
|
|
|
<div>工作间</div>
|
|
|
</el-col>
|
|
|
<el-col :span="6" class="center text-18px" style="border: none;">
|
|
|
<div>导师</div>
|
|
|
</el-col>
|
|
|
- <el-col :span="10" class="center text-18px" style="border: none;">
|
|
|
+ <!-- <el-col :span="10" class="center text-18px" style="border: none;">
|
|
|
<div>缺勤时间段</div>
|
|
|
- </el-col>
|
|
|
+ </el-col> -->
|
|
|
</el-row>
|
|
|
</div>
|
|
|
- </div> -->
|
|
|
+ </div>
|
|
|
|
|
|
<vue3ScrollSeamless class="scroll-wraps text-color" :classOptions="class2Options" :dataList="list2">
|
|
|
<div v-if="list2.length > 0">
|
|
|
<el-row v-for="(item, i) of list2" :key="i" class="shouye">
|
|
|
- <el-col :span="6" class="center" style="padding: 10px; border: none;">
|
|
|
+ <!-- <el-col :span="6" class="center" style="padding: 10px; border: none;">
|
|
|
<div>{{ item.ID }}</div>
|
|
|
+ </el-col> -->
|
|
|
+ <el-col :span="6" class="center" style="padding: 10px; border: none;">
|
|
|
+ <div>{{ item.studentName }}</div>
|
|
|
</el-col>
|
|
|
<el-col :span="6" class="center" style="padding: 10px; border: none;">
|
|
|
- <div>{{ item.destination }}</div>
|
|
|
+ <div>{{ item.userNumber }}</div>
|
|
|
</el-col>
|
|
|
<el-col :span="6" class="center" style="padding: 10px; border: none;">
|
|
|
- <div>{{ item.trainNumber }}</div>
|
|
|
+ <div>{{ item.deptName }}</div>
|
|
|
</el-col>
|
|
|
<el-col :span="6" class="center" style="padding: 10px; border: none;">
|
|
|
- <div>{{ item.departureTime }}</div>
|
|
|
+ <div>{{ item.supervisor }}</div>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
</div>
|
|
@@ -241,28 +268,15 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
<div class="demoss">
|
|
|
- <!-- <div class="table-header">
|
|
|
- <div class="header">
|
|
|
- <el-row class="shouye">
|
|
|
- <el-col :span="12" class="center text-18px">
|
|
|
- <div>工作间</div>
|
|
|
- </el-col>
|
|
|
- <el-col :span="15" class="center text-18px ">
|
|
|
- <div>学生毕业条件达成率</div>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- </div>
|
|
|
- </div> -->
|
|
|
-
|
|
|
<vue3ScrollSeamless class="scroll-wrapss text-color" :classOptions="list1Options" :dataList="list1">
|
|
|
<div v-if="list1.length > 0">
|
|
|
<el-row v-for="(item, i) of list1" :key="i" class="shouye" :style="{ marginBottom: '10px' }">
|
|
|
<!-- 增加行与行之间的间距 -->
|
|
|
<el-col :span="12" class="center" style="padding: 8px;"> <!-- 增加内边距 -->
|
|
|
- <div>{{ item.destination }}</div>
|
|
|
+ <div>{{ item.name }}</div>
|
|
|
</el-col>
|
|
|
<el-col :span="12" class="center" style="padding: 8px;">
|
|
|
- <div>{{ item.departureNumber }}</div>
|
|
|
+ <div>{{ item.graduationRate }}%</div>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
</div>
|
|
@@ -283,7 +297,6 @@
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
-import { set } from 'lodash-es'
|
|
|
import { EChartsOption, List } from 'echarts'
|
|
|
import { formatTime } from '@/utils'
|
|
|
import { useUserStore } from '@/store/modules/user'
|
|
@@ -293,6 +306,7 @@ import { pieOptions, barOptions } from './echarts-data'
|
|
|
import { reactive, onMounted, watchEffect } from "vue";
|
|
|
import { vue3ScrollSeamless } from "vue3-scroll-seamless";
|
|
|
import * as UserApi from '@/api/system/user'
|
|
|
+import * as DeptApi from '@/api/system/dept'
|
|
|
import { StudentAttendanceApi } from '@/api/system/studentAttendance'
|
|
|
|
|
|
defineOptions({ name: 'Home' })
|
|
@@ -352,7 +366,7 @@ const getProject = async () => {
|
|
|
let totalSate = reactive<WorkplaceTotal>({
|
|
|
normalNum: 0,
|
|
|
errorNum: 0,
|
|
|
- ExcuNum: 0,
|
|
|
+ excuseNum: 0,
|
|
|
})
|
|
|
const getCount = async () => {
|
|
|
const data = await StudentAttendanceApi.getDayAttendance()
|
|
@@ -360,14 +374,13 @@ const getCount = async () => {
|
|
|
totalSate = Object.assign(totalSate, data)
|
|
|
}
|
|
|
|
|
|
-/**毕业达成统计 */
|
|
|
+// /**毕业达成统计 */
|
|
|
let pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
|
|
|
-const getUserAccessSource = async () => {
|
|
|
- const data = [
|
|
|
- { value: 1048, name: '达成人数' },
|
|
|
- { value: 735, name: '未达成人数' },
|
|
|
- ];
|
|
|
-
|
|
|
+const getGraduateCount = async () => {
|
|
|
+ const data = await UserApi.getGraduateCount()
|
|
|
+ console.log("毕业达成统计", data);
|
|
|
+ const studentNum = data[0].studentNum;
|
|
|
+ const graduateNum = data[1].graduateNum;
|
|
|
const options = {
|
|
|
title: {
|
|
|
text: null,
|
|
@@ -388,31 +401,50 @@ const getUserAccessSource = async () => {
|
|
|
center: ['50%', '85%'], // 中心位置
|
|
|
startAngle: 180,
|
|
|
endAngle: 360,
|
|
|
- data: data.map(v => ({
|
|
|
- value: v.value,
|
|
|
- name: t(v.name),
|
|
|
- label: {
|
|
|
- show: true,
|
|
|
- position: 'outside', // 拉出标注
|
|
|
- formatter: '{b}: {c}', // 显示名称和数值
|
|
|
- // 可以自定义引线的样式
|
|
|
- emphasis: {
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ value: graduateNum, // 畢業生数量
|
|
|
+ name: '已达成', // 国际化名称或直接用字符串 '毕业生'
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ position: 'outside', // 拉出标注
|
|
|
+ formatter: '{b}: {c}', // 显示名称和数值
|
|
|
+ emphasis: {
|
|
|
+ show: true,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ labelLine: {
|
|
|
show: true,
|
|
|
+ length: 75,
|
|
|
+ length2: 10,
|
|
|
+ smooth: true,
|
|
|
+ lineDash: [5, 5],
|
|
|
},
|
|
|
},
|
|
|
- // 自定义引线样式
|
|
|
- labelLine: {
|
|
|
- show: true,
|
|
|
- length: 75, // 引线长度
|
|
|
- length2: 10, // 后半段引线的长度
|
|
|
- smooth: true, // 引线是否平滑
|
|
|
- lineDash: [5, 5], // 引线虚线样式
|
|
|
+ {
|
|
|
+ value: studentNum, // 在校生数量(总在校生减去毕业生)
|
|
|
+ name: '未达成', // 国际化名称或直接用字符串 '在校生'
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ position: 'outside',
|
|
|
+ formatter: '{b}: {c}',
|
|
|
+ emphasis: {
|
|
|
+ show: true,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ labelLine: {
|
|
|
+ show: true,
|
|
|
+ length: 75,
|
|
|
+ length2: 10,
|
|
|
+ smooth: true,
|
|
|
+ lineDash: [5, 5],
|
|
|
+ },
|
|
|
},
|
|
|
- })),
|
|
|
+ ],
|
|
|
itemStyle: {
|
|
|
// 设置饼图的颜色
|
|
|
color: function (params) {
|
|
|
- const colorList = ['#94d6da', '#00a6ac']; // 定义颜色数组
|
|
|
+ const colorList = ['#2585a6','#5cb5e3' ]; // 定义颜色数组
|
|
|
return colorList[params.dataIndex]; // 根据数据索引返回对应的颜色
|
|
|
}
|
|
|
}
|
|
@@ -420,61 +452,39 @@ const getUserAccessSource = async () => {
|
|
|
],
|
|
|
};
|
|
|
|
|
|
- set(
|
|
|
- pieOptionsData,
|
|
|
- 'legend.data',
|
|
|
- data.map((v) => t(v.name))
|
|
|
- );
|
|
|
+// // set(
|
|
|
+// // pieOptionsData,
|
|
|
+// // 'legend.data',
|
|
|
+// // data.map((v) => t(v.name))
|
|
|
+// // );
|
|
|
|
|
|
pieOptionsData = Object.assign(pieOptionsData, options);
|
|
|
};
|
|
|
|
|
|
/**周出勤情况 */
|
|
|
let barOptionsData = reactive<EChartsOption>(barOptions) as EChartsOption
|
|
|
- const getWeekend = async () => {
|
|
|
- try {
|
|
|
- const res = await StudentAttendanceApi.getWeekendAttendance();
|
|
|
- console.log("周出勤情况", res);
|
|
|
- // 更新图表数据
|
|
|
- barOptionsData.series = [
|
|
|
- {
|
|
|
- name: '正常出勤',
|
|
|
- type: 'bar',
|
|
|
- data: [res.normalNum], // 将正常出勤人数放入数据中
|
|
|
- },
|
|
|
- {
|
|
|
- name: '缺勤',
|
|
|
- type: 'bar',
|
|
|
- data: [res.errorNum], // 将缺勤人数放入数据中
|
|
|
- }
|
|
|
- ];
|
|
|
- } catch (error) {
|
|
|
- console.error("获取周出勤情况失败:", error);
|
|
|
- }
|
|
|
+const getWeekend = async () => {
|
|
|
+ const data = await StudentAttendanceApi.getWeekendAttendance();
|
|
|
+ console.log("周出勤情况", data);
|
|
|
+ const normalData = data.dailyNormalList
|
|
|
+ const errorData = data.dailyErrorList;
|
|
|
+ const excuseData = data.dailyExcuseList;
|
|
|
+ barOptionsData.series[0].data = normalData;
|
|
|
+ barOptionsData.series[1].data = errorData;
|
|
|
+ barOptionsData.series[2].data = excuseData;
|
|
|
};
|
|
|
|
|
|
|
|
|
+const params = reactive({
|
|
|
+ list: []
|
|
|
+})
|
|
|
/**缺勤预警 */
|
|
|
-const list2 = reactive([
|
|
|
- { trainNumber: '张一', destination: '101', departureTime: '09:00', ID: '10' },
|
|
|
- { trainNumber: '张二', destination: '102', departureTime: '09:15', ID: '15' },
|
|
|
- { trainNumber: '张三', destination: '103', departureTime: '09:30', ID: '13' },
|
|
|
- { trainNumber: '张四', destination: '104', departureTime: '09:45', ID: '18' },
|
|
|
- { trainNumber: '张五', destination: '105', departureTime: '10:00', ID: '19' },
|
|
|
- { trainNumber: '李一', destination: '201', departureTime: '10:15', ID: '12' },
|
|
|
- { trainNumber: '李二', destination: '202', departureTime: '10:30', ID: '02' },
|
|
|
- { trainNumber: '李三', destination: '203', departureTime: '10:45', ID: '09' },
|
|
|
- { trainNumber: '李四', destination: '204', departureTime: '11:00', ID: '16' },
|
|
|
- { trainNumber: '李五', destination: '205', departureTime: '11:15', ID: '20' },
|
|
|
- { trainNumber: '王一', destination: '301', departureTime: '11:30', ID: '28' },
|
|
|
- { trainNumber: '王二', destination: '302', departureTime: '11:45', ID: '36' },
|
|
|
- { trainNumber: '王三', destination: '303', departureTime: '12:00', ID: '07' },
|
|
|
- { trainNumber: '王四', destination: '304', departureTime: '12:15', ID: '41' },
|
|
|
- { trainNumber: '王五', destination: '305', departureTime: '12:30', ID: '15' },
|
|
|
- { trainNumber: '王六', destination: '306', departureTime: '12:45', ID: '22' },
|
|
|
- { trainNumber: '王七', destination: '307', departureTime: '13:00', ID: '27' }
|
|
|
-
|
|
|
-]);
|
|
|
+let list2 = reactive([]);
|
|
|
+const getStudentAttendanceError = async () => {
|
|
|
+ const data = await StudentAttendanceApi.getStudentAttendanceErrorPage(params)
|
|
|
+ console.log("缺勤列表", data);
|
|
|
+ list2.splice(0, list2.length, ...data.list);
|
|
|
+}
|
|
|
const class2Options = reactive({
|
|
|
step: 0.5,//滚动速度值越大越快,但是值太小会卡顿
|
|
|
limitMoveNum: list2.length,//无缝滚动列表元素的长度,一般设置为列表的长度
|
|
@@ -483,26 +493,12 @@ const class2Options = reactive({
|
|
|
|
|
|
/**实时打卡状态 */
|
|
|
//打卡滚动列表
|
|
|
-const list = reactive([
|
|
|
- { trainNumber: '张一', destination: '101', departureTime: '09:00', status: '准点' },
|
|
|
- { trainNumber: '张二', destination: '102', departureTime: '09:15', status: '准点' },
|
|
|
- { trainNumber: '张三', destination: '103', departureTime: '09:30', status: '晚点' },
|
|
|
- { trainNumber: '张四', destination: '104', departureTime: '09:45', status: '准点' },
|
|
|
- { trainNumber: '张五', destination: '105', departureTime: '10:00', status: '准点' },
|
|
|
- { trainNumber: '李一', destination: '201', departureTime: '10:15', status: '准点' },
|
|
|
- { trainNumber: '李二', destination: '202', departureTime: '10:30', status: '晚点' },
|
|
|
- { trainNumber: '李三', destination: '203', departureTime: '10:45', status: '准点' },
|
|
|
- { trainNumber: '李四', destination: '204', departureTime: '11:00', status: '晚点' },
|
|
|
- { trainNumber: '李五', destination: '205', departureTime: '11:15', status: '准点' },
|
|
|
- { trainNumber: '王一', destination: '301', departureTime: '11:30', status: '准点' },
|
|
|
- { trainNumber: '王二', destination: '302', departureTime: '11:45', status: '准点' },
|
|
|
- { trainNumber: '王三', destination: '303', departureTime: '12:00', status: '晚点' },
|
|
|
- { trainNumber: '王四', destination: '304', departureTime: '12:15', status: '准点' },
|
|
|
- { trainNumber: '王五', destination: '305', departureTime: '12:30', status: '准点' },
|
|
|
- { trainNumber: '王六', destination: '306', departureTime: '12:45', status: '准点' },
|
|
|
- { trainNumber: '王七', destination: '307', departureTime: '13:00', status: '晚点' }
|
|
|
-
|
|
|
-]);
|
|
|
+const list = reactive([]);
|
|
|
+const getStudentAttendance = async () => {
|
|
|
+ const data = await StudentAttendanceApi.getStudentAttendancePage(params)
|
|
|
+ console.log("打卡列表", data);
|
|
|
+ list.splice(0, list.length, ...data.list);
|
|
|
+}
|
|
|
const classOptions = reactive({
|
|
|
step: 0.5,//滚动速度值越大越快,但是值太小会卡顿
|
|
|
limitMoveNum: list.length,//无缝滚动列表元素的长度,一般设置为列表的长度
|
|
@@ -512,31 +508,16 @@ const classOptions = reactive({
|
|
|
|
|
|
/** 学生毕业条件达成率 */
|
|
|
//在线情况滚动列表
|
|
|
-const list1 = reactive([
|
|
|
- { destination: '101', departureNumber: '98%' },
|
|
|
- { destination: '102', departureNumber: '94%' },
|
|
|
- { destination: '103', departureNumber: '96%' },
|
|
|
- { destination: '104', departureNumber: '98%' },
|
|
|
- { destination: '105', departureNumber: '93%' },
|
|
|
- { destination: '201', departureNumber: '97%' },
|
|
|
- { destination: '202', departureNumber: '95%' },
|
|
|
- { destination: '203', departureNumber: '98%' },
|
|
|
- { destination: '204', departureNumber: '96%' },
|
|
|
- { destination: '205', departureNumber: '94%' },
|
|
|
- { destination: '301', departureNumber: '97%' },
|
|
|
- { destination: '302', departureNumber: '96%' },
|
|
|
- { destination: '303', departureNumber: '95%' },
|
|
|
- { destination: '304', departureNumber: '97%' },
|
|
|
- { destination: '305', departureNumber: '98%' },
|
|
|
- { destination: '306', departureNumber: '99%' },
|
|
|
- { destination: '307', departureNumber: '96%' }
|
|
|
-
|
|
|
-]);
|
|
|
+const list1 = reactive([]);
|
|
|
+const getGraduationSource = async () => {
|
|
|
+ const data = await DeptApi.getGraduationSource()
|
|
|
+ console.log("毕业条件达成率", data);
|
|
|
+ list1.splice(0, list1.length, ...data);
|
|
|
+}
|
|
|
const list1Options = reactive({
|
|
|
step: 0.5,//滚动速度值越大越快,但是值太小会卡顿
|
|
|
limitMoveNum: list1.length,//无缝滚动列表元素的长度,一般设置为列表的长度
|
|
|
direction: 1,//方向: 0 往下 1 往上 2 向左 3 向右。
|
|
|
-
|
|
|
});
|
|
|
|
|
|
onMounted(() => {
|
|
@@ -544,7 +525,10 @@ onMounted(() => {
|
|
|
getWeekend()
|
|
|
getProject()
|
|
|
getCount()
|
|
|
- // getNotice()
|
|
|
+ getStudentAttendanceError()
|
|
|
+ getStudentAttendance()
|
|
|
+ getGraduationSource()
|
|
|
+ getGraduateCount()
|
|
|
})
|
|
|
|
|
|
|
|
@@ -780,6 +764,11 @@ const getAllApi = async () => {
|
|
|
await getDetail(),
|
|
|
getCount(),
|
|
|
getProject(),
|
|
|
+ getWeekend(),
|
|
|
+ getStudentAttendanceError(),
|
|
|
+ getStudentAttendance(),
|
|
|
+ getGraduationSource(),
|
|
|
+ getGraduateCount(),
|
|
|
// getNotice(),
|
|
|
// getBasic(),
|
|
|
// getShortcut(),
|
|
@@ -789,15 +778,6 @@ const getAllApi = async () => {
|
|
|
loading.value = false
|
|
|
}
|
|
|
|
|
|
-watchEffect(detail => {
|
|
|
- console.log('detail', detail)
|
|
|
- // getBasic()
|
|
|
-})
|
|
|
-
|
|
|
-// onMounted(() => {
|
|
|
-
|
|
|
-// })
|
|
|
-
|
|
|
getAllApi()
|
|
|
</script>
|
|
|
|