因为自己要写一个单页面去完成一个考勤报表计算的需求,所以通过一个html去完成此需求,再次记录遇到的问题(忽略乱七八糟的命名)。
1、下载对应的插件到本地,import引用。
2、前端通过xlsx 导入表格,获取对应的json,计算考勤数据。
3、导出功能以后在完善。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title>考勤计算</title>
<!-- https://unpkg.com/element-ui/lib/theme-chalk/index.css -->
<link rel="stylesheet" href="css/index.css">
<!-- https://cdn.jsdelivr.net/npm/vue@2.7.10 -->
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
<!-- https://unpkg.com/element-ui/lib/index.js -->
<script src="js/elementUI.js" type="text/javascript" charset="utf-8"></script>
<!-- https://unpkg.com/dayjs@1.8.21/dayjs.min.js -->
<script src="js/dayjs.js" type="text/javascript" charset="utf-8"></script>
<!-- https://cdn.jsdelivr.net/npm/xlsx@0.16.9/dist/xlsx.full.min.js -->
<script src="js/xlsx.js" type="text/javascript" charset="utf-8"></script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
display: flex;
}
body {
margin: 0;
}
.log {
display: flex;
justify-content: center;
border: 1px solid #F2F2F2;
}
p {
width: 100px;
margin: 0;
padding: 8px 4px;
border-right: 1px solid #F2F2F2;
}
</style>
</head>
<body>
<div id="app" style="display: flex;">
<div style="display: flex;flex-direction: column;">
<h3>考勤表</h3>
<el-upload
class="upload-demo"
drag
action
:auto-upload="false"
:on-change="uploadImport"
:limit="1">
<i class="el-icon-upload"></I>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
<h3>打卡表</h3>
<el-upload
class="upload-demo"
drag
action
:auto-upload="false"
:on-change="uploadImport2"
:limit="1">
<i class="el-icon-upload"></I>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
<el-button style="margin-top: 32px;" @click="handleCalculate">计算</el-button>
</div>
<div style="flex: 1;padding-left: 32px;">
<h1>log日志</h1>
<div class="log">
<p>考勤</p>
<p style="width: 160px;">职位</p>
<p>工号</p>
<p>姓名</p>
<p style="width: 150px;">时间</p>
<p style="flex: 1;">备注</p>
</div>
<div style="height:calc(100vh - 150px);overflow-y: auto;">
<div class="log" v-for="(item, index) in resultList">
<p>{{item.kq}}</p>
<p style="width: 160px;">职位:{{item.job}}</p>
<p>{{item.number}}</p>
<p>{{item.name}}</p>
<p style="width: 150px;">{{item.time}}</p>
<p style="flex: 1;">{{item.remark}}</p>
</div>
</div>
</div>
</div>
<script type="text/javascript">
var app = new Vue({
el: '#app',
data: {
kqList: [],
dkList: [],
resultList: [],
defaultTimeString: '',
},
methods: {
handleCalculate() {
const year = dayjs(this.defaultTimeString).year()
const month = dayjs(this.defaultTimeString).month() + 1
const yearMonth = year + '-' + month
const days = dayjs(yearMonth).daysInMonth()
this.kqList.forEach((kqItem, kqIndex) => {
for(let i = 1; i <= days; i++) {
let day = (i + '').padStart(2, 0)
let kqTime = yearMonth + '-' + day
if (['调岗','休', '轮休', '年假', '婚假', '丧假', '育儿假', '病假', '工伤', '产假', '陪产假', '事假', '新员工', '离职', '旷工'].indexOf(kqItem[kqTime]) > -1) {
this.printLog(kqItem[kqTime], kqItem['岗位'], kqItem['工号'], kqItem['姓名'], kqTime)
} else if (kqItem[kqTime] === '早' || kqItem[kqTime] === '双早') {
// 默认8h
this.calculateTimeLengthWithZao(kqItem, kqTime, 8)
} else if (kqItem[kqTime].indexOf('早') > -1) {
// 包含早
let arr = kqItem[kqTime].split('早')
this.calculateTimeLengthWithZao(kqItem, kqTime, arr[1])
} else if (kqItem[kqTime] === '夜' || kqItem[kqTime] === '双夜') {
// 默认8h
this.calculateTimeLengthWithYe(kqItem, kqTime, 8)
} else if (kqItem[kqTime].indexOf('夜') > -1) {
// 包含夜
let arr = kqItem[kqTime].split('夜')
this.calculateTimeLengthWithYe(kqItem, kqTime, arr[1])
}
}
})
},
// 计算时长 -- 夜班
calculateTimeLengthWithYe(kqItem, kqTime, timeLength) {
let kqString = kqItem[kqTime] // 考勤
let job = kqItem['岗位']
let userNumber = kqItem['工号'] // 工号
let userName = kqItem['姓名'] // 姓名
let remark = '' // 备注
// 上班对象
let startItem = null
// 下班对象
let endItem = null
// 获取上班打卡记录
let startSiftArr = []
startSiftArr = this.dkList.filter((dkItem) => {
return kqTime == dkItem['打卡日期2'] && kqItem['姓名'] == dkItem['姓名'] && dkItem['员工编码'] == kqItem['工号']
})
// 判断是否漏打上班卡
if (startSiftArr && startSiftArr.length > 0) {
startSiftArr = this.sortData(startSiftArr)
let sbTimeArr = startSiftArr.filter((item) => {
return this.compareDate(item['打卡日期2'] + ' ' + item['打卡时间'], item['打卡日期2'] + ' 12:00:00')
})
// startItem = sbTimeArr
if (sbTimeArr && sbTimeArr.length > 0) {
startItem = sbTimeArr[0]
} else {
return this.printLog(kqString, job, userNumber, userName, kqTime, '漏打上班卡')
// remark = '漏打上班卡'
}
} else {
return this.printLog(kqString, job, userNumber, userName, kqTime, '漏打上班卡')
// remark = '漏打上班卡'
}
// 获取下班打卡记录
let endDKDate = dayjs(kqTime).add(1, 'day')
let endTime = dayjs(endDKDate).format("YYYY-MM-DD")
let endSiftArr = []
endSiftArr = this.dkList.filter((dkItem) => {
return endTime == dkItem['打卡日期2'] && kqItem['姓名'] == dkItem['姓名'] && dkItem['员工编码'] == kqItem['工号']
})
// 判断是否漏打下班卡
if (endSiftArr && endSiftArr.length > 0) {
endSiftArr = this.sortData(endSiftArr)
let xbTimeArr = endSiftArr.filter((item) => {
return this.compareDate(item['打卡日期2'] + ' 12:00:00', item['打卡日期2'] + ' ' + item['打卡时间'])
})
if (xbTimeArr && xbTimeArr.length > 0) {
endItem = xbTimeArr[xbTimeArr.length - 1]
} else {
return this.printLog(kqString, job, userNumber, userName, kqTime, '漏打下班卡')
// remark = remark ? remark + '漏打下班卡' : '漏打下班卡'
}
} else {
return this.printLog(kqString, job, userNumber, userName, kqTime, '漏打下班卡')
// remark = remark ? remark + '漏打下班卡' : '漏打下班卡'
}
let sjStartTime = startItem['打卡日期2'] + ' ' + startItem['打卡时间']
let sjEndTime = endItem['打卡日期2'] + ' ' + endItem['打卡时间']
let gdStartTime = startItem['打卡日期2']
let gdEndTime = endItem['打卡日期2']
if (startItem['打卡位置'].indexOf('XX') > -1) {
gdStartTime = gdStartTime + ' ' + '20:00:00'
} else {
gdStartTime = gdStartTime + ' ' + '20:00:00'
}
if (endItem['打卡位置'].indexOf('XX') > -1) {
gdEndTime = gdEndTime + ' ' + '8:00:00'
} else {
gdEndTime = gdEndTime + ' ' + '8:00:00'
}
if (this.compareDate(sjStartTime, gdStartTime)) {
// 打卡时间 > 规定时间
remark = '上班迟到'
}
if (this.compareDate(gdEndTime, sjEndTime)) {
// 下班打卡时间 > 下班规定时间
remark = remark ? `${remark},下班早退` : '下班早退'
}
// 计算工时
let t1 = new Date(sjStartTime).getTime()
let t2 = new Date(sjEndTime).getTime()
let resultV = (t2 - t1 - 1000 * 60 * 60 * 0.5) / (1000 * 60 * 60)
if (resultV >= parseFloat(timeLength)) {
remark = remark ? `${remark},满足${timeLength}h` : `满足${timeLength}h`
} else {
// 计算缺少的分钟数
let qsfz = (t2 - t1 - 1000 * 60 * 60 * 0.5 - (1000 * 60 * 60 * timeLength)) / (1000 * 60)
remark = remark ? `${remark},不满足${timeLength}h(缺少:${(Math.abs(qsfz)).toFixed(2)}分钟)` : `不满足${timeLength}h(缺少:${(Math.abs(qsfz)).toFixed(2)}分钟)`
}
this.printLog(kqString, job, userNumber, userName, kqTime, remark)
},
// 计算时长 -- 早班
calculateTimeLengthWithZao(kqItem, kqTime, timeLength) {
let kqString = kqItem[kqTime] // 考勤
let job = kqItem['岗位']
let userNumber = kqItem['工号'] // 工号
let userName = kqItem['姓名'] // 姓名
let remark = '' // 备注
let siftArr = []
siftArr = this.dkList.filter((dkItem) => {
return kqTime == dkItem['打卡日期2'] && kqItem['姓名'] == dkItem['姓名'] && dkItem['员工编码'] == kqItem['工号']
})
if (siftArr && siftArr.length > 1) {
// 排序
siftArr = this.sortData(siftArr)
// 上班对象
let startItem = siftArr[0]
// 下班对象
let endItem = siftArr[siftArr.length - 1]
let sjStartTime = startItem['打卡日期2'] + ' ' + startItem['打卡时间']
let sjEndTime = endItem['打卡日期2'] + ' ' + endItem['打卡时间']
let gdStartTime = startItem['打卡日期2']
let gdEndTime = endItem['打卡日期2']
if (startItem['打卡位置'].indexOf('XX') > -1) {
gdStartTime = gdStartTime + ' ' + '08:30:00'
} else {
gdStartTime = gdStartTime + ' ' + '08:00:00'
}
if (endItem['打卡位置'].indexOf('XX') > -1) {
gdEndTime = gdEndTime + ' ' + '17:00:00'
} else {
gdEndTime = gdEndTime + ' ' + '16:30:00'
}
if (this.compareDate(sjStartTime, gdStartTime)) {
// 打卡时间 > 规定时间
remark = '上班迟到'
}
if (this.compareDate(gdEndTime, sjEndTime)) {
// 下班打卡时间 > 下班规定时间
remark = remark ? `${remark},下班早退` : '下班早退'
}
// 计算工时
let t1 = new Date(sjStartTime).getTime()
let t2 = new Date(sjEndTime).getTime()
let resultV = (t2 - t1 - 1000 * 60 * 60 * 0.5) / (1000 * 60 * 60)
if (resultV >= parseFloat(timeLength)) {
remark = remark ? `${remark},满足${timeLength}h` : `满足${timeLength}h`
} else {
// 计算缺少的分钟数
let qsfz = (t2 - t1 - 1000 * 60 * 60 * 0.5 - (1000 * 60 * 60 * timeLength)) / (1000 * 60)
remark = remark ? `${remark},不满足${timeLength}h(缺少:${(Math.abs(qsfz)).toFixed(2)}分钟)` : `不满足${timeLength}h(缺少:${(Math.abs(qsfz)).toFixed(2)}分钟)`
}
this.printLog(kqString, job, userNumber, userName, kqTime, remark)
} else {
// 早班 - 只有一个时间的
this.printLog(kqString, job, userNumber, userName, kqTime, '漏打卡')
}
},
// 按照时间排序
sortData(arr) {
arr.sort((a, b) => {
let obj1 = a['打卡日期2'] + ' ' + a['打卡时间']
let obj2 = b['打卡日期2'] + ' ' + b['打卡时间']
let v1 = Math.floor(new Date(obj1).getTime() / 1000)
let v2 = Math.floor(new Date(obj2).getTime() / 1000)
return v1 - v2
})
return arr
},
// 比较时间大小
compareDate(date1, date2) {
let _date1 = new Date(date1)
let _date2 = new Date(date2)
if (_date1.getTime() > _date2.getTime()) {
return true
} else {
return false
}
},
// 打印日志
printLog(kq, job, number, name, time, remark='无') {
console.log('考勤',kq,'职位',job, '工号', number, '姓名', name, '时间', time, '备注', remark)
this.resultList.push({
kq,
job,
number,
name,
time,
remark
})
},
async uploadImport(file) {
// 读取文件
let dataBinary = await this.readFile(file.raw);
let workBook = XLSX.read(dataBinary, { type: "binary", cellDates: true });
let workSheet = workBook.Sheets[workBook.SheetNames[0]];
let data = XLSX.utils.sheet_to_json(workSheet);
console.log(data)
this.kqList = data
},
async uploadImport2(file) {
// 读取文件
let dataBinary = await this.readFile(file.raw);
let workBook = XLSX.read(dataBinary, { type: "binary", cellDates: true });
let workSheet = workBook.Sheets[workBook.SheetNames[0]];
let data = XLSX.utils.sheet_to_json(workSheet);
data.forEach((item, index) => {
item['打卡日期2'] = dayjs(dayjs(item['打卡日期']).add(1, 'day').$d).format("YYYY-MM-DD")
if (index == 1) {
this.defaultTimeString = item['打卡日期2']
}
})
console.log(data)
this.dkList = data
},
readFile(file) {
return new Promise(resolve => {
let reader = new FileReader();
reader.readAsBinaryString(file);
reader.onload = ev => {
resolve(ev.target.result);
}
})
}
}
})
</script>
</body>
</html>
遇到的问题:
1、将elementUI 和对应的css 文件下载本地后,页面出现错误
处理:elementUI提供icons.woff,element-icons.ttf 下载路径,通过添加下载,把这两个文件放到对应的css 文件夹下就可以了。