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

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
})
}
// 查询列表
export function getAttDayReportDetailsList(query) {
return request({
@ -18,7 +18,7 @@ export function getAttDayReportDetailsList(query) {
params: query
})
}
// 导出
export function exportDayReport(query) {
return request({
@ -27,4 +27,13 @@ export function exportDayReport(query) {
responseType: 'blob',
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>
<div class="content">
<div class="left-box">
<div class="title-box">
<div style="margin-left: 10px;font-size: 22px;font-weight: bold;">今日出勤状态</div>
<div>
<el-date-picker
v-model="date" @change="handleDateChange" :clearable="false"
type="date" value-format="yyyy-MM-dd" :picker-options="pickerOptions"
placeholder="选择日期">
</el-date-picker>
</div>
</div>
<div @click="toggleDialog(1)">
<div id="pieBox" style="width: 100%;height: 260px;" ></div>
</div>
<div class="content">
<div class="left-box">
<div class="title-box">
<div style="margin-left: 10px;font-size: 22px;font-weight: bold;">今日出勤状态</div>
<div>
<el-date-picker
v-model="date" @change="handleDateChange" :clearable="false"
type="date" value-format="yyyy-MM-dd" :picker-options="pickerOptions"
placeholder="选择日期">
</el-date-picker>
</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 >
<div id="pieBox" style="width: 100%;height: 260px;"></div>
<!-- 点击区域 -->
<div class="clickable-texts">
<div class="clickable-text expected" @click="handleClick('0')">应出勤人数</div>
<div class="clickable-text actual" @click="handleClick('1')">实际出勤人数</div>
<div class="clickable-text absent" @click="handleClick('2')">未出勤人数</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>
<script>
import * as echarts from 'echarts';
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() {
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() {
},
mounted() {
this.getInitData()
},
methods: {
handleClick(type){
this.toggleDialog(1,type)
},
mounted() {
this.getInitData()
disabledDate(time) {
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: {
disabledDate(time) {
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;
},
handleDateChange(val){
this.getInitData()
},
getInitData(){
let param={
date:this.date
}
getHomePageData(param).then(response => {
this.pageData = response.data;
this.$emit('getHomeData', this.pageData)
this.todayAbnormalBean=this.pageData.todayAbnormalBean
this.$nextTick(() => {
this.initChart()
})
});
},
initChart(){
this.pieCharts = echarts.init(document.getElementById('pieBox'))
var data={
title:'今日出勤状态',
percentage:this.pageData.todayAttBean.attRate+'%',
data:[
{value:Number(this.pageData.todayAttBean.shouldAttNum)||0,name:'应出勤人数'},
{value:Number(this.pageData.todayAttBean.actualAttNum)||0,name:'实际出勤人数'}
],
}
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;
},
handleDateChange(val) {
this.getInitData()
},
getInitData() {
let param = {
date: this.date
}
getHomePageData(param).then(response => {
this.pageData = response.data;
this.$emit('getHomeData', this.pageData)
this.todayAbnormalBean = this.pageData.todayAbnormalBean
this.$nextTick(() => {
this.initChart()
})
});
},
initChart() {
this.pieCharts = echarts.init(document.getElementById('pieBox'))
var data = {
title: '今日出勤状态',
percentage: this.pageData.todayAttBean.shouldAttNum,
data: [
{value: Number(this.pageData.todayAttBean.actualAttNum) || 0, name: '实际出勤人数'},
{value: Number(this.pageData.todayAttBean.noActualAttNum) || 0, name: '未出勤人数'}
],
}
var option = {
title: [
{
//
text: '{a|' + data.percentage + '}\n{b|' + "应出勤人数" + '}',
textStyle: {
rich: {
a: {
fontSize: 22,
color: '#0f0f0f',
fontWeight: '600',
},
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) {
this.$emit('openDialog', { order: v, attStatus:v,attCurrentDay:this.date})
b: {
fontSize: 14,
color: '#0f0f0f',
padding: [6, 0, 14, 0]
}
}
},
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: [
{
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>
<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%;
height: 100%;
padding-top: 20px;
display: flex;
background: transparent;
color: transparent;
}
.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;
}
}
.actual{
top: 35.5%;
left: 5%;
cursor: pointer;
}
.expected{
top: 24%;
left: 12%;
cursor: pointer;
}
.absent{
top: 35.5%;
left: 17%;
cursor: pointer;
}
.title-box{
width: 100%;
height: 30px;
display: flex;
justify-content: space-between;
align-items: center;
.clickable-text {
position: absolute;
/* 默认样式,将在 mounted 钩子中根据需要进行调整 */
}
}
}
</style>

View File

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

View File

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

View File

@ -1,6 +1,6 @@
<template>
<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-input
v-model="queryParams.userName"
@ -13,6 +13,14 @@
<el-form-item label="部门名称" prop="orgId">
<treeselect v-model="queryParams.orgId" :options="deptOptions" :normalizer="normalizer" placeholder="选择部门" style="width: 240px"/>
</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-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>
@ -300,7 +308,12 @@
pageSize: 10,
userName: undefined,
orgId: undefined,
isAttend: undefined,
},
dictList:[
{"value":"1","label":"是"},
{"value":"0","label":"否"}
],
roleList:[],
//
form: {},