考勤申请模块调试完成

This commit is contained in:
BianLzhaoMin 2025-08-17 18:23:07 +08:00
parent 7bf1659d13
commit da89c2499b
7 changed files with 1153 additions and 228 deletions

View File

@ -0,0 +1,56 @@
import request from '@/utils/request'
import requestFormData from '@/utils/request_formdata'
// 新增补卡申请
export const addCardReplacementApplyAPI = (data) => {
return requestFormData({
url: '/bmw/cardApply/saveRepairCardApply',
method: 'POST',
data,
})
}
// 获取补卡人员列表接口
export const getCardReplacementPersonListAPI = (data) => {
return request({
url: '/bmw/cardApply/workPersonList',
method: 'GET',
params: data,
})
}
// 获取补卡人员考勤记录
export const getCardReplacementPersonCheckRecordAPI = (data) => {
return request({
url: '/bmw/cardApply/getCheckRecord',
method: 'GET',
params: data,
})
}
// 修改补卡申请
export const editCardReplacementApplyAPI = (data) => {
return request({
url: '/bmw/pmAttDevice/updatePmAttDevice',
method: 'POST',
data,
})
}
// 删除补卡申请
export const deleteCardReplacementApplyAPI = (data) => {
return request({
url: '/bmw/pmAttDevice/delPmAttDevice',
method: 'POST',
data,
})
}
// 获取补卡申请列表
export const getCardReplacementApplyListAPI = (data) => {
return request({
url: '/bmw/cardApply/list',
method: 'GET',
params: data,
})
}

View File

@ -1,14 +1,29 @@
<template>
<div class="attendance-calendar">
<!-- 月份导航 -->
<!-- 图例说明 -->
<div class="legend" v-if="isShowLegend">
<div class="legend-item">
<span class="legend-mark active"></span>
<span>已打卡</span>
</div>
<div class="legend-item">
<span class="legend-mark inactive"></span>
<span>未打卡</span>
</div>
<div class="legend-item">
<span class="legend-mark not-in-range"></span>
<span>补卡</span>
</div>
</div>
<!-- 循环展示每个月份的日历 -->
<div
v-for="(monthData, index) in monthDataList"
:key="index"
class="month-calendar"
>
<!-- 月份标题 -->
<div class="calendar-header">
<button @click="prevMonth">
<i class="el-icon-arrow-left"></i>
</button>
<h3>{{ currentYear }} {{ currentMonth }}</h3>
<button @click="nextMonth">
<i class="el-icon-arrow-right"></i>
</button>
<h3>{{ monthData.year }} {{ monthData.month }}</h3>
</div>
<!-- 星期标题 -->
@ -20,58 +35,45 @@
<!-- 日历主体 -->
<div class="calendar-grid">
<!-- 上月日期占位 -->
<!-- 空白占位月初 -->
<div
class="calendar-day prev-month"
v-for="day in prevMonthDays"
:key="'prev-' + day"
class="calendar-day empty"
:key="'empty-start-' + i"
v-for="(empty, i) in getFirstMonthEmptyDays(monthData)"
>
{{ day }}
</div>
<!-- 本月日期 -->
<div
class="calendar-day current-month"
v-for="day in currentMonthDays"
:key="day"
:class="getAttendanceClass(day)"
@click="handleDateClick(day)"
class="calendar-day current-month"
v-for="day in getDaysInMonth(monthData)"
:class="getDayClass(monthData, day)"
>
<span class="day-number">{{ day }}</span>
<span
class="attendance-mark"
v-if="getAttendanceStatus(day)"
></span>
<!-- 当isActive为1时显示复选框 -->
<!-- <el-checkbox
v-if="getDayInfo(monthData, day)?.isActive === 1"
class="day-checkbox"
v-model="
checkedDays[
`${monthData.year}-${monthData.month}-${day}`
]
"
@change="handleCheckboxChange(monthData, day, $event)"
></el-checkbox> -->
<el-checkbox v-model="checked" />
</div>
<!-- 下月日期占位 -->
<!-- 空白占位月末 -->
<div
class="calendar-day next-month"
v-for="day in nextMonthDays"
:key="'next-' + day"
class="calendar-day empty"
v-for="(empty, i) in getLastMonthEmptyDays(monthData)"
:key="'empty-end-' + i"
>
{{ day }}
</div>
</div>
<!-- 图例说明 -->
<div class="legend">
<div class="legend-item">
<span class="legend-mark normal"></span>
<span>正常</span>
</div>
<div class="legend-item">
<span class="legend-mark late"></span>
<span>迟到</span>
</div>
<div class="legend-item">
<span class="legend-mark absent"></span>
<span>缺勤</span>
</div>
<div class="legend-item">
<span class="legend-mark leave"></span>
<span>请假</span>
</div>
</div>
</div>
</template>
@ -80,121 +82,112 @@
export default {
name: 'AttendanceCalendar',
props: {
// : { date: '2023-10-01', status: 'normal|late|absent|leave' }
attendanceData: {
//
monthDataList: {
type: Array,
default: () => [],
required: true,
},
isShowLegend: {
type: Boolean,
default: false,
},
},
data() {
return {
currentDate: new Date(),
weekDays: ['日', '一', '二', '三', '四', '五', '六'],
checkedDays: {}, //
checked: false,
}
},
computed: {
//
currentYear() {
return this.currentDate.getFullYear()
},
// (1-12)
currentMonth() {
return this.currentDate.getMonth() + 1
},
//
currentMonthDays() {
return new Date(this.currentYear, this.currentMonth, 0).getDate()
},
// (0-6)
firstDayOfMonth() {
return new Date(this.currentYear, this.currentMonth - 1, 1).getDay()
},
//
prevMonthDays() {
const prevMonth = this.currentMonth - 1 || 12
const prevMonthYear =
prevMonth === 12 ? this.currentYear - 1 : this.currentYear
const prevMonthTotalDays = new Date(
prevMonthYear,
prevMonth,
0,
).getDate()
return Array.from({ length: this.firstDayOfMonth }, (_, i) => {
return prevMonthTotalDays - this.firstDayOfMonth + i + 1
})
},
//
nextMonthDays() {
const totalDays = this.prevMonthDays.length + this.currentMonthDays
const nextDaysCount = 7 - (totalDays % 7 || 7)
return nextDaysCount < 7
? Array.from({ length: nextDaysCount }, (_, i) => i + 1)
: []
},
created() {
//
this.initCheckedDays()
},
methods: {
//
prevMonth() {
this.currentDate = new Date(
this.currentYear,
this.currentMonth - 2,
1,
)
this.$emit('month-change', {
year: this.currentYear,
month: this.currentMonth,
//
initCheckedDays() {
this.monthDataList.forEach((monthData) => {
monthData.day.forEach((day) => {
if (day.isActive === 1) {
const key = `${monthData.year}-${monthData.month}-${day.dayIndex}`
this.$set(this.checkedDays, key, false)
}
})
})
},
//
nextMonth() {
this.currentDate = new Date(this.currentYear, this.currentMonth, 1)
this.$emit('month-change', {
year: this.currentYear,
month: this.currentMonth,
})
//
getDaysInMonth(monthData) {
const year = parseInt(monthData.year)
const month = parseInt(monthData.month)
return new Date(year, month, 0).getDate()
},
//
getAttendanceStatus(day) {
const dateStr = `${this.currentYear}-${String(
this.currentMonth,
).padStart(2, '0')}-${String(day).padStart(2, '0')}`
const record = this.attendanceData.find(
(item) => item.date === dateStr,
)
return record ? record.status : null
//
getFirstDayOfMonth(monthData) {
const year = parseInt(monthData.year)
const month = parseInt(monthData.month) - 1 // 0-based
return new Date(year, month, 1).getDay()
},
//
getAttendanceClass(day) {
const status = this.getAttendanceStatus(day)
//
getFirstMonthEmptyDays(monthData) {
return this.getFirstDayOfMonth(monthData)
},
//
getLastMonthEmptyDays(monthData) {
const totalDays = this.getDaysInMonth(monthData)
const lastDay = new Date(
parseInt(monthData.year),
parseInt(monthData.month) - 1,
totalDays,
).getDay()
return 6 - lastDay
},
//
getDayInfo(monthData, day) {
const dayStr = String(day).padStart(2, '0')
return monthData.day.find((item) => item.dayIndex === dayStr)
},
//
getDayClass(monthData, day) {
const dayInfo = this.getDayInfo(monthData, day)
//
if (!dayInfo) {
return 'not-in-range'
}
return {
'has-attendance': !!status,
normal: status === 'normal',
late: status === 'late',
absent: status === 'absent',
leave: status === 'leave',
'not-in-range': !dayInfo.isRange,
active: dayInfo.isActive === 1,
inactive: dayInfo.isActive === 0,
}
},
//
handleDateClick(day) {
const date = new Date(this.currentYear, this.currentMonth - 1, day)
const dateStr = `${this.currentYear}-${String(
this.currentMonth,
).padStart(2, '0')}-${String(day).padStart(2, '0')}`
const attendanceInfo = this.attendanceData.find(
(item) => item.date === dateStr,
)
this.$emit('date-click', {
date,
dateStr,
attendanceInfo,
//
handleCheckboxChange(monthData, day, checked) {
const dayStr = String(day).padStart(2, '0')
this.$emit('checkbox-change', {
year: monthData.year,
month: monthData.month,
day: dayStr,
checked,
})
},
},
watch: {
currentMonth() {
//
//
monthDataList: {
deep: true,
handler() {
this.initCheckedDays()
},
},
},
}
@ -204,6 +197,7 @@ export default {
.attendance-calendar {
font-family: 'Arial', sans-serif;
max-width: 700px;
min-width: 460px;
margin: 0 auto;
padding: 20px;
border: 1px solid #e0e0e0;
@ -211,29 +205,26 @@ export default {
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
.month-calendar {
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid #f0f0f0;
}
.month-calendar:last-child {
margin-bottom: 0;
padding-bottom: 0;
border-bottom: none;
}
.calendar-header {
display: flex;
justify-content: space-between;
justify-content: center;
align-items: center;
margin-bottom: 20px;
padding: 0 10px;
}
.calendar-header button {
background: none;
border: none;
cursor: pointer;
font-size: 18px;
color: #666;
padding: 5px 10px;
border-radius: 4px;
transition: background-color 0.2s;
}
.calendar-header button:hover {
background-color: #f5f5f5;
}
.calendar-header h3 {
margin: 0;
color: #333;
@ -262,66 +253,61 @@ export default {
.calendar-day {
aspect-ratio: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 6px;
position: relative;
cursor: pointer;
transition: background-color 0.2s;
font-size: 14px;
}
.calendar-day:hover {
background-color: #f0f0f0;
}
.prev-month,
.next-month {
color: #ccc;
background-color: #fafafa;
.empty {
background-color: #f9f9f9;
border: 1px solid transparent;
}
.current-month {
background-color: #fff;
border: 1px solid #eee;
cursor: pointer;
}
/* 非考勤范围样式 */
.not-in-range {
background-color: #f0f0f0;
color: #999;
}
/* 已打卡样式 */
.active {
background-color: #19be6b;
color: #000;
}
/* 未打卡样式 */
.inactive {
background-color: #f56c6c;
color: #000;
}
.day-number {
z-index: 1;
}
.attendance-mark {
/* 复选框样式 */
.day-checkbox {
position: absolute;
bottom: 5px;
width: 6px;
height: 6px;
border-radius: 50%;
}
.normal .attendance-mark {
background-color: #4cd964; /* 正常-绿色 */
}
.late .attendance-mark {
background-color: #ffcc00; /* 迟到-黄色 */
}
.absent .attendance-mark {
background-color: #ff3b30; /* 缺勤-红色 */
}
.leave .attendance-mark {
background-color: #5ac8fa; /* 请假-蓝色 */
top: 2px;
right: 2px;
z-index: 2;
}
/* 图例样式 */
.legend {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 20px;
padding-top: 10px;
border-top: 1px solid #eee;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.legend-item {
@ -334,24 +320,23 @@ export default {
.legend-mark {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
width: 36px;
height: 36px;
border-radius: 3px;
}
.legend-mark.normal {
background-color: #4cd964;
.legend-mark.active {
background-color: #19be6b;
/* border: 1px solid #4cd964; */
}
.legend-mark.late {
background-color: #ffcc00;
.legend-mark.inactive {
background-color: #f56c6c;
/* border: 1px solid #ff3b30; */
}
.legend-mark.absent {
background-color: #ff3b30;
}
.legend-mark.leave {
background-color: #5ac8fa;
.legend-mark.not-in-range {
background-color: #ff9900;
/* border: 1px solid #ddd; */
}
</style>

View File

@ -55,7 +55,7 @@
<el-row :gutter="6">
<!-- 左侧考勤日历 -->
<el-col :span="12">
<AttendanceCalendar />
<!-- <AttendanceCalendar /> -->
</el-col>
<!-- 右侧考勤表格 -->
@ -109,12 +109,12 @@
</template>
<script>
import AttendanceCalendar from '@/components/AttendanceCalendar'
// import AttendanceCalendar from '@/components/AttendanceCalendar'
import { getAttendanceDetailsListAPI } from '@/api/construction-person/attendance-manage/attendance-count'
export default {
name: 'AttendanceDetails',
components: {
AttendanceCalendar,
// AttendanceCalendar,
},
props: {
teamId: {

View File

@ -0,0 +1,272 @@
<template>
<div style="width: 100%">
<el-row>
<el-col :span="20">
<TitleTip
borderBottom="none"
padding="24px 10px"
titleText="补卡人员清单"
justifyContent="flex-start"
>
<template name="right">
<span>{{ selectProjectName }}</span>
</template>
</TitleTip>
</el-col>
<el-col :span="4">
<el-button
type="primary"
size="mini"
icon="el-icon-plus"
@click="handleSelectPerson"
>
选择补卡人员
</el-button>
</el-col>
</el-row>
<el-table :data="tableData">
<el-table-column align="center" label="姓名" prop="name" />
<el-table-column align="center" label="身份证" prop="idNumber" />
<el-table-column align="center" label="补卡天数" prop="repairDay" />
<el-table-column
align="center"
width="300"
label="补卡日期"
prop="repairDate"
show-overflow-tooltip
/>
<el-table-column align="center" label="补卡工程" prop="proName" />
<el-table-column align="center" label="联系方式" prop="phone" />
<el-table-column align="center" label="所属班组" prop="teamName" />
<el-table-column align="center" label="操作" width="160">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
@click="onHandleEdit(scope.row)"
>
修改
</el-button>
<el-button
size="mini"
type="text"
style="color: #f56c6c"
@click="onHandleDelete($scope.index, scope)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<TitleTip
borderBottom="none"
padding="24px 10px"
titleText="补卡说明"
justifyContent="flex-start"
/>
<el-form
label-width="140px"
ref="addOrEditFormRef"
:model="addApplyForm"
>
<el-row>
<el-col :span="12">
<el-form-item label="补卡说明" prop="repairRemark">
<el-input
clearable
maxlength="30"
type="textarea"
show-word-limit
placeholder="请输入考勤机编码"
v-model="addParams.repairRemark"
:autosize="{ minRows: 4, maxRows: 8 }"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="附件">
<UploadFileFormData
:limit="3"
:file-size="20"
:multiple="true"
uploadTip="补卡说明附件上传"
:file-list.sync="addParams.fileList"
:file-type="[
'jpg',
'png',
'jpeg',
'pdf',
'doc',
'docx',
]"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<DialogModel
:dialogConfig="dialogConfig"
@closeDialogOuter="handleCloseDialogOuter"
>
<template slot="outerContent">
<SelectPersonAndDate
ref="selectPersonAndDateRef"
@onPersonSubmit="onPersonSubmit"
:selectProjectId="selectProjectId"
:selectProjectName="selectProjectName"
/>
<el-row class="dialog-footer-btn">
<el-button size="medium" @click="handleCloseDialogOuter">
取消
</el-button>
<el-button
size="medium"
type="primary"
@click="onHandleConfirm"
>
确定
</el-button>
</el-row>
</template>
</DialogModel>
</div>
</template>
<script>
import DialogModel from '@/components/DialogModel'
import UploadFileFormData from '@/components/UploadFileFormData'
import SelectPersonAndDate from './select-person-and-date.vue'
import { addCardReplacementApplyAPI } from '@/api/construction-person/attendance-manage/card-replacement-apply'
export default {
name: 'AddApplyForm',
props: {
selectProjectName: {
type: String,
default: () => '',
},
selectProjectId: {
type: [Number, String],
default: () => '',
},
},
components: {
DialogModel,
SelectPersonAndDate,
UploadFileFormData,
},
data() {
return {
addApplyForm: {
reason: '',
},
dialogConfig: {
outerTitle: '选择补卡人员以及补卡日期',
outerWidth: '',
minHeight: '90vh',
maxHeight: '90vh',
outerVisible: false,
},
tableData: [],
addParams: {
// proId: '', // id
// repairNum: '', //
// repairDay: '', //
repairRemark: '', //
repairMonth: '2025-08', //
repairCardRecords: [],
fileList: [],
},
}
},
methods: {
handleSelectPerson() {
// console.log(this.addApplyForm)
this.dialogConfig.outerVisible = true
},
handleCloseDialogOuter() {
this.dialogConfig.outerVisible = false
},
// ----
onHandleConfirm() {
this.$refs.selectPersonAndDateRef.onPersonSubmit()
this.dialogConfig.outerVisible = false
},
//
onPersonSubmit(val) {
//
if (val && val.length > 0) {
val.forEach((item) => {
console.log(item, 'item----')
this.tableData.push(item)
})
}
},
//
onHandleEdit(row) {
console.log(row)
},
//
onHandleDelete(index, row) {
this.tableData.splice(index, 1)
},
//
async onHandleConfirmFinishFun() {
//
const { repairMonth, repairRemark, fileList } = this.addParams
const repairNum = this.tableData.length
const formData = new FormData()
const fileMsg = []
const repairDay = this.tableData.reduce((acc, curr) => {
return acc + curr.repairDay
}, 0)
const params = {
proId: this.selectProjectId,
repairMonth,
repairRemark,
repairNum,
repairDay,
repairCardRecords: this.tableData.map((e) => {
return {
name: e.name,
phone: e.phone,
proId: e.proId,
teamId: e.teamId,
idNumber: e.idNumber,
workerId: e.workerId,
repairDay: e.repairDay,
repairDate: e.repairDate,
}
}),
}
if (fileList.length > 0) {
fileList.forEach((e) => {
fileMsg.push({
type: 1,
name: '附件',
})
formData.append('file', e.raw)
})
}
formData.append('fileMsg', JSON.stringify(fileMsg))
formData.append('params', JSON.stringify(params))
const res = await addCardReplacementApplyAPI(formData)
console.log(res)
},
},
}
</script>
<style></style>

View File

@ -0,0 +1,52 @@
export const formLabel = [
{
isShow: false, // 是否展示label
f_type: 'ipt',
f_label: '姓名',
f_model: 'deviceCode',
},
{
isShow: false, // 是否展示label
f_type: 'ipt',
f_label: '工程',
f_model: 'deviceName',
},
{
isShow: false, // 是否展示label
f_type: 'ipt',
f_label: '状态',
f_model: 'proName',
},
]
export const columnsList = [
{ t_props: 'deviceCode', t_label: '工程名称' },
{ t_props: 'deviceName', t_label: '补卡人数' },
{ t_props: 'proName', t_label: '补卡天数' },
{ t_slot: 'isShanghai', t_label: '补卡说明' },
{
t_label: '申请人',
t_slot: 'onLine',
},
{ t_label: '绑定人', t_props: '申请时间' },
{
t_label: '审核状态',
t_props: 'updateTime',
},
{
t_label: '审核人',
t_props: 'updateTime',
},
{
t_label: '审核时间',
t_props: 'updateTime',
},
]
export const dialogConfig = {
outerTitle: '',
minHeight: '',
maxHeight: '',
outerWidth: '40%',
outerVisible: false,
}

View File

@ -1,11 +1,268 @@
<template>
<!-- 施工人员 ---- 考勤管理 ---- 补卡申请-->
<div class="app-container">
<h1>补卡申请</h1>
<TableModel
:formLabel="formLabel"
:showOperation="true"
:showRightTools="true"
:columnsList="columnsList"
ref="cardReplacementApplyTableRef"
:request-api="getCardReplacementApplyListAPI"
>
<template slot="btn" slot-scope="{ queryParams }">
<el-button
plain
size="mini"
type="success"
icon="el-icon-download"
@click="onHandleExportAttendanceMachine(queryParams)"
>
导出
</el-button>
<el-button
plain
size="mini"
type="primary"
icon="el-icon-plus"
v-hasPermi="['attendance:machine:add']"
@click="onHandleAddCardReplacementApply(1, null)"
>
新增
</el-button>
</template>
<template slot="handle" slot-scope="{ data }">
<!-- <el-button
plain
size="mini"
type="warning"
icon="el-icon-edit"
v-hasPermi="['attendance:machine:unbind']"
@click="onHandleUnBindAttendanceMachine(data)"
>
解绑
</el-button> -->
<el-button
size="mini"
type="danger"
icon="el-icon-delete"
v-hasPermi="['attendance:machine:delete']"
@click="onHandleDeleteSubBaseInfo(data)"
>
删除
</el-button>
</template>
</TableModel>
<DialogModel
:dialogConfig="dialogConfig"
@closeDialogOuter="handleCloseDialogOuter"
>
<template slot="outerContent">
<template v-if="dialogConfig.outerTitle === '选择补卡工程'">
<el-form label-width="100px">
<el-form-item label="补卡工程">
<el-select
style="width: 100%"
placeholder="请选择"
v-model="selectProjectId"
@change="handleSelectProject"
>
<el-option
:key="item.value"
:label="item.label"
:value="item.value"
v-for="item in lotProjectOptions"
/>
</el-select>
</el-form-item>
</el-form>
<el-row class="dialog-footer-btn">
<el-button
size="medium"
type="primary"
@click="onHandleNextStep"
>
下一步
</el-button>
</el-row>
</template>
<template v-if="dialogConfig.outerTitle === '新增补卡申请'">
<AddApplyForm
ref="addApplyFormRef"
:selectProjectId="selectProjectId"
:selectProjectName="selectProjectName"
/>
<el-row class="dialog-footer-btn">
<el-button
size="medium"
@click="handleCloseDialogOuter"
>
取消
</el-button>
<el-button
size="medium"
type="primary"
@click="onHandleConfirmFinish"
>
确定
</el-button>
</el-row>
</template>
</template>
</DialogModel>
</div>
</template>
<script>
export default {}
</script>
import TableModel from '@/components/TableModel'
import DialogModel from '@/components/DialogModel'
<style></style>
import AddApplyForm from './add-apply-form.vue'
import { formLabel, columnsList, dialogConfig } from './config'
import {
deleteAttendanceMachineAPI,
getCardReplacementApplyListAPI,
} from '@/api/construction-person/attendance-manage/card-replacement-apply'
import { getLotProjectSelectListCommonFun } from '@/utils/getCommonData'
export default {
name: 'CardReplacementApply',
components: {
TableModel,
DialogModel,
AddApplyForm,
},
data() {
return {
formLabel,
columnsList,
dialogConfig,
editFormData: {}, //
getCardReplacementApplyListAPI,
lotProjectOptions: [],
selectProjectId: '',
selectProjectName: '',
}
},
methods: {
//
onHandleExportAttendanceMachine(queryParams) {
this.download(
'/bmw/pmAttDevice/export',
{
...queryParams,
},
`考勤机列表.xlsx`,
)
},
//
onHandleAddOrBindAttendanceMachine(type, data) {
this.dialogConfig.outerTitle =
type === 1 ? '新增考勤机' : '绑定考勤机'
if (type === 2) {
const { proId, deviceCode, deviceName } = data
this.editFormData = {
isUpdate: true,
proId,
deviceCode,
deviceName,
}
} else {
this.editFormData = {}
}
this.dialogConfig.outerVisible = true
},
//
onHandleAddCardReplacementApply(type, data) {
this.dialogConfig.outerTitle = '选择补卡工程'
this.dialogConfig.outerWidth = '30%'
this.dialogConfig.minHeight = ''
this.dialogConfig.maxHeight = ''
this.dialogConfig.outerVisible = true
},
//
onHandleDeleteSubBaseInfo(data) {
this.$confirm('确定删除该考勤机吗?', '温馨提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
const res = await deleteAttendanceMachineAPI({
deviceCode: data.deviceCode,
})
if (res.code === 200) {
this.$modal.msgSuccess('删除成功')
this.$refs.attendanceMachineTableRef.getTableList() //
}
})
.catch(() => {
// console.log('')
})
},
//
onHandleConfirm() {
console.log('确定')
},
//
async onHandleConfirmFinish() {
this.$refs.addApplyFormRef.onHandleConfirmFinishFun()
// try {
// await
// this.handleCloseDialogOuter()
// // this.$refs.attendanceMachineTableRef.getTableList()
// } catch (error) {
// // console.log('', error)
// }
},
//
handleCloseDialogOuter() {
this.dialogConfig.outerVisible = false
},
//
onHandleNextStep() {
if (!this.selectProjectId) {
this.$modal.msgError('请选择补卡工程')
return
}
this.dialogConfig.outerTitle = '新增补卡申请'
this.dialogConfig.outerWidth = '80%'
this.dialogConfig.minHeight = '80vh'
this.dialogConfig.maxHeight = '80h'
this.dialogConfig.outerVisible = true
},
//
handleSelectProject() {
this.selectProjectName = this.lotProjectOptions.find(
(item) => item.value === this.selectProjectId,
).label
},
},
async created() {
const lot = await getLotProjectSelectListCommonFun()
this.lotProjectOptions = lot.map((item) => ({
value: item.id,
label: item.proName,
}))
},
}
</script>

View File

@ -0,0 +1,303 @@
<template>
<!-- 选择补卡人员及补卡日期页面 -->
<div style="width: 100%">
<el-form :model="queryPersonForm" :inline="true" size="mini">
<el-form-item prop="reason">
<el-select
style="width: 100%"
placeholder="请选择人员"
@change="onChangePerson"
v-model="queryPersonForm.selectPersonId"
>
<el-option
:key="item.value"
:label="item.label"
:value="item.value"
v-for="item in personOptions"
/>
</el-select>
</el-form-item>
<el-form-item prop="reason">
<el-date-picker
type="daterange"
style="width: 240px"
range-separator="至"
value-format="yyyy-MM-dd"
start-placeholder="开始日期"
end-placeholder="结束日期"
v-model="queryPersonForm.time"
/>
</el-form-item>
<el-form-item prop="reason">
<el-button
type="primary"
icon="el-icon-search"
@click="handleQuery"
>
查询
</el-button>
<el-button
plain
type="warning"
icon="el-icon-refresh"
@click="resetQuery"
>
重置
</el-button>
</el-form-item>
</el-form>
<div v-if="idNumber">
<span>
{{ name }}
</span>
<span> ({{ idNumber }}) </span>
</div>
<!-- 补卡日历 -->
<div class="date-ist" v-if="cardReplacementDateList.length > 0">
<div
:key="item.currentDate"
v-for="(item, index) in cardReplacementDateList"
style="
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
"
>
<span
style="
padding: 4px 0;
font-size: 16px;
font-weight: bold;
font-family: 'PingFang SC';
"
>
{{ item.month }}
</span>
<div
class="item"
:ref="`item${index}`"
:style="{
height: initHeight + 'px',
backgroundColor: item.inRange
? item.isActive == 1
? '#19be6b'
: '#f56c6c'
: '#999',
}"
:class="{
'item-active': item.isChecked,
}"
>
{{ item.date }}
<el-checkbox
class="check-box-item"
v-model="item.isChecked"
v-if="item.isActive == 0 && item.inRange"
/>
</div>
</div>
</div>
</div>
</template>
<script>
import {
getCardReplacementPersonListAPI,
getCardReplacementPersonCheckRecordAPI,
} from '@/api/construction-person/attendance-manage/card-replacement-apply'
export default {
name: 'SelectPersonAndDate',
props: {
selectProjectId: {
type: [Number, String],
default: () => '',
},
selectProjectName: {
type: String,
default: () => '',
},
},
components: {
// AttendanceCalendar,
},
data() {
return {
queryPersonForm: {
selectPersonId: '',
time: [
new Date().toISOString().split('T')[0],
new Date().toISOString().split('T')[0],
],
},
personOptions: [],
name: '',
idNumber: '',
initHeight: 0,
//
dateList: [],
//
cardReplacementDateList: [],
}
},
methods: {
handleCloseDialogOuter() {
this.dialogConfig.outerVisible = false
},
//
async getCardReplacementPersonList() {
const { data: res } = await getCardReplacementPersonListAPI({
proId: this.selectProjectId,
})
this.personOptions = res.map((item) => ({
...item,
value: item.id,
label: item.name,
}))
},
//
handleQuery() {
this.getCardReplacementPersonCheckRecord()
},
resetQuery() {
this.queryPersonForm.selectPersonId = ''
this.queryPersonForm.time = [
new Date().toISOString().split('T')[0],
new Date().toISOString().split('T')[0],
]
this.getCardReplacementPersonCheckRecord()
},
//
onPersonSubmit() {
const tempList = this.cardReplacementDateList.filter(
(item) => item.isChecked,
)
if (tempList.length > 0) {
const repairDate = tempList.map((item) => item.currentDate)
this.dateList[0].repairDate = repairDate.join(',')
this.dateList[0].repairDay = repairDate.length
this.$emit('onPersonSubmit', this.dateList)
}
},
//
async getCardReplacementPersonCheckRecord() {
const workerId = this.personOptions.find(
(item) => item.value === this.queryPersonForm.selectPersonId,
).id
const { data: res } = await getCardReplacementPersonCheckRecordAPI({
workerId,
proId: this.selectProjectId,
startTime: this.queryPersonForm.time[0],
endTime: this.queryPersonForm.time[1],
})
const tempList = []
if (Object.keys(res).length > 0) {
for (const key in res) {
tempList.push({
month: key.split('-')[1],
date: key.split('-')[2],
inRange: res[key].inRange,
isActive: res[key].isActive,
currentDate: key,
isChecked: false,
})
}
}
this.cardReplacementDateList = tempList.sort((a, b) => {
return a.date - b.date
})
this.$nextTick(() => {
this.getInitHeight()
})
},
//
onChangePerson(val) {
if (!val) {
this.dateList = []
return
}
const personInfo = this.personOptions.find(
(item) => item.value === val,
)
console.log(personInfo, 'personInfo')
const { name, idNumber, phone, teamName, id, teamId } = personInfo
this.dateList = []
this.dateList.push({
teamName, //
name, //
phone, //
idNumber, //
repairDay: '', //
repairDate: '', //
workerId: id, // id
teamId, // id
proName: this.selectProjectName, //
proId: this.selectProjectId, // id
})
},
getInitHeight() {
const item = this.$refs[`item0`][0]
if (item) {
this.initHeight = item.offsetWidth
}
},
},
created() {
this.getCardReplacementPersonList()
},
}
</script>
<style scoped lang="scss">
.date-ist {
width: 80%;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 10px;
.item {
width: 100%;
position: relative;
padding: 10px;
display: flex;
border-radius: 5px;
align-items: center;
border-radius: 6px;
justify-content: center;
color: #fff;
font-size: 18px;
.check-box-item {
position: absolute;
right: 6px;
top: 6px;
}
}
.item-active {
background-color: #b58bae !important;
}
}
</style>