diff --git a/web/src/main/java/com/example/web/controller/UserController.java b/web/src/main/java/com/example/web/controller/UserController.java index 14fe072..d7d965a 100644 --- a/web/src/main/java/com/example/web/controller/UserController.java +++ b/web/src/main/java/com/example/web/controller/UserController.java @@ -4,6 +4,7 @@ import com.example.web.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.util.List; import java.util.Map; @RestController @@ -91,4 +92,14 @@ public class UserController { public Map jianDaoYun(@RequestBody Map params) { return userService.jianDaoYun(params); } + + @GetMapping("/users/managers") + public List getManagersList() { + return userService.getManagersList(); + } + + @PostMapping("/users/assign") + public Map assignCustomers(@RequestBody Map params) { + return userService.assignCustomers(params); + } } diff --git a/web/src/main/java/com/example/web/mapper/ManagersMapper.java b/web/src/main/java/com/example/web/mapper/ManagersMapper.java new file mode 100644 index 0000000..41ab1db --- /dev/null +++ b/web/src/main/java/com/example/web/mapper/ManagersMapper.java @@ -0,0 +1,14 @@ +package com.example.web.mapper; + +import com.example.web.entity.Managers; +import com.example.web.annotation.DataSource; + +import java.util.List; + +public interface ManagersMapper { + @DataSource("primary") + List findAllManagers(); + + @DataSource("primary") + Managers findByManagerId(String managerId); +} \ No newline at end of file diff --git a/web/src/main/java/com/example/web/mapper/UsersManagementsMapper.java b/web/src/main/java/com/example/web/mapper/UsersManagementsMapper.java index a587a49..db50d9d 100644 --- a/web/src/main/java/com/example/web/mapper/UsersManagementsMapper.java +++ b/web/src/main/java/com/example/web/mapper/UsersManagementsMapper.java @@ -4,6 +4,7 @@ import com.example.web.entity.UsersManagements; import com.example.web.annotation.DataSource; import java.util.List; +import java.util.Map; public interface UsersManagementsMapper { @DataSource("wechat") @@ -11,4 +12,13 @@ public interface UsersManagementsMapper { @DataSource("wechat") List findByUserNameList(String userName); + + @DataSource("wechat") + UsersManagements findByUserId(String userId); + + @DataSource("wechat") + int insert(UsersManagements usersManagements); + + @DataSource("wechat") + int update(UsersManagements usersManagements); } diff --git a/web/src/main/java/com/example/web/service/UserService.java b/web/src/main/java/com/example/web/service/UserService.java index 8e07f4b..cfb5824 100644 --- a/web/src/main/java/com/example/web/service/UserService.java +++ b/web/src/main/java/com/example/web/service/UserService.java @@ -1,5 +1,6 @@ package com.example.web.service; +import com.example.web.entity.Managers; import com.example.web.entity.Users; import java.util.List; @@ -14,4 +15,6 @@ public interface UserService { Map jianDaoYun(Map params); List getPersonalUsers(int page, int size, String userName); List getPublicUsers(int page, int size); + List getManagersList(); + Map assignCustomers(Map params); } diff --git a/web/src/main/java/com/example/web/service/impl/UserServiceImpl.java b/web/src/main/java/com/example/web/service/impl/UserServiceImpl.java index d44aef1..1cb3a5c 100644 --- a/web/src/main/java/com/example/web/service/impl/UserServiceImpl.java +++ b/web/src/main/java/com/example/web/service/impl/UserServiceImpl.java @@ -1,7 +1,11 @@ package com.example.web.service.impl; +import com.example.web.entity.Managers; import com.example.web.entity.Users; +import com.example.web.entity.UsersManagements; +import com.example.web.mapper.ManagersMapper; import com.example.web.mapper.UsersMapper; +import com.example.web.mapper.UsersManagementsMapper; import com.example.web.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -16,6 +20,12 @@ public class UserServiceImpl implements UserService { @Autowired private UsersMapper usersMapper; + @Autowired + private ManagersMapper managersMapper; + + @Autowired + private UsersManagementsMapper usersManagementsMapper; + @Override public Map getUserList(Map requestParams) { Map result = new HashMap<>(); @@ -253,4 +263,84 @@ public class UserServiceImpl implements UserService { return result; } + + @Override + public List getManagersList() { + return managersMapper.findAllManagers(); + } + + @Override + public Map assignCustomers(Map params) { + Map result = new HashMap<>(); + + try { + List userIds = (List) params.get("userIds"); + String managerId = (String) params.get("managerId"); + String userName = (String) params.get("userName"); + + if (userIds == null || userIds.isEmpty() || managerId == null || userName == null) { + result.put("success", false); + result.put("message", "缺少必要参数"); + return result; + } + + // 1. 到managers表获取完整的负责人信息 + Managers selectedManager = managersMapper.findByManagerId(managerId); + + if (selectedManager == null) { + result.put("success", false); + result.put("message", "未找到负责人信息"); + return result; + } + + // 2. 遍历客户ID列表,为每个客户分配负责人 + for (String userId : userIds) { + // 3. 检查客户是否已经在usersmanagements表中有记录 + UsersManagements existingRecord = usersManagementsMapper.findByUserId(userId); + + if (existingRecord != null) { + // 4. 如果有记录,更新记录 + existingRecord.setManagerId(selectedManager.getManagerId()); + existingRecord.setUserName(selectedManager.getUserName()); + existingRecord.setManagercompany(selectedManager.getManagercompany()); + existingRecord.setManagerdepartment(selectedManager.getManagerdepartment()); + existingRecord.setOrganization(selectedManager.getOrganization()); + existingRecord.setRole(selectedManager.getRole()); + existingRecord.setRoot(selectedManager.getRoot()); + existingRecord.setAssistant(selectedManager.getAssistant()); + usersManagementsMapper.update(existingRecord); + } else { + // 5. 如果没有记录,插入新记录 + UsersManagements newRecord = new UsersManagements(); + newRecord.setUserId(userId); + newRecord.setManagerId(selectedManager.getManagerId()); + newRecord.setUserName(selectedManager.getUserName()); + newRecord.setManagercompany(selectedManager.getManagercompany()); + newRecord.setManagerdepartment(selectedManager.getManagerdepartment()); + newRecord.setOrganization(selectedManager.getOrganization()); + newRecord.setRole(selectedManager.getRole()); + newRecord.setRoot(selectedManager.getRoot()); + newRecord.setAssistant(selectedManager.getAssistant()); + usersManagementsMapper.insert(newRecord); + } + + // 不需要更新users表,因为我们已经通过UsersManagementsMapper更新了所有必要的字段 + // Map updateParams = new HashMap<>(); + // updateParams.put("userId", userId); + // updateParams.put("userName", selectedManager.getUserName()); + // usersMapper.updateUsersManagements(updateParams); + + // 不更新users表的sync_status,保持原始值 + // usersMapper.updateSyncStatus(updateParams); + } + + result.put("success", true); + result.put("message", "分配成功"); + } catch (Exception e) { + result.put("success", false); + result.put("message", "分配失败: " + e.getMessage()); + } + + return result; + } } diff --git a/web/src/main/resources/mapper/ManagersMapper.xml b/web/src/main/resources/mapper/ManagersMapper.xml new file mode 100644 index 0000000..6b00865 --- /dev/null +++ b/web/src/main/resources/mapper/ManagersMapper.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/web/src/main/resources/mapper/UsersManagementsMapper.xml b/web/src/main/resources/mapper/UsersManagementsMapper.xml index 0998cd4..348a1be 100644 --- a/web/src/main/resources/mapper/UsersManagementsMapper.xml +++ b/web/src/main/resources/mapper/UsersManagementsMapper.xml @@ -7,4 +7,24 @@ + + + INSERT INTO usermanagements (userId, managerId, managercompany, managerdepartment, organization, role, root, created_at, updated_at, userName, assistant) + VALUES (#{userId}, #{managerId}, #{managercompany}, #{managerdepartment}, #{organization}, #{role}, #{root}, NOW(), NOW(), #{userName}, #{assistant}) + + + UPDATE usermanagements SET + managerId = #{managerId}, + managercompany = #{managercompany}, + managerdepartment = #{managerdepartment}, + organization = #{organization}, + role = #{role}, + root = #{root}, + updated_at = NOW(), + userName = #{userName}, + assistant = #{assistant} + WHERE userId = #{userId} + \ No newline at end of file diff --git a/web/src/main/resources/static/index.html b/web/src/main/resources/static/index.html index 5d50fb9..6532af7 100644 --- a/web/src/main/resources/static/index.html +++ b/web/src/main/resources/static/index.html @@ -57,9 +57,18 @@ } .container { - max-width: 1200px; - margin: 30px auto; - padding: 0 30px; + width: 100%; + margin: 30px 0; + padding: 0 20px; + box-sizing: border-box; + } + + .data-section { + width: 100%; + } + + table { + width: 100%; } .user-details { @@ -217,84 +226,98 @@ width: 100%; border-collapse: collapse; font-size: 14px; - table-layout: fixed; + table-layout: auto; } + /* 复选框列 */ th:nth-child(1), td:nth-child(1) { - max-width: 120px; - width: 120px; + width: 40px; + text-align: center; } - th:nth-child(2), td:nth-child(2) { - max-width: 100px; - width: 100px; + /* 跟进内容列 */ + th:nth-child(6), td:nth-child(6) { + min-width: 200px; } - th:nth-child(3), td:nth-child(3) { - max-width: 80px; - width: 80px; + /* 操作列 */ + th:nth-child(9), td:nth-child(9) { + min-width: 100px; } - th:nth-child(4), td:nth-child(4) { - max-width: 130px; - width: 130px; + th { + background-color: #1890ff; + color: white; + padding: 14px 12px; + text-align: left; + font-weight: bold; + border-bottom: 2px solid #1890ff; + font-size: 14px; } - th:nth-child(5), td:nth-child(5) { - max-width: 100px; - width: 100px; + td { + padding: 12px; + border-bottom: 1px solid #e8e8e8; + color: #333; + word-wrap: break-word; + word-break: break-all; + font-size: 14px; } - th:nth-child(6), td:nth-child(6) { - max-width: 100px; - width: 100px; + tr:hover { + background-color: #f0f7ff; } - th:nth-child(7), td:nth-child(7) { - max-width: 100px; - width: 100px; + tr:nth-child(even) { + background-color: #fafafa; } - th:nth-child(8), td:nth-child(8) { - max-width: 120px; - width: 120px; + table { + border-collapse: collapse; + box-shadow: 0 2px 8px rgba(0,0,0,0.08); } - th { - background-color: #fafafa; - padding: 12px; - text-align: left; - font-weight: bold; - color: #333; - border-bottom: 1px solid #e8e8e8; + .data-section { + background: white; + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0,0,0,0.1); + overflow: hidden; } - td { - padding: 12px; - border-bottom: 1px solid #f0f0f0; - color: #666; - word-wrap: break-word; - word-break: break-all; + .section-content { + padding: 0; } - tr:hover { - background-color: #f5f5f5; + .filter-bar { + margin: 0; + padding: 15px 20px; + background-color: #fafafa; + border-bottom: 1px solid #e8e8e8; } .pagination { - margin-top: 20px; + margin: 0; + padding: 15px 20px; display: flex; justify-content: center; - gap: 5px; + gap: 8px; + background-color: #fafafa; + border-top: 1px solid #e8e8e8; } .pagination button { - padding: 6px 12px; + padding: 8px 14px; border: 1px solid #d9d9d9; background-color: white; border-radius: 4px; cursor: pointer; font-size: 14px; + transition: all 0.3s ease; + } + + .pagination button:hover:not(:disabled) { + border-color: #1890ff; + color: #1890ff; } .pagination button.active { @@ -306,6 +329,7 @@ .pagination button:disabled { color: #ccc; cursor: not-allowed; + border-color: #e8e8e8; } .empty-state { @@ -444,6 +468,7 @@ + 昵称 手机号 类型 @@ -475,6 +501,7 @@
+ 昵称 手机号 类型 @@ -513,6 +541,8 @@ var publicPageSize = 10; var personalFilter = 'all'; // all, followed, unfollowed + var managersList = []; + function init() { var savedUserInfo = localStorage.getItem('userInfo'); if (!savedUserInfo) { @@ -521,6 +551,7 @@ } userInfo = JSON.parse(savedUserInfo); displayUserInfo(); + loadManagers(); loadPersonalData(); } @@ -537,6 +568,17 @@ document.getElementById('managerDepartment').innerHTML = personnel ? personnel.managerdepartment : '-'; document.getElementById('organization').innerHTML = personnel ? personnel.organization : '-'; document.getElementById('role').innerHTML = usersManagements ? usersManagements.role : '-'; + + // 只有管理员显示分配按钮 + var isAdmin = loginInfo.projectName === '管理员'; + var assignButton = document.getElementById('assignButton'); + var publicAssignButton = document.getElementById('publicAssignButton'); + if (assignButton) { + assignButton.style.display = isAdmin ? 'inline-block' : 'none'; + } + if (publicAssignButton) { + publicAssignButton.style.display = isAdmin ? 'inline-block' : 'none'; + } } function switchTab(tabName, button) { @@ -736,10 +778,50 @@
`; + // 分配弹窗HTML + var assignModalHTML = ` + + `; + + // 提示弹窗HTML + var alertModalHTML = ` + + `; + // 添加弹窗到页面 document.body.insertAdjacentHTML('beforeend', followupModalHTML); document.body.insertAdjacentHTML('beforeend', returnModalHTML); document.body.insertAdjacentHTML('beforeend', jianDaoYunModalHTML); + document.body.insertAdjacentHTML('beforeend', assignModalHTML); + document.body.insertAdjacentHTML('beforeend', alertModalHTML); function formatDateTime(dateTimeString) { if (!dateTimeString) return '-'; @@ -788,14 +870,23 @@ var personalEmpty = document.getElementById('personalEmpty'); var managerHeader = document.getElementById('managerHeader'); var personalPagination = document.getElementById('personalPagination'); + var selectAllPersonal = document.getElementById('selectAllPersonal'); personalBody.innerHTML = ''; - // 检查用户角色,只对管理员显示负责人列 + // 检查用户角色,只对管理员显示负责人列和复选框列 var userRole = userInfo.loginInfo.projectName; - if (userRole === '管理员') { + var isAdmin = userRole === '管理员'; + + if (isAdmin) { managerHeader.style.display = 'table-cell'; + if (selectAllPersonal) { + selectAllPersonal.style.display = 'block'; + } } else { managerHeader.style.display = 'none'; + if (selectAllPersonal) { + selectAllPersonal.style.display = 'none'; + } } console.log('后端返回的数据量:', data.users ? data.users.length : 0); @@ -844,10 +935,14 @@ jianDaoYunButton = ''; } else { // 未写入简道云,显示正常按钮 - jianDaoYunButton = ''; + jianDaoYunButton = ''; } + // 只有管理员显示复选框 + var checkboxCell = isAdmin ? '' : ''; + var row = '' + + checkboxCell + '' + (user.nickName || '-') + '' + '' + (user.phoneNumber || '-') + '' + '' + mapUserType(user.type) + '' + @@ -878,15 +973,24 @@ var publicBody = document.getElementById('publicBody'); var publicEmpty = document.getElementById('publicEmpty'); var publicManagerHeader = document.getElementById('publicManagerHeader'); - var publicPagination = document.getElementById('publicPagination'); // New: Get pagination element + var publicPagination = document.getElementById('publicPagination'); + var selectAllPublic = document.getElementById('selectAllPublic'); publicBody.innerHTML = ''; - // 检查用户角色,只对管理员显示负责人列 + // 检查用户角色,只对管理员显示负责人列和复选框列 var userRole = userInfo.loginInfo.projectName; - if (userRole === '管理员') { + var isAdmin = userRole === '管理员'; + + if (isAdmin) { publicManagerHeader.style.display = 'table-cell'; + if (selectAllPublic) { + selectAllPublic.style.display = 'block'; + } } else { publicManagerHeader.style.display = 'none'; + if (selectAllPublic) { + selectAllPublic.style.display = 'none'; + } } if (data.users && data.users.length > 0) { @@ -909,6 +1013,7 @@ } var row = '' + + '' + '' + (user.nickName || '-') + '' + '' + (user.phoneNumber || '-') + '' + '' + mapUserType(user.type) + '' + @@ -956,12 +1061,12 @@ if (xhr.readyState == 4 && xhr.status == 200) { var data = JSON.parse(xhr.responseText); if (data.success) { - alert('认领成功'); + showAlert('认领成功'); // 重新加载数据 loadPublicData(); loadPersonalData(); } else { - alert('认领失败: ' + data.message); + showAlert('认领失败: ' + data.message); } } }; @@ -986,7 +1091,7 @@ var usersManagements = userInfo.usersManagements; if (!content) { - alert('请填写跟进内容'); + showAlert('请填写跟进内容'); return; } @@ -1009,7 +1114,7 @@ if (xhr.readyState == 4 && xhr.status == 200) { var data = JSON.parse(xhr.responseText); if (data.success) { - alert('跟进成功'); + showAlert('跟进成功'); closeFollowupModal(); // 重新加载数据 if (document.getElementById('personal').classList.contains('active')) { @@ -1018,7 +1123,7 @@ loadPublicData(); } } else { - alert('跟进失败: ' + data.message); + showAlert('跟进失败: ' + data.message); } } }; @@ -1037,7 +1142,12 @@ document.getElementById('returnModal').style.display = 'none'; } - function openJianDaoYunModal(userId, userName) { + function openJianDaoYunModal(userId, userName, followup) { + // 检查是否已跟进 + if (!followup || followup === '-') { + showAlert('需要先跟进才能点击领入简道云'); + return; + } document.getElementById('jianDaoYunUserId').value = userId; document.getElementById('jianDaoYunModal').style.display = 'block'; } @@ -1046,6 +1156,15 @@ document.getElementById('jianDaoYunModal').style.display = 'none'; } + function showAlert(message) { + document.getElementById('alertMessage').textContent = message; + document.getElementById('alertModal').style.display = 'block'; + } + + function closeAlertModal() { + document.getElementById('alertModal').style.display = 'none'; + } + function saveJianDaoYun() { var userId = document.getElementById('jianDaoYunUserId').value; @@ -1062,12 +1181,12 @@ if (xhr.readyState == 4 && xhr.status == 200) { var data = JSON.parse(xhr.responseText); if (data.success) { - alert('操作成功'); + showAlert('操作成功'); closeJianDaoYunModal(); // 重新加载数据 loadPersonalData(); } else { - alert('操作失败: ' + data.message); + showAlert('操作失败: ' + data.message); } } }; @@ -1092,13 +1211,13 @@ if (xhr.readyState == 4 && xhr.status == 200) { var data = JSON.parse(xhr.responseText); if (data.success) { - alert('归还成功'); + showAlert('归还成功'); closeReturnModal(); // 重新加载数据 loadPersonalData(); loadPublicData(); } else { - alert('归还失败: ' + data.message); + showAlert('归还失败: ' + data.message); } } }; @@ -1200,6 +1319,111 @@ window.location.href = 'login.html'; } + function selectAll(tableType) { + var checkboxes = document.querySelectorAll('.userCheckbox'); + var selectAllCheckbox = document.getElementById('selectAll' + tableType.charAt(0).toUpperCase() + tableType.slice(1)); + var checked = selectAllCheckbox.checked; + + checkboxes.forEach(function(checkbox) { + checkbox.checked = checked; + }); + } + + function loadManagers() { + var url = '/KH/api/users/managers'; + + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4 && xhr.status == 200) { + managersList = JSON.parse(xhr.responseText); + populateManagerSelect(); + } else if (xhr.readyState == 4) { + console.error('加载负责人列表失败:', xhr.status, xhr.statusText); + } + }; + xhr.send(); + } + + function populateManagerSelect() { + var select = document.getElementById('managerSelect'); + select.innerHTML = ''; + + managersList.forEach(function(manager) { + var option = document.createElement('option'); + option.value = manager.managerId; + option.textContent = manager.userName; + option.setAttribute('data-managercompany', manager.managercompany); + option.setAttribute('data-managerdepartment', manager.managerdepartment); + option.setAttribute('data-organization', manager.organization); + option.setAttribute('data-role', manager.role); + option.setAttribute('data-root', manager.root); + option.setAttribute('data-assistant', manager.assistant); + select.appendChild(option); + }); + } + + function openAssignModal() { + var checkboxes = document.querySelectorAll('.userCheckbox:checked'); + var selectedCount = checkboxes.length; + + if (selectedCount === 0) { + showAlert('请至少选择一个客户'); + return; + } + + document.getElementById('selectedCount').value = selectedCount; + document.getElementById('assignModal').style.display = 'block'; + } + + function closeAssignModal() { + document.getElementById('assignModal').style.display = 'none'; + } + + function saveAssign() { + var checkboxes = document.querySelectorAll('.userCheckbox:checked'); + var selectedUserIds = []; + + checkboxes.forEach(function(checkbox) { + selectedUserIds.push(checkbox.getAttribute('data-userid')); + }); + + var managerSelect = document.getElementById('managerSelect'); + var selectedOption = managerSelect.options[managerSelect.selectedIndex]; + + if (!selectedOption || selectedOption.value === '') { + showAlert('请选择负责人'); + return; + } + + var params = { + userIds: selectedUserIds, + managerId: selectedOption.value, + userName: selectedOption.textContent + }; + + var url = '/KH/api/users/assign'; + + var xhr = new XMLHttpRequest(); + xhr.open('POST', url, true); + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4 && xhr.status == 200) { + var data = JSON.parse(xhr.responseText); + if (data.success) { + showAlert('分配成功'); + closeAssignModal(); + // 重新加载数据 + loadPersonalData(); + loadPublicData(); + } else { + showAlert('分配失败: ' + data.message); + } + } + }; + xhr.send(JSON.stringify(params)); + } + window.onload = init;