Browse Source

Merge pull request '实现通知功能:' (#4) from update-loginmm into master

Reviewed-on: http://8.137.125.67:4000/SwtTt29/Webpage--users/pulls/4
master
SwtTt29 3 months ago
parent
commit
2db73b2f18
  1. 26
      src/main/java/com/example/web/controller/CustomerController.java
  2. 2
      src/main/java/com/example/web/controller/EnterpriseController.java
  3. 28
      src/main/java/com/example/web/controller/SupplyCustomerController.java
  4. 2
      src/main/java/com/example/web/controller/SupplyEnterpriseController.java
  5. 14
      src/main/java/com/example/web/dto/UnifiedCustomerDTO.java
  6. 9
      src/main/java/com/example/web/dto/UserProductCartDTO.java
  7. 12
      src/main/java/com/example/web/entity/Users.java
  8. 4
      src/main/java/com/example/web/mapper/SupplyUsersMapper.java
  9. 11
      src/main/java/com/example/web/mapper/UsersMapper.java
  10. 17
      src/main/java/com/example/web/service/PoolCustomerService.java
  11. 17
      src/main/java/com/example/web/service/SupplyPoolCustomerService.java
  12. 21
      src/main/resources/mapper/SupplyUsersMapper.xml
  13. 15
      src/main/resources/mapper/UsersMapper.xml
  14. 2
      src/main/resources/static/loginmm.html
  15. 453
      src/main/resources/static/mainapp-sells.html
  16. 454
      src/main/resources/static/mainapp-supplys.html

26
src/main/java/com/example/web/controller/CustomerController.java

@ -40,6 +40,8 @@ public class CustomerController {
@Autowired
private UsersManagementsMapper usersManagementsMapper;
@Autowired
private com.example.web.mapper.UsersMapper usersMapper;
@Autowired
private InformationTraService informationTraService;
@ -49,6 +51,28 @@ public class CustomerController {
* 根据公司ID查询客户详情 - 优先处理wechat数据源
* GET /pool/customers/{id}
*/
// 更新客户通知状态
@PutMapping("/customers/{id}/notice")
public ResponseEntity<Map<String, Object>> updateCustomerNotice(@PathVariable String id) {
try {
// 直接调用mapper更新通知状态
System.out.println("🔄 手动更新通知状态, userId: " + id + ", 从banold改为old");
usersMapper.updateNotice(id, "old");
// 返回成功响应
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "通知状态更新成功");
return ResponseEntity.ok(response);
} catch (Exception e) {
System.err.println("❌ 更新通知状态失败: " + e.getMessage());
// 返回失败响应
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "通知状态更新失败: " + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}
}
@GetMapping("/customers/{id}")
public ResponseEntity<Map<String, Object>> getById(@PathVariable String id, HttpServletRequest request) {
System.out.println("====================================================");
@ -350,6 +374,8 @@ public class CustomerController {
dto.setLevel(userInfo.getLevel() != null ? userInfo.getLevel() : "公海池");
dto.setCreated_at(userInfo.getCreated_at());
dto.setUpdated_at(userInfo.getUpdated_at());
// 添加通知信息转换
dto.setNotice(userInfo.getNotice() != null ? userInfo.getNotice() : "");
// 关键:设置数据源标识为wechat
dto.setDataSource("wechat");

2
src/main/java/com/example/web/controller/EnterpriseController.java

@ -631,6 +631,8 @@ public class EnterpriseController {
dto.setLevel(userInfo.getLevel() != null ? userInfo.getLevel() : "公海池");
dto.setCreated_at(userInfo.getCreated_at());
dto.setUpdated_at(userInfo.getUpdated_at());
// 添加通知信息转换
dto.setNotice(userInfo.getNotice() != null ? userInfo.getNotice() : "");
// 关键:设置数据源标识为wechat
dto.setDataSource("wechat");

28
src/main/java/com/example/web/controller/SupplyCustomerController.java

@ -35,10 +35,36 @@ public class SupplyCustomerController {
@Autowired
private SupplyUsersManagementsMapper supplyUsersManagementsMapper;
@Autowired
private com.example.web.mapper.SupplyUsersMapper supplyUsersMapper;
@Autowired
private InformationTraService informationTraService;
/**
* 采购端 - 更新客户通知状态
*/
@PutMapping("/customers/{id}/notice")
public ResponseEntity<Map<String, Object>> updateCustomerNotice(@PathVariable String id) {
try {
// 直接调用mapper更新通知状态
System.out.println("🔄 采购端手动更新通知状态, userId: " + id + ", 从banold改为old");
supplyUsersMapper.updateNotice(id, "old");
// 返回成功响应
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "通知状态更新成功");
return ResponseEntity.ok(response);
} catch (Exception e) {
System.err.println("❌ 采购端更新通知状态失败: " + e.getMessage());
// 返回失败响应
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "通知状态更新失败: " + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}
}
/**
* 根据公司ID查询客户详情 - 优先处理wechat数据源
* GET /supply/pool/customers/{id}
@ -340,6 +366,8 @@ public class SupplyCustomerController {
dto.setLevel(userInfo.getLevel() != null ? userInfo.getLevel() : "公海池");
dto.setCreated_at(userInfo.getCreated_at());
dto.setUpdated_at(userInfo.getUpdated_at());
// 添加通知信息转换
dto.setNotice(userInfo.getNotice() != null ? userInfo.getNotice() : "");
// 关键:设置数据源标识为wechat
dto.setDataSource("wechat");

2
src/main/java/com/example/web/controller/SupplyEnterpriseController.java

@ -608,6 +608,8 @@ public class SupplyEnterpriseController {
dto.setLevel(userInfo.getLevel() != null ? userInfo.getLevel() : "公海池");
dto.setCreated_at(userInfo.getCreated_at());
dto.setUpdated_at(userInfo.getUpdated_at());
// 添加通知信息转换
dto.setNotice(userInfo.getNotice() != null ? userInfo.getNotice() : "");
// 关键:设置数据源标识为wechat
dto.setDataSource("wechat");

14
src/main/java/com/example/web/dto/UnifiedCustomerDTO.java

@ -45,7 +45,7 @@ public class UnifiedCustomerDTO {
private String role = ""; // 负责人角色
private String userName = ""; // 负责人用户名
private String assistant = ""; // 协助人
private String notice = ""; // 通知信息
// 时间信息
private LocalDateTime created_at;
@ -305,7 +305,17 @@ public class UnifiedCustomerDTO {
public void setUserName(String userName) { this.userName = userName; }
public String getAssistant() { return assistant; }
public void setAssistant(String assistant) { this.assistant = assistant; }
public void setAssistant(String assistant) {
this.assistant = assistant;
}
public String getNotice() {
return notice;
}
public void setNotice(String notice) {
this.notice = notice;
}
public LocalDateTime getCreated_at() { return created_at; }
public void setCreated_at(LocalDateTime created_at) { this.created_at = created_at; }

9
src/main/java/com/example/web/dto/UserProductCartDTO.java

@ -22,6 +22,7 @@ public class UserProductCartDTO {
private String spec;//规格
private LocalDateTime created_at;//创建时间
private LocalDateTime updated_at;//更新时间
private String notice;//通知状态
private List<ProductInfo> products= new ArrayList<>();;//产品信息
private List<CartItem> cartItems= new ArrayList<>();;//购物车信息
private List<UsersContacts> usersContacts= new ArrayList<>();;//联系人信息
@ -98,6 +99,14 @@ public class UserProductCartDTO {
this.spec = spec;
}
public String getNotice() {
return notice;
}
public void setNotice(String notice) {
this.notice = notice;
}
public LocalDateTime getCreated_at() {
return created_at;
}

12
src/main/java/com/example/web/entity/Users.java

@ -33,8 +33,9 @@ public class Users {
private String demand;//客户需求
private String spec;//规格
private String followup;//跟进信息
private String notice;//通知状态
public Users(Integer id, String openid, String userId, String nickName, String avatarUrl, String phoneNumber, String type, Integer gender, String country, String province, String city, String language, String session_key, LocalDateTime created_at, LocalDateTime updated_at, String company, String region, String level, String demand, String spec, String followup) {
public Users(Integer id, String openid, String userId, String nickName, String avatarUrl, String phoneNumber, String type, Integer gender, String country, String province, String city, String language, String session_key, LocalDateTime created_at, LocalDateTime updated_at, String company, String region, String level, String demand, String spec, String followup, String notice) {
this.id = id;
this.openid = openid;
this.userId = userId;
@ -56,6 +57,7 @@ public class Users {
this.demand = demand;
this.spec = spec;
this.followup = followup;
this.notice = notice;
}
public Integer getId() {
@ -225,4 +227,12 @@ public class Users {
public void setFollowup(String followup) {
this.followup = followup;
}
public String getNotice() {
return notice;
}
public void setNotice(String notice) {
this.notice = notice;
}
}

4
src/main/java/com/example/web/mapper/SupplyUsersMapper.java

@ -263,4 +263,8 @@ public interface SupplyUsersMapper {
// 更新跟进信息
@Update("UPDATE users SET followup = #{followup} WHERE phoneNumber = #{phoneNumber}")
int updateFollowUpByPhone(@Param("phoneNumber") String phoneNumber, @Param("followup") String followup);
// 🔥 新增:更新用户通知状态
@Update("UPDATE users SET notice = #{notice} WHERE user_id = #{userId}")
int updateNotice(@Param("userId") String userId, @Param("notice") String notice);
}

11
src/main/java/com/example/web/mapper/UsersMapper.java

@ -259,11 +259,18 @@ public interface UsersMapper {
@Param("authInfo") ManagerAuthInfo authInfo);
// 🔥 新增:批量查询用户联系人信息
List<ContactInfo> getUserContactsByUserIds(@Param("userIds") List<String> userIds);
List<UsersMapper.ContactInfo> getUserContactsByUserIds(@Param("userIds") List<String> userIds);
// 🔥 新增:批量查询用户购物车信息
List<CartItem> getCartItemsByUserIds(@Param("userIds") List<String> userIds);
List<UserProductCartDTO.CartItem> getBuyerCartItemsByUserIds(@Param("userIds") List<String> userIds);
// 🔥 新增:批量查询用户购物车信息(兼容旧方法名)
List<UsersMapper.CartItem> getCartItemsByUserIds(@Param("userIds") List<String> userIds);
// 🔥 新增:批量查询用户基本信息
List<UserProductCartDTO> getUserBasicInfoByUserIds(@Param("userIds") List<String> userIds);
// 🔥 新增:更新用户通知状态
@Update("UPDATE users SET notice = #{notice} WHERE user_id = #{userId}")
int updateNotice(@Param("userId") String userId, @Param("notice") String notice);
}

17
src/main/java/com/example/web/service/PoolCustomerService.java

@ -172,7 +172,10 @@ public class PoolCustomerService {
List<UserProductCartDTO> authorizedUsers = usersMapper.getAuthorizedCustomers(authInfo, limit, offset);
System.out.println("✅ 授权客户数据查询完成");
System.out.println("📊 当前页获取到授权客户数据条数: " + (authorizedUsers != null ? authorizedUsers.size() : "null"));
System.out.println("📊 当前页获取到授权客户数据条数: " + (authorizedUsers != null ? authorizedUsers.size() : "null"));
// 统计通知状态为banold的用户数量
long banoldCount = authorizedUsers != null ? authorizedUsers.stream().filter(user -> "banold".equals(user.getNotice())).count() : 0;
System.out.println("📊 通知状态为banold的用户数量: " + banoldCount);
List<UserProductCartDTO> result = new ArrayList<>();
@ -441,6 +444,13 @@ public class PoolCustomerService {
return null;
}
// 2. 如果通知状态为banold,则更新为old
if ("banold".equals(userInfo.getNotice())) {
System.out.println("🔄 更新通知状态, userId: " + userId + ", 从banold改为old");
usersMapper.updateNotice(userId, "old");
userInfo.setNotice("old");
}
// 销售端权限校验
if (!"buyer".equals(userInfo.getType()) && !"both".equals(userInfo.getType())) {
System.out.println("🚫 过滤掉非客户端客户: " + userId + " (类型: " + userInfo.getType() + ")");
@ -549,7 +559,10 @@ public class PoolCustomerService {
List<UserProductCartDTO> allUsers = usersMapper.getAllUserBasicInfo();
System.out.println("✅ 基础用户数据查询完成");
System.out.println("📊 获取到基础用户数据条数: " + (allUsers != null ? allUsers.size() : "null"));
System.out.println("📊 获取到基础用户数据条数: " + (allUsers != null ? allUsers.size() : "null"));
// 统计通知状态为banold的用户数量
long banoldCount = allUsers != null ? allUsers.stream().filter(user -> "banold".equals(user.getNotice())).count() : 0;
System.out.println("📊 通知状态为banold的用户数量: " + banoldCount);
List<UserProductCartDTO> result = new ArrayList<>();

17
src/main/java/com/example/web/service/SupplyPoolCustomerService.java

@ -173,6 +173,11 @@ public class SupplyPoolCustomerService {
System.out.println("✅ 授权客户数据查询完成");
System.out.println("📊 当前页获取到授权客户数据条数: " + (authorizedUsers != null ? authorizedUsers.size() : "null"));
// 统计通知状态为banold的授权客户数量
if (authorizedUsers != null) {
long banoldCount = authorizedUsers.stream().filter(user -> "banold".equals(user.getNotice())).count();
System.out.println("📊 通知状态为banold的授权客户数量: " + banoldCount);
}
List<UserProductCartDTO> result = new ArrayList<>();
@ -457,6 +462,13 @@ public class SupplyPoolCustomerService {
System.out.println("🚫 过滤掉非供应端客户: " + userId + " (类型: " + userInfo.getType() + ")");
return null; // 返回null而不是抛出异常
}
// 如果通知状态为banold,则更新为old
if ("banold".equals(userInfo.getNotice())) {
System.out.println("🔄 更新通知状态: " + userId + " 从banold改为old");
supplyusersMapper.updateNotice(userId, "old");
userInfo.setNotice("old");
}
// 🔥 新增:查询负责人信息
try {
UsersManagements userManager = supplyUsersManagementsMapper.findByUserId(userId);
@ -563,6 +575,11 @@ public class SupplyPoolCustomerService {
System.out.println("✅ 基础用户数据查询完成");
System.out.println("📊 获取到基础用户数据条数: " + (allUsers != null ? allUsers.size() : "null"));
// 统计通知状态为banold的用户数量
if (allUsers != null) {
long banoldCount = allUsers.stream().filter(user -> "banold".equals(user.getNotice())).count();
System.out.println("📊 通知状态为banold的用户数量: " + banoldCount);
}
List<UserProductCartDTO> result = new ArrayList<>();

21
src/main/resources/mapper/SupplyUsersMapper.xml

@ -25,6 +25,7 @@
<result property="level" column="level"/>
<result property="demand" column="demand" jdbcType="VARCHAR"/>
<result property="spec" column="spec" jdbcType="VARCHAR"/>
<result property="notice" column="notice" jdbcType="VARCHAR"/>
</resultMap>
<!-- 用户基本信息映射 -->
@ -40,6 +41,7 @@
<result property="level" column="level"/>
<result property="demand" column="demand" jdbcType="VARCHAR"/>
<result property="spec" column="spec" jdbcType="VARCHAR"/>
<result property="notice" column="notice" jdbcType="VARCHAR"/>
</resultMap>
<!-- 产品信息映射 -->
@ -82,7 +84,8 @@
u.demand,
u.spec,
u.created_at,
u.updated_at
u.updated_at,
u.notice
FROM users u
LEFT JOIN usermanagements um ON u.userId = um.userId
WHERE u.phoneNumber IS NOT NULL
@ -190,7 +193,8 @@
u.demand,
u.spec,
u.created_at,
u.updated_at
u.updated_at,
u.notice
FROM users u
LEFT JOIN usermanagements um ON u.userId = um.userId
WHERE u.phoneNumber = #{phoneNumber}
@ -230,7 +234,8 @@
demand,
spec,
created_at,
updated_at
updated_at,
notice
FROM users
WHERE userId = #{userId}
</select>
@ -267,6 +272,7 @@
level,
demand,
spec,
notice,
created_at,
updated_at
FROM users
@ -289,7 +295,8 @@
demand,
spec,
created_at,
updated_at
updated_at,
notice
FROM users
WHERE phoneNumber = #{phoneNumber}
AND (type = 'seller' OR type = 'both')
@ -308,7 +315,8 @@
demand,
spec,
created_at,
updated_at
updated_at,
notice
FROM users
WHERE userId = #{userId}
AND (type = 'seller' OR type = 'both')
@ -612,7 +620,8 @@
demand,
spec,
created_at,
updated_at
updated_at,
notice
FROM users
WHERE userId IN
<foreach collection="userIds" item="userId" open="(" separator="," close=")">

15
src/main/resources/mapper/UsersMapper.xml

@ -31,6 +31,7 @@
<result property="level" column="level"/>
<result property="demand" column="demand" jdbcType="VARCHAR"/>
<result property="spec" column="spec" jdbcType="VARCHAR"/>
<result property="notice" column="notice" jdbcType="VARCHAR"/>
</resultMap>
<!-- 用户基本信息映射 -->
@ -46,6 +47,7 @@
<result property="level" column="level"/>
<result property="demand" column="demand" jdbcType="VARCHAR"/>
<result property="spec" column="spec" jdbcType="VARCHAR"/>
<result property="notice" column="notice" jdbcType="VARCHAR"/>
</resultMap>
<!-- 产品信息映射 -->
@ -88,7 +90,8 @@
u.demand,
u.spec,
u.created_at,
u.updated_at
u.updated_at,
u.notice
FROM users u
LEFT JOIN usermanagements um ON u.userId = um.userId
WHERE u.phoneNumber IS NOT NULL
@ -178,7 +181,8 @@
demand,
spec,
created_at,
updated_at
updated_at,
notice
FROM users
WHERE userId = #{userId}
</select>
@ -219,6 +223,7 @@
level,
demand,
spec,
notice,
created_at,
updated_at
FROM users
@ -259,7 +264,8 @@
demand,
spec,
created_at,
updated_at
updated_at,
notice
FROM users
WHERE userId = #{userId}
</select>
@ -625,7 +631,8 @@
demand,
spec,
created_at,
updated_at
updated_at,
notice
FROM users
WHERE userId IN
<foreach collection="userIds" item="userId" open="(" separator="," close=")">

2
src/main/resources/static/loginmm.html

@ -520,7 +520,7 @@
errorElement.classList.remove('show');
}
const API_BASE_URL = 'http://8.137.125.67:8080/DL'; // 服务器API地址
const API_BASE_URL = 'http://localhost:8081/DL'; // 服务器API地址
async function sendLoginRequest(projectName, userName, password) {
try {
// 使用URL编码的表单数据

453
src/main/resources/static/mainapp-sells.html

@ -604,6 +604,7 @@
transition: all 0.3s ease;
margin-right: 10px;
color: #666;
position: relative;
}
.notification-btn:hover {
@ -615,6 +616,47 @@
.notification-btn i {
font-size: 18px;
transition: all 0.3s ease;
}
/* 通知铃铛激活状态样式 */
.notification-btn.notification-active {
background-color: #fee2e2;
border-color: #fecaca;
color: #ef4444;
}
/* 通知数量徽章样式 */
.notification-count {
position: absolute;
top: -5px;
right: -5px;
background: #ef4444;
color: white;
font-size: 12px;
font-weight: bold;
border-radius: 50%;
min-width: 18px;
height: 18px;
display: flex;
align-items: center;
justify-content: center;
padding: 0 6px;
}
/* 铃铛摇晃动画 */
@keyframes ring {
0% { transform: rotate(0deg); }
10% { transform: rotate(15deg); }
20% { transform: rotate(-15deg); }
30% { transform: rotate(10deg); }
40% { transform: rotate(-10deg); }
50% { transform: rotate(5deg); }
60% { transform: rotate(-5deg); }
70% { transform: rotate(2deg); }
80% { transform: rotate(-2deg); }
90% { transform: rotate(1deg); }
100% { transform: rotate(0deg); }
}
/* 头部操作容器样式 */
@ -2733,6 +2775,21 @@
<button class="notification-btn" id="notificationButton">
<i class="fas fa-bell"></i>
</button>
<!-- 通知弹窗 -->
<div class="modal" id="notificationModal">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">客户通知</h2>
<button class="close-modal" id="closeNotificationModal">&times;</button>
</div>
<div class="modal-body">
<div id="notificationContent">
<!-- 通知内容将通过JavaScript动态生成 -->
</div>
</div>
</div>
</div>
<button class="primary-btn login-btn" id="loginButton">
<i class="fas fa-sign-in-alt"></i> 登录
</button>
@ -4097,6 +4154,12 @@
const data = result.data || {};
const allPublicSeaCustomers = Array.isArray(data) ? data : Object.values(data);
// 统计notice为banold的客户数量并更新通知铃铛
this.updateNotificationStatus(allPublicSeaCustomers);
// 保存所有公海客户数据到缓存中,用于通知弹窗
this.allPublicSeaCustomers = allPublicSeaCustomers;
// 对每个等级分别过滤和缓存
for (const level of levels) {
const filteredCustomers = this.filterPublicSeaCustomersByLoginInfo(
@ -4326,6 +4389,50 @@
}
}
// 更新通知铃铛状态
updateNotificationStatus(customers) {
// 统计notice为banold的客户数量
console.log('📊 更新通知状态 - 客户列表:', customers.length, '个客户');
const banoldCount = customers.filter(customer => customer.notice === 'banold').length;
console.log('🔔 待处理通知数量:', banoldCount);
// 更新通知铃铛样式
const notificationButton = document.getElementById('notificationButton');
console.log('🔘 通知按钮存在:', !!notificationButton);
if (notificationButton) {
const bellIcon = notificationButton.querySelector('i');
if (bellIcon) {
if (banoldCount > 0) {
notificationButton.classList.add('notification-active');
bellIcon.style.animation = 'ring 1s ease-in-out';
// 添加或更新通知数量显示
let countBadge = notificationButton.querySelector('.notification-count');
if (!countBadge) {
countBadge = document.createElement('span');
countBadge.className = 'notification-count';
notificationButton.appendChild(countBadge);
}
countBadge.textContent = banoldCount;
} else {
notificationButton.classList.remove('notification-active');
bellIcon.style.animation = 'none';
// 移除通知数量显示
const countBadge = notificationButton.querySelector('.notification-count');
if (countBadge) {
countBadge.remove();
}
}
}
// 绑定通知点击事件
this.bindNotificationEvents();
}
return banoldCount;
}
// 获取公海池数据的辅助方法
async fetchPublicSeaData(loginInfo, level) {
const url = appendAuthParams(`${API_BASE_URL}/pool/all-customers`);
@ -4344,10 +4451,148 @@
const data = result.data || {};
const customersArray = Array.isArray(data) ? data : Object.values(data);
// 统计notice为banold的客户数量并更新通知铃铛
this.updateNotificationStatus(customersArray);
// 根据登录信息过滤公海池数据
return this.filterPublicSeaCustomersByLoginInfo(customersArray, loginInfo, level);
}
// 绑定通知事件
bindNotificationEvents() {
const notificationButton = document.getElementById('notificationButton');
const notificationModal = document.getElementById('notificationModal');
const closeNotificationModal = document.getElementById('closeNotificationModal');
if (notificationButton && notificationModal && closeNotificationModal) {
// 移除现有的点击事件监听器,避免重复绑定
notificationButton.onclick = null;
closeNotificationModal.onclick = null;
// 点击通知按钮显示弹窗
notificationButton.addEventListener('click', () => {
this.showNotificationModal();
});
// 点击关闭按钮关闭弹窗
closeNotificationModal.addEventListener('click', () => {
this.hideNotificationModal();
});
// 点击弹窗外部关闭弹窗
notificationModal.addEventListener('click', (e) => {
if (e.target === notificationModal) {
this.hideNotificationModal();
}
});
}
}
// 显示通知弹窗
async showNotificationModal() {
console.log('🎯 显示通知弹窗');
const notificationModal = document.getElementById('notificationModal');
const notificationContent = document.getElementById('notificationContent');
// 显示加载状态
notificationContent.innerHTML = '<p style="text-align: center; color: #666;">加载中...</p>';
notificationModal.classList.add('active');
// 阻止背景滚动和操作
document.body.style.overflow = 'hidden';
notificationModal.style.zIndex = '1000'; // 确保弹窗在最上层
try {
// 先尝试从缓存获取数据
let allCustomers = [];
if (this.allPublicSeaCustomers) {
allCustomers = this.allPublicSeaCustomers;
console.log('📋 从缓存获取客户数据:', allCustomers.length, '条');
console.log('📋 缓存完整数据:', JSON.stringify(this.allPublicSeaCustomers));
} else {
// 如果缓存中没有数据,直接从API获取
const url = appendAuthParams(`${API_BASE_URL}/pool/all-customers`);
console.log('🌐 请求API地址:', url);
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP错误: ${response.status}`);
const result = await response.json();
console.log('📥 API完整响应:', JSON.stringify(result));
if (!result.success) throw new Error(result.message);
const data = result.data || {};
allCustomers = Array.isArray(data) ? data : Object.values(data);
console.log('🔄 转换后客户数组:', allCustomers.length, '条');
console.log('🔄 转换后完整数据:', JSON.stringify(allCustomers));
// 更新缓存
this.allPublicSeaCustomers = allCustomers;
}
// 获取notice为banold的客户
const banoldCustomers = allCustomers.filter(customer => customer.notice === 'banold');
console.log('🔔 符合条件的banold客户:', banoldCustomers.length, '条');
console.log('🔔 banold客户详情:', JSON.stringify(banoldCustomers));
if (banoldCustomers.length === 0) {
notificationContent.innerHTML = '<div class="notification-empty"><p>暂无通知</p></div>';
} else {
// 生成通知内容
let contentHTML = '<div class="notification-list">';
banoldCustomers.forEach(customer => {
const customerName = customer.company || customer.companyName || '未知';
contentHTML += `
<div class="notification-item">
<div class="notification-header">
<div class="notification-icon">
<i class="fas fa-user-clock" style="color: #ff9800;"></i>
</div>
<div class="notification-content">
<div class="notification-title" style="cursor: pointer; color: #1890ff; font-weight: 600;" onclick="viewCustomerDetails('${customer.id}', '${customer.phoneNumber || ''}', null, true)">
${customerName}
</div>
<div class="notification-meta">
<span class="customer-id">客户ID: ${customer.id}</span>
<span class="customer-phone">${customer.phoneNumber || '无电话'}</span>
</div>
<div class="notification-message">
<i class="fas fa-exclamation-circle" style="color: #ff9800; margin-right: 6px;"></i>
该客户已满足过期条件
</div>
<div class="notification-footer">
<div class="notification-time">${new Date().toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })}</div>
<button class="notification-action-btn" onclick="updateCustomerNotice('${customer.id}', this.closest('.notification-item'))">
标记已处理
</button>
</div>
</div>
</div>
</div>
`;
});
contentHTML += '</div>';
notificationContent.innerHTML = contentHTML;
}
} catch (error) {
console.error('❌ 获取通知数据失败:', error);
console.error('❌ 错误详情:', error.stack);
notificationContent.innerHTML = '<p style="text-align: center; color: #ff6b6b;">加载通知失败,请稍后重试</p>';
}
}
// 隐藏通知弹窗
hideNotificationModal() {
const notificationModal = document.getElementById('notificationModal');
notificationModal.classList.remove('active');
}
// 恢复背景滚动
document.body.style.overflow = 'auto';
}
// 获取普通等级数据的辅助方法
async fetchNormalLevelData(loginInfo, level) {
const url = appendAuthParams(`${API_BASE_URL}/pool/customers`);
@ -4365,6 +4610,9 @@
const dataMap = result.data || {};
let allCustomers = Object.values(dataMap);
// 统计notice为banold的客户数量并更新通知铃铛
this.updateNotificationStatus(allCustomers);
// 根据登录信息和等级过滤数据
allCustomers = this.filterCustomersByLoginInfo(allCustomers, loginInfo, level);
@ -11418,7 +11666,7 @@
// 移除未定义变量的console日志
// console.log('查看客户详情参数:', { customerId, phoneNumber, isCompanyId });
// 优化的客户详情查看功能
async function viewCustomerDetails(customerId, phoneNumber, targetCartItemId = null) {
async function viewCustomerDetails(customerId, phoneNumber, targetCartItemId = null, fromNotification = false) {
// 重置客户数据状态
resetCustomerDataState();
@ -11440,6 +11688,40 @@
showNotification('未找到客户信息,请检查客户是否存在或数据是否加载完成', 'error');
return;
}
// 如果是从通知弹窗点击进入,更新notice状态为old
if (fromNotification) {
console.log('📋 从通知弹窗查看客户详情,更新notice状态为old');
// 发送请求更新notice状态
fetch(`/pool/customers/${customerId}/notice`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ notice: 'old' })
})
.then(response => response.json())
.then(data => {
console.log('✅ 更新客户通知状态成功,响应:', data);
// 从通知列表中移除对应的通知项
const notificationItems = document.querySelectorAll('.notification-item');
notificationItems.forEach(item => {
const customerIdElement = item.querySelector('.customer-id');
if (customerIdElement && customerIdElement.textContent.includes(customerId)) {
item.remove();
}
});
// 检查是否还有通知项
const remainingItems = document.querySelectorAll('.notification-item');
if (remainingItems.length === 0) {
const notificationContent = document.getElementById('notificationContent');
notificationContent.innerHTML = '<div class="notification-empty"><p>暂无通知</p></div>';
}
})
.catch(error => {
console.error('❌ 更新客户通知状态失败:', error);
});
}
// 处理客户数据
const { customer, dataSource, apiUsed } = customerData;
@ -13942,6 +14224,45 @@
}
}
});
// 更新客户通知状态的全局函数
function updateCustomerNotice(customerId, notificationItem) {
console.log('⚙️ 处理客户通知状态更新,客户ID:', customerId);
// 发送请求到后端更新客户的notice状态
fetch(`/pool/customers/${customerId}/notice`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ notice: 'old' })
})
.then(response => response.json())
.then(data => {
console.log('✅ 更新客户通知状态成功,响应:', data);
if (data.success) {
// 从通知列表中移除该项
if (notificationItem && notificationItem.parentNode) {
notificationItem.remove();
}
// 更新通知铃铛状态
const notificationContent = document.getElementById('notificationContent');
const remainingNotifications = notificationContent.querySelectorAll('.notification-item').length;
if (remainingNotifications === 0) {
notificationContent.innerHTML = '<p style="text-align: center; color: #666;">暂无通知</p>';
}
// 重新获取数据并更新通知铃铛
if (typeof CustomerDataCache !== 'undefined') {
CustomerDataCache.refreshPublicSeaLevels();
}
}
})
.catch(error => {
console.error('❌ 更新客户通知状态失败:', error);
});
}
// 确保数据缓存系统在页面加载时正确初始化
document.addEventListener('DOMContentLoaded', async function() {
console.log('🚀 初始化客户数据系统');
@ -14047,7 +14368,135 @@
});
// 绑定行点击事件的功能已在setupEnhancedEventDelegation中实现
// 添加通知弹窗样式
const style = document.createElement('style');
style.textContent = `
/* 通知弹窗样式 */
.notification-list {
display: flex;
flex-direction: column;
gap: 16px;
padding: 16px;
}
.notification-item {
background: #ffffff;
border: 1px solid #e8e8e8;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.notification-item:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
transform: translateY(-2px);
}
.notification-header {
display: flex;
align-items: flex-start;
gap: 12px;
}
.notification-icon {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
background: #fff7e6;
border-radius: 50%;
flex-shrink: 0;
}
.notification-content {
flex: 1;
}
.notification-title {
font-size: 16px;
font-weight: 600;
margin-bottom: 8px;
transition: color 0.3s ease;
}
.notification-title:hover {
color: #40a9ff !important;
text-decoration: underline;
}
.notification-meta {
display: flex;
gap: 16px;
margin-bottom: 8px;
font-size: 14px;
color: #666666;
}
.notification-message {
font-size: 14px;
color: #ff7875;
margin-bottom: 12px;
display: flex;
align-items: center;
}
.notification-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
color: #999999;
}
.notification-time {
white-space: nowrap;
}
.notification-action-btn {
padding: 6px 16px;
background: #1890ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
transition: all 0.3s ease;
}
.notification-action-btn:hover {
background: #40a9ff;
transform: translateY(-1px);
}
.notification-empty {
display: flex;
align-items: center;
justify-content: center;
padding: 48px 24px;
color: #999999;
background: #fafafa;
border-radius: 8px;
font-size: 14px;
}
/* 适配不同屏幕尺寸 */
@media (max-width: 768px) {
.notification-meta {
flex-direction: column;
gap: 4px;
}
.notification-footer {
flex-direction: column;
gap: 8px;
align-items: flex-start;
}
}
`;
document.head.appendChild(style);
</script>
</body>

454
src/main/resources/static/mainapp-supplys.html

@ -604,6 +604,7 @@
transition: all 0.3s ease;
margin-right: 10px;
color: #666;
position: relative;
}
.notification-btn:hover {
@ -615,6 +616,47 @@
.notification-btn i {
font-size: 18px;
transition: all 0.3s ease;
}
/* 通知铃铛激活状态样式 */
.notification-btn.notification-active {
background-color: #fee2e2;
border-color: #fecaca;
color: #ef4444;
}
/* 通知数量徽章样式 */
.notification-count {
position: absolute;
top: -5px;
right: -5px;
background: #ef4444;
color: white;
font-size: 12px;
font-weight: bold;
border-radius: 50%;
min-width: 18px;
height: 18px;
display: flex;
align-items: center;
justify-content: center;
padding: 0 6px;
}
/* 铃铛摇晃动画 */
@keyframes ring {
0% { transform: rotate(0deg); }
10% { transform: rotate(15deg); }
20% { transform: rotate(-15deg); }
30% { transform: rotate(10deg); }
40% { transform: rotate(-10deg); }
50% { transform: rotate(5deg); }
60% { transform: rotate(-5deg); }
70% { transform: rotate(2deg); }
80% { transform: rotate(-2deg); }
90% { transform: rotate(1deg); }
100% { transform: rotate(0deg); }
}
/* 头部操作容器样式 */
@ -2733,6 +2775,21 @@
<button class="notification-btn" id="notificationButton">
<i class="fas fa-bell"></i>
</button>
<!-- 通知弹窗 -->
<div class="modal" id="notificationModal">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">客户通知</h2>
<button class="close-modal" id="closeNotificationModal">&times;</button>
</div>
<div class="modal-body">
<div id="notificationContent">
<!-- 通知内容将通过JavaScript动态生成 -->
</div>
</div>
</div>
</div>
<button class="primary-btn login-btn" id="loginButton">
<i class="fas fa-sign-in-alt"></i> 登录
</button>
@ -4097,6 +4154,12 @@
const data = result.data || {};
const allPublicSeaCustomers = Array.isArray(data) ? data : Object.values(data);
// 统计notice为banold的客户数量并更新通知铃铛
this.updateNotificationStatus(allPublicSeaCustomers);
// 保存所有公海客户数据到缓存中,用于通知弹窗
this.allPublicSeaCustomers = allPublicSeaCustomers;
// 对每个等级分别过滤和缓存
for (const level of levels) {
const filteredCustomers = this.filterPublicSeaCustomersByLoginInfo(
@ -4315,8 +4378,52 @@
}
// 获取公海池数据的辅助方法
// 更新通知铃铛状态
updateNotificationStatus(customers) {
// 统计notice为banold的客户数量
console.log('📊 更新通知状态 - 客户列表:', customers.length, '个客户');
const banoldCount = customers.filter(customer => customer.notice === 'banold').length;
console.log('🔔 待处理通知数量:', banoldCount);
// 更新通知铃铛样式
const notificationButton = document.getElementById('notificationButton');
console.log('🔘 通知按钮存在:', !!notificationButton);
if (notificationButton) {
const bellIcon = notificationButton.querySelector('i');
if (bellIcon) {
if (banoldCount > 0) {
notificationButton.classList.add('notification-active');
bellIcon.style.animation = 'ring 1s ease-in-out';
// 添加或更新通知数量显示
let countBadge = notificationButton.querySelector('.notification-count');
if (!countBadge) {
countBadge = document.createElement('span');
countBadge.className = 'notification-count';
notificationButton.appendChild(countBadge);
}
countBadge.textContent = banoldCount;
} else {
notificationButton.classList.remove('notification-active');
bellIcon.style.animation = 'none';
// 移除通知数量显示
const countBadge = notificationButton.querySelector('.notification-count');
if (countBadge) {
countBadge.remove();
}
}
}
// 绑定通知点击事件
this.bindNotificationEvents();
}
return banoldCount;
}
async fetchPublicSeaData(loginInfo, level) {
const url = appendAuthParams(`${API_BASE_URL}/pool/all-customers`);
const url = appendAuthParams(`${API_BASE_URL}/supply/pool/all-customers`);
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
@ -4332,10 +4439,147 @@
const data = result.data || {};
const customersArray = Array.isArray(data) ? data : Object.values(data);
// 统计notice为banold的客户数量并更新通知铃铛
this.updateNotificationStatus(customersArray);
// 根据登录信息过滤公海池数据
return this.filterPublicSeaCustomersByLoginInfo(customersArray, loginInfo, level);
}
// 绑定通知事件
bindNotificationEvents() {
const notificationButton = document.getElementById('notificationButton');
const notificationModal = document.getElementById('notificationModal');
const closeNotificationModal = document.getElementById('closeNotificationModal');
if (notificationButton && notificationModal && closeNotificationModal) {
// 移除现有的点击事件监听器,避免重复绑定
notificationButton.onclick = null;
closeNotificationModal.onclick = null;
// 点击通知按钮显示弹窗
notificationButton.addEventListener('click', () => {
this.showNotificationModal();
});
// 点击关闭按钮关闭弹窗
closeNotificationModal.addEventListener('click', () => {
this.hideNotificationModal();
});
// 点击弹窗外部关闭弹窗
notificationModal.addEventListener('click', (e) => {
if (e.target === notificationModal) {
this.hideNotificationModal();
}
});
}
}
// 显示通知弹窗
async showNotificationModal() {
console.log('🎯 显示通知弹窗');
const notificationModal = document.getElementById('notificationModal');
const notificationContent = document.getElementById('notificationContent');
// 显示加载状态
notificationContent.innerHTML = '<p style="text-align: center; color: #666;">加载中...</p>';
notificationModal.classList.add('active');
// 阻止背景滚动和操作
document.body.style.overflow = 'hidden';
notificationModal.style.zIndex = '1000'; // 确保弹窗在最上层
try {
// 先尝试从缓存获取数据
let allCustomers = [];
if (this.allPublicSeaCustomers) {
allCustomers = this.allPublicSeaCustomers;
console.log('📋 从缓存获取客户数据:', allCustomers.length, '条');
console.log('📋 缓存完整数据:', JSON.stringify(this.allPublicSeaCustomers));
} else {
// 如果缓存中没有数据,直接从API获取
const url = appendAuthParams(`${API_BASE_URL}/supply/pool/all-customers`);
console.log('🌐 请求API地址:', url);
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP错误: ${response.status}`);
const result = await response.json();
console.log('📥 API完整响应:', JSON.stringify(result));
if (!result.success) throw new Error(result.message);
const data = result.data || {};
allCustomers = Array.isArray(data) ? data : Object.values(data);
console.log('🔄 转换后客户数组:', allCustomers.length, '条');
console.log('🔄 转换后完整数据:', JSON.stringify(allCustomers));
// 更新缓存
this.allPublicSeaCustomers = allCustomers;
}
// 获取notice为banold的客户
const banoldCustomers = allCustomers.filter(customer => customer.notice === 'banold');
console.log('🔔 符合条件的banold客户:', banoldCustomers.length, '条');
console.log('🔔 banold客户详情:', JSON.stringify(banoldCustomers));
if (banoldCustomers.length === 0) {
notificationContent.innerHTML = '<div class="notification-empty"><p>暂无通知</p></div>';
} else {
// 生成通知内容
let contentHTML = '<div class="notification-list">';
banoldCustomers.forEach(customer => {
const customerName = customer.company || customer.companyName || '未知';
contentHTML += `
<div class="notification-item">
<div class="notification-header">
<div class="notification-icon">
<i class="fas fa-user-clock" style="color: #ff9800;"></i>
</div>
<div class="notification-content">
<div class="notification-title" style="cursor: pointer; color: #1890ff; font-weight: 600;" onclick="viewCustomerDetails('${customer.id}', '${customer.phoneNumber || ''}', null, true)">
${customerName}
</div>
<div class="notification-meta">
<span class="customer-id">客户ID: ${customer.id}</span>
<span class="customer-phone">${customer.phoneNumber || '无电话'}</span>
</div>
<div class="notification-message">
<i class="fas fa-exclamation-circle" style="color: #ff9800; margin-right: 6px;"></i>
该客户已满足过期条件
</div>
<div class="notification-footer">
<div class="notification-time">${new Date().toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })}</div>
<button class="notification-action-btn" onclick="updateCustomerNotice('${customer.id}', this.closest('.notification-item'))">
标记已处理
</button>
</div>
</div>
</div>
</div>
`;
});
contentHTML += '</div>';
notificationContent.innerHTML = contentHTML;
}
} catch (error) {
console.error('❌ 获取通知数据失败:', error);
console.error('❌ 错误详情:', error.stack);
notificationContent.innerHTML = '<p style="text-align: center; color: #ff6b6b;">加载通知失败,请稍后重试</p>';
}
}
// 隐藏通知弹窗
hideNotificationModal() {
const notificationModal = document.getElementById('notificationModal');
notificationModal.classList.remove('active');
// 恢复背景滚动
document.body.style.overflow = 'auto';
}
// 获取普通等级数据的辅助方法
async fetchNormalLevelData(loginInfo, level) {
const url = appendAuthParams(`${API_BASE_URL}/pool/customers`);
@ -4354,6 +4598,9 @@
const dataMap = result.data || {};
let allCustomers = Object.values(dataMap);
// 统计notice为banold的客户数量并更新通知铃铛
this.updateNotificationStatus(allCustomers);
// 根据登录信息和等级过滤数据
allCustomers = this.filterCustomersByLoginInfo(allCustomers, loginInfo, level);
@ -11247,7 +11494,7 @@
// 移除未定义变量的console日志
// console.log('查看客户详情参数:', { customerId, phoneNumber, isCompanyId });
// 优化的客户详情查看功能
async function viewCustomerDetails(customerId, phoneNumber, targetProductItemId = null) {
async function viewCustomerDetails(customerId, phoneNumber, targetProductItemId = null, fromNotification = false) {
// 重置客户数据状态
resetCustomerDataState();
@ -11269,6 +11516,40 @@
showNotification('未找到客户信息,请检查客户是否存在或数据是否加载完成', 'error');
return;
}
// 如果是从通知弹窗点击进入,更新notice状态为old
if (fromNotification) {
console.log('📋 从通知弹窗查看客户详情,更新notice状态为old');
// 发送请求更新notice状态
fetch(`/pool/customers/${customerId}/notice`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ notice: 'old' })
})
.then(response => response.json())
.then(data => {
console.log('✅ 更新客户通知状态成功,响应:', data);
// 从通知列表中移除对应的通知项
const notificationItems = document.querySelectorAll('.notification-item');
notificationItems.forEach(item => {
const customerIdElement = item.querySelector('.customer-id');
if (customerIdElement && customerIdElement.textContent.includes(customerId)) {
item.remove();
}
});
// 检查是否还有通知项
const remainingItems = document.querySelectorAll('.notification-item');
if (remainingItems.length === 0) {
const notificationContent = document.getElementById('notificationContent');
notificationContent.innerHTML = '<div class="notification-empty"><p>暂无通知</p></div>';
}
})
.catch(error => {
console.error('❌ 更新客户通知状态失败:', error);
});
}
// 处理客户数据
const { customer, dataSource, apiUsed } = customerData;
@ -13905,7 +14186,174 @@
});
// 绑定行点击事件的功能已在setupEnhancedEventDelegation中实现
// 更新客户通知状态的全局函数
function updateCustomerNotice(customerId, notificationItem) {
console.log('⚙️ 处理客户通知状态更新,客户ID:', customerId);
// 发送请求到后端更新客户的notice状态
fetch(`/supply/pool/customers/${customerId}/notice`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ notice: 'old' })
})
.then(response => response.json())
.then(data => {
console.log('✅ 更新客户通知状态成功,响应:', data);
if (data.success) {
// 从通知列表中移除该项
if (notificationItem && notificationItem.parentNode) {
notificationItem.remove();
}
// 更新通知铃铛状态
const notificationContent = document.getElementById('notificationContent');
const remainingNotifications = notificationContent.querySelectorAll('.notification-item').length;
if (remainingNotifications === 0) {
notificationContent.innerHTML = '<p style="text-align: center; color: #666;">暂无通知</p>';
}
// 重新获取数据并更新通知铃铛
if (typeof CustomerDataCache !== 'undefined') {
CustomerDataCache.refreshPublicSeaLevels();
}
}
})
.catch(error => {
console.error('❌ 更新客户通知状态失败:', error);
});
}
// 添加通知弹窗样式
const style = document.createElement('style');
style.textContent = `
/* 通知弹窗样式 */
.notification-list {
display: flex;
flex-direction: column;
gap: 16px;
padding: 16px;
}
.notification-item {
background: #ffffff;
border: 1px solid #e8e8e8;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.notification-item:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
transform: translateY(-2px);
}
.notification-header {
display: flex;
align-items: flex-start;
gap: 12px;
}
.notification-icon {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
background: #fff7e6;
border-radius: 50%;
flex-shrink: 0;
}
.notification-content {
flex: 1;
}
.notification-title {
font-size: 16px;
font-weight: 600;
margin-bottom: 8px;
transition: color 0.3s ease;
}
.notification-title:hover {
color: #40a9ff !important;
text-decoration: underline;
}
.notification-meta {
display: flex;
gap: 16px;
margin-bottom: 8px;
font-size: 14px;
color: #666666;
}
.notification-message {
font-size: 14px;
color: #ff7875;
margin-bottom: 12px;
display: flex;
align-items: center;
}
.notification-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
color: #999999;
}
.notification-time {
white-space: nowrap;
}
.notification-action-btn {
padding: 6px 16px;
background: #1890ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
transition: all 0.3s ease;
}
.notification-action-btn:hover {
background: #40a9ff;
transform: translateY(-1px);
}
.notification-empty {
display: flex;
align-items: center;
justify-content: center;
padding: 48px 24px;
color: #999999;
background: #fafafa;
border-radius: 8px;
font-size: 14px;
}
/* 适配不同屏幕尺寸 */
@media (max-width: 768px) {
.notification-meta {
flex-direction: column;
gap: 4px;
}
.notification-footer {
flex-direction: column;
gap: 8px;
align-items: flex-start;
}
}
`;
document.head.appendChild(style);
</script>
</body>

Loading…
Cancel
Save