1. 生产模块按工序拆分为两个一级菜单 - 生产-前缫(落丝记录)、生产-后缫(纤度检验、黑板检验) - 旧的「生产管理」(/prod) 目录及子菜单由种子自动清理,页面组件与权限标识保持不变 - 消息中心跳转路径、首页快捷入口路径同步更新 2. 职工管理独立为一级菜单(原挂在「基础数据」目录下),并清理历史子菜单 3. 修复侧边栏菜单展开卡顿(基础数据等子项较多的目录尤其明显) - 去掉 Element Plus 的 height 过渡动画(会逐帧重排全部子项) - 收起时改用 height:0 + visibility:hidden 而非 display:none,子项提前完成布局 - 菜单区独立滚动并预留滚动条宽度(scrollbar-gutter),消除展开时的横向抖动 - 菜单项增加 contain 布局隔离,淡入动画改为容器级 4. 修复页面首次打开重复请求 - 分页组件挂载时会触发一次 current-change,页码未变化时不再重复加载(18 个页面) - 职工管理去掉 onMounted 与默认选中部门的重复加载 - 工种管理、原料庄口的注册表下拉由按 key 多次请求改为一次全量拉取 5. 后端增加前端静态托管与 SPA 路由回退,支持 IIS 单站点部署(前后端同源) 6. 新增部署脚本 deploy-iis.ps1(首次部署)与 update-iis.ps1(更新发布)
1022 lines
37 KiB
Vue
1022 lines
37 KiB
Vue
<script setup>
|
||
import { ref, reactive, nextTick, onMounted } from 'vue';
|
||
import request from '../../../utils/request';
|
||
import { buildDeptTree } from '../../../utils/dept';
|
||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||
|
||
const workStatusOptions = [
|
||
{ label: '全部', value: null },
|
||
{ label: '在职', value: 0 },
|
||
{ label: '离职', value: 1 }
|
||
];
|
||
const banciOptions = [
|
||
{ label: '全部', value: null },
|
||
{ label: '甲', value: 1 },
|
||
{ label: '乙', value: 2 }
|
||
];
|
||
|
||
const deptTree = ref([]);
|
||
const deptTreeRef = ref(null);
|
||
const currentDept = ref(null);
|
||
const worktypes = ref([]);
|
||
|
||
const list = ref([]);
|
||
const total = ref(0);
|
||
const loading = ref(false);
|
||
const selectedRow = ref(null);
|
||
const query = reactive({ workStatus: 0, worktypeId: 0, banci: null, like: '', pageIndex: 1, pageSize: 20 });
|
||
|
||
// 注册表下拉(基础数据\员工档案)
|
||
const registryOptions = reactive({
|
||
gender: [],
|
||
minzu: [],
|
||
hunyin: [],
|
||
xueli: [],
|
||
hetongType: [],
|
||
lizhiYuanyin: []
|
||
});
|
||
|
||
const dialogVisible = ref(false);
|
||
const dialogTitle = ref('新增职工');
|
||
const isEdit = ref(false);
|
||
const editId = ref(null);
|
||
const form = reactive({
|
||
depId: null,
|
||
worktypeId: 0,
|
||
banci: 1,
|
||
code: '',
|
||
name: '',
|
||
gangwei: '',
|
||
phone: '',
|
||
idCard: '',
|
||
birthDate: null,
|
||
address: '',
|
||
gender: '',
|
||
minzu: '',
|
||
hunyinStatus: '',
|
||
huji: '',
|
||
ruzhiDate: null,
|
||
jinjiLianxiren: '',
|
||
jinjiLianxirenPhone: '',
|
||
hetongType: '',
|
||
hetongStartDate: null,
|
||
hetongEndDate: null,
|
||
tuixiuDate: null,
|
||
shebaoDate: null,
|
||
shebaoDesc: '',
|
||
bankcardCode: '',
|
||
biyeYuanxiao: '',
|
||
zhuanye: '',
|
||
xueli: '',
|
||
workGroup: '',
|
||
sort: 0,
|
||
namePinyin: '',
|
||
memo: ''
|
||
});
|
||
|
||
// 离职 / 调岗 / 生产信息
|
||
const lizhiVisible = ref(false);
|
||
const lizhiForm = reactive({ lizhiDate: null, lizhiYuanyin: '' });
|
||
const tiaogangVisible = ref(false);
|
||
const tiaogangTitle = ref('调岗');
|
||
const isRenew = ref(false);
|
||
const tiaogangForm = reactive({ date: null, depId: null, worktypeId: 0, gangweiName: '', banci: 1 });
|
||
const scVisible = ref(false);
|
||
const scForm = reactive({ workGroup: '', sort: 0 });
|
||
|
||
// 变更记录 / 调岗记录
|
||
const changeVisible = ref(false);
|
||
const changeRecords = ref([]);
|
||
const tiaogangRecordVisible = ref(false);
|
||
const tiaogangRecords = ref([]);
|
||
|
||
async function loadDepts() {
|
||
const res = await request.get('/sys/department');
|
||
deptTree.value = buildDeptTree(res.data);
|
||
await nextTick();
|
||
if (!currentDept.value && res.data?.length) {
|
||
const first = res.data[0];
|
||
selectDept(first.id, first.name);
|
||
}
|
||
}
|
||
|
||
// 工种及其可属部门一次性拉取(接口已返回 departmentIds),避免逐条请求导致页面卡顿
|
||
async function loadWorktypes() {
|
||
const res = await request.get('/sys/worktype', { params: { pageIndex: 1, pageSize: 500 } });
|
||
worktypes.value = (res.data.items || []).map((w) => ({
|
||
id: Number(w.id),
|
||
workName: w.workName,
|
||
depIds: (w.departmentIds || []).map(Number)
|
||
}));
|
||
}
|
||
|
||
function worktypesOfDep(depId) {
|
||
if (!depId) return [];
|
||
return worktypes.value.filter((w) => (w.depIds || []).includes(Number(depId)));
|
||
}
|
||
|
||
function onDepSelectWorktype() {
|
||
const options = worktypesOfDep(form.depId);
|
||
if (!options.some((w) => w.id === form.worktypeId)) form.worktypeId = 0;
|
||
}
|
||
|
||
function onWorktypeSelect() {
|
||
const hit = worktypes.value.find((w) => w.id === form.worktypeId);
|
||
if (hit) form.gangwei = hit.workName;
|
||
}
|
||
|
||
function onTgDepChange() {
|
||
const options = worktypesOfDep(tiaogangForm.depId);
|
||
if (!options.some((w) => w.id === tiaogangForm.worktypeId)) tiaogangForm.worktypeId = 0;
|
||
}
|
||
|
||
function onTgWorktypeChange() {
|
||
const hit = worktypes.value.find((w) => w.id === tiaogangForm.worktypeId);
|
||
if (hit) tiaogangForm.gangweiName = hit.workName;
|
||
}
|
||
|
||
// 注册表下拉:一次拉取全量后本地取用,避免每个键各发一次请求
|
||
async function loadRegistryOptions() {
|
||
try {
|
||
const res = await request.get('/sys/registry');
|
||
const all = res.data || [];
|
||
const pick = (keyName) => {
|
||
const hit = all.find((x) => x.category === '基础数据\\员工档案' && x.keyName === keyName);
|
||
return (hit?.value || '')
|
||
.split(',')
|
||
.map((s) => s.trim())
|
||
.filter(Boolean);
|
||
};
|
||
registryOptions.gender = pick('性别');
|
||
registryOptions.minzu = pick('民族');
|
||
registryOptions.hunyin = pick('婚姻状态');
|
||
registryOptions.xueli = pick('学历');
|
||
registryOptions.hetongType = pick('合同类型');
|
||
registryOptions.lizhiYuanyin = pick('离职原因');
|
||
} catch (e) {
|
||
// 注册表未配置时下拉为空
|
||
}
|
||
}
|
||
|
||
function selectDept(id, name) {
|
||
currentDept.value = { id, name };
|
||
deptTreeRef.value?.setCurrentKey(id);
|
||
query.pageIndex = 1;
|
||
loadList();
|
||
}
|
||
|
||
function handleDeptClick(node) {
|
||
currentDept.value = { id: node.id, name: node.name };
|
||
query.pageIndex = 1;
|
||
loadList();
|
||
}
|
||
|
||
// 分页组件挂载时会触发一次 current-change:页码未变化时直接忽略,避免页面首次打开重复请求
|
||
function handlePageChange(p) {
|
||
if (p === query.pageIndex) return;
|
||
query.pageIndex = p;
|
||
loadList();
|
||
}
|
||
|
||
async function loadList() {
|
||
if (!currentDept.value) {
|
||
ElMessage.warning('请选择部门');
|
||
return;
|
||
}
|
||
loading.value = true;
|
||
try {
|
||
const res = await request.get('/sys/employee', {
|
||
params: {
|
||
depId: currentDept.value.id,
|
||
worktypeId: query.worktypeId || 0,
|
||
workStatus: query.workStatus === null ? undefined : query.workStatus,
|
||
banci: query.banci === null ? undefined : query.banci,
|
||
like: query.like || undefined,
|
||
pageIndex: query.pageIndex,
|
||
pageSize: query.pageSize
|
||
}
|
||
});
|
||
list.value = res.data.items || [];
|
||
total.value = res.data.total || 0;
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
|
||
function handleSearch() {
|
||
query.pageIndex = 1;
|
||
loadList();
|
||
}
|
||
|
||
function handleRowClick(row) {
|
||
selectedRow.value = row;
|
||
}
|
||
|
||
function requireSelected() {
|
||
if (!selectedRow.value) {
|
||
ElMessage.warning('请先选择职工');
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/* ==================== 新增 / 编辑 ==================== */
|
||
function openCreate() {
|
||
if (!currentDept.value) {
|
||
ElMessage.warning('请选择部门');
|
||
return;
|
||
}
|
||
isEdit.value = false;
|
||
dialogTitle.value = '新增职工';
|
||
Object.assign(form, {
|
||
depId: currentDept.value.id,
|
||
worktypeId: 0,
|
||
banci: 1,
|
||
code: '',
|
||
name: '',
|
||
gangwei: '',
|
||
phone: '',
|
||
idCard: '',
|
||
birthDate: null,
|
||
address: '',
|
||
gender: '男',
|
||
minzu: '汉',
|
||
hunyinStatus: '未婚',
|
||
huji: '',
|
||
ruzhiDate: new Date().toISOString().slice(0, 10),
|
||
jinjiLianxiren: '',
|
||
jinjiLianxirenPhone: '',
|
||
hetongType: '',
|
||
hetongStartDate: null,
|
||
hetongEndDate: null,
|
||
tuixiuDate: null,
|
||
shebaoDate: null,
|
||
shebaoDesc: '',
|
||
bankcardCode: '',
|
||
biyeYuanxiao: '',
|
||
zhuanye: '',
|
||
xueli: '',
|
||
workGroup: '',
|
||
sort: 0,
|
||
namePinyin: '',
|
||
memo: ''
|
||
});
|
||
dialogVisible.value = true;
|
||
}
|
||
|
||
async function openEdit(row) {
|
||
const res = await request.get(`/sys/employee/${row.id}`);
|
||
const e = res.data;
|
||
isEdit.value = true;
|
||
editId.value = e.id;
|
||
dialogTitle.value = `修改职工「${e.name}」`;
|
||
Object.assign(form, {
|
||
depId: Number(e.depId),
|
||
worktypeId: Number(e.worktypeId),
|
||
banci: e.banci ?? 1,
|
||
code: e.code || '',
|
||
name: e.name || '',
|
||
gangwei: e.gangwei || '',
|
||
phone: e.phone || '',
|
||
idCard: e.idCard || '',
|
||
birthDate: e.birthDate ? String(e.birthDate).slice(0, 10) : null,
|
||
address: e.address || '',
|
||
gender: e.gender || '',
|
||
minzu: e.minzu || '',
|
||
hunyinStatus: e.hunyinStatus || '',
|
||
huji: e.huji || '',
|
||
ruzhiDate: e.ruzhiDate ? String(e.ruzhiDate).slice(0, 10) : null,
|
||
jinjiLianxiren: e.jinjiLianxiren || '',
|
||
jinjiLianxirenPhone: e.jinjiLianxirenPhone || '',
|
||
hetongType: e.hetongType || '',
|
||
hetongStartDate: e.hetongStartDate ? String(e.hetongStartDate).slice(0, 10) : null,
|
||
hetongEndDate: e.hetongEndDate ? String(e.hetongEndDate).slice(0, 10) : null,
|
||
tuixiuDate: e.tuixiuDate ? String(e.tuixiuDate).slice(0, 10) : null,
|
||
shebaoDate: e.shebaoDate ? String(e.shebaoDate).slice(0, 10) : null,
|
||
shebaoDesc: e.shebaoDesc || '',
|
||
bankcardCode: e.bankcardCode || '',
|
||
biyeYuanxiao: e.biyeYuanxiao || '',
|
||
zhuanye: e.zhuanye || '',
|
||
xueli: e.xueli || '',
|
||
workGroup: e.workGroup || '',
|
||
sort: e.sort || 0,
|
||
namePinyin: e.namePinyin || '',
|
||
memo: e.memo || ''
|
||
});
|
||
dialogVisible.value = true;
|
||
}
|
||
|
||
async function handleSave() {
|
||
if (!form.name) {
|
||
ElMessage.warning('请输入姓名');
|
||
return;
|
||
}
|
||
if (!form.depId) {
|
||
ElMessage.warning('请选择部门');
|
||
return;
|
||
}
|
||
try {
|
||
if (isEdit.value) {
|
||
await request.put(`/sys/employee/${editId.value}`, {
|
||
code: form.code,
|
||
name: form.name,
|
||
gangwei: form.gangwei,
|
||
phone: form.phone,
|
||
idCard: form.idCard,
|
||
birthDate: form.birthDate,
|
||
address: form.address,
|
||
gender: form.gender,
|
||
minzu: form.minzu,
|
||
hunyinStatus: form.hunyinStatus,
|
||
huji: form.huji,
|
||
ruzhiDate: form.ruzhiDate,
|
||
jinjiLianxiren: form.jinjiLianxiren,
|
||
jinjiLianxirenPhone: form.jinjiLianxirenPhone,
|
||
hetongType: form.hetongType,
|
||
hetongStartDate: form.hetongStartDate,
|
||
hetongEndDate: form.hetongEndDate,
|
||
tuixiuDate: form.tuixiuDate,
|
||
shebaoDate: form.shebaoDate,
|
||
shebaoDesc: form.shebaoDesc,
|
||
bankcardCode: form.bankcardCode,
|
||
biyeYuanxiao: form.biyeYuanxiao,
|
||
zhuanye: form.zhuanye,
|
||
xueli: form.xueli,
|
||
namePinyin: form.namePinyin,
|
||
memo: form.memo
|
||
});
|
||
ElMessage.success('修改成功');
|
||
} else {
|
||
await request.post('/sys/employee', {
|
||
depId: form.depId,
|
||
worktypeId: form.worktypeId,
|
||
code: form.code,
|
||
name: form.name,
|
||
gangwei: form.gangwei,
|
||
phone: form.phone,
|
||
idCard: form.idCard,
|
||
birthDate: form.birthDate,
|
||
address: form.address,
|
||
gender: form.gender,
|
||
minzu: form.minzu,
|
||
hunyinStatus: form.hunyinStatus,
|
||
huji: form.huji,
|
||
ruzhiDate: form.ruzhiDate,
|
||
jinjiLianxiren: form.jinjiLianxiren,
|
||
jinjiLianxirenPhone: form.jinjiLianxirenPhone,
|
||
hetongType: form.hetongType,
|
||
hetongStartDate: form.hetongStartDate,
|
||
hetongEndDate: form.hetongEndDate,
|
||
tuixiuDate: form.tuixiuDate,
|
||
shebaoDate: form.shebaoDate,
|
||
shebaoDesc: form.shebaoDesc,
|
||
bankcardCode: form.bankcardCode,
|
||
biyeYuanxiao: form.biyeYuanxiao,
|
||
zhuanye: form.zhuanye,
|
||
xueli: form.xueli,
|
||
banci: form.banci,
|
||
workGroup: form.workGroup,
|
||
sort: Number(form.sort) || 0,
|
||
namePinyin: form.namePinyin,
|
||
memo: form.memo
|
||
});
|
||
ElMessage.success('新增成功');
|
||
}
|
||
dialogVisible.value = false;
|
||
loadList();
|
||
} catch (e) {}
|
||
}
|
||
|
||
async function handleDelete(row) {
|
||
await ElMessageBox.confirm(`确定删除职工「${row.name}」吗?`, '提示', { type: 'warning' });
|
||
try {
|
||
await request.delete(`/sys/employee/${row.id}`);
|
||
ElMessage.success('删除成功');
|
||
loadList();
|
||
} catch (e) {}
|
||
}
|
||
|
||
/* ==================== 下拉操作 ==================== */
|
||
async function handleCommand(command) {
|
||
if (!requireSelected()) return;
|
||
const row = selectedRow.value;
|
||
|
||
if (command === 'zhuanzheng') {
|
||
try {
|
||
await request.put(`/sys/employee/${row.id}/zhuanzheng`, { zhuanzhengDate: new Date().toISOString().slice(0, 10) });
|
||
ElMessage.success('转正成功');
|
||
loadList();
|
||
} catch (e) {}
|
||
} else if (command === 'zhuanzhengCancel') {
|
||
await ElMessageBox.confirm('是否取消转正?', '提示', { type: 'warning' });
|
||
try {
|
||
await request.put(`/sys/employee/${row.id}/zhuanzheng-cancel`);
|
||
ElMessage.success('已取消转正');
|
||
loadList();
|
||
} catch (e) {}
|
||
} else if (command === 'lizhi') {
|
||
lizhiForm.lizhiDate = new Date().toISOString().slice(0, 10);
|
||
lizhiForm.lizhiYuanyin = registryOptions.lizhiYuanyin[0] || '';
|
||
lizhiVisible.value = true;
|
||
} else if (command === 'lizhiCancel') {
|
||
await ElMessageBox.confirm('是否取消离职?', '提示', { type: 'warning' });
|
||
try {
|
||
await request.put(`/sys/employee/${row.id}/lizhi-cancel`);
|
||
ElMessage.success('已取消离职');
|
||
loadList();
|
||
} catch (e) {}
|
||
} else if (command === 'tiaogang' || command === 'renew') {
|
||
if (command === 'renew' && Number(row.workStatus) !== 1) {
|
||
ElMessage.warning('请选择离职人员');
|
||
return;
|
||
}
|
||
isRenew.value = command === 'renew';
|
||
tiaogangTitle.value = isRenew.value ? '重新入职' : '调岗';
|
||
Object.assign(tiaogangForm, {
|
||
date: new Date().toISOString().slice(0, 10),
|
||
depId: Number(row.depId),
|
||
worktypeId: Number(row.worktypeId),
|
||
gangweiName: row.gangwei || '',
|
||
banci: row.banci ?? 1
|
||
});
|
||
tiaogangVisible.value = true;
|
||
} else if (command === 'sc') {
|
||
scForm.workGroup = row.workGroup || '';
|
||
scForm.sort = row.sort || 0;
|
||
scVisible.value = true;
|
||
} else if (command === 'changeRecords') {
|
||
const res = await request.get(`/sys/employee/${row.id}/change-records`);
|
||
changeRecords.value = res.data || [];
|
||
changeVisible.value = true;
|
||
} else if (command === 'tiaogangRecords') {
|
||
const res = await request.get(`/sys/employee/${row.id}/tiaogang-records`);
|
||
tiaogangRecords.value = res.data || [];
|
||
tiaogangRecordVisible.value = true;
|
||
}
|
||
}
|
||
|
||
async function saveLizhi() {
|
||
if (!lizhiForm.lizhiYuanyin) {
|
||
ElMessage.warning('请选择离职原因');
|
||
return;
|
||
}
|
||
try {
|
||
await request.put(`/sys/employee/${selectedRow.value.id}/lizhi`, {
|
||
lizhiDate: lizhiForm.lizhiDate,
|
||
lizhiYuanyin: lizhiForm.lizhiYuanyin
|
||
});
|
||
ElMessage.success('已办理离职');
|
||
lizhiVisible.value = false;
|
||
loadList();
|
||
} catch (e) {}
|
||
}
|
||
|
||
async function saveTiaogang() {
|
||
if (!tiaogangForm.depId) {
|
||
ElMessage.warning('请选择部门');
|
||
return;
|
||
}
|
||
const row = selectedRow.value;
|
||
const payload = {
|
||
depId: tiaogangForm.depId,
|
||
workTypeId: tiaogangForm.worktypeId,
|
||
date: tiaogangForm.date,
|
||
gangweiName: tiaogangForm.gangweiName,
|
||
sc_Banci: tiaogangForm.banci
|
||
};
|
||
try {
|
||
if (isRenew.value) await request.put(`/sys/employee/${row.id}/renew-ruzhis`, payload);
|
||
else await request.put(`/sys/employee/${row.id}/tiaogang`, payload);
|
||
ElMessage.success(isRenew.value ? '重新入职成功' : '调岗成功');
|
||
tiaogangVisible.value = false;
|
||
loadList();
|
||
} catch (e) {}
|
||
}
|
||
|
||
async function saveSc() {
|
||
try {
|
||
await request.put(`/sys/employee/${selectedRow.value.id}/sc`, {
|
||
sc_WorkGroup: scForm.workGroup,
|
||
sc_Sort: Number(scForm.sort) || 0
|
||
});
|
||
ElMessage.success('修改成功');
|
||
scVisible.value = false;
|
||
loadList();
|
||
} catch (e) {}
|
||
}
|
||
|
||
function fmtDate(v) {
|
||
return v ? String(v).slice(0, 10) : '';
|
||
}
|
||
|
||
onMounted(async () => {
|
||
await Promise.all([loadDepts(), loadWorktypes(), loadRegistryOptions()]);
|
||
// loadDepts 默认选中首个部门时已触发加载,此处不再重复请求
|
||
if (!currentDept.value) loadList();
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<el-card shadow="never">
|
||
<template #header>
|
||
<div class="card-header">
|
||
<span>职工管理</span>
|
||
<div>
|
||
<el-button type="primary" @click="openCreate">新增职工</el-button>
|
||
<el-dropdown style="margin-left: 8px" @command="handleCommand">
|
||
<el-button>
|
||
更多操作<el-icon class="el-icon--right"><arrow-down /></el-icon>
|
||
</el-button>
|
||
<template #dropdown>
|
||
<el-dropdown-menu>
|
||
<el-dropdown-item command="zhuanzheng">转正</el-dropdown-item>
|
||
<el-dropdown-item command="zhuanzhengCancel">取消转正</el-dropdown-item>
|
||
<el-dropdown-item command="tiaogang">调岗</el-dropdown-item>
|
||
<el-dropdown-item command="sc">修改生产信息</el-dropdown-item>
|
||
<el-dropdown-item command="lizhi">离职</el-dropdown-item>
|
||
<el-dropdown-item command="lizhiCancel">取消离职</el-dropdown-item>
|
||
<el-dropdown-item command="renew">重新入职</el-dropdown-item>
|
||
<el-dropdown-item divided command="changeRecords">变更记录</el-dropdown-item>
|
||
<el-dropdown-item command="tiaogangRecords">调岗记录</el-dropdown-item>
|
||
</el-dropdown-menu>
|
||
</template>
|
||
</el-dropdown>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<div class="employee-body">
|
||
<div class="tree-panel">
|
||
<div class="tree-title">部门</div>
|
||
<el-tree
|
||
ref="deptTreeRef"
|
||
:data="deptTree"
|
||
node-key="id"
|
||
highlight-current
|
||
default-expand-all
|
||
:props="{ label: 'name', children: 'children' }"
|
||
@node-click="handleDeptClick"
|
||
/>
|
||
</div>
|
||
|
||
<div class="table-panel">
|
||
<div class="search-bar">
|
||
<el-select v-model="query.workStatus" style="width: 110px" @change="handleSearch">
|
||
<el-option v-for="s in workStatusOptions" :key="String(s.value)" :label="s.label" :value="s.value" />
|
||
</el-select>
|
||
<el-select v-model="query.worktypeId" placeholder="工种(全部)" clearable filterable style="width: 150px" @change="handleSearch">
|
||
<el-option v-for="w in worktypes" :key="w.id" :label="w.workName" :value="w.id" />
|
||
</el-select>
|
||
<el-select v-model="query.banci" placeholder="班次(全部)" clearable style="width: 130px" @change="handleSearch">
|
||
<el-option v-for="b in banciOptions" :key="String(b.value)" :label="b.label" :value="b.value" />
|
||
</el-select>
|
||
<el-input
|
||
v-model="query.like"
|
||
placeholder="姓名 / 工号 / 电话 / 身份证 / 岗位"
|
||
clearable
|
||
style="width: 250px"
|
||
@keyup.enter="handleSearch"
|
||
@clear="handleSearch"
|
||
/>
|
||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||
<span class="tip">当前部门:{{ currentDept?.name || '未选择' }}(含子部门)</span>
|
||
</div>
|
||
|
||
<el-table
|
||
v-loading="loading"
|
||
:data="list"
|
||
row-key="id"
|
||
border
|
||
stripe
|
||
height="560"
|
||
highlight-current-row
|
||
style="width: 100%"
|
||
@row-click="handleRowClick"
|
||
>
|
||
<el-table-column type="index" label="序号" width="60" align="center" fixed="left" />
|
||
<el-table-column prop="depName" label="部门" width="110" fixed="left" />
|
||
<el-table-column prop="worktypeName" label="工种" width="100" />
|
||
<el-table-column label="班次" width="70">
|
||
<template #default="{ row }">{{ row.banci === 1 ? '甲' : row.banci === 2 ? '乙' : '' }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="gangwei" label="岗位" width="100" />
|
||
<el-table-column label="状态" width="130" fixed="left">
|
||
<template #default="{ row }">
|
||
<el-tag :type="Number(row.workStatus) === 0 ? 'success' : 'warning'">
|
||
{{ Number(row.workStatus) === 0 ? '在职' : '离职' }}
|
||
</el-tag>
|
||
<el-tag v-if="!row.isZhuanzheng" type="danger" style="margin-left: 4px">未转正</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="name" label="姓名" width="90" />
|
||
<el-table-column prop="gender" label="性别" width="60" />
|
||
<el-table-column prop="nianling" label="年龄" width="60" />
|
||
<el-table-column prop="idCard" label="身份证" width="180" />
|
||
<el-table-column prop="phone" label="电话" width="120" />
|
||
<el-table-column label="入职日期" width="110">
|
||
<template #default="{ row }">{{ fmtDate(row.ruzhiDate) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="转正" width="70">
|
||
<template #default="{ row }">{{ row.isZhuanzheng ? '已转正' : '未转正' }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="转正日期" width="110">
|
||
<template #default="{ row }">{{ fmtDate(row.zhuanzhengDate) }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="workGroup" label="工作组" width="100" />
|
||
<el-table-column prop="sort" label="顺序号" width="80" />
|
||
<el-table-column prop="bankcardCode" label="银行卡" width="180" />
|
||
<el-table-column prop="hetongType" label="合同类型" width="100" />
|
||
<el-table-column label="合同开始" width="110">
|
||
<template #default="{ row }">{{ fmtDate(row.hetongStartDate) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="合同结束" width="110">
|
||
<template #default="{ row }">{{ fmtDate(row.hetongEndDate) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="退休日期" width="110">
|
||
<template #default="{ row }">{{ fmtDate(row.tuixiuDate) }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="社保参保" width="110">
|
||
<template #default="{ row }">{{ fmtDate(row.shebaoDate) }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="minzu" label="民族" width="80" />
|
||
<el-table-column prop="hunyinStatus" label="婚姻" width="80" />
|
||
<el-table-column prop="xueli" label="学历" width="80" />
|
||
<el-table-column prop="huji" label="户籍" width="110" />
|
||
<el-table-column prop="address" label="住址" width="160" show-overflow-tooltip />
|
||
<el-table-column prop="jinjiLianxiren" label="紧急联系人" width="110" />
|
||
<el-table-column prop="jinjiLianxirenPhone" label="紧急联系电话" width="130" />
|
||
<el-table-column label="离职日期" width="110">
|
||
<template #default="{ row }">{{ fmtDate(row.lizhiDate) }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="lizhiYuanyin" label="离职原因" width="110" />
|
||
<el-table-column prop="biyeYuanxiao" label="毕业院校" width="130" show-overflow-tooltip />
|
||
<el-table-column prop="zhuanye" label="专业" width="110" />
|
||
<el-table-column prop="code" label="工号" width="100" />
|
||
<el-table-column prop="memo" label="备注" min-width="140" show-overflow-tooltip />
|
||
<el-table-column label="操作" width="130" fixed="right">
|
||
<template #default="{ row }">
|
||
<el-button size="small" type="primary" link @click.stop="openEdit(row)">编辑</el-button>
|
||
<el-button size="small" type="danger" link @click.stop="handleDelete(row)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<el-pagination
|
||
:current-page="query.pageIndex"
|
||
:page-size="query.pageSize"
|
||
:total="total"
|
||
layout="total, prev, pager, next"
|
||
class="pager"
|
||
@current-change="handlePageChange"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 新增 / 编辑 -->
|
||
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="760px" top="6vh">
|
||
<el-tabs>
|
||
<el-tab-pane label="基本信息">
|
||
<el-form :model="form" label-width="110px">
|
||
<el-row :gutter="12">
|
||
<el-col :span="12">
|
||
<el-form-item label="部门" required>
|
||
<el-select v-model="form.depId" filterable style="width: 100%" @change="onDepSelectWorktype">
|
||
<template v-for="root in deptTree" :key="root.id">
|
||
<el-option :label="root.name" :value="root.id" />
|
||
<el-option v-for="c in root.children || []" :key="c.id" :label="` ${c.name}`" :value="c.id" />
|
||
<template v-for="c in root.children || []" :key="`g-${c.id}`">
|
||
<el-option v-for="g in c.children || []" :key="g.id" :label="` ${g.name}`" :value="g.id" />
|
||
</template>
|
||
</template>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="工种">
|
||
<el-select v-model="form.worktypeId" filterable clearable style="width: 100%" @change="onWorktypeSelect">
|
||
<el-option v-for="w in worktypesOfDep(form.depId)" :key="w.id" :label="w.workName" :value="w.id" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="姓名" required>
|
||
<el-input v-model="form.name" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="岗位">
|
||
<el-input v-model="form.gangwei" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="工号">
|
||
<el-input v-model="form.code" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="班次" required>
|
||
<el-select v-model="form.banci" style="width: 100%">
|
||
<el-option label="甲" :value="1" />
|
||
<el-option label="乙" :value="2" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="性别">
|
||
<el-select v-model="form.gender" filterable allow-create style="width: 100%">
|
||
<el-option v-for="v in registryOptions.gender" :key="v" :label="v" :value="v" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="电话">
|
||
<el-input v-model="form.phone" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="身份证">
|
||
<el-input v-model="form.idCard" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="出生日期">
|
||
<el-date-picker v-model="form.birthDate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="民族">
|
||
<el-select v-model="form.minzu" filterable allow-create style="width: 100%">
|
||
<el-option v-for="v in registryOptions.minzu" :key="v" :label="v" :value="v" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="婚姻状态">
|
||
<el-select v-model="form.hunyinStatus" filterable allow-create style="width: 100%">
|
||
<el-option v-for="v in registryOptions.hunyin" :key="v" :label="v" :value="v" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="户籍">
|
||
<el-input v-model="form.huji" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="24">
|
||
<el-form-item label="住址">
|
||
<el-input v-model="form.address" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="紧急联系人">
|
||
<el-input v-model="form.jinjiLianxiren" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="紧急联系电话">
|
||
<el-input v-model="form.jinjiLianxirenPhone" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="合同与社保">
|
||
<el-form :model="form" label-width="130px">
|
||
<el-row :gutter="12">
|
||
<el-col :span="12">
|
||
<el-form-item label="入职日期">
|
||
<el-date-picker v-model="form.ruzhiDate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="合同类型">
|
||
<el-select v-model="form.hetongType" filterable allow-create style="width: 100%">
|
||
<el-option v-for="v in registryOptions.hetongType" :key="v" :label="v" :value="v" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="合同开始日期">
|
||
<el-date-picker v-model="form.hetongStartDate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="合同结束日期">
|
||
<el-date-picker v-model="form.hetongEndDate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="社保参保日期">
|
||
<el-date-picker v-model="form.shebaoDate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="退休日期">
|
||
<el-date-picker v-model="form.tuixiuDate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="24">
|
||
<el-form-item label="社保参保情况">
|
||
<el-input v-model="form.shebaoDesc" type="textarea" :rows="2" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="银行卡号">
|
||
<el-input v-model="form.bankcardCode" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="教育与生产">
|
||
<el-form :model="form" label-width="130px">
|
||
<el-row :gutter="12">
|
||
<el-col :span="12">
|
||
<el-form-item label="学历">
|
||
<el-select v-model="form.xueli" filterable allow-create style="width: 100%">
|
||
<el-option v-for="v in registryOptions.xueli" :key="v" :label="v" :value="v" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="毕业院校">
|
||
<el-input v-model="form.biyeYuanxiao" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="专业">
|
||
<el-input v-model="form.zhuanye" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="姓名简拼">
|
||
<el-input v-model="form.namePinyin" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="工作组">
|
||
<el-input v-model="form.workGroup" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="顺序号">
|
||
<el-input-number v-model="form.sort" :min="0" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="24">
|
||
<el-form-item label="备注">
|
||
<el-input v-model="form.memo" type="textarea" :rows="2" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
|
||
<template #footer>
|
||
<el-button @click="dialogVisible = false">取消</el-button>
|
||
<el-button type="primary" @click="handleSave">确定</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 离职 -->
|
||
<el-dialog v-model="lizhiVisible" title="离职" width="420px">
|
||
<el-form :model="lizhiForm" label-width="90px">
|
||
<el-form-item label="离职日期">
|
||
<el-date-picker v-model="lizhiForm.lizhiDate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
|
||
</el-form-item>
|
||
<el-form-item label="离职原因" required>
|
||
<el-select v-model="lizhiForm.lizhiYuanyin" filterable allow-create style="width: 100%">
|
||
<el-option v-for="v in registryOptions.lizhiYuanyin" :key="v" :label="v" :value="v" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="lizhiVisible = false">取消</el-button>
|
||
<el-button type="primary" @click="saveLizhi">确定</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 调岗 / 重新入职 -->
|
||
<el-dialog v-model="tiaogangVisible" :title="tiaogangTitle" width="480px">
|
||
<el-form :model="tiaogangForm" label-width="90px">
|
||
<el-form-item :label="isRenew ? '入职日期' : '调岗日期'">
|
||
<el-date-picker v-model="tiaogangForm.date" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
|
||
</el-form-item>
|
||
<el-form-item label="部门" required>
|
||
<el-select v-model="tiaogangForm.depId" filterable style="width: 100%" @change="onTgDepChange">
|
||
<template v-for="root in deptTree" :key="root.id">
|
||
<el-option :label="root.name" :value="root.id" />
|
||
<el-option v-for="c in root.children || []" :key="c.id" :label="` ${c.name}`" :value="c.id" />
|
||
</template>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="工种">
|
||
<el-select v-model="tiaogangForm.worktypeId" filterable clearable style="width: 100%" @change="onTgWorktypeChange">
|
||
<el-option v-for="w in worktypesOfDep(tiaogangForm.depId)" :key="w.id" :label="w.workName" :value="w.id" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="岗位">
|
||
<el-input v-model="tiaogangForm.gangweiName" />
|
||
</el-form-item>
|
||
<el-form-item label="班次">
|
||
<el-select v-model="tiaogangForm.banci" style="width: 100%">
|
||
<el-option label="甲" :value="1" />
|
||
<el-option label="乙" :value="2" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="tiaogangVisible = false">取消</el-button>
|
||
<el-button type="primary" @click="saveTiaogang">确定</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 生产信息 -->
|
||
<el-dialog v-model="scVisible" title="修改生产信息" width="420px">
|
||
<el-form :model="scForm" label-width="90px">
|
||
<el-form-item label="工作组">
|
||
<el-input v-model="scForm.workGroup" />
|
||
</el-form-item>
|
||
<el-form-item label="顺序号">
|
||
<el-input-number v-model="scForm.sort" :min="0" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="scVisible = false">取消</el-button>
|
||
<el-button type="primary" @click="saveSc">确定</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 变更记录 -->
|
||
<el-dialog v-model="changeVisible" title="变更记录" width="620px">
|
||
<el-table :data="changeRecords" border stripe max-height="420">
|
||
<el-table-column label="日期" width="120">
|
||
<template #default="{ row }">{{ fmtDate(row.date) }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="changeStatusName" label="类型" width="100" />
|
||
<el-table-column prop="changeShuoming" label="变更说明" min-width="180" />
|
||
<el-table-column prop="changeYuanyin" label="原因" width="120" />
|
||
</el-table>
|
||
</el-dialog>
|
||
|
||
<!-- 调岗记录 -->
|
||
<el-dialog v-model="tiaogangRecordVisible" title="调岗记录" width="720px">
|
||
<el-table :data="tiaogangRecords" border stripe max-height="420">
|
||
<el-table-column label="调岗日期" width="120">
|
||
<template #default="{ row }">{{ fmtDate(row.tiaogangDate) }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="oldDepName" label="原部门" width="120" />
|
||
<el-table-column prop="oldWorktypeName" label="原工种" width="110" />
|
||
<el-table-column prop="newDepName" label="新部门" width="120" />
|
||
<el-table-column prop="newWorktypeName" label="新工种" width="110" />
|
||
</el-table>
|
||
</el-dialog>
|
||
</el-card>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.card-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
.employee-body {
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
.tree-panel {
|
||
width: 220px;
|
||
flex-shrink: 0;
|
||
border-right: 1px solid var(--el-border-color-lighter);
|
||
padding-right: 8px;
|
||
max-height: 640px;
|
||
overflow: auto;
|
||
}
|
||
.tree-title {
|
||
font-weight: 600;
|
||
margin-bottom: 8px;
|
||
}
|
||
.table-panel {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
.search-bar {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
align-items: center;
|
||
margin-bottom: 12px;
|
||
}
|
||
.tip {
|
||
color: var(--el-text-color-secondary);
|
||
font-size: 12px;
|
||
}
|
||
.pager {
|
||
margin-top: 12px;
|
||
justify-content: flex-end;
|
||
}
|
||
</style>
|