<template>
<div class="app-container">
<el-row :gutter="20">
<!--部门数据-->
<el-col :span="4" :xs="24">
<div class="head-container">
<el-input v-model="deptName" placeholder="请输入部门名称" clearable size="small" prefix-icon="el-icon-search" style="margin-bottom: 20px"/>
</div>
<div class="head-container">
<el-tree :data="deptOptions" :props="defaultProps" :expand-on-click-node="false" :filter-node-method="filterNode" ref="tree" default-expand-all highlight-current @node-click="handleNodeClick"/>
</div>
</el-col>
<el-col :span="20" :xs="24">
<!-- 顶部搜索和筛选区域 -->
<div class="search-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="80px">
<el-form-item label="用户姓名" prop="nickName">
<el-input v-model="queryParams.nickName" placeholder="请输入用户姓名" clearable style="width: 180px" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="打卡地点" prop="location">
<el-input v-model="queryParams.location" placeholder="请输入打卡地点" clearable style="width: 180px" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="真实日期" prop="realDate">
<el-date-picker clearable v-model="queryParams.realDate" type="date" value-format="yyyy-MM-dd" placeholder="请选择真实日期" style="width: 180px"></el-date-picker>
</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>
</el-form-item>
</el-form>
</div>
<TableCard>
<!-- 打卡记录类型切换 -->
<div class="record-type-tabs">
<el-radio-group v-model="recordType" @change="handleRecordTypeChange">
<el-radio-button label="all">打卡记录</el-radio-button>
<el-radio-button label="external">外勤打卡记录</el-radio-button>
</el-radio-group>
</div>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['oa:workAttendances:export']">
导出
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-upload" size="mini" @click="handleImport" v-hasPermi="['oa:workAttendances:edit']">
导入外部考勤数据
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table :border="Global.tableShowBorder" v-loading="loading" :data="workAttendancesList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center"/>
<el-table-column label="用户姓名" align="center" prop="nickName"/>
<el-table-column label="打卡地点" align="center" prop="location">
<template slot-scope="scope">
<span v-if="!util.isEmpty(scope.row.location)">{{ scope.row.location }}</span>
<span v-if="util.isEmpty(scope.row.location)" style="color: #e6a23c">未记录地点</span>
</template>
</el-table-column>
<el-table-column label="真实日期" align="center" prop="realDate">
<template slot-scope="scope">
<span v-if="!util.isEmpty(scope.row.realDate)">{{ parseTime(scope.row.realDate, '{y}-{m}-{d}') }}</span>
<span v-if="util.isEmpty(scope.row.realDate)" style="color: #e6a23c">未有打卡记录</span>
</template>
</el-table-column>
<el-table-column label="星期" align="center" prop="realDate" >
<template slot-scope="scope">
<span v-if="!util.isEmpty(scope.row.realDate)">{{ util.getWeekDay(scope.row.realDate) }}</span>
<span v-if="util.isEmpty(scope.row.realDate)" style="color: #e6a23c">未有打卡记录</span>
</template>
</el-table-column>
<el-table-column label="年度周" align="center" prop="realDate" >
<template slot-scope="scope">
<span v-if="!util.isEmpty(scope.row.realDate)">{{scope.row.realDate.split("-")[0]}}年第{{ util.getYearWeek(scope.row.realDate) }}周</span>
<span v-if="util.isEmpty(scope.row.realDate)" style="color: #e6a23c">未有打卡记录</span>
</template>
</el-table-column>
<el-table-column label="签到时间" align="center" prop="signInTime" >
<template slot-scope="scope">
<span v-if="!util.isEmpty(scope.row.signInTime)">{{ scope.row.signInTime }}</span>
<span v-if="util.isEmpty(scope.row.signInTime)" style="color: #f56c6c">未打卡</span>
</template>
</el-table-column>
<el-table-column label="签到状态" align="center" prop="signInStatus" >
<template slot-scope="scope">
<span v-if="scope.row.signInStatus=='0'" style="color: #67c23a">正常</span>
<span v-if="scope.row.signInStatus=='1'" style="color: #f56c6c">迟到</span>
<span v-if="util.isEmpty(scope.row.signInStatus)" style="color: #f56c6c">未打卡</span>
</template>
</el-table-column>
<el-table-column label="签退时间" align="center" prop="signOutTime" >
<template slot-scope="scope">
<span v-if="!util.isEmpty(scope.row.signOutTime)">{{ scope.row.signOutTime }}</span>
<span v-if="util.isEmpty(scope.row.signOutTime)" style="color: #f56c6c">未打卡</span>
</template>
</el-table-column>
<el-table-column label="签退状态" align="center" prop="signOutStatus" >
<template slot-scope="scope">
<span v-if="scope.row.signOutStatus=='0'" style="color: #67c23a">正常</span>
<span v-if="scope.row.signOutStatus=='1'" style="color: #f56c6c">早退</span>
<span v-if="util.isEmpty(scope.row.signOutStatus)" style="color: #f56c6c">未打卡</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="nickName"/>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['oa:workAttendances:edit']">
修改
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['oa:workAttendances:remove']">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
</TableCard>
</el-col>
</el-row>
<!-- 添加或修改打卡记录对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<el-form-item label="用户姓名" prop="nickName">
<el-input v-model="form.nickName" :disabled="true"/>
</el-form-item>
<el-form-item label="打卡地点" prop="location">
<el-input v-model="form.location" placeholder="请输入打卡地点"/>
</el-form-item>
<el-form-item label="签到时间" prop="signInTime">
<el-time-picker clearable v-model="form.signInTime" :picker-options="{format:'HH:mm'}" value-format="HH:mm" placeholder="请选择签到时间">
</el-time-picker>
</el-form-item>
<el-form-item label="签到状态" prop="signInStatus">
<el-radio-group v-model="form.signInStatus">
<el-radio label="0">正常</el-radio>
<el-radio label="1">迟到</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="签退时间" prop="signOutTime">
<el-time-picker clearable v-model="form.signOutTime" :picker-options="{format:'HH:mm'}" value-format="HH:mm" placeholder="请选择签退时间">
</el-time-picker>
</el-form-item>
<el-form-item label="签退状态" prop="signOutStatus">
<el-radio-group v-model="form.signOutStatus">
<el-radio label="0">正常</el-radio>
<el-radio label="1">早退</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="真实日期" prop="realDate">
<el-date-picker clearable v-model="form.realDate" type="date" value-format="yyyy-MM-dd" placeholder="请选择真实日期">
</el-date-picker>
</el-form-item>
<el-form-item label="考勤类型" prop="attendanceType">
<el-radio-group v-model="form.attendanceType">
<el-radio label="0">正常打卡</el-radio>
<el-radio label="1">外勤打卡</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
<!-- 导入外部考勤数据对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="500px" append-to-body>
<div class="upload-container">
<div class="upload-tip">
将文件拖到此处,或点击上传
</div>
<div class="upload-format">
仅允许导入xls、xlsx格式文件。
</div>
<div class="upload-template">
<el-button type="text" @click="importTemplate">下载模板</el-button>
</div>
<div class="upload-action">
<el-button type="primary" @click="submitFileForm">确定</el-button>
<el-button @click="upload.open = false">取消</el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import {listWorkAttendances, getWorkAttendances, delWorkAttendances, addWorkAttendances, updateWorkAttendances} from "@/api/hrm/workAttendances";
import {getUser} from "@/api/system/user"
import {getToken} from "@/utils/auth";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import {treeselect} from "@/api/system/dept";
export default {
name: "WorkAttendances",
components: {Treeselect},
data() {
return {
// 部门名称
deptName: undefined,
// 部门树选项
deptOptions: [],
defaultProps: {
children: "children",
label: "label"
},
// 打卡记录类型
// recordType: 'all',
recordType: 'normal',
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 打卡记录表格数据
workAttendancesList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 20,
signUserId: null,
signNickName: null,
signInTime: null,
signOutTime: null,
realDate: null,
weekDays: null,
weekNo: null,
location: null,
deptId: null,
nickName: "",
attendanceType: '0' // 新增考勤类型参数
},
attendanceTypeEnum: {
NORMAL: '0', // 或者后端期望的值
EXTERNAL: '1'
},
// 表单参数
form: {},
// 表单校验
rules: {
attendanceType: [
{ required: true, message: "考勤类型不能为空", trigger: "change" }
]
},
// 用户导入参数
upload: {
// 是否显示弹出层(用户导入)
open: false,
// 弹出层标题(用户导入)
title: "导入外部考勤数据",
// 是否禁用上传
isUploading: false,
// 是否更新已经存在的用户数据
updateSupport: 0,
// 设置上传的请求头部
headers: {Authorization: "Bearer " + getToken()},
// 上传的地址
url: process.env.VUE_APP_BASE_API + "/hrm/workAttendances/importData"
},
};
},
watch: {
// 根据名称筛选部门树
deptName(val) {
this.$refs.tree.filter(val);
}
},
created() {
this.getTreeselect();
this.getList();
},
methods: {
/** 导入按钮操作 */
handleImport() {
this.upload.title = "导入外部考勤数据";
this.upload.open = true;
},
/** 下载模板操作 */
importTemplate() {
this.download('hrm/workAttendances/importTemplate', {}, `考勤模板_${new Date().getTime()}.xlsx`)
},
// 文件上传中处理
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true;
},
// 文件上传成功处理
handleFileSuccess(response, file, fileList) {
this.upload.open = false;
this.upload.isUploading = false;
this.$refs.upload.clearFiles();
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", {dangerouslyUseHTMLString: true});
this.getList();
},
// 提交上传文件
submitFileForm() {
this.$refs.upload.submit();
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
id: null,
signUserId: null,
signNickName: null,
signInTime: null,
signOutTime: null,
realDate: null,
weekDays: null,
weekNo: null,
location: null,
attendanceType: "0" // 默认正常打卡
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
// 保存关键参数
const { deptId, attendanceType } = this.queryParams;
this.queryParams = {
pageNum: 1,
pageSize: 20,
nickName: "",
location: null,
realDate: null,
deptId: deptId || null,
attendanceType: attendanceType || '0' // 确保有默认值
};
this.getList();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加打卡记录";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
if(row.id){
const id = row.id || this.ids
getWorkAttendances(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改打卡记录";
});
}else{
getUser(row.userId).then(res=>{
let data = {};
data.signUserId = row.userId;
data.nickName = res.data.nickName;
this.form = data;
this.open = true;
this.title = "修改打卡记录";
})
}
},
/** 保存按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id != null) {
updateWorkAttendances(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addWorkAttendances(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$modal.confirm('是否确认删除打卡记录的数据项?').then(function () {
return delWorkAttendances(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
});
},
/** 导出按钮操作 */
handleExport() {
this.download('hrm/workAttendances/export', {
...this.queryParams
}, `workAttendances_${new Date().getTime()}.xlsx`)
},
/** 查询部门下拉树结构 */
getTreeselect() {
treeselect().then(response => {
this.deptOptions = response.data;
});
},
// 筛选节点
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
// 节点单击事件
handleNodeClick(data) {
console.log('节点数据:', data);
this.queryParams.deptId = data.deptId || data.id;
console.log('设置 deptId:', this.queryParams.deptId);
this.getList(); // 直接调用 getList 更可靠
},
// 打卡记录类型切换
handleRecordTypeChange(val) {
this.recordType = val;
// 根据类型设置 attendanceType 查询参数
if (val === 'external') {
this.queryParams.attendanceType = '1'; // 外勤打卡
} else {
this.queryParams.attendanceType = '0'; // 正常打卡
}
console.log('切换打卡类型:', val, '参数值:', this.queryParams.attendanceType);
// 重新获取数据
this.getList();
},
/** 查询打卡记录列表 */
getList() {
// 验证参数
if (this.queryParams.attendanceType === '' || this.queryParams.attendanceType === undefined) {
this.queryParams.attendanceType = '0'; // 设置默认值
}
console.log('验证后的参数:', this.queryParams);
this.loading = true;
listWorkAttendances(this.queryParams)
.then(response => {
this.workAttendancesList = response.rows;
this.total = response.total;
this.loading = false;
})
.catch(error => {
console.error('API 错误:', error);
this.$modal.msgError("加载失败");
this.loading = false;
});
},
}
};
</script>
<style scoped>
.search-container {
background: #fff;
padding: 20px;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.record-type-tabs {
margin-top: 15px;
text-align: left;
}
.upload-container {
text-align: center;
padding: 20px;
}
.upload-tip {
font-size: 16px;
color: #606266;
margin-bottom: 10px;
}
.upload-format {
font-size: 14px;
color: #909399;
margin-bottom: 15px;
}
.upload-template {
margin-bottom: 20px;
}
.upload-action {
margin-top: 20px;
}
</style>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['oa:workAttendances:export']">
导出
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-upload" size="mini" @click="handleImport" v-hasPermi="['oa:workAttendances:edit']">
导入外部考勤数据
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>怎么不显示
最新发布