Files
ccdi/ruoyi-ui/src/views/dpcEmployee/index.vue
2026-01-29 09:07:50 +08:00

744 lines
25 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="姓名" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入姓名"
clearable
style="width: 240px"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="柜员号" prop="tellerNo">
<el-input
v-model="queryParams.tellerNo"
placeholder="请输入柜员号"
clearable
style="width: 240px"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="所属部门" prop="deptId">
<treeselect v-model="queryParams.deptId" :options="deptOptions" :show-count="true" placeholder="请选择所属部门" clearable style="width: 240px" />
</el-form-item>
<el-form-item label="身份证号" prop="idCard">
<el-input
v-model="queryParams.idCard"
placeholder="请输入身份证号"
clearable
style="width: 240px"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="状态" clearable style="width: 240px">
<el-option label="全部" value="" />
<el-option label="在职" value="0" />
<el-option label="离职" value="1" />
</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>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['dpc:employee:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-upload2"
size="mini"
@click="handleImport"
v-hasPermi="['dpc:employee:import']"
>导入</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="employeeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="姓名" align="center" prop="name" :show-overflow-tooltip="true"/>
<el-table-column label="柜员号" align="center" prop="tellerNo" :show-overflow-tooltip="true"/>
<el-table-column label="身份证号" align="center" prop="idCard" :show-overflow-tooltip="true"/>
<el-table-column label="所属部门" align="center" prop="deptName" :show-overflow-tooltip="true"/>
<el-table-column label="电话" align="center" prop="phone" width="120"/>
<el-table-column label="状态" align="center" prop="status" width="100">
<template slot-scope="scope">
<el-tag v-if="scope.row.status === '0'" type="success">在职</el-tag>
<el-tag v-else type="danger">离职</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-view"
@click="handleDetail(scope.row)"
v-hasPermi="['dpc:employee:query']"
>详情</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['dpc:employee:edit']"
>编辑</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['dpc:employee: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"
/>
<!-- 添加或修改对话框 -->
<el-dialog :title="title" :visible.sync="open" width="1200px" append-to-body class="employee-edit-dialog">
<el-form ref="form" :model="form" :rules="rules" label-width="90px">
<!-- 基本信息 -->
<div class="section-header">基本信息</div>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name" placeholder="请输入姓名" maxlength="100" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="柜员号" prop="tellerNo">
<el-input v-model="form.tellerNo" placeholder="请输入柜员号" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="所属部门" prop="deptId">
<treeselect v-model="form.deptId" :options="enabledDeptOptions" :show-count="true" placeholder="请选择所属部门" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="身份证号" prop="idCard">
<el-input v-model="form.idCard" placeholder="请输入身份证号" maxlength="18" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="电话" prop="phone">
<el-input v-model="form.phone" placeholder="请输入电话" maxlength="11" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入职时间" prop="hireDate">
<el-date-picker v-model="form.hireDate" type="date" placeholder="选择入职时间" value-format="yyyy-MM-dd" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio label="0">在职</el-radio>
<el-radio label="1">离职</el-radio>
</el-radio-group>
</el-form-item>
<!-- 亲属信息 -->
<div class="section-header" style="margin-top: 24px;">
<span>亲属信息</span>
<span v-if="form.relatives && form.relatives.length > 0" class="relative-count">({{ form.relatives.length }})</span>
<el-button type="text" icon="el-icon-plus" @click="addRelative">添加亲属</el-button>
</div>
<el-table :data="form.relatives" border size="small" v-if="form.relatives && form.relatives.length > 0" class="relatives-table">
<el-table-column type="index" label="序号" width="50" align="center" />
<el-table-column label="亲属姓名" align="center">
<template slot-scope="scope">
<el-input v-model="scope.row.relativeName" placeholder="亲属姓名" size="small" />
</template>
</el-table-column>
<el-table-column label="身份证号" align="center">
<template slot-scope="scope">
<el-input v-model="scope.row.relativeIdCard" placeholder="身份证号" size="small" />
</template>
</el-table-column>
<el-table-column label="电话" align="center" width="130">
<template slot-scope="scope">
<el-input v-model="scope.row.relativePhone" placeholder="电话" size="small" />
</template>
</el-table-column>
<el-table-column label="关系" align="center" width="140">
<template slot-scope="scope">
<el-select v-model="scope.row.relationship" placeholder="关系" size="small" filterable allow-create>
<el-option label="配偶" value="配偶" />
<el-option label="父亲" value="父亲" />
<el-option label="母亲" value="母亲" />
<el-option label="子女" value="子女" />
<el-option label="兄弟姐妹" value="兄弟姐妹" />
</el-select>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="60">
<template slot-scope="scope">
<el-button type="text" icon="el-icon-delete" @click="removeRelative(scope.$index)" />
</template>
</el-table-column>
</el-table>
<div v-else class="empty-relatives">
<i class="el-icon-info"></i>
<span>暂无亲属信息</span>
<el-button type="text" @click="addRelative">立即添加</el-button>
</div>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</div>
</el-dialog>
<!-- 详情对话框 -->
<el-dialog title="员工详情" :visible.sync="detailOpen" width="900px" append-to-body class="employee-detail-dialog">
<div class="detail-container">
<!-- 基本信息卡片 -->
<div class="info-section">
<div class="section-title">
<i class="el-icon-user"></i>
<span>基本信息</span>
</div>
<el-descriptions :column="2" border>
<el-descriptions-item label="姓名">{{ employeeDetail.name || '-' }}</el-descriptions-item>
<el-descriptions-item label="柜员号">{{ employeeDetail.tellerNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="所属部门">{{ employeeDetail.deptName || '-' }}</el-descriptions-item>
<el-descriptions-item label="身份证号">{{ employeeDetail.idCard || '-' }}</el-descriptions-item>
<el-descriptions-item label="电话">{{ employeeDetail.phone || '-' }}</el-descriptions-item>
<el-descriptions-item label="入职时间">
{{ employeeDetail.hireDate ? parseTime(employeeDetail.hireDate, '{y}-{m}-{d}') : '-' }}
</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag v-if="employeeDetail.status === '0'" type="success" size="small">在职</el-tag>
<el-tag v-else type="danger" size="small">离职</el-tag>
</el-descriptions-item>
<el-descriptions-item label="创建时间">
{{ employeeDetail.createTime ? parseTime(employeeDetail.createTime) : '-' }}
</el-descriptions-item>
</el-descriptions>
</div>
<!-- 亲属信息卡片 -->
<div class="info-section" style="margin-top: 20px;">
<div class="section-title">
<i class="el-icon-s-custom"></i>
<span>亲属信息</span>
<el-tag v-if="employeeDetail.relatives && employeeDetail.relatives.length > 0" size="mini" type="info" style="margin-left: 10px;">
{{ employeeDetail.relatives.length }}
</el-tag>
</div>
<div v-if="employeeDetail.relatives && employeeDetail.relatives.length > 0" class="relatives-container">
<el-table :data="employeeDetail.relatives" border style="width: 100%" size="small">
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="亲属姓名" align="center" prop="relativeName" min-width="100" />
<el-table-column label="身份证号" align="center" prop="relativeIdCard" min-width="180" />
<el-table-column label="电话" align="center" prop="relativePhone" width="130" />
<el-table-column label="关系" align="center" prop="relationship" width="100">
<template slot-scope="scope">
<el-tag size="mini" type="primary">{{ scope.row.relationship }}</el-tag>
</template>
</el-table-column>
</el-table>
</div>
<div v-else class="empty-relatives">
<i class="el-icon-info"></i>
<span>暂无亲属信息</span>
</div>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="detailOpen = false" icon="el-icon-close"> </el-button>
</div>
</el-dialog>
<!-- 导入对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body @close="handleImportDialogClose" v-loading="upload.isUploading" element-loading-text="正在导入数据,请稍候..." element-loading-spinner="el-icon-loading" element-loading-background="rgba(0, 0, 0, 0.7)">
<el-upload
ref="upload"
:limit="1"
accept=".xlsx, .xls"
:headers="upload.headers"
:action="upload.url + '?updateSupport=' + upload.updateSupport"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
drag
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的员工数据
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
</div>
<div class="el-upload__tip" slot="tip">
<span>仅允许导入"xls""xlsx"格式文件</span>
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm" :loading="upload.isUploading"> </el-button>
<el-button @click="upload.open = false" :disabled="upload.isUploading"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {addEmployee, delEmployee, getEmployee, listEmployee, updateEmployee} from "@/api/dpcEmployee";
import {deptTreeSelect} from "@/api/system/user";
import {getToken} from "@/utils/auth";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
// 身份证号校验正则
const idCardPattern = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
// 手机号校验正则
const phonePattern = /^1[3|4|5|6|7|8|9][0-9]\d{8}$/;
export default {
name: "Employee",
components: { Treeselect },
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 员工表格数据
employeeList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 是否显示详情弹出层
detailOpen: false,
// 员工详情
employeeDetail: {},
// 所有部门树选项
deptOptions: undefined,
// 过滤掉已禁用部门树选项
enabledDeptOptions: undefined,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
name: null,
tellerNo: null,
deptId: null,
idCard: null,
status: null
},
// 表单参数
form: {},
// 表单校验
rules: {
name: [
{ required: true, message: "姓名不能为空", trigger: "blur" },
{ max: 100, message: "姓名长度不能超过100个字符", trigger: "blur" }
],
tellerNo: [
{ required: true, message: "柜员号不能为空", trigger: "blur" },
{ max: 50, message: "柜员号长度不能超过50个字符", trigger: "blur" }
],
idCard: [
{ required: true, message: "身份证号不能为空", trigger: "blur" },
{ pattern: idCardPattern, message: "请输入正确的18位身份证号", trigger: "blur" }
],
phone: [
{ pattern: phonePattern, message: "请输入正确的11位手机号", trigger: "blur" }
],
status: [
{ required: true, message: "请选择状态", trigger: "change" }
]
},
// 导入参数
upload: {
// 是否显示弹出层
open: false,
// 弹出层标题
title: "",
// 是否禁用上传
isUploading: false,
// 是否更新已经存在的数据
updateSupport: 0,
// 设置上传的请求头部
headers: { Authorization: "Bearer " + getToken() },
// 上传的地址
url: process.env.VUE_APP_BASE_API + "/dpc/employee/importData"
}
};
},
created() {
this.getList();
this.getDeptTree();
},
methods: {
/** 查询员工列表 */
getList() {
this.loading = true;
listEmployee(this.queryParams).then(response => {
this.employeeList = response.rows;
this.total = response.total;
this.loading = false;
});
},
/** 查询部门下拉树结构 */
getDeptTree() {
deptTreeSelect().then(response => {
this.deptOptions = response.data;
this.enabledDeptOptions = this.filterDisabledDept(JSON.parse(JSON.stringify(response.data)));
});
},
// 过滤禁用的部门
filterDisabledDept(deptList) {
return deptList.filter(dept => {
if (dept.disabled) {
return false;
}
if (dept.children && dept.children.length) {
dept.children = this.filterDisabledDept(dept.children);
}
return true;
});
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
employeeId: null,
name: null,
tellerNo: null,
deptId: null,
idCard: null,
phone: null,
hireDate: null,
status: "0",
relatives: []
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 多选框选中数据 */
handleSelectionChange(selection) {
this.ids = selection.map(item => item.employeeId);
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "新增员工";
},
/** 详情按钮操作 */
handleDetail(row) {
const employeeId = row.employeeId;
getEmployee(employeeId).then(response => {
this.employeeDetail = response.data;
this.detailOpen = true;
});
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const employeeId = row.employeeId || this.ids[0];
getEmployee(employeeId).then(response => {
this.form = response.data;
if (!this.form.relatives) {
this.form.relatives = [];
}
this.open = true;
this.title = "编辑员工";
});
},
/** 添加亲属 */
addRelative() {
if (!this.form.relatives) {
this.form.relatives = [];
}
this.form.relatives.push({
relativeId: null,
relativeName: null,
relativeIdCard: null,
relativePhone: null,
relationship: null
});
},
/** 删除亲属 */
removeRelative(index) {
this.form.relatives.splice(index, 1);
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
// 验证亲属信息
if (this.form.relatives && this.form.relatives.length > 0) {
for (let i = 0; i < this.form.relatives.length; i++) {
const relative = this.form.relatives[i];
// 验证亲属姓名
if (!relative.relativeName || relative.relativeName.trim() === '') {
this.$modal.msgError("第" + (i + 1) + "行亲属姓名不能为空");
return;
}
// 验证关系
if (!relative.relationship || relative.relationship.trim() === '') {
this.$modal.msgError("第" + (i + 1) + "行关系不能为空");
return;
}
// 验证亲属手机号格式(填写时才验证)
if (relative.relativePhone && !phonePattern.test(relative.relativePhone)) {
this.$modal.msgError("第" + (i + 1) + "行亲属手机号格式不正确");
return;
}
}
}
if (this.form.employeeId != null) {
updateEmployee(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addEmployee(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const employeeIds = row.employeeId || this.ids;
this.$modal.confirm('是否确认删除员工编号为"' + employeeIds + '"的数据项?').then(function() {
return delEmployee(employeeIds);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导入按钮操作 */
handleImport() {
this.upload.title = "员工数据导入";
this.upload.open = true;
},
/** 导入对话框关闭事件 */
handleImportDialogClose() {
this.$nextTick(() => {
if (this.$refs.upload) {
this.$refs.upload.clearFiles();
}
});
},
/** 下载模板操作 */
importTemplate() {
this.download('dpc/employee/importTemplate', {}, `员工信息模板_${new Date().getTime()}.xlsx`)
},
// 文件上传中处理
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true;
},
// 文件上传成功处理
handleFileSuccess(response, file, fileList) {
this.upload.isUploading = false;
this.upload.open = false;
this.getList();
this.$alert(response.msg, "导入结果", {
dangerouslyUseHTMLString: true,
customClass: 'import-result-dialog'
});
},
// 提交上传文件
submitFileForm() {
this.$refs.upload.submit();
}
}
};
</script>
<style scoped>
.detail-form .el-form-item {
margin-bottom: 10px;
}
/* 详情弹窗样式 */
.employee-detail-dialog .detail-container {
padding: 10px 0;
}
.employee-detail-dialog .info-section {
background: #f9fafb;
border-radius: 8px;
padding: 16px;
}
.employee-detail-dialog .section-title {
display: flex;
align-items: center;
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-bottom: 10px;
border-bottom: 1px solid #e4e7ed;
}
.employee-detail-dialog .section-title i {
margin-right: 6px;
color: #409eff;
font-size: 16px;
}
.employee-detail-dialog .el-descriptions {
background: #fff;
}
.employee-detail-dialog .relatives-container {
background: #fff;
border-radius: 4px;
padding: 10px;
}
.employee-detail-dialog .empty-relatives {
text-align: center;
padding: 30px 0;
color: #909399;
background: #fff;
border-radius: 4px;
}
.employee-detail-dialog .empty-relatives i {
font-size: 40px;
color: #c0c4cc;
margin-bottom: 10px;
}
.employee-detail-dialog .empty-relatives span {
display: block;
font-size: 14px;
}
/* 编辑弹窗样式 */
.employee-edit-dialog .section-header {
display: flex;
align-items: center;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #e4e7ed;
font-size: 14px;
font-weight: 500;
color: #303133;
}
.employee-edit-dialog .section-header span:first-child {
flex: 1;
}
.employee-edit-dialog .relative-count {
color: #909399;
font-size: 12px;
margin-right: 8px;
}
.employee-edit-dialog .el-form-item {
margin-bottom: 16px;
}
.employee-edit-dialog .el-radio {
margin-right: 20px;
}
.employee-edit-dialog .relatives-table {
width: 100%;
}
.employee-edit-dialog .relatives-table .el-form-item {
margin-bottom: 0;
}
.employee-edit-dialog .empty-relatives {
text-align: center;
padding: 30px 0;
color: #909399;
border: 1px dashed #dcdfe6;
border-radius: 4px;
}
.employee-edit-dialog .empty-relatives i {
font-size: 32px;
color: #c0c4cc;
margin-bottom: 6px;
}
.employee-edit-dialog .empty-relatives span {
font-size: 13px;
margin-right: 8px;
}
/* 导入结果弹窗样式 */
.import-result-dialog {
max-height: 70vh;
overflow: auto;
}
.import-result-dialog .el-message-box__content {
max-height: 60vh;
overflow-y: auto;
padding: 10px 20px;
}