人员信息、出差报备功能修改

This commit is contained in:
lSun 2024-12-18 16:55:46 +08:00
parent e29bfdbd53
commit 18d4b9abbe
6 changed files with 906 additions and 809 deletions

View File

@ -9,7 +9,7 @@ export function getdayAttReport(query) {
params: query params: query
}) })
} }
// 查询列表 // 查询列表
export function getAttDayReportDetailsList(query) { export function getAttDayReportDetailsList(query) {
return request({ return request({
@ -18,7 +18,7 @@ export function getAttDayReportDetailsList(query) {
params: query params: query
}) })
} }
// 导出 // 导出
export function exportDayReport(query) { export function exportDayReport(query) {
return request({ return request({
@ -27,4 +27,13 @@ export function exportDayReport(query) {
responseType: 'blob', responseType: 'blob',
params: query params: query
}) })
} }
// 查询列表
export function getAttDayList(query) {
return request({
url: '/system/attDetails/getAttDayList',
method: 'get',
params: query
})
}

File diff suppressed because it is too large Load Diff

View File

@ -1,333 +1,380 @@
<template> <template>
<div class="content"> <div class="content">
<div class="left-box"> <div class="left-box">
<div class="title-box"> <div class="title-box">
<div style="margin-left: 10px;font-size: 22px;font-weight: bold;">今日出勤状态</div> <div style="margin-left: 10px;font-size: 22px;font-weight: bold;">今日出勤状态</div>
<div> <div>
<el-date-picker <el-date-picker
v-model="date" @change="handleDateChange" :clearable="false" v-model="date" @change="handleDateChange" :clearable="false"
type="date" value-format="yyyy-MM-dd" :picker-options="pickerOptions" type="date" value-format="yyyy-MM-dd" :picker-options="pickerOptions"
placeholder="选择日期"> placeholder="选择日期">
</el-date-picker> </el-date-picker>
</div>
</div>
<div @click="toggleDialog(1)">
<div id="pieBox" style="width: 100%;height: 260px;" ></div>
</div>
</div> </div>
</div>
<div class="right-box"> <div >
<div class="title-box"> <div id="pieBox" style="width: 100%;height: 260px;"></div>
<div style="margin-left: 10px;font-size: 22px;font-weight: bold;">今日异常统计</div> <!-- 点击区域 -->
</div> <div class="clickable-texts">
<div class="right-list"> <div class="clickable-text expected" @click="handleClick('0')">应出勤人数</div>
<div class="listItem" style="background-color: #FFF7F1;" @click="toggleDialog(2)"> <div class="clickable-text actual" @click="handleClick('1')">实际出勤人数</div>
<div>迟到人数</div> <div class="clickable-text absent" @click="handleClick('2')">未出勤人数</div>
<h2>{{todayAbnormalBean.lateNum}}</h2>
</div>
<div class="listItem" style="background-color: #F7F8FA;" @click="toggleDialog(4)">
<div>早退人数</div>
<h2>{{todayAbnormalBean.earlyNum}}</h2>
</div>
<div class="listItem" style="background-color: #FFF2F2;" @click="toggleDialog(3)">
<div>旷工人数</div>
<h2>{{todayAbnormalBean.skippingNum}}</h2>
</div>
</div>
<div class="right-list">
<div class="listItem" style="background-color: #F0F8FF;" @click="toggleDialog(6)">
<div>请假人数</div>
<h2>{{todayAbnormalBean.leaveNum}}</h2>
</div>
<div class="listItem" style="background-color: #FBFFE3;" @click="toggleDialog(9)">
<div>打卡地异常</div>
<h2>{{todayAbnormalBean.addressErrorNum}}</h2>
</div>
<div class="listItem" style="background-color: #FFF0FB;" @click="toggleDialog(8)">
<div>出入异常</div>
<h2>{{todayAbnormalBean.einErrorNum}}</h2>
</div>
</div>
</div> </div>
</div>
</div> </div>
<div class="right-box">
<div class="title-box">
<div style="margin-left: 10px;font-size: 22px;font-weight: bold;">今日异常统计</div>
</div>
<div class="right-list">
<div class="listItem" style="background-color: #FFF7F1;" @click="toggleDialog(2)">
<div>迟到人数</div>
<h2>{{ todayAbnormalBean.lateNum }}</h2>
</div>
<div class="listItem" style="background-color: #F7F8FA;" @click="toggleDialog(4)">
<div>早退人数</div>
<h2>{{ todayAbnormalBean.earlyNum }}</h2>
</div>
<div class="listItem" style="background-color: #FFF2F2;" @click="toggleDialog(3)">
<div>旷工人数</div>
<h2>{{ todayAbnormalBean.skippingNum }}</h2>
</div>
</div>
<div class="right-list">
<div class="listItem" style="background-color: #F0F8FF;" @click="toggleDialog(6)">
<div>请假人数</div>
<h2>{{ todayAbnormalBean.leaveNum }}</h2>
</div>
<div class="listItem" style="background-color: #FBFFE3;" @click="toggleDialog(9)">
<div>打卡地异常</div>
<h2>{{ todayAbnormalBean.addressErrorNum }}</h2>
</div>
<div class="listItem" style="background-color: #FFF0FB;" @click="toggleDialog(8)">
<div>出入异常</div>
<h2>{{ todayAbnormalBean.einErrorNum }}</h2>
</div>
</div>
</div>
</div>
</template> </template>
<script> <script>
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import { getHomePageData } from '@/api/dashboard' import {getHomePageData} from '@/api/dashboard'
export default {
components: {
},
name: 'topOne',
data() {
return {
pieCharts: null,
date:new Date().toISOString().substring(0, 10),
pickerOptions: {
disabledDate: this.disabledDate,
},
pageData:{},
todayAbnormalBean:{
lateNum:'0',
earlyNum:'0',
skippingNum:'0',
leaveNum:'0',
addressErrorNum:'0',
einErrorNum:'0',
}
}
},
created() {
export default {
components: {},
name: 'topOne',
data() {
return {
pieCharts: null,
date: new Date().toISOString().substring(0, 10),
pickerOptions: {
disabledDate: this.disabledDate,
},
pageData: {},
todayAbnormalBean: {
lateNum: '0',
earlyNum: '0',
skippingNum: '0',
leaveNum: '0',
addressErrorNum: '0',
einErrorNum: '0',
}
}
},
created() {
},
mounted() {
this.getInitData()
},
methods: {
handleClick(type){
this.toggleDialog(1,type)
}, },
mounted() { disabledDate(time) {
this.getInitData() let str = this.date;
//
var currentDate = new Date(str);
//
var firstDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1);
//
var lastDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0);
return time < firstDayOfMonth || time > lastDayOfMonth;
}, },
methods: { handleDateChange(val) {
disabledDate(time) { this.getInitData()
let str=this.date; },
// getInitData() {
var currentDate = new Date(str); let param = {
// date: this.date
var firstDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1); }
// getHomePageData(param).then(response => {
var lastDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0); this.pageData = response.data;
return time < firstDayOfMonth || time > lastDayOfMonth; this.$emit('getHomeData', this.pageData)
}, this.todayAbnormalBean = this.pageData.todayAbnormalBean
handleDateChange(val){ this.$nextTick(() => {
this.getInitData() this.initChart()
}, })
getInitData(){ });
let param={ },
date:this.date initChart() {
} this.pieCharts = echarts.init(document.getElementById('pieBox'))
getHomePageData(param).then(response => { var data = {
this.pageData = response.data; title: '今日出勤状态',
this.$emit('getHomeData', this.pageData) percentage: this.pageData.todayAttBean.shouldAttNum,
this.todayAbnormalBean=this.pageData.todayAbnormalBean data: [
this.$nextTick(() => { {value: Number(this.pageData.todayAttBean.actualAttNum) || 0, name: '实际出勤人数'},
this.initChart() {value: Number(this.pageData.todayAttBean.noActualAttNum) || 0, name: '未出勤人数'}
}) ],
}); }
}, var option = {
initChart(){ title: [
this.pieCharts = echarts.init(document.getElementById('pieBox')) {
var data={ //
title:'今日出勤状态', text: '{a|' + data.percentage + '}\n{b|' + "应出勤人数" + '}',
percentage:this.pageData.todayAttBean.attRate+'%', textStyle: {
data:[ rich: {
{value:Number(this.pageData.todayAttBean.shouldAttNum)||0,name:'应出勤人数'}, a: {
{value:Number(this.pageData.todayAttBean.actualAttNum)||0,name:'实际出勤人数'} fontSize: 22,
], color: '#0f0f0f',
} fontWeight: '600',
var option = {
title: [
{
//
text: data.percentage, //
textStyle: {
//
color: "#0f0f0f",
lineHeight:50,
fontSize: 24,
fontWeight:"400",
},
left: "50%", //
top: "40%", //
textAlign: "center", //
},
],
tooltip: {
trigger: "item",
formatter: function (params) {
let tip = "";
if (params.seriesIndex === 0) {
tip =
params.seriesName + ":" +"<br>"+
params.marker +
params.name +
": " +
params.value
// " " +
// "(" +
// params.percent +
// "%)";
} else if (params.seriesIndex === 1) {
tip = "";
}
return tip;
},
}, //
legend: {
left: "center",
orient: 'horizontal',
bottom: "5%",
itemGap: 80,
icon: 'circle',
itemWidth: 8,
itemHeight: 8,
itemStyle: {
borderColor: '#fff', //
borderWidth: 0 // 0
},
textStyle: {
color: "#333",
rich: {
a: {
verticalAlign: "right",
align: "left",
width: 90,
fontSize: 14,
},
b: {
align: "left",
fontSize: 14,
color: "#ff5722",
},
df: {
align: "left",
fontSize: 16,
fontWeight: "600",
color: "#f7c122",
},
yjf: {
align: "left",
fontSize: 16,
fontWeight: "600",
color: "#6395fa",
},
},
},
formatter: (name) => {
let total = 0;
let target;
let className = "b";
for (let i = 0, l = data.data.length; i < l; i++) {
total += Number(data.data[i].value);
if (data.data[i].name == name) {
target = data.data[i].value;
}
if (name == "应出勤人数") {
className = "yjf";
}
if (name == "实际出勤人数") {
className = "df";
}
}
let str = "{a|" + name + "}{" + className + "|" + target +"}";
return str;
},
}, },
series: [ b: {
{ fontSize: 14,
name: data.title, //tooltip color: '#0f0f0f',
type: "pie", // padding: [6, 0, 14, 0]
roseType: 'radius', }
radius: ["45%", "65%"], // }
// center: ["50%", "50%"], },
color: ["#6CB0F8", "#F4B07A"], left: "50%", //
label: { top: "40%", //
position: "center", textAlign: "center", //
normal: { },
show: false, ],
}, tooltip: {
}, trigger: "item",
formatter: function (params) {
data: [ let tip = "";
{ value: data.data[0].value, name: data.data[0].name }, if (params.seriesIndex === 0) {
{ value: data.data[1].value, name: data.data[1].name }, tip =
], // params.seriesName + ":" + "<br>" +
}, params.marker +
{ params.name +
type: "pie", ": " +
// roseType: 'radius', params.value
radius: ["0%", "35%"], // // " " +
center: ["50%", "50%"], // "(" +
hoverAnimation: false, // params.percent +
color: "#fff", // "%)";
label: { } else if (params.seriesIndex === 1) {
position: "center", tip = "";
normal: { }
show: false, return tip;
}, },
}, }, //
data: [ legend: {
{ left: "center",
value: 0, orient: 'horizontal',
itemStyle: { bottom: "5%",
normal: { itemGap: 80,
shadowColor: "#e3e3e3", icon: 'circle',
shadowBlur: 20, itemWidth: 8,
}, itemHeight: 8,
}, itemStyle: {
}, borderColor: '#fff', //
], borderWidth: 0 // 0
}, },
], textStyle: {
}; color: "#333",
this.pieCharts.setOption(option) rich: {
}, a: {
toggleDialog(v) { verticalAlign: "right",
this.$emit('openDialog', { order: v, attStatus:v,attCurrentDay:this.date}) align: "left",
width: 90,
fontSize: 14,
},
b: {
align: "left",
fontSize: 14,
color: "#ff5722",
},
df: {
align: "left",
fontSize: 16,
fontWeight: "600",
color: "#f7c122",
},
yjf: {
align: "left",
fontSize: 16,
fontWeight: "600",
color: "#6395fa",
},
},
},
formatter: (name) => {
let total = 0;
let target;
let className = "b";
for (let i = 0, l = data.data.length; i < l; i++) {
total += Number(data.data[i].value);
if (data.data[i].name == name) {
target = data.data[i].value;
}
if (name == "应出勤人数") {
className = "yjf";
}
if (name == "实际出勤人数") {
className = "df";
}
}
let str = "{a|" + name + "}{" + className + "|" + target + "}";
return str;
},
}, },
series: [
{
name: data.title, //tooltip
type: "pie", //
roseType: 'radius',
radius: ["45%", "65%"], //
// center: ["50%", "50%"],
color: ["#6CB0F8", "#F4B07A"],
label: {
position: "center",
normal: {
show: false,
},
},
data: [
{value: data.data[0].value, name: data.data[0].name},
{value: data.data[1].value, name: data.data[1].name},
], //
},
{
type: "pie",
// roseType: 'radius',
radius: ["0%", "35%"], //
center: ["50%", "50%"],
hoverAnimation: false,
color: "#fff",
label: {
position: "center",
normal: {
show: false,
},
},
data: [
{
value: 0,
itemStyle: {
normal: {
shadowColor: "#e3e3e3",
shadowBlur: 20,
},
},
},
],
},
],
};
this.pieCharts.setOption(option)
}, },
toggleDialog(v,type) {
this.$emit('openDialog', {order: v, attStatus: v, attCurrentDay: this.date,attendType:type})
},
},
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.content{ .content {
width: 100%;
height: 100%;
padding-top: 20px;
display: flex;
.left-box {
width: 35%;
height: 100%;
background-color: #FFF;
border-radius: 10px;
padding: 10px;
}
.right-box {
width: 63%;
height: 100%;
margin: 0px 20px;
background-color: #FFF;
border-radius: 10px;
padding: 10px;
}
.right-list {
width: 100%;
height: 100px;
display: flex;
align-items: center;
justify-content: space-between;
.listItem {
width: 30%;
height: 70px;
display: flex;
align-items: center;
justify-content: space-between;
border-radius: 10px;
padding: 10px;
cursor: pointer;
}
}
.title-box {
width: 100%;
height: 30px;
display: flex;
justify-content: space-between;
align-items: center;
}
.clickable-texts {
position: absolute;
top: 0;
left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
padding-top: 20px; background: transparent;
display: flex; color: transparent;
}
.left-box{ .actual{
width: 35%; top: 35.5%;
height: 100%; left: 5%;
background-color: #FFF; cursor: pointer;
border-radius: 10px; }
padding: 10px; .expected{
} top: 24%;
.right-box{ left: 12%;
width: 63%; cursor: pointer;
height: 100%; }
margin: 0px 20px; .absent{
background-color: #FFF; top: 35.5%;
border-radius: 10px; left: 17%;
padding: 10px; cursor: pointer;
} }
.right-list{
width: 100%;
height: 100px;
display: flex;
align-items: center;
justify-content: space-between;
.listItem{
width: 30%;
height: 70px;
display: flex;
align-items: center;
justify-content: space-between;
border-radius: 10px;
padding: 10px;
cursor: pointer;
}
}
.title-box{ .clickable-text {
width: 100%; position: absolute;
height: 30px; /* 默认样式,将在 mounted 钩子中根据需要进行调整 */
display: flex; }
justify-content: space-between;
align-items: center;
}
} }
</style> </style>

View File

@ -183,8 +183,7 @@
</template> </template>
<script> <script>
import { getDetailsList,updateAttDetails,exportAttRecord,synchronous } from "@/api/report/attReport"; import { getDetailsList,updateAttDetails,exportAttRecord,synchronous,listDept } from "@/api/report/attReport";
import { listDept } from "@/api/system/dept";
import Treeselect from "@riophae/vue-treeselect"; import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css"; import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import {checkPersonAssignment} from "@/api/system/userInfo"; import {checkPersonAssignment} from "@/api/system/userInfo";

View File

@ -696,4 +696,4 @@ export default {
} }
} }
}; };
</script> </script>

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"> <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="姓名" prop="userName"> <el-form-item label="姓名" prop="userName">
<el-input <el-input
v-model="queryParams.userName" v-model="queryParams.userName"
@ -13,6 +13,14 @@
<el-form-item label="部门名称" prop="orgId"> <el-form-item label="部门名称" prop="orgId">
<treeselect v-model="queryParams.orgId" :options="deptOptions" :normalizer="normalizer" placeholder="选择部门" style="width: 240px"/> <treeselect v-model="queryParams.orgId" :options="deptOptions" :normalizer="normalizer" placeholder="选择部门" style="width: 240px"/>
</el-form-item> </el-form-item>
<el-form-item label="是否参加考勤" prop="isAttend">
<el-select v-model="queryParams.isAttend" placeholder="是否参加考勤" clearable style="width: 240px">
<el-option v-for="dict in dictList" :key="dict.value" :label="dict.label"
:value="dict.value" />
</el-select>
</el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@ -300,7 +308,12 @@
pageSize: 10, pageSize: 10,
userName: undefined, userName: undefined,
orgId: undefined, orgId: undefined,
isAttend: undefined,
}, },
dictList:[
{"value":"1","label":"是"},
{"value":"0","label":"否"}
],
roleList:[], roleList:[],
// //
form: {}, form: {},