From c357ad173c8b89356b1443bb5c50edcfdbee9bba Mon Sep 17 00:00:00 2001 From: "1147680382@qq.com" Date: Wed, 21 May 2025 14:55:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=88=86=E7=BB=84=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/modules/index.js | 33 ++ src/views/groupManagement/index.vue | 769 +++++++++++++++++++++++----- 2 files changed, 682 insertions(+), 120 deletions(-) diff --git a/src/api/modules/index.js b/src/api/modules/index.js index 16afd82..a5494ea 100644 --- a/src/api/modules/index.js +++ b/src/api/modules/index.js @@ -79,3 +79,36 @@ export function obtainUserInformationApi(groupId, userid) { method: "get", }); } + +// 获取用户工资配置 +export function getUserSalaryConfig(userid) { + return service({ + url: `/user/salary/config/${userid}`, + method: "get", + }); +} + +// 更新用户工资配置 +export function updateUserSalaryConfig(data) { + return service({ + url: "/user/salary/config", + method: "put", + data, + }); +} + +// 添加用户到组 +export function addUserToGroup(groupId, userid, role) { + return service({ + url: `/group/${groupId}/user/${userid}/role/${role}`, + method: 'post' + }); +} + +// 同步钉钉成员 +export function syncUsers() { + return service({ + url: "/user/sync", + method: "post" + }); +} diff --git a/src/views/groupManagement/index.vue b/src/views/groupManagement/index.vue index 9880ad3..ee6b805 100644 --- a/src/views/groupManagement/index.vue +++ b/src/views/groupManagement/index.vue @@ -5,30 +5,46 @@ - - - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + 同步钉钉成员 + +
+
+ + + +
+
+ + + + + + + + + +
@@ -155,33 +320,98 @@ import { addGroup, updateGroup, deleteGroup, - getNonGroupUsers, updateGroupUserRole, getGroupUsers, deleteGroupUser, + getUserSalaryConfig, + updateUserSalaryConfig, + getNonGroupUsers, + addUserToGroup, + syncUsers, } from "../../api/modules/index"; +import { Refresh, Search, Plus } from '@element-plus/icons-vue'; // 组别列表数据 const groupList = ref([]); const currentGroup = ref(null); +const groupSearchKeyword = ref(''); +const originalGroupList = ref([]); // 用于存储原始数据 // 对话框相关 const dialogVisible = ref(false); const dialogType = ref("add"); // add 或 edit +const groupFormRef = ref(null); const groupForm = ref({ groupId: null, groupName: "", }); // 成员管理相关 -const nonGroupUsers = ref([]); const groupUsers = ref([]); +const memberSearchKeyword = ref(''); +const originalGroupUsers = ref([]); // 用于存储原始数据 + +// 工资配置相关 +const salaryDialogVisible = ref(false); +const salaryFormRef = ref(null); +const salaryForm = ref({ + userid: '', + baseSalary: 0, + managerAllowance: 0, + transportationAllowance: 0, + otherAllowance: 0, + senioritySalary: 0, + annualPerformanceBonus: 0, + monthlyPerformanceBonus: 0, + otherBonus: 0 +}); + +// 添加成员相关 +const addUserDialogVisible = ref(false); +const nonGroupUsers = ref([]); +const searchKeyword = ref(''); + +// 表单校验规则 +const salaryRules = { + baseSalary: [ + { required: true, message: '请输入基本工资', trigger: 'blur' } + ], + managerAllowance: [ + { required: true, message: '请输入总经理补贴', trigger: 'blur' } + ], + transportationAllowance: [ + { required: true, message: '请输入交通补贴', trigger: 'blur' } + ], + otherAllowance: [ + { required: true, message: '请输入其他补贴', trigger: 'blur' } + ], + senioritySalary: [ + { required: true, message: '请输入工龄工资', trigger: 'blur' } + ], + annualPerformanceBonus: [ + { required: true, message: '请输入年度绩效奖金', trigger: 'blur' } + ], + monthlyPerformanceBonus: [ + { required: true, message: '请输入月度绩效奖金', trigger: 'blur' } + ], + otherBonus: [ + { required: true, message: '请输入其他奖金', trigger: 'blur' } + ] +}; + +// 组别表单校验规则 +const groupRules = { + groupName: [ + { required: true, message: '请输入组别名称', trigger: 'blur' } + ] +}; // 获取组别列表 const fetchGroupList = async () => { try { const res = await getGroupList(); if (res.errcode === 0) { + originalGroupList.value = res.result; groupList.value = res.result; if (groupList.value && groupList.value.length) { handleGroupSelect(groupList.value[0]); @@ -192,25 +422,21 @@ const fetchGroupList = async () => { } }; +// 搜索组别 +const handleGroupSearch = () => { + if (!groupSearchKeyword.value) { + groupList.value = originalGroupList.value; + return; + } + groupList.value = originalGroupList.value.filter(group => + group.groupName.toLowerCase().includes(groupSearchKeyword.value.toLowerCase()) + ); +}; + // 选择组别 const handleGroupSelect = async (row) => { currentGroup.value = row; - await Promise.all([fetchNonGroupUsers(), fetchGroupUsers(row.groupId)]); -}; - -// 获取未分组人员列表 -const fetchNonGroupUsers = async () => { - try { - const res = await getNonGroupUsers(); - if (res.errcode === 0) { - nonGroupUsers.value = res.result.map((user) => ({ - ...user, - selectedRole: 0, - })); - } - } catch (error) { - ElMessage.error("获取未分组人员列表失败"); - } + await fetchGroupUsers(row.groupId); }; // 获取组内人员列表 @@ -218,6 +444,7 @@ const fetchGroupUsers = async (groupId) => { try { const res = await getGroupUsers(groupId); if (res.errcode === 0) { + originalGroupUsers.value = res.result; groupUsers.value = res.result; } } catch (error) { @@ -225,24 +452,15 @@ const fetchGroupUsers = async (groupId) => { } }; -// 添加成员 -const handleAddUser = async (user) => { - try { - const res = await updateGroupUserRole( - currentGroup.value.groupId, - user.userid, - user.selectedRole - ); - if (res.errcode === 0) { - ElMessage.success("添加成功"); - await Promise.all([ - fetchNonGroupUsers(), - fetchGroupUsers(currentGroup.value.groupId), - ]); - } - } catch (error) { - ElMessage.error("添加失败"); +// 搜索成员 +const handleMemberSearch = () => { + if (!memberSearchKeyword.value) { + groupUsers.value = originalGroupUsers.value; + return; } + groupUsers.value = originalGroupUsers.value.filter(user => + user.name.toLowerCase().includes(memberSearchKeyword.value.toLowerCase()) + ); }; // 修改成员角色 @@ -267,10 +485,7 @@ const handleRemoveUser = async (user) => { const res = await deleteGroupUser(currentGroup.value.groupId, user.userid); if (res.errcode === 0) { ElMessage.success("移除成功"); - await Promise.all([ - fetchNonGroupUsers(), - fetchGroupUsers(currentGroup.value.groupId), - ]); + fetchGroupUsers(currentGroup.value.groupId); } } catch (error) { ElMessage.error("移除失败"); @@ -321,35 +536,163 @@ const handleDelete = (row) => { // 提交表单 const handleSubmit = async () => { - if (!groupForm.value.groupName) { - ElMessage.warning("请输入组别名称"); - return; - } - - try { - if (dialogType.value === "add") { - const res = await addGroup({ groupName: groupForm.value.groupName }); - if (res.errcode === 0) { - ElMessage.success("添加成功"); - dialogVisible.value = false; - fetchGroupList(); - } - } else { - const res = await updateGroup({ - groupId: groupForm.value.groupId, - groupName: groupForm.value.groupName, - }); - if (res.errcode === 0) { - ElMessage.success("修改成功"); - dialogVisible.value = false; - fetchGroupList(); + if (!groupFormRef.value) return; + + await groupFormRef.value.validate(async (valid) => { + if (valid) { + try { + if (dialogType.value === "add") { + const res = await addGroup({ groupName: groupForm.value.groupName }); + if (res.errcode === 0) { + ElMessage.success("添加成功"); + dialogVisible.value = false; + fetchGroupList(); + } + } else { + const res = await updateGroup({ + groupId: groupForm.value.groupId, + groupName: groupForm.value.groupName, + }); + if (res.errcode === 0) { + ElMessage.success("修改成功"); + dialogVisible.value = false; + fetchGroupList(); + } + } + } catch (error) { + ElMessage.error(dialogType.value === "add" ? "添加失败" : "修改失败"); } } + }); +}; + +// 查看工资配置 +const handleViewSalary = async (user) => { + try { + const res = await getUserSalaryConfig(user.userid); + if (res.errcode === 0) { + const result = res.result; + salaryForm.value = { + userid: user.userid, + baseSalary: result.baseSalary, + managerAllowance: result.managerAllowance, + transportationAllowance: result.transportationAllowance, + otherAllowance: result.otherAllowance, + senioritySalary: result.senioritySalary, + annualPerformanceBonus: result.annualPerformanceBonus, + monthlyPerformanceBonus: result.monthlyPerformanceBonus, + otherBonus: result.otherBonus + }; + salaryDialogVisible.value = true; + } else { + ElMessage.error('获取工资配置失败'); + } } catch (error) { - ElMessage.error(dialogType.value === "add" ? "添加失败" : "修改失败"); + ElMessage.error('获取工资配置失败'); + console.error(error); } }; +// 提交工资配置 +const handleSalarySubmit = async () => { + if (!salaryFormRef.value) return; + + await salaryFormRef.value.validate(async (valid) => { + if (valid) { + try { + const data = { + userid: salaryForm.value.userid, + baseSalary: salaryForm.value.baseSalary, + managerAllowance: salaryForm.value.managerAllowance, + transportationAllowance: salaryForm.value.transportationAllowance, + otherAllowance: salaryForm.value.otherAllowance, + senioritySalary: salaryForm.value.senioritySalary, + annualPerformanceBonus: salaryForm.value.annualPerformanceBonus, + monthlyPerformanceBonus: salaryForm.value.monthlyPerformanceBonus, + otherBonus: salaryForm.value.otherBonus + }; + const res = await updateUserSalaryConfig(data); + if (res.errcode === 0) { + ElMessage.success('保存成功'); + salaryDialogVisible.value = false; + } else { + ElMessage.error('保存失败'); + } + } catch (error) { + ElMessage.error('保存失败'); + console.error(error); + } + } + }); +}; + +// 表格行样式 +const tableRowClassName = ({ row }) => { + if (currentGroup.value && row.groupId === currentGroup.value.groupId) { + return 'selected-row'; + } + return ''; +}; + +// 打开添加成员对话框 +const handleAddUser = async () => { + addUserDialogVisible.value = true; + await fetchNonGroupUsers(); +}; + +// 获取不在组内的用户列表 +const fetchNonGroupUsers = async () => { + try { + const res = await getNonGroupUsers(); + if (res.errcode === 0) { + nonGroupUsers.value = res.result; + } + } catch (error) { + ElMessage.error("获取用户列表失败"); + } +}; + +// 添加用户到组 +const handleAddToGroup = async (user) => { + try { + const res = await addUserToGroup(currentGroup.value.groupId, user.userid, 0); // 默认角色为普通员工 + if (res.errcode === 0) { + ElMessage.success("添加成功"); + addUserDialogVisible.value = false; + fetchGroupUsers(currentGroup.value.groupId); + } + } catch (error) { + ElMessage.error("添加失败"); + } +}; + +// 同步钉钉成员 +const handleSyncUsers = async () => { + try { + const res = await syncUsers(); + if (res.errcode === 0) { + ElMessage.success("同步成功"); + await fetchNonGroupUsers(); // 重新获取未加入成员列表 + } else { + ElMessage.error("同步失败"); + } + } catch (error) { + ElMessage.error("同步失败"); + console.error(error); + } +}; + +// 搜索用户 +const handleSearch = () => { + if (!searchKeyword.value) { + fetchNonGroupUsers(); + return; + } + nonGroupUsers.value = nonGroupUsers.value.filter(user => + user.name.toLowerCase().includes(searchKeyword.value.toLowerCase()) + ); +}; + // 页面加载时获取组别列表 onMounted(() => { fetchGroupList(); @@ -362,21 +705,121 @@ onMounted(() => { gap: 20px; height: calc(100vh - 120px); padding: 20px; + background-color: var(--el-bg-color-page); .left-panel { width: 40%; min-width: 500px; + + :deep(.el-card) { + height: 100%; + display: flex; + flex-direction: column; + border-radius: 8px; + box-shadow: var(--el-box-shadow-light); + + .el-card__body { + flex: 1; + overflow: hidden; + padding: 0; + display: flex; + flex-direction: column; + } + } + + :deep(.el-table) { + flex: 1; + + .el-table__body-wrapper { + height: 100%; + } + + .el-table__cell { + padding-left: 20px; + padding-right: 20px; + } + + .el-table__header { + th { + background-color: var(--el-bg-color); + font-weight: 600; + color: var(--el-text-color-primary); + height: 48px; + } + } + + .el-table__row { + height: 56px; + } + } } .right-panel { flex: 1; min-width: 600px; + + :deep(.el-card) { + height: 100%; + display: flex; + flex-direction: column; + border-radius: 8px; + box-shadow: var(--el-box-shadow-light); + + .el-card__body { + flex: 1; + overflow: hidden; + padding: 0; + display: flex; + flex-direction: column; + } + } + + :deep(.el-table) { + flex: 1; + + .el-table__body-wrapper { + height: 100%; + } + + .el-table__cell { + padding-left: 20px; + padding-right: 20px; + } + + .el-table__header { + th { + background-color: var(--el-bg-color); + font-weight: 600; + color: var(--el-text-color-primary); + height: 48px; + } + } + + .el-table__row { + height: 56px; + } + } } .card-header { display: flex; justify-content: space-between; align-items: center; + padding: 16px 20px; + border-bottom: 1px solid var(--el-border-color-light); + background-color: var(--el-bg-color); + + .title { + font-size: 16px; + font-weight: 600; + color: var(--el-text-color-primary); + } + + .header-right { + display: flex; + align-items: center; + gap: 8px; + } } } @@ -384,17 +827,103 @@ onMounted(() => { .user-container { display: flex; gap: 20px; + height: 100%; + padding: 20px; } .user-section { flex: 1; - min-width: 0; // 防止flex子项溢出 - - h3 { - margin-bottom: 10px; - font-size: 16px; - font-weight: 500; - } + min-width: 0; } } + +:deep(.selected-row) { + background-color: #f1f1f1 !important; +} + +:deep(.el-table__row .current-row) { + background-color: #f1f1f1 !important; +} + +:deep(.el-table__row:hover) { + background-color: #f1f1f1 !important; +} + +:deep(.el-table) { + .el-button { + padding: 4px 8px; + } + + .el-select { + width: 120px; + } + + .el-avatar { + border: 1px solid var(--el-border-color-light); + } +} + +.dialog-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; + padding: 0 20px; + + .left { + display: flex; + gap: 8px; + } + + .right { + display: flex; + gap: 8px; + } +} + +:deep(.el-button) { + display: inline-flex; + align-items: center; + gap: 4px; + font-weight: 500; + + &.el-button--primary { + --el-button-hover-bg-color: var(--el-color-primary-light-3); + --el-button-hover-border-color: var(--el-color-primary-light-3); + } +} + +:deep(.el-input__wrapper) { + box-shadow: 0 0 0 1px var(--el-border-color) inset; + + &:hover { + box-shadow: 0 0 0 1px var(--el-border-color-hover) inset; + } + + &.is-focus { + box-shadow: 0 0 0 1px var(--el-color-primary) inset; + } +} + +:deep(.el-dialog__body) { + padding: 20px; +} + +:deep(.el-dialog__header) { + margin: 0; + padding: 20px; + border-bottom: 1px solid var(--el-border-color-light); + background-color: var(--el-bg-color); +} + +:deep(.el-dialog__footer) { + padding: 20px; + border-top: 1px solid var(--el-border-color-light); + background-color: var(--el-bg-color); +} + +:deep(.el-dialog) { + border-radius: 8px; + overflow: hidden; +}