From 9ece2fa04981cc26ea951e4bd44320cfb8251466 Mon Sep 17 00:00:00 2001 From: Trae AI Date: Mon, 26 Jan 2026 17:34:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=94=B3=E8=AF=B7=E5=92=8C=E5=AE=A1=E6=89=B9=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8D=E5=95=86=E5=93=81=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E5=92=8C=E5=88=86=E9=85=8D=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/web/config/SecurityConfig.java | 2 +- .../web/controller/UserController.java | 32 + .../com/example/web/entity/CustomerApply.java | 33 + .../java/com/example/web/entity/Product.java | 45 + .../com/example/web/entity/UserTrace.java | 21 + .../web/mapper/CustomerApplyMapper.java | 54 ++ .../example/web/mapper/PersonnelMapper.java | 5 + .../com/example/web/mapper/ProductMapper.java | 31 + .../example/web/mapper/UserTraceMapper.java | 12 + .../com/example/web/mapper/UsersMapper.java | 9 + .../web/service/CustomerApplyService.java | 41 + .../com/example/web/service/UserService.java | 12 + .../impl/CustomerApplyServiceImpl.java | 245 +++++ .../web/service/impl/UserServiceImpl.java | 183 +++- .../resources/mapper/CustomerApplyMapper.xml | 44 + .../main/resources/mapper/PersonnelMapper.xml | 4 + .../main/resources/mapper/ProductMapper.xml | 50 + .../main/resources/mapper/UserTraceMapper.xml | 13 + web/src/main/resources/mapper/UsersMapper.xml | 12 + web/src/main/resources/static/index.html | 884 ++++++++++++++++++ web/src/main/resources/static/login.html | 2 +- 21 files changed, 1710 insertions(+), 24 deletions(-) create mode 100644 web/src/main/java/com/example/web/entity/CustomerApply.java create mode 100644 web/src/main/java/com/example/web/entity/Product.java create mode 100644 web/src/main/java/com/example/web/entity/UserTrace.java create mode 100644 web/src/main/java/com/example/web/mapper/CustomerApplyMapper.java create mode 100644 web/src/main/java/com/example/web/mapper/ProductMapper.java create mode 100644 web/src/main/java/com/example/web/mapper/UserTraceMapper.java create mode 100644 web/src/main/java/com/example/web/service/CustomerApplyService.java create mode 100644 web/src/main/java/com/example/web/service/impl/CustomerApplyServiceImpl.java create mode 100644 web/src/main/resources/mapper/CustomerApplyMapper.xml create mode 100644 web/src/main/resources/mapper/ProductMapper.xml create mode 100644 web/src/main/resources/mapper/UserTraceMapper.xml diff --git a/web/src/main/java/com/example/web/config/SecurityConfig.java b/web/src/main/java/com/example/web/config/SecurityConfig.java index 652b4bc..e01a429 100644 --- a/web/src/main/java/com/example/web/config/SecurityConfig.java +++ b/web/src/main/java/com/example/web/config/SecurityConfig.java @@ -14,7 +14,7 @@ public class SecurityConfig { public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeRequests() - .requestMatchers("/login.html", "/index.html", "/api/login", "/api/users/**").permitAll() + .requestMatchers("/login.html", "/index.html", "/api/login", "/api/users/**", "/api/products/**").permitAll() .requestMatchers("/static/**").permitAll() .anyRequest().authenticated() .and() 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 a66c10c..3778380 100644 --- a/web/src/main/java/com/example/web/controller/UserController.java +++ b/web/src/main/java/com/example/web/controller/UserController.java @@ -103,8 +103,40 @@ public class UserController { return userService.assignCustomers(params); } + @PostMapping("/users/apply") + public Map applyCustomer(@RequestBody Map params) { + return userService.applyCustomer(params); + } + + @PostMapping("/users/approve") + public Map approveApply(@RequestBody Map params) { + return userService.approveApply(params); + } + + @GetMapping("/users/apply/list") + public List getApplyList( + @RequestParam(required = false) Integer status) { + return userService.getApplyList(status); + } + @PostMapping("/users/updateAllocationStatus") public Map updateAllocationStatus(@RequestBody List> params) { return userService.updateAllocationStatus(params); } + + // 商品相关API + @GetMapping("/products") + public Map getProducts( + @RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "10") int size, + @RequestParam String userName, + @RequestParam String userRole) { + return userService.getProducts(userName, userRole, page, size); + } + + @GetMapping("/products/detail") + public Map getProductDetail( + @RequestParam String productId) { + return userService.getProductDetail(productId); + } } diff --git a/web/src/main/java/com/example/web/entity/CustomerApply.java b/web/src/main/java/com/example/web/entity/CustomerApply.java new file mode 100644 index 0000000..06f1f3d --- /dev/null +++ b/web/src/main/java/com/example/web/entity/CustomerApply.java @@ -0,0 +1,33 @@ +package com.example.web.entity; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * 客户申请实体类 + * 用于存储业务员申请客户的记录 + */ +@Data +@NoArgsConstructor +public class CustomerApply { + private Integer id; // 自增主键 + private String user_id; // 客户ID + private String sales_id; // 业务员ID + private String sales_name; // 业务员姓名 + private Integer status; // 申请状态:0-申请中,1-通过,2-失败 + private LocalDateTime apply_time; // 申请时间 + private LocalDateTime approve_time; // 审批时间 + private String approve_by; // 审批人 + private String reason; // 申请理由/失败原因 + + public CustomerApply(String user_id, String sales_id, String sales_name, String reason) { + this.user_id = user_id; + this.sales_id = sales_id; + this.sales_name = sales_name; + this.status = 0; // 默认状态为申请中 + this.apply_time = LocalDateTime.now(); + this.reason = reason; + } +} diff --git a/web/src/main/java/com/example/web/entity/Product.java b/web/src/main/java/com/example/web/entity/Product.java new file mode 100644 index 0000000..91bb70a --- /dev/null +++ b/web/src/main/java/com/example/web/entity/Product.java @@ -0,0 +1,45 @@ +package com.example.web.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.sql.Timestamp; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Product { + private String id; + private String productId; + private String sellerId; + private String productName; + private String price; + private String costprice; + private String quantity; + private String grossWeight; + private String yolk; + private String specification; + private Timestamp created_at; + private Timestamp updated_at; + private String status; + private String region; + private String sourceType; + private String supplyStatus; + private String category; + private String producting; + private String description; + private Integer frequency; + private String contact_phone; + private String product_contact; + private String negotiateStatus; + private String fullRegion; + private Timestamp createdAt; + private Integer reservedCount; + private Integer reservedCountDisplay; + private Integer sales; + private Integer totalStock; + private Integer originalTotalStock; + private String displaySpecification; + private String displayYolk; +} diff --git a/web/src/main/java/com/example/web/entity/UserTrace.java b/web/src/main/java/com/example/web/entity/UserTrace.java new file mode 100644 index 0000000..a1c3a08 --- /dev/null +++ b/web/src/main/java/com/example/web/entity/UserTrace.java @@ -0,0 +1,21 @@ +package com.example.web.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.sql.Timestamp; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UserTrace { + private Integer id; + private String phoneNumber; + private String userId; + private Timestamp operationTime; + private String originalData; + private String nickName; + private String followup; + private String managerName; +} diff --git a/web/src/main/java/com/example/web/mapper/CustomerApplyMapper.java b/web/src/main/java/com/example/web/mapper/CustomerApplyMapper.java new file mode 100644 index 0000000..08f81be --- /dev/null +++ b/web/src/main/java/com/example/web/mapper/CustomerApplyMapper.java @@ -0,0 +1,54 @@ +package com.example.web.mapper; + +import com.example.web.entity.CustomerApply; + +import java.util.List; +import java.util.Map; + +/** + * 客户申请Mapper接口 + */ +public interface CustomerApplyMapper { + + /** + * 插入申请记录 + * @param apply 申请对象 + * @return 影响行数 + */ + int insert(CustomerApply apply); + + /** + * 更新申请状态 + * @param params 参数Map,包含id、status、approve_by、reason + * @return 影响行数 + */ + int updateStatus(Map params); + + /** + * 根据客户ID和业务员ID查询申请记录 + * @param user_id 客户ID + * @param sales_id 业务员ID + * @return 申请记录 + */ + CustomerApply findByUserIdAndSalesId(String user_id, String sales_id); + + /** + * 查询所有申请记录 + * @return 申请记录列表 + */ + List findAll(); + + /** + * 根据状态查询申请记录 + * @param status 状态 + * @return 申请记录列表 + */ + List findByStatus(int status); + + /** + * 根据id查询申请记录 + * @param id 申请记录id + * @return 申请记录 + */ + CustomerApply findById(Integer id); +} diff --git a/web/src/main/java/com/example/web/mapper/PersonnelMapper.java b/web/src/main/java/com/example/web/mapper/PersonnelMapper.java index 1a0ee9a..06e4b30 100644 --- a/web/src/main/java/com/example/web/mapper/PersonnelMapper.java +++ b/web/src/main/java/com/example/web/mapper/PersonnelMapper.java @@ -3,10 +3,15 @@ package com.example.web.mapper; import com.example.web.entity.Personnel; import com.example.web.annotation.DataSource; +import java.util.List; + public interface PersonnelMapper { @DataSource("primary") Personnel findByName(String name); @DataSource("primary") Personnel findByNameAndProjectName(String name, String projectName); + + @DataSource("primary") + List findAll(); } diff --git a/web/src/main/java/com/example/web/mapper/ProductMapper.java b/web/src/main/java/com/example/web/mapper/ProductMapper.java new file mode 100644 index 0000000..a92b5c7 --- /dev/null +++ b/web/src/main/java/com/example/web/mapper/ProductMapper.java @@ -0,0 +1,31 @@ +package com.example.web.mapper; + +import com.example.web.annotation.DataSource; +import com.example.web.entity.Product; + +import java.util.List; +import java.util.Map; + +public interface ProductMapper { + + @DataSource("wechat") + List getSellerProducts(Map params); + + @DataSource("wechat") + List getBuyerProducts(Map params); + + @DataSource("wechat") + List getAllProducts(Map params); + + @DataSource("wechat") + Integer getSellerProductsCount(List phoneNumbers); + + @DataSource("wechat") + Integer getBuyerProductsCount(List userIds); + + @DataSource("wechat") + Integer getAllProductsCount(); + + @DataSource("wechat") + Product getProductById(String productId); +} diff --git a/web/src/main/java/com/example/web/mapper/UserTraceMapper.java b/web/src/main/java/com/example/web/mapper/UserTraceMapper.java new file mode 100644 index 0000000..8662c49 --- /dev/null +++ b/web/src/main/java/com/example/web/mapper/UserTraceMapper.java @@ -0,0 +1,12 @@ +package com.example.web.mapper; + +import com.example.web.annotation.DataSource; +import com.example.web.entity.UserTrace; + +import java.util.List; + +public interface UserTraceMapper { + + @DataSource("wechat") + List getProductTraces(String productId); +} diff --git a/web/src/main/java/com/example/web/mapper/UsersMapper.java b/web/src/main/java/com/example/web/mapper/UsersMapper.java index 5fcfba8..9fd2c8b 100644 --- a/web/src/main/java/com/example/web/mapper/UsersMapper.java +++ b/web/src/main/java/com/example/web/mapper/UsersMapper.java @@ -66,4 +66,13 @@ public interface UsersMapper { @DataSource("wechat") int updateSyncStatusWithValue(Map params); + + @DataSource("wechat") + List findByUserName(String userName); + + @DataSource("wechat") + List findWechatUsersByPhone(String phoneNumber); + + @DataSource("wechat") + List findAllUsers(); } diff --git a/web/src/main/java/com/example/web/service/CustomerApplyService.java b/web/src/main/java/com/example/web/service/CustomerApplyService.java new file mode 100644 index 0000000..6b8b4ea --- /dev/null +++ b/web/src/main/java/com/example/web/service/CustomerApplyService.java @@ -0,0 +1,41 @@ +package com.example.web.service; + +import com.example.web.entity.CustomerApply; + +import java.util.List; +import java.util.Map; + +/** + * 客户申请服务接口 + */ +public interface CustomerApplyService { + + /** + * 提交申请 + * @param params 参数Map,包含userId、salesId、salesName、reason + * @return 结果Map,包含success和message + */ + Map submitApply(Map params); + + /** + * 审批申请 + * @param params 参数Map,包含id、status、approve_by、reason + * @return 结果Map,包含success和message + */ + Map approveApply(Map params); + + /** + * 获取申请列表 + * @param status 状态,null表示所有状态 + * @return 申请列表 + */ + List getApplyList(Integer status); + + /** + * 根据客户ID和业务员ID查询申请记录 + * @param userId 客户ID + * @param salesId 业务员ID + * @return 申请记录 + */ + CustomerApply getApplyByUserIdAndSalesId(String userId, String salesId); +} 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 dd30529..69487bd 100644 --- a/web/src/main/java/com/example/web/service/UserService.java +++ b/web/src/main/java/com/example/web/service/UserService.java @@ -1,7 +1,10 @@ package com.example.web.service; import com.example.web.entity.Managers; +import com.example.web.entity.Product; +import com.example.web.entity.UserTrace; import com.example.web.entity.Users; +import com.example.web.entity.CustomerApply; import java.util.List; import java.util.Map; @@ -20,4 +23,13 @@ public interface UserService { Map assignCustomers(Map params); Map updateAllocationStatus(List> params); + + // 商品相关方法 + Map getProducts(String userName, String userRole, int page, int size); + Map getProductDetail(String productId); + + // 申请相关方法 + Map applyCustomer(Map params); + Map approveApply(Map params); + List getApplyList(Integer status); } diff --git a/web/src/main/java/com/example/web/service/impl/CustomerApplyServiceImpl.java b/web/src/main/java/com/example/web/service/impl/CustomerApplyServiceImpl.java new file mode 100644 index 0000000..bba3b0e --- /dev/null +++ b/web/src/main/java/com/example/web/service/impl/CustomerApplyServiceImpl.java @@ -0,0 +1,245 @@ +package com.example.web.service.impl; + +import com.example.web.entity.CustomerApply; +import com.example.web.entity.Personnel; +import com.example.web.entity.Users; +import com.example.web.mapper.CustomerApplyMapper; +import com.example.web.mapper.PersonnelMapper; +import com.example.web.mapper.UsersMapper; +import com.example.web.mapper.UsersManagementsMapper; +import com.example.web.service.CustomerApplyService; +import com.example.web.config.DataSourceContextHolder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 客户申请服务实现类 + */ +@Service +public class CustomerApplyServiceImpl implements CustomerApplyService { + + @Autowired + private CustomerApplyMapper customerApplyMapper; + + @Autowired + private UsersManagementsMapper usersManagementsMapper; + + @Autowired + private PersonnelMapper personnelMapper; + + @Autowired + private UsersMapper usersMapper; + + @Override + public Map submitApply(Map params) { + Map result = new HashMap<>(); + + try { + // 切换到wechat数据源 + DataSourceContextHolder.setDataSource("wechat"); + + String userId = (String) params.get("userId"); + String salesId = (String) params.get("salesId"); + String salesName = (String) params.get("salesName"); + String reason = (String) params.get("reason"); + + if (userId == null || salesId == null || salesName == null) { + result.put("success", false); + result.put("message", "缺少必要参数"); + return result; + } + + // 检查是否已经有申请记录 + CustomerApply existingApply = customerApplyMapper.findByUserIdAndSalesId(userId, salesId); + if (existingApply != null && existingApply.getStatus() == 0) { + result.put("success", false); + result.put("message", "该客户已有申请记录,正在审批中"); + return result; + } + + // 创建新的申请记录 + CustomerApply apply = new CustomerApply(userId, salesId, salesName, reason); + int count = customerApplyMapper.insert(apply); + + if (count > 0) { + result.put("success", true); + result.put("message", "申请提交成功"); + } else { + result.put("success", false); + result.put("message", "申请提交失败"); + } + + } catch (Exception e) { + e.printStackTrace(); + result.put("success", false); + result.put("message", "系统异常: " + e.getMessage()); + } finally { + // 清除数据源上下文 + DataSourceContextHolder.clearDataSource(); + } + + return result; + } + + @Override + public Map approveApply(Map params) { + Map result = new HashMap<>(); + + try { + // 切换到wechat数据源 + DataSourceContextHolder.setDataSource("wechat"); + + Integer id = (Integer) params.get("id"); + Integer status = (Integer) params.get("status"); + String approveBy = (String) params.get("approve_by"); + String reason = (String) params.get("reason"); + + if (id == null || status == null || approveBy == null) { + result.put("success", false); + result.put("message", "缺少必要参数"); + return result; + } + + // 更新申请状态 + Map updateParams = new HashMap<>(); + updateParams.put("id", id); + updateParams.put("status", status); + updateParams.put("approve_by", approveBy); + updateParams.put("reason", reason); + updateParams.put("approve_time", LocalDateTime.now()); + + int count = customerApplyMapper.updateStatus(updateParams); + + if (count > 0) { + // 如果审批通过,需要更新客户的负责人信息 + if (status == 1) { + // 根据id查询完整的申请记录 + CustomerApply apply = customerApplyMapper.findById(id); + if (apply != null) { + // 实现更新客户负责人的逻辑 + com.example.web.entity.UsersManagements usersManagements = usersManagementsMapper.findByUserId(apply.getUser_id()); + + // 切换到primary数据源查询personnel表 + DataSourceContextHolder.setDataSource("primary"); + + // 从personnel表中查询业务员的详细信息 + Personnel personnel = null; + try { + // 只通过name查询业务员信息 + personnel = personnelMapper.findByName(apply.getSales_name()); + } catch (Exception e) { + e.printStackTrace(); + System.out.println("查询Personnel时发生错误: " + e.getMessage()); + } + + // 切换回wechat数据源 + DataSourceContextHolder.setDataSource("wechat"); + + if (usersManagements != null) { + // 更新现有记录 + usersManagements.setManagerId(apply.getSales_id()); + usersManagements.setUserName(apply.getSales_name()); + if (personnel != null) { + usersManagements.setManagercompany(personnel.getManagercompany()); + usersManagements.setManagerdepartment(personnel.getManagerdepartment()); + usersManagements.setOrganization(personnel.getOrganization()); + usersManagements.setRole(personnel.getProjectName()); // projectName与role同值 + } + usersManagements.setUpdated_at(LocalDateTime.now()); + usersManagementsMapper.update(usersManagements); + } else { + // 检查客户是否存在于users表中 + Users user = null; + try { + user = usersMapper.findByUserId(apply.getUser_id()); + } catch (Exception e) { + e.printStackTrace(); + } + + if (user != null) { + // 创建新记录 + com.example.web.entity.UsersManagements newUsersManagements = new com.example.web.entity.UsersManagements(); + newUsersManagements.setUserId(apply.getUser_id()); + newUsersManagements.setManagerId(apply.getSales_id()); + newUsersManagements.setUserName(apply.getSales_name()); + if (personnel != null) { + newUsersManagements.setManagercompany(personnel.getManagercompany()); + newUsersManagements.setManagerdepartment(personnel.getManagerdepartment()); + newUsersManagements.setOrganization(personnel.getOrganization()); + newUsersManagements.setRole(personnel.getProjectName()); // projectName与role同值 + } + newUsersManagements.setCreated_at(LocalDateTime.now()); + newUsersManagements.setUpdated_at(LocalDateTime.now()); + try { + usersManagementsMapper.insert(newUsersManagements); + } catch (Exception e) { + e.printStackTrace(); + System.out.println("创建UsersManagements记录时发生错误: " + e.getMessage()); + } + } else { + System.out.println("客户ID " + apply.getUser_id() + " 在users表中不存在,无法创建usermanagements记录"); + } + } + } + } + + result.put("success", true); + result.put("message", "审批操作成功"); + } else { + result.put("success", false); + result.put("message", "审批操作失败"); + } + + } catch (Exception e) { + e.printStackTrace(); + result.put("success", false); + result.put("message", "系统异常: " + e.getMessage()); + } finally { + // 清除数据源上下文 + DataSourceContextHolder.clearDataSource(); + } + + return result; + } + + @Override + public List getApplyList(Integer status) { + try { + // 切换到wechat数据源 + DataSourceContextHolder.setDataSource("wechat"); + + if (status == null) { + return customerApplyMapper.findAll(); + } else { + return customerApplyMapper.findByStatus(status); + } + } catch (Exception e) { + e.printStackTrace(); + return null; + } finally { + // 清除数据源上下文 + DataSourceContextHolder.clearDataSource(); + } + } + + @Override + public CustomerApply getApplyByUserIdAndSalesId(String userId, String salesId) { + try { + // 切换到wechat数据源 + DataSourceContextHolder.setDataSource("wechat"); + + return customerApplyMapper.findByUserIdAndSalesId(userId, salesId); + } catch (Exception e) { + e.printStackTrace(); + return null; + } finally { + // 清除数据源上下文 + DataSourceContextHolder.clearDataSource(); + } + } +} 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 a002279..8dc44a1 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 @@ -3,18 +3,26 @@ package com.example.web.service.impl; import com.example.web.entity.InformationTra; import com.example.web.entity.Managers; import com.example.web.entity.Personnel; +import com.example.web.entity.Product; +import com.example.web.entity.UserTrace; import com.example.web.entity.Users; import com.example.web.entity.UsersManagements; +import com.example.web.entity.CustomerApply; import com.example.web.mapper.PersonnelMapper; +import com.example.web.mapper.ProductMapper; +import com.example.web.mapper.UserTraceMapper; import com.example.web.mapper.UsersMapper; import com.example.web.mapper.ManagersMapper; import com.example.web.mapper.UsersManagementsMapper; import com.example.web.service.InformationTraService; import com.example.web.service.UserService; +import com.example.web.service.CustomerApplyService; +import com.example.web.config.DataSourceContextHolder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -37,6 +45,15 @@ public class UserServiceImpl implements UserService { @Autowired private InformationTraService informationTraService; + @Autowired + private CustomerApplyService customerApplyService; + + @Autowired + private ProductMapper productMapper; + + @Autowired + private UserTraceMapper userTraceMapper; + @Override public Map getUserList(Map requestParams) { Map result = new HashMap<>(); @@ -484,7 +501,7 @@ public class UserServiceImpl implements UserService { return result; } - // 1. 到managers表获取完整的负责人信息 + // 1. 到managers表获取完整的负责人信息 (使用primary数据源) Managers selectedManager = managersMapper.findByManagerId(managerId); if (selectedManager == null) { @@ -500,6 +517,9 @@ public class UserServiceImpl implements UserService { return result; } + // 切换到wechat数据源 + DataSourceContextHolder.setDataSource("wechat"); + // 2. 遍历客户ID列表,为每个客户分配负责人 for (String userId : userIds) { // 3. 检查客户是否已经在usersmanagements表中有记录 @@ -517,29 +537,28 @@ public class UserServiceImpl implements UserService { 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已经使用wechat数据源,这里不需要额外切换 + Users user = usersMapper.findByUserId(userId); + if (user != null) { + // 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); + } else { + System.out.println("用户ID " + userId + " 不存在于users表中,跳过分配"); + continue; + } } - // 不需要更新users表,因为我们已经通过UsersManagementsMapper更新了所有必要的字段 - // Map updateParams = new HashMap<>(); - // updateParams.put("userId", userId); - // updateParams.put("userName", selectedManager.getUserName()); - // usersMapper.updateUsersManagements(updateParams); - - // 不更新users表的sync_status,保持原始值 - // usersMapper.updateSyncStatus(updateParams); - // 记录分配操作 InformationTra tra = createInformationTra( managercompany, @@ -557,8 +576,12 @@ public class UserServiceImpl implements UserService { result.put("success", true); result.put("message", "分配成功"); } catch (Exception e) { + e.printStackTrace(); result.put("success", false); result.put("message", "分配失败: " + e.getMessage()); + } finally { + // 清除数据源上下文 + DataSourceContextHolder.clearDataSource(); } return result; @@ -639,4 +662,120 @@ public class UserServiceImpl implements UserService { return result; } + + @Override + public Map getProducts(String userName, String userRole, int page, int size) { + Map result = new HashMap<>(); + List products = new ArrayList<>(); + int total = 0; + + try { + int offset = (page - 1) * size; + + if ("管理员".equals(userRole)) { + // 管理员:查看所有商品 + Map params = new HashMap<>(); + params.put("pageSize", size); + params.put("offset", offset); + products = productMapper.getAllProducts(params); + total = productMapper.getAllProductsCount(); + } else if ("销售员".equals(userRole)) { + // 销售员:从personnel表获取电话号码,到products表查询contact_phone相同的 + Personnel personnel = personnelMapper.findByName(userName); + if (personnel != null && personnel.getPhoneNumber() != null) { + List phoneNumbers = new ArrayList<>(); + phoneNumbers.add(personnel.getPhoneNumber()); + Map params = new HashMap<>(); + params.put("list", phoneNumbers); + params.put("pageSize", size); + params.put("offset", offset); + products = productMapper.getSellerProducts(params); + total = productMapper.getSellerProductsCount(phoneNumbers); + } + } else if ("采购员".equals(userRole)) { + // 采购员:从users表获取phoneNumber相同的userId,到products表查询sellerId相同的 + List users = usersMapper.findByUserName(userName); + if (users != null && !users.isEmpty()) { + List userIds = new ArrayList<>(); + for (Users user : users) { + if (user.getPhoneNumber() != null) { + List wechatUsers = usersMapper.findWechatUsersByPhone(user.getPhoneNumber()); + for (Users wechatUser : wechatUsers) { + if (wechatUser.getUserId() != null) { + userIds.add(wechatUser.getUserId()); + } + } + } + } + if (!userIds.isEmpty()) { + Map params = new HashMap<>(); + params.put("list", userIds); + params.put("pageSize", size); + params.put("offset", offset); + products = productMapper.getBuyerProducts(params); + total = productMapper.getBuyerProductsCount(userIds); + } + } + } + + result.put("success", true); + result.put("products", products); + result.put("total", total); + result.put("page", page); + result.put("size", size); + result.put("pages", (total + size - 1) / size); + + } catch (Exception e) { + e.printStackTrace(); + result.put("success", false); + result.put("message", "获取商品列表失败: " + e.getMessage()); + } + + return result; + } + + @Override + public Map getProductDetail(String productId) { + Map result = new HashMap<>(); + + try { + // 获取商品详情 + Product product = productMapper.getProductById(productId); + if (product == null) { + result.put("success", false); + result.put("message", "商品不存在"); + return result; + } + + // 获取浏览记录 + List traces = userTraceMapper.getProductTraces(productId); + + result.put("success", true); + result.put("product", product); + result.put("traces", traces); + result.put("traceCount", traces.size()); + + } catch (Exception e) { + e.printStackTrace(); + result.put("success", false); + result.put("message", "获取商品详情失败: " + e.getMessage()); + } + + return result; + } + + @Override + public Map applyCustomer(Map params) { + return customerApplyService.submitApply(params); + } + + @Override + public Map approveApply(Map params) { + return customerApplyService.approveApply(params); + } + + @Override + public List getApplyList(Integer status) { + return customerApplyService.getApplyList(status); + } } diff --git a/web/src/main/resources/mapper/CustomerApplyMapper.xml b/web/src/main/resources/mapper/CustomerApplyMapper.xml new file mode 100644 index 0000000..bf173a1 --- /dev/null +++ b/web/src/main/resources/mapper/CustomerApplyMapper.xml @@ -0,0 +1,44 @@ + + + + + + + INSERT INTO customer_apply (user_id, sales_id, sales_name, status, apply_time, reason) + VALUES (#{user_id}, #{sales_id}, #{sales_name}, #{status}, #{apply_time}, #{reason}) + + + + UPDATE customer_apply + SET status = #{status}, + approve_time = NOW(), + approve_by = #{approve_by}, + reason = #{reason} + WHERE id = #{id} + + + + + + + + + + diff --git a/web/src/main/resources/mapper/PersonnelMapper.xml b/web/src/main/resources/mapper/PersonnelMapper.xml index 1b743a5..5422209 100644 --- a/web/src/main/resources/mapper/PersonnelMapper.xml +++ b/web/src/main/resources/mapper/PersonnelMapper.xml @@ -8,4 +8,8 @@ + + \ No newline at end of file diff --git a/web/src/main/resources/mapper/ProductMapper.xml b/web/src/main/resources/mapper/ProductMapper.xml new file mode 100644 index 0000000..c56508e --- /dev/null +++ b/web/src/main/resources/mapper/ProductMapper.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + diff --git a/web/src/main/resources/mapper/UserTraceMapper.xml b/web/src/main/resources/mapper/UserTraceMapper.xml new file mode 100644 index 0000000..30eb739 --- /dev/null +++ b/web/src/main/resources/mapper/UserTraceMapper.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/web/src/main/resources/mapper/UsersMapper.xml b/web/src/main/resources/mapper/UsersMapper.xml index ed19e07..307213e 100644 --- a/web/src/main/resources/mapper/UsersMapper.xml +++ b/web/src/main/resources/mapper/UsersMapper.xml @@ -269,4 +269,16 @@ + + + + + + \ 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 6fc0c43..66079c1 100644 --- a/web/src/main/resources/static/index.html +++ b/web/src/main/resources/static/index.html @@ -573,6 +573,7 @@ @@ -616,6 +617,7 @@
+
@@ -688,6 +690,35 @@ + +
+
+
+ +
+
+ +
+ + + + + + + + + + + + + +
商品名称价格库存地区创建时间操作
+
+
暂无货源数据
+ +
+
@@ -758,13 +789,18 @@ var userInfo = null; var personalPage = 1; var publicPage = 1; + var productsPage = 1; var personalPageSize = 10; var publicPageSize = 10; + var productsPageSize = 10; var personalFilter = 'all'; // all, followed, unfollowed var managersList = []; var allPersonalData = []; var allPublicData = []; + var allProducts = []; + var productsTotal = 0; + var productsTotalPages = 0; var isLoadingAllData = false; var currentManagerFilter = null; var currentFilterTable = 'personal'; @@ -819,6 +855,7 @@ var assignButton = document.getElementById('assignButton'); var publicAssignButton = document.getElementById('publicAssignButton'); var permissionButton = document.getElementById('permissionButton'); + var applyButton = document.getElementById('applyButton'); if (assignButton) { assignButton.style.display = isAdmin ? 'inline-block' : 'none'; } @@ -828,6 +865,9 @@ if (permissionButton) { permissionButton.style.display = isAdmin ? 'inline-block' : 'none'; } + if (applyButton) { + applyButton.style.display = isAdmin ? 'inline-block' : 'none'; + } } function switchTab(tabName, button) { @@ -873,6 +913,8 @@ loadPersonalData(); } else if (tabName === 'public') { loadPublicData(); + } else if (tabName === 'products') { + loadProducts(); } } catch (e) { console.error('加载数据失败:', e); @@ -1065,6 +1107,13 @@ jianDaoYunButton = ''; } + // 生成申请按钮,只对普通用户显示 + var applyButton = ''; + if (!isAdmin) { + applyButton = ' '; + + } + // 只有管理员显示复选框 var checkboxCell = isAdmin ? '' : ''; @@ -1855,6 +1904,589 @@ `; + // 加载货源数据 + function loadProducts() { + var userRole = userInfo.loginInfo.projectName; + var userName = userInfo.loginInfo.userName; + + var url = '/KH/api/products?userName=' + encodeURIComponent(userName) + '&userRole=' + encodeURIComponent(userRole) + '&page=' + productsPage + '&size=' + productsPageSize; + + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4 && xhr.status == 200) { + var data = JSON.parse(xhr.responseText); + if (data.success) { + allProducts = data.products || []; + productsTotal = data.total || 0; + productsTotalPages = data.pages || 0; + displayProducts(data.products); + // 确保使用当前的productsPageSize值渲染分页组件 + renderProductsPagination(productsPage, productsTotalPages, productsTotal); + } else { + showAlert('加载货源失败: ' + data.message); + } + } else if (xhr.readyState == 4) { + console.error('加载货源失败:', xhr.status, xhr.statusText); + showAlert('加载货源失败,请刷新页面重试'); + } + }; + xhr.send(); + } + + // 显示货源数据 + function displayProducts(products) { + var productsBody = document.getElementById('productsBody'); + var productsEmpty = document.getElementById('productsEmpty'); + var productsPagination = document.getElementById('productsPagination'); + productsBody.innerHTML = ''; + + if (products && products.length > 0) { + productsEmpty.style.display = 'none'; + productsPagination.style.display = 'flex'; + + for (var i = 0; i < products.length; i++) { + var product = products[i]; + var row = '' + + '' + (product.productName || '-') + '' + + '' + (product.price || '-') + '' + + '' + (product.quantity || '-') + '' + + '' + (product.fullRegion || product.region || '-') + '' + + '' + formatDateTime(product.created_at) + '' + + '' + + ''; + productsBody.innerHTML += row; + } + } else { + productsEmpty.style.display = 'block'; + productsPagination.style.display = 'none'; + } + } + + // 打开商品详情弹窗 + function openProductDetailModal(productId) { + var url = '/KH/api/products/detail?productId=' + encodeURIComponent(productId); + + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4 && xhr.status == 200) { + var data = JSON.parse(xhr.responseText); + if (data.success) { + showProductDetail(data.product, data.traces); + } else { + showAlert('获取商品详情失败: ' + data.message); + } + } else if (xhr.readyState == 4) { + console.error('获取商品详情失败:', xhr.status, xhr.statusText); + showAlert('获取商品详情失败,请重试'); + } + }; + xhr.send(); + } + + // 显示商品详情 + function showProductDetail(product, traces) { + // 对traces数据进行分组,按用户ID分组 + var groupedTraces = {}; + if (traces && traces.length > 0) { + traces.forEach(trace => { + var key = trace.userId || (trace.phoneNumber || trace.nickName); + if (!groupedTraces[key]) { + groupedTraces[key] = { + nickName: trace.nickName, + phoneNumber: trace.phoneNumber, + followup: trace.followup, + responseTime: trace.responseTime, + managerName: trace.managerName, + operationTimes: [] + }; + } + groupedTraces[key].operationTimes.push(trace.operationTime); + }); + } + + // 将分组后的数据转换为数组 + var mergedTraces = Object.values(groupedTraces); + + // 检查是否为管理员 + var isAdmin = userInfo && userInfo.loginInfo && userInfo.loginInfo.projectName === '管理员'; + + // 创建详情弹窗 + var modal = document.createElement('div'); + modal.style.cssText = ` + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0,0,0,0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; + `; + + var modalContent = document.createElement('div'); + modalContent.style.cssText = ` + background: white; + border-radius: 8px; + width: 90%; + max-width: 1400px; + max-height: 80%; + display: flex; + flex-direction: column; + box-shadow: 0 4px 12px rgba(0,0,0,0.15); + `; + + // 固定导航栏 + var navBar = ` +
+

商品详情

+ +
+ `; + + // 滚动内容区域 + var contentArea = document.createElement('div'); + contentArea.style.cssText = ` + padding: 20px; + overflow-y: auto; + flex: 1; + `; + + // 商品基本信息 + var productInfo = ` +
+

基本信息

+
+
商品名称: ${product.productName || '-'}
+
价格: ${product.price || '-'}
+
成本价: ${product.costprice || '-'}
+
库存: ${product.quantity || '-'}
+
地区: ${product.fullRegion || product.region || '-'}
+
分类: ${product.category || '-'}
+
来源类型: ${product.sourceType || '-'}
+
供应状态: ${product.supplyStatus || '-'}
+
规格: ${product.displaySpecification || product.specification || '-'}
+
蛋黄: ${product.displayYolk || product.yolk || '-'}
+
创建时间: ${formatDateTime(product.created_at)}
+
+
+
+

商品描述

+
+ ${product.description || '-'} +
+
+ `; + + // 浏览记录 + var traceInfo = ` +
+

浏览记录 (${traces ? traces.length : 0}次)

+ ${traces && traces.length > 0 ? ` + + + + + + + + + + + + + + ${mergedTraces.map(trace => ` + + + + + + + + + + `).join('')} + +
昵称手机号浏览时间跟进内容响应时间负责人操作
${trace.nickName || '-'}${trace.phoneNumber || '-'} + ${trace.operationTimes.map(time => formatDateTime(time)).join('
')} +
${trace.followup || '-'}${trace.responseTime || '-'}${trace.managerName || '-'} + ${!isAdmin ? `` : '-'} +
+ ` : '
暂无浏览记录
'} +
+ `; + + // 组装弹窗内容 + modalContent.innerHTML = navBar; + contentArea.innerHTML = productInfo + traceInfo; + modalContent.appendChild(contentArea); + modal.appendChild(modalContent); + document.body.appendChild(modal); + + // 加载申请状态并更新按钮 + loadApplyStatusForTraces(mergedTraces); + } + + // 加载浏览记录中客户的申请状态 + function loadApplyStatusForTraces(traces) { + if (!traces || traces.length === 0) return; + + // 获取所有客户ID + var userIds = traces.map(trace => trace.userId).filter(Boolean); + if (userIds.length === 0) return; + + var url = '/KH/api/users/apply/list'; + + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4 && xhr.status == 200) { + var applyList = JSON.parse(xhr.responseText); + if (applyList && applyList.length > 0) { + // 创建申请状态映射 + var applyStatusMap = {}; + applyList.forEach(apply => { + if (apply.user_id) { + applyStatusMap[apply.user_id] = apply.status; + } + }); + + // 更新按钮状态 + // 查找所有包含浏览记录的表格 + var tables = document.querySelectorAll('table'); + tables.forEach(table => { + // 检查表格是否包含浏览记录 + var header = table.querySelector('th'); + if (header && header.textContent === '昵称') { + var tbody = table.querySelector('tbody'); + if (tbody) { + var rows = tbody.querySelectorAll('tr'); + rows.forEach((row, index) => { + // 获取对应的trace + var trace = traces[index]; + if (trace && trace.userId) { + var status = applyStatusMap[trace.userId]; + var button = row.querySelector('button[onclick^="applyCustomer"]'); + if (button && status !== undefined) { + if (status === 0) { + // 申请中 + button.textContent = '申请中'; + button.disabled = true; + button.style.backgroundColor = '#faad14'; + } else if (status === 1) { + // 申请通过 + button.textContent = '申请通过'; + button.disabled = true; + button.style.backgroundColor = '#52c41a'; + } else if (status === 2) { + // 申请失败 + button.textContent = '申请失败'; + button.disabled = false; + button.style.backgroundColor = '#722ed1'; + } + } + } + }); + } + } + }); + } + } + }; + xhr.send(); + } + + // 显示提示信息 + function showAlert(message) { + var alert = document.createElement('div'); + alert.style.cssText = ` + position: fixed; + top: 20px; + right: 20px; + background-color: #ff4d4f; + color: white; + padding: 12px 20px; + border-radius: 4px; + box-shadow: 0 4px 12px rgba(0,0,0,0.15); + z-index: 1001; + animation: slideIn 0.3s ease; + `; + alert.textContent = message; + document.body.appendChild(alert); + + setTimeout(function() { + alert.style.animation = 'fadeOut 0.3s ease'; + setTimeout(function() { + alert.remove(); + }, 300); + }, 3000); + } + + // 渲染商品分页控件 + function renderProductsPagination(currentPage, totalPages, totalItems) { + // 更新全局变量,确保事件处理函数能获取到最新值 + productsTotalPages = totalPages; + productsTotal = totalItems; + var paginationContainer = document.getElementById('productsPagination'); + paginationContainer.innerHTML = ''; + paginationContainer.style.display = 'flex'; + paginationContainer.style.justifyContent = 'center'; + paginationContainer.style.alignItems = 'center'; + paginationContainer.style.gap = '8px'; + paginationContainer.style.flexWrap = 'wrap'; + + // 当总页数大于1时显示分页组件 + // 或者当用户选择的每页显示条数不等于默认值时也显示分页组件 + var defaultPageSize = 10; + if (totalPages <= 1 && totalItems <= productsPageSize && productsPageSize === defaultPageSize) { + paginationContainer.style.display = 'none'; + return; + } + + // 首页按钮 + var firstButton = document.createElement('button'); + firstButton.textContent = '首页'; + firstButton.disabled = currentPage === 1; + firstButton.onclick = function() { + if (productsPage > 1) { + productsPage = 1; + loadProducts(); + } + }; + paginationContainer.appendChild(firstButton); + + // 上一页按钮 + var prevButton = document.createElement('button'); + prevButton.textContent = '上一页'; + prevButton.disabled = currentPage === 1; + prevButton.onclick = function() { + if (productsPage > 1) { + productsPage = productsPage - 1; + loadProducts(); + } + }; + paginationContainer.appendChild(prevButton); + + // 页码信息显示 + var pageInfo = document.createElement('span'); + pageInfo.textContent = '第' + currentPage + '页,共' + totalPages + '页'; + pageInfo.style.margin = '0 10px'; + paginationContainer.appendChild(pageInfo); + + // 下一页按钮 + var nextButton = document.createElement('button'); + nextButton.textContent = '下一页'; + nextButton.disabled = currentPage === totalPages; + nextButton.onclick = function() { + if (productsPage < productsTotalPages) { + productsPage = productsPage + 1; + loadProducts(); + } + }; + paginationContainer.appendChild(nextButton); + + // 末页按钮 + var lastButton = document.createElement('button'); + lastButton.textContent = '末页'; + lastButton.disabled = currentPage === totalPages; + lastButton.onclick = function() { + if (productsPage < productsTotalPages) { + productsPage = productsTotalPages; + loadProducts(); + } + }; + paginationContainer.appendChild(lastButton); + + // 总数显示 + var totalInfo = document.createElement('span'); + totalInfo.textContent = '总数:' + totalItems + '条'; + totalInfo.style.margin = '0 10px'; + paginationContainer.appendChild(totalInfo); + + // 每页显示条数选择 + var pageSizeInfo = document.createElement('span'); + pageSizeInfo.textContent = '每页'; + paginationContainer.appendChild(pageSizeInfo); + + var pageSizeSelect = document.createElement('select'); + var pageSizes = [10, 20, 50, 100]; + for (var i = 0; i < pageSizes.length; i++) { + var option = document.createElement('option'); + option.value = pageSizes[i]; + option.textContent = pageSizes[i] + '条'; + pageSizeSelect.appendChild(option); + } + // 先添加option元素,然后再设置select元素的value属性 + pageSizeSelect.value = productsPageSize; + console.log('Setting pageSizeSelect value to:', productsPageSize); + // 使用闭包确保正确获取select元素 + (function(selectElement) { + selectElement.onchange = function() { + // 确保正确更新全局变量 + var selectedSize = parseInt(selectElement.value); + console.log('Selected page size:', selectedSize); + productsPageSize = selectedSize; + console.log('Updated productsPageSize to:', productsPageSize); + productsPage = 1; // 重置为第一页 + loadProducts(); + }; + })(pageSizeSelect); + paginationContainer.appendChild(pageSizeSelect); + + // 跳转功能 + var jumpInfo = document.createElement('span'); + jumpInfo.textContent = '跳转'; + jumpInfo.style.margin = '0 10px'; + paginationContainer.appendChild(jumpInfo); + + var jumpInput = document.createElement('input'); + jumpInput.type = 'number'; + jumpInput.value = currentPage; + jumpInput.min = 1; + jumpInput.max = totalPages; + jumpInput.style.width = '60px'; + jumpInput.style.padding = '4px'; + paginationContainer.appendChild(jumpInput); + + var jumpButton = document.createElement('button'); + jumpButton.textContent = '确定'; + jumpButton.onclick = function() { + // 直接获取输入框的当前值 + var jumpInputElement = document.querySelector('#productsPagination input[type="number"]'); + if (jumpInputElement) { + var jumpPage = parseInt(jumpInputElement.value); + if (jumpPage >= 1 && jumpPage <= productsTotalPages) { + productsPage = jumpPage; + loadProducts(); + } else { + showAlert('请输入有效的页码'); + } + } + }; + paginationContainer.appendChild(jumpButton); + } + + // 渲染商品分页控件(旧版,已废弃) + function renderProductsPaginationOld(currentPage, totalPages, totalItems) { + var paginationContainer = document.getElementById('productsPagination'); + paginationContainer.innerHTML = ''; + + if (totalPages <= 1) { + return; + } + + // 上一页按钮 + var prevButton = document.createElement('button'); + prevButton.textContent = '上一页'; + prevButton.disabled = currentPage === 1; + prevButton.onclick = function() { + if (currentPage > 1) { + productsPage = currentPage - 1; + loadProducts(); + } + }; + paginationContainer.appendChild(prevButton); + + // 页码按钮 + var startPage = Math.max(1, currentPage - 2); + var endPage = Math.min(totalPages, startPage + 4); + + if (startPage > 1) { + var firstButton = document.createElement('button'); + firstButton.textContent = '1'; + firstButton.onclick = function() { + productsPage = 1; + loadProducts(); + }; + paginationContainer.appendChild(firstButton); + + if (startPage > 2) { + var ellipsis = document.createElement('span'); + ellipsis.textContent = '...'; + ellipsis.style.padding = '0 10px'; + paginationContainer.appendChild(ellipsis); + } + } + + for (var i = startPage; i <= endPage; i++) { + var pageButton = document.createElement('button'); + pageButton.textContent = i; + pageButton.classList.toggle('active', i === currentPage); + pageButton.onclick = function() { + productsPage = parseInt(this.textContent); + loadProducts(); + }; + paginationContainer.appendChild(pageButton); + } + + if (endPage < totalPages) { + if (endPage < totalPages - 1) { + var ellipsis = document.createElement('span'); + ellipsis.textContent = '...'; + ellipsis.style.padding = '0 10px'; + paginationContainer.appendChild(ellipsis); + } + + var lastButton = document.createElement('button'); + lastButton.textContent = totalPages; + lastButton.onclick = function() { + productsPage = totalPages; + loadProducts(); + }; + paginationContainer.appendChild(lastButton); + } + + // 下一页按钮 + var nextButton = document.createElement('button'); + nextButton.textContent = '下一页'; + nextButton.disabled = currentPage === totalPages; + nextButton.onclick = function() { + if (currentPage < totalPages) { + productsPage = currentPage + 1; + loadProducts(); + } + }; + paginationContainer.appendChild(nextButton); + + // 显示总条数 + var totalInfo = document.createElement('span'); + totalInfo.textContent = '共 ' + totalItems + ' 条'; + totalInfo.style.marginLeft = '20px'; + totalInfo.style.fontSize = '14px'; + totalInfo.style.color = '#666'; + paginationContainer.appendChild(totalInfo); + } + + // 淡入淡出动画 + var style = document.createElement('style'); + style.textContent = ` + @keyframes slideIn { + from { + transform: translateX(100%); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } + } + @keyframes fadeOut { + from { + opacity: 1; + } + to { + opacity: 0; + } + } + `; + document.head.appendChild(style); + // 将样式添加到页面头部 document.head.insertAdjacentHTML('beforeend', formHoverStyles); @@ -2378,6 +3010,51 @@
`; + // 申请管理模态框HTML + var applyModalHTML = ` + + `; + // 权限管理模态框HTML var permissionModalHTML = `