Browse Source

修复联系人选择功能和WebSocket性能优化

Boss2
Default User 2 months ago
parent
commit
b8322ddf82
  1. 193
      Reject.js
  2. 3
      package.json
  3. 397
      supply.html

193
Reject.js

@ -3,10 +3,88 @@ const bodyParser = require('body-parser');
const cors = require('cors'); const cors = require('cors');
const mysql = require('mysql2/promise'); const mysql = require('mysql2/promise');
const path = require('path'); const path = require('path');
const http = require('http');
const WebSocket = require('ws');
const OssUploader = require('./oss-uploader'); const OssUploader = require('./oss-uploader');
const app = express(); const app = express();
const PORT = 3000; const PORT = 3000;
// 创建HTTP服务器
const server = http.createServer(app);
// 创建WebSocket服务器
const wss = new WebSocket.Server({ server });
// 客户端连接集合
const clients = new Set();
// WebSocket连接处理
wss.on('connection', (ws) => {
console.log('WebSocket客户端已连接');
// 将新连接的客户端添加到集合中
clients.add(ws);
// 接收消息处理
ws.on('message', (message) => {
try {
const data = JSON.parse(message);
console.log('收到WebSocket消息:', data);
// 处理不同类型的消息
switch (data.type) {
case 'login':
// 登录消息,存储用户信息
ws.userId = data.userId;
console.log(`用户 ${data.userId} 已登录`);
break;
case 'pong':
// 心跳响应,无需处理
break;
default:
console.log('未知消息类型:', data.type);
}
} catch (error) {
console.error('解析WebSocket消息失败:', error);
}
});
// 连接关闭处理
ws.on('close', () => {
console.log('WebSocket客户端已断开连接');
clients.delete(ws);
});
// 连接错误处理
ws.on('error', (error) => {
console.error('WebSocket连接错误:', error);
});
});
// 广播消息给所有客户端
function broadcastMessage(message) {
const messageStr = JSON.stringify(message);
clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(messageStr);
}
});
}
// 广播消息给特定用户
function broadcastToUser(userId, message) {
const messageStr = JSON.stringify(message);
clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN && client.userId === userId) {
client.send(messageStr);
}
});
}
// 启动心跳机制,每30秒发送一次ping消息
setInterval(() => {
broadcastMessage({ type: 'ping' });
// console.log('发送心跳消息: ping');
}, 30000);
// 配置CORS // 配置CORS
app.use(cors()); app.use(cors());
app.use((req, res, next) => { app.use((req, res, next) => {
@ -873,6 +951,33 @@ app.post('/api/supplies/create', async (req, res) => {
await connection.commit(); await connection.commit();
connection.release(); connection.release();
// 发送WebSocket消息通知所有客户端
broadcastMessage({
type: 'supply_create',
supplyId: result.insertId,
action: 'create',
data: {
id: result.insertId,
productId: productId,
productName,
category: category || '',
price: price.toString(),
costprice: costprice || '',
quantity,
grossWeight,
yolk,
specification,
producting: producting || '',
region,
status: 'published',
supplyStatus: supplyStatus || '',
sourceType: sourceType || '',
description: description || '',
sellerId,
imageUrls: uploadedImageUrls
}
});
sendResponse(res, true, { productId: result.insertId }, '货源创建成功'); sendResponse(res, true, { productId: result.insertId }, '货源创建成功');
} catch (error) { } catch (error) {
if (connection) { if (connection) {
@ -1023,9 +1128,9 @@ app.post('/api/supplies/:id/unpublish', async (req, res) => {
return sendResponse(res, false, null, '货源不存在'); return sendResponse(res, false, null, '货源不存在');
} }
// 更新状态为下架 // 更新状态为下架
await connection.query( await connection.query(
'UPDATE products SET status = ? WHERE id = ?', 'UPDATE products SET status = ?, updated_at = NOW() WHERE id = ?',
['sold_out', productId] ['sold_out', productId]
); );
@ -1033,6 +1138,14 @@ app.post('/api/supplies/:id/unpublish', async (req, res) => {
await connection.commit(); await connection.commit();
connection.release(); connection.release();
// 发送WebSocket消息通知所有客户端
broadcastMessage({
type: 'supply_status_change',
supplyId: productId,
action: 'unpublish',
status: 'sold_out'
});
sendResponse(res, true, null, '货源下架成功'); sendResponse(res, true, null, '货源下架成功');
} catch (error) { } catch (error) {
console.error('下架货源失败:', error.message); console.error('下架货源失败:', error.message);
@ -1052,9 +1165,9 @@ app.post('/api/supplies/:id/publish', async (req, res) => {
// 开始事务 // 开始事务
await connection.beginTransaction(); await connection.beginTransaction();
// 检查当前状态 // 检查当前状态和label
const [currentProduct] = await connection.query( const [currentProduct] = await connection.query(
'SELECT status FROM products WHERE id = ?', 'SELECT status, label FROM products WHERE id = ?',
[productId] [productId]
); );
@ -1064,6 +1177,13 @@ app.post('/api/supplies/:id/publish', async (req, res) => {
return sendResponse(res, false, null, '货源不存在'); return sendResponse(res, false, null, '货源不存在');
} }
// 检查是否被锁定
if (currentProduct[0].label === 1) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '货源已被锁定,无法上架');
}
// 更新状态为已发布(直接上架,不需要审核),同时更新updated_at和autoOfflineHours // 更新状态为已发布(直接上架,不需要审核),同时更新updated_at和autoOfflineHours
await connection.query( await connection.query(
'UPDATE products SET status = ?, updated_at = NOW() WHERE id = ?', 'UPDATE products SET status = ?, updated_at = NOW() WHERE id = ?',
@ -1074,7 +1194,15 @@ app.post('/api/supplies/:id/publish', async (req, res) => {
await connection.commit(); await connection.commit();
connection.release(); connection.release();
sendResponse(res, true, null, '货源已成功上架'); // 发送WebSocket消息通知所有客户端
broadcastMessage({
type: 'supply_status_change',
supplyId: productId,
action: 'publish',
status: 'published'
});
sendResponse(res, true, null, '货源上架成功');
} catch (error) { } catch (error) {
console.error('上架货源失败:', error.message); console.error('上架货源失败:', error.message);
console.error('错误详情:', error); console.error('错误详情:', error);
@ -1115,6 +1243,13 @@ app.post('/api/supplies/:id/delete', async (req, res) => {
await connection.commit(); await connection.commit();
connection.release(); connection.release();
// 发送WebSocket消息通知所有客户端
broadcastMessage({
type: 'supply_update',
supplyId: productId,
action: 'delete'
});
sendResponse(res, true, null, '货源删除成功'); sendResponse(res, true, null, '货源删除成功');
} catch (error) { } catch (error) {
console.error('删除货源失败:', error.message); console.error('删除货源失败:', error.message);
@ -1155,7 +1290,7 @@ app.put('/api/supplies/:id', async (req, res) => {
// 更新状态 // 更新状态
await connection.query( await connection.query(
'UPDATE products SET status = ? WHERE id = ?', 'UPDATE products SET status = ?, updated_at = NOW() WHERE id = ?',
[status, productId] [status, productId]
); );
@ -1163,7 +1298,15 @@ app.put('/api/supplies/:id', async (req, res) => {
await connection.commit(); await connection.commit();
connection.release(); connection.release();
sendResponse(res, true, null, '货源状态更新成功'); // 发送WebSocket消息通知所有客户端
broadcastMessage({
type: 'supply_status_change',
supplyId: productId,
action: 'update_status',
status: status
});
sendResponse(res, true, null, '状态更新成功');
} catch (error) { } catch (error) {
console.error('更新货源状态失败:', error.message); console.error('更新货源状态失败:', error.message);
console.error('错误详情:', error); console.error('错误详情:', error);
@ -1183,9 +1326,9 @@ app.put('/api/supplies/:id/edit', async (req, res) => {
// 开始事务 // 开始事务
await connection.beginTransaction(); await connection.beginTransaction();
// 检查当前状态 // 检查当前状态和label
const [currentProduct] = await connection.query( const [currentProduct] = await connection.query(
'SELECT status FROM products WHERE id = ?', 'SELECT status, label FROM products WHERE id = ?',
[productId] [productId]
); );
@ -1195,6 +1338,13 @@ app.put('/api/supplies/:id/edit', async (req, res) => {
return sendResponse(res, false, null, '货源不存在'); return sendResponse(res, false, null, '货源不存在');
} }
// 检查是否被锁定
if (currentProduct[0].label === 1) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '货源已被锁定,无法编辑');
}
// 处理联系人信息 // 处理联系人信息
let productContact = ''; let productContact = '';
let contactPhone = ''; let contactPhone = '';
@ -1298,6 +1448,28 @@ app.put('/api/supplies/:id/edit', async (req, res) => {
await connection.commit(); await connection.commit();
connection.release(); connection.release();
// 发送WebSocket消息通知所有客户端
broadcastMessage({
type: 'supply_update',
supplyId: productId,
action: 'update',
data: {
id: productId,
productName,
price: price.toString(),
costprice: costprice || '',
quantity,
grossWeight,
yolk,
specification,
supplyStatus,
description,
region,
producting,
imageUrls: uploadedImageUrls.length > 0 ? uploadedImageUrls : imageUrls
}
});
sendResponse(res, true, null, '货源编辑成功'); sendResponse(res, true, null, '货源编辑成功');
} catch (error) { } catch (error) {
console.error('编辑货源失败:', error.message); console.error('编辑货源失败:', error.message);
@ -1392,9 +1564,10 @@ async function startServer() {
try { try {
await initDatabase(); await initDatabase();
app.listen(PORT, () => { server.listen(PORT, () => {
console.log(`服务器已启动,监听端口 ${PORT}`); console.log(`服务器已启动,监听端口 ${PORT}`);
console.log(`访问地址: http://localhost:${PORT}`); console.log(`访问地址: http://localhost:${PORT}`);
console.log(`WebSocket服务已启动,端口: ${PORT}`);
}); });
} catch (error) { } catch (error) {
console.error('服务器启动失败:', error.message); console.error('服务器启动失败:', error.message);

3
package.json

@ -6,7 +6,8 @@
"cors": "^2.8.5", "cors": "^2.8.5",
"express": "^5.1.0", "express": "^5.1.0",
"mysql2": "^3.15.3", "mysql2": "^3.15.3",
"sharp": "^0.34.5" "sharp": "^0.34.5",
"ws": "^8.19.0"
}, },
"devDependencies": { "devDependencies": {
"chai": "^6.2.1", "chai": "^6.2.1",

397
supply.html

@ -2054,6 +2054,7 @@
// 联系人数据 // 联系人数据
let contacts = []; let contacts = [];
let isLoadingContacts = false; // 标记联系人是否正在加载中
// 登录检查 // 登录检查
function checkLogin() { function checkLogin() {
@ -2090,6 +2091,154 @@
return parsedUserInfo; return parsedUserInfo;
} }
// WebSocket连接管理
let ws = null;
let wsReconnectAttempts = 0;
const maxReconnectAttempts = 5;
const reconnectDelay = 3000; // 3秒
// 保存定时器引用,方便后续清理
let timers = {
loadSupplies: null,
updateCountdowns: null,
checkAutoOffline: null
};
// 防止loadSupplies并发执行的标志
let isLoadingSupplies = false;
// 初始化WebSocket连接
function initWebSocket() {
// 获取当前页面的协议和主机
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const host = window.location.host;
const wsUrl = `${protocol}//${host}/ws`;
console.log('正在尝试连接WebSocket服务器:', wsUrl);
// 关闭现有的WebSocket连接,避免连接泄漏
if (ws) {
try {
// 关闭码1000表示正常关闭
ws.close(1000, '重新连接');
ws = null;
} catch (error) {
console.error('关闭旧WebSocket连接失败:', error);
ws = null;
}
}
try {
ws = new WebSocket(wsUrl);
// 连接打开事件
ws.onopen = function() {
console.log('WebSocket连接已打开');
wsReconnectAttempts = 0;
// 发送登录用户信息
const userInfo = JSON.parse(localStorage.getItem('userInfo'));
if (userInfo) {
ws.send(JSON.stringify({
type: 'login',
userId: userInfo.userId || userInfo.id
}));
}
};
// 消息接收事件
ws.onmessage = function(event) {
console.log('收到WebSocket消息:', event.data);
try {
handleWebSocketMessage(event.data);
} catch (error) {
console.error('处理WebSocket消息失败:', error);
}
};
// 连接关闭事件
ws.onclose = function(event) {
console.log('WebSocket连接已关闭:', event.code, event.reason);
// 只有在非正常关闭时才重连
// 正常关闭码1000表示主动关闭,不需要重连
if (event.code !== 1000) {
attemptReconnect();
}
};
// 连接错误事件
ws.onerror = function(error) {
console.error('WebSocket错误:', error);
console.error('WebSocket错误详情:', error.message);
// 错误发生后,onclose事件会被触发,由onclose处理重连
};
} catch (error) {
console.error('WebSocket连接失败:', error);
attemptReconnect();
}
}
// 尝试重新连接
function attemptReconnect() {
if (wsReconnectAttempts < maxReconnectAttempts) {
wsReconnectAttempts++;
console.log(`尝试重新连接WebSocket (${wsReconnectAttempts}/${maxReconnectAttempts})...`);
setTimeout(initWebSocket, reconnectDelay);
} else {
console.error('WebSocket重连失败,已达到最大重试次数');
}
}
// 处理WebSocket消息
function handleWebSocketMessage(message) {
try {
const data = JSON.parse(message);
console.log('处理WebSocket消息:', data);
switch (data.type) {
case 'supply_update':
// 货源更新通知,重新加载数据
console.log('收到货源更新通知,重新加载数据');
loadSupplies();
break;
case 'supply_lock':
// 货源锁定状态更新
console.log('收到货源锁定状态更新,重新加载数据');
loadSupplies();
break;
case 'supply_status_change':
// 货源状态变更
console.log('收到货源状态变更,重新加载数据');
loadSupplies();
break;
case 'auto_offline':
// 自动下架通知
console.log('收到自动下架通知,重新加载数据');
loadSupplies();
break;
case 'ping':
// 心跳响应
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'pong' }));
}
break;
default:
console.log('未知的WebSocket消息类型:', data.type);
}
} catch (error) {
console.error('解析WebSocket消息失败:', error);
}
}
// 向WebSocket服务器发送消息
function sendWebSocketMessage(message) {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(message));
} else {
console.warn('WebSocket未连接,无法发送消息');
}
}
// 初始化 // 初始化
window.onload = function() { window.onload = function() {
// 登录检查 // 登录检查
@ -2098,6 +2247,9 @@
return; return;
} }
// 初始化WebSocket连接
initWebSocket();
loadSupplies(); loadSupplies();
loadContacts(); loadContacts();
loadFormData(); // 加载保存的表单数据,包括联系人信息 loadFormData(); // 加载保存的表单数据,包括联系人信息
@ -2111,48 +2263,70 @@
startCountdowns(); startCountdowns();
// 启动自动检查下架时间 // 启动自动检查下架时间
startAutoOfflineCheck(); startAutoOfflineCheck();
// 启动定期刷新数据,确保label字段变化能实时显示
timers.loadSupplies = setInterval(loadSupplies, 5000); // 每5秒刷新一次
}; };
// 页面卸载时清理资源
window.addEventListener('beforeunload', function() {
console.log('页面正在卸载,清理资源...');
// 关闭WebSocket连接
if (ws) {
ws.close(1000, '页面卸载');
ws = null;
}
// 清除所有定时器
for (const timerName in timers) {
if (timers[timerName]) {
clearInterval(timers[timerName]);
timers[timerName] = null;
}
}
});
// 启动倒计时 // 启动倒计时
function startCountdowns() { function startCountdowns() {
// 立即更新一次倒计时 // 立即更新一次倒计时
updateCountdowns(); updateCountdowns();
// 每秒更新一次倒计时 // 每秒更新一次倒计时
setInterval(updateCountdowns, 1000); timers.updateCountdowns = setInterval(updateCountdowns, 1000);
} }
// 更新倒计时显示 // 更新倒计时显示
function updateCountdowns() { function updateCountdowns() {
console.log('=== 更新倒计时 ==='); // console.log('=== 更新倒计时 ===');
// 更新所有倒计时元素 // 更新所有倒计时元素
const countdownElements = document.querySelectorAll('.countdown-badge, .countdown'); const countdownElements = document.querySelectorAll('.countdown-badge, .countdown');
console.log('找到的倒计时元素数量:', countdownElements.length); // console.log('找到的倒计时元素数量:', countdownElements.length);
countdownElements.forEach(element => { countdownElements.forEach(element => {
const supplyId = element.dataset.id; const supplyId = element.dataset.id;
console.log('处理倒计时元素,supplyId:', supplyId); // console.log('处理倒计时元素,supplyId:', supplyId);
// 查找对应的货源 // 查找对应的货源
const supply = supplyData.supplies.find(s => String(s.id) === String(supplyId)); const supply = supplyData.supplies.find(s => String(s.id) === String(supplyId));
if (supply) { if (supply) {
console.log('找到对应的货源:', supply.id, supply.productName); // console.log('找到对应的货源:', supply.id, supply.productName);
// 检查是否有显式设置的自动下架时间 // 检查是否有显式设置的自动下架时间
if (supply.autoOfflineHours && supply.autoOfflineHours !== '' && supply.autoOfflineHours !== null) { if (supply.autoOfflineHours && supply.autoOfflineHours !== '' && supply.autoOfflineHours !== null) {
console.log('有显式设置的自动下架时间'); // console.log('有显式设置的自动下架时间');
// 计算下架时间:updated_at + autoOfflineHours(使用毫秒数计算,更精确) // 计算下架时间:updated_at + autoOfflineHours(使用毫秒数计算,更精确)
const updatedAt = new Date(supply.updated_at || supply.created_at); const updatedAt = new Date(supply.updated_at || supply.created_at);
console.log('updatedAt:', updatedAt, 'supply.updated_at:', supply.updated_at, 'supply.created_at:', supply.created_at); // console.log('updatedAt:', updatedAt, 'supply.updated_at:', supply.updated_at, 'supply.created_at:', supply.created_at);
const autoOfflineHours = parseFloat(supply.autoOfflineHours); const autoOfflineHours = parseFloat(supply.autoOfflineHours);
console.log('autoOfflineHours:', autoOfflineHours, 'supply.autoOfflineHours:', supply.autoOfflineHours); // console.log('autoOfflineHours:', autoOfflineHours, 'supply.autoOfflineHours:', supply.autoOfflineHours);
// 将小时转换为毫秒,使用setTime方法更精确 // 将小时转换为毫秒,使用setTime方法更精确
const autoOfflineMs = autoOfflineHours * 60 * 60 * 1000; const autoOfflineMs = autoOfflineHours * 60 * 60 * 1000;
const offlineTime = new Date(updatedAt.getTime() + autoOfflineMs); const offlineTime = new Date(updatedAt.getTime() + autoOfflineMs);
console.log('offlineTime:', offlineTime); // console.log('offlineTime:', offlineTime);
const now = new Date(); const now = new Date();
console.log('当前时间:', now); // console.log('当前时间:', now);
const timeDiff = offlineTime - now; const timeDiff = offlineTime - now;
console.log('时间差:', timeDiff, '毫秒'); // console.log('时间差:', timeDiff, '毫秒');
if (timeDiff > 0) { if (timeDiff > 0) {
// 计算剩余下架时间 // 计算剩余下架时间
@ -2214,45 +2388,45 @@
// 启动自动检查下架时间 // 启动自动检查下架时间
function startAutoOfflineCheck() { function startAutoOfflineCheck() {
// 每30秒检查一次,平衡及时性和服务器负载 // 每30秒检查一次,平衡及时性和服务器负载
setInterval(checkAutoOffline, 30000); timers.checkAutoOffline = setInterval(checkAutoOffline, 30000);
// 立即检查一次 // 立即检查一次
checkAutoOffline(); checkAutoOffline();
} }
// 检查自动下架时间 // 检查自动下架时间
async function checkAutoOffline() { async function checkAutoOffline() {
console.log('=== 开始检查自动下架时间 ==='); // console.log('=== 开始检查自动下架时间 ===');
const allSupplies = supplyData.supplies; const allSupplies = supplyData.supplies;
const now = new Date(); const now = new Date();
console.log('检查的货源总数:', allSupplies.length); // console.log('检查的货源总数:', allSupplies.length);
for (const supply of allSupplies) { for (const supply of allSupplies) {
console.log('检查货源:', supply.id, supply.productName, '当前状态:', supply.status); // console.log('检查货源:', supply.id, supply.productName, '当前状态:', supply.status);
// 检查所有状态为published的货源,不管前端显示什么 // 检查所有状态为published的货源,不管前端显示什么
if (supply.status === 'published') { if (supply.status === 'published') {
console.log('处理上架状态的货源:', supply.id); // console.log('处理上架状态的货源:', supply.id);
// 只有在显式设置了自动下架时间的情况下才应用自动下架逻辑 // 只有在显式设置了自动下架时间的情况下才应用自动下架逻辑
if (supply.autoOfflineHours && supply.autoOfflineHours !== '' && supply.autoOfflineHours !== null) { if (supply.autoOfflineHours && supply.autoOfflineHours !== '' && supply.autoOfflineHours !== null) {
console.log('有显式设置的自动下架时间'); // console.log('有显式设置的自动下架时间');
// 计算下架时间:updated_at + autoOfflineHours(使用毫秒数计算,更精确) // 计算下架时间:updated_at + autoOfflineHours(使用毫秒数计算,更精确)
const updatedAt = new Date(supply.updated_at || supply.created_at); const updatedAt = new Date(supply.updated_at || supply.created_at);
const autoOfflineHours = parseFloat(supply.autoOfflineHours); // 只转换,不设置默认值 const autoOfflineHours = parseFloat(supply.autoOfflineHours); // 只转换,不设置默认值
console.log('autoOfflineHours:', autoOfflineHours); // console.log('autoOfflineHours:', autoOfflineHours);
// 将小时转换为毫秒,使用setTime方法更精确 // 将小时转换为毫秒,使用setTime方法更精确
const autoOfflineMs = autoOfflineHours * 60 * 60 * 1000; const autoOfflineMs = autoOfflineHours * 60 * 60 * 1000;
const offlineTime = new Date(updatedAt.getTime() + autoOfflineMs); const offlineTime = new Date(updatedAt.getTime() + autoOfflineMs);
console.log('计算的下架时间:', offlineTime, '当前时间:', now); // console.log('计算的下架时间:', offlineTime, '当前时间:', now);
if (now >= offlineTime) { if (now >= offlineTime) {
// 时间到了,修改状态 // 时间到了,修改状态
console.log('时间到了,调用updateSupplyStatus:', supply.id, '从published改为sold_out'); // console.log('时间到了,调用updateSupplyStatus:', supply.id, '从published改为sold_out');
await updateSupplyStatus(supply.id, 'sold_out'); await updateSupplyStatus(supply.id, 'sold_out');
} }
} else { } else {
console.log('没有显式设置自动下架时间,跳过自动下架检查'); // console.log('没有显式设置自动下架时间,跳过自动下架检查');
} }
} }
} }
@ -2271,6 +2445,14 @@
}); });
if (response.ok) { if (response.ok) {
// 向WebSocket服务器发送消息,通知其他客户端货源状态已更新
sendWebSocketMessage({
type: 'supply_status_change',
supplyId: supplyId,
action: 'update_status',
status: status
});
// 重新加载货源列表 // 重新加载货源列表
loadSupplies(); loadSupplies();
console.log(`货源 ${supplyId} 状态已更新为 ${status}`); console.log(`货源 ${supplyId} 状态已更新为 ${status}`);
@ -2975,8 +3157,23 @@
contactIdSelectModal.classList.add('active'); contactIdSelectModal.classList.add('active');
// 重置搜索输入 // 重置搜索输入
document.getElementById('contactIdSearchInput').value = ''; document.getElementById('contactIdSearchInput').value = '';
filteredContactIdOptions = [...contacts];
generateContactIdOptions(); // 检查contacts数组是否为空,如果为空则尝试重新加载
if (contacts.length === 0) {
console.log('contacts数组为空,尝试重新加载...');
loadContacts().then(() => {
filteredContactIdOptions = [...contacts];
generateContactIdOptions();
}).catch(error => {
console.error('重新加载联系人失败:', error);
// 即使加载失败,也要确保UI更新
filteredContactIdOptions = [...contacts];
generateContactIdOptions();
});
} else {
filteredContactIdOptions = [...contacts];
generateContactIdOptions();
}
} }
// 隐藏联系人选择弹窗 // 隐藏联系人选择弹窗
@ -3841,15 +4038,25 @@
// 加载联系人数据 // 加载联系人数据
async function loadContacts() { async function loadContacts() {
// 防止并发执行
if (isLoadingContacts) {
console.log('loadContacts已在执行中,跳过当前请求');
return Promise.resolve();
}
isLoadingContacts = true;
try { try {
console.log('开始加载联系人数据...');
const response = await fetch('/api/contacts'); const response = await fetch('/api/contacts');
if (!response.ok) { if (!response.ok) {
throw new Error('服务器响应异常'); throw new Error(`服务器响应异常: ${response.status} ${response.statusText}`);
} }
const result = await response.json(); const result = await response.json();
console.log('联系人API返回结果:', result);
// 确保contacts是一个数组 // 确保contacts是一个数组
contacts = result.data || []; contacts = result.data || [];
console.log('联系人数据加载成功:', contacts); console.log('联系人数据加载成功,共', contacts.length, '个联系人:', contacts);
// 保存到本地缓存,添加时间戳和版本号 // 保存到本地缓存,添加时间戳和版本号
const contactsCache = { const contactsCache = {
@ -3872,7 +4079,7 @@
const cacheExpiry = 7 * 24 * 60 * 60 * 1000; const cacheExpiry = 7 * 24 * 60 * 60 * 1000;
if (Date.now() - contactsCache.timestamp < cacheExpiry) { if (Date.now() - contactsCache.timestamp < cacheExpiry) {
contacts = contactsCache.data || []; contacts = contactsCache.data || [];
console.log('从本地缓存加载联系人数据:', contacts); console.log('从本地缓存加载联系人数据,共', contacts.length, '个联系人:', contacts);
updateContactSelects(); updateContactSelects();
return; return;
} else { } else {
@ -3885,6 +4092,9 @@
// 出错且无有效缓存时,将contacts设为空数组 // 出错且无有效缓存时,将contacts设为空数组
contacts = []; contacts = [];
} finally {
// 无论成功还是失败,都要重置加载状态
isLoadingContacts = false;
} }
} }
@ -3922,6 +4132,14 @@
// 加载货源列表 // 加载货源列表
async function loadSupplies() { async function loadSupplies() {
// 防止并发执行
if (isLoadingSupplies) {
console.log('loadSupplies已在执行中,跳过当前请求');
return;
}
isLoadingSupplies = true;
try { try {
// 获取当前登录用户信息 // 获取当前登录用户信息
const userInfo = checkLogin(); const userInfo = checkLogin();
@ -3962,6 +4180,9 @@
} }
} catch (error) { } catch (error) {
console.error('加载货源失败:', error); console.error('加载货源失败:', error);
} finally {
// 无论成功还是失败,都要重置加载状态
isLoadingSupplies = false;
} }
} }
@ -5512,6 +5733,15 @@
// 重置表单状态,确保下次打开时是空白状态 // 重置表单状态,确保下次打开时是空白状态
resetForm(); resetForm();
hideAddSupplyModal(); hideAddSupplyModal();
// 向WebSocket服务器发送消息,通知其他客户端有新的货源创建
sendWebSocketMessage({
type: 'supply_create',
supplyId: result.data.id,
action: 'create',
data: result.data
});
loadSupplies(); loadSupplies();
} else { } else {
// 创建失败,保持保存的数据 // 创建失败,保持保存的数据
@ -5890,6 +6120,14 @@
}); });
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
// 向WebSocket服务器发送消息,通知其他客户端货源已下架
sendWebSocketMessage({
type: 'supply_status_change',
supplyId: id,
action: 'unpublish',
status: 'hidden'
});
alert('下架成功'); alert('下架成功');
loadSupplies(); loadSupplies();
} else { } else {
@ -5912,6 +6150,13 @@
}); });
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
// 向WebSocket服务器发送消息,通知其他客户端货源已删除
sendWebSocketMessage({
type: 'supply_update',
supplyId: id,
action: 'delete'
});
alert('删除成功'); alert('删除成功');
loadSupplies(); loadSupplies();
} else { } else {
@ -5972,6 +6217,9 @@
return; return;
} }
// 检查是否被锁定
const isLocked = supply.label === 1;
currentEditSupplyId = id; currentEditSupplyId = id;
// 保存发布模式状态 // 保存发布模式状态
window.currentEditPublishMode = publishMode; window.currentEditPublishMode = publishMode;
@ -6122,18 +6370,68 @@
// 保存当前图片列表到全局变量,用于编辑时使用 // 保存当前图片列表到全局变量,用于编辑时使用
editCurrentImages = supply.imageUrls && Array.isArray(supply.imageUrls) ? [...supply.imageUrls] : []; editCurrentImages = supply.imageUrls && Array.isArray(supply.imageUrls) ? [...supply.imageUrls] : [];
// 根据模式修改保存按钮文本 // 根据模式修改保存按钮文本和状态
const saveButton = document.querySelector('#editSupplyModal .modal-btn-primary'); const saveButton = document.querySelector('#editSupplyModal .modal-btn-primary');
if (saveButton) { if (saveButton) {
if (publishMode) { if (isLocked) {
saveButton.textContent = '上架'; // 锁定状态:按钮变灰色,不可点击
saveButton.onclick = publishSupplyAfterEdit; saveButton.textContent = publishMode ? '已锁定' : '已锁定';
saveButton.className = 'btn-disabled';
saveButton.onclick = null;
} else { } else {
saveButton.textContent = '保存'; // 未锁定状态:正常功能
saveButton.onclick = saveEditSupply; if (publishMode) {
saveButton.textContent = '上架';
saveButton.className = 'btn-primary';
saveButton.onclick = publishSupplyAfterEdit;
} else {
saveButton.textContent = '保存';
saveButton.className = 'btn-primary';
saveButton.onclick = saveEditSupply;
}
} }
} }
// 设置表单元素的只读状态
const formElements = document.querySelectorAll('#editSupplyModal input, #editSupplyModal select, #editSupplyModal textarea, #editSupplyModal .modal-select-btn, #editSupplyModal .add-quantity-btn, #editSupplyModal .remove-quantity-btn, #editSupplyModal .delete-image-btn');
formElements.forEach(element => {
if (isLocked) {
// 锁定状态:设置为只读或禁用
if (element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA') {
element.readOnly = true;
element.style.opacity = '0.6';
element.style.cursor = 'not-allowed';
} else {
// 按钮元素
element.disabled = true;
element.className = 'btn-disabled';
element.style.cursor = 'not-allowed';
}
} else {
// 未锁定状态:恢复正常
if (element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA') {
// 只有规格输入框是只读的,其他可以编辑
if (element.className.includes('spec-value')) {
element.readOnly = true;
} else {
element.readOnly = false;
}
element.style.opacity = '1';
element.style.cursor = 'default';
} else {
// 按钮元素
element.disabled = false;
// 恢复按钮原有样式
if (element.className.includes('modal-select-btn')) {
element.className = 'modal-select-btn';
} else if (element.className.includes('add-quantity-btn')) {
element.className = 'add-quantity-btn';
}
element.style.cursor = 'pointer';
}
}
});
// 显示编辑模态框 // 显示编辑模态框
document.getElementById('editSupplyModal').classList.add('active'); document.getElementById('editSupplyModal').classList.add('active');
// 隐藏body滚动条,避免双滚动条 // 隐藏body滚动条,避免双滚动条
@ -6186,6 +6484,14 @@
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
// 向WebSocket服务器发送消息,通知其他客户端货源已上架
sendWebSocketMessage({
type: 'supply_status_change',
supplyId: id,
action: 'publish',
status: 'published'
});
alert('上架成功'); alert('上架成功');
loadSupplies(); loadSupplies();
} else { } else {
@ -7003,8 +7309,23 @@
const contactIdSelectModal = document.getElementById('editContactIdSelectModal'); const contactIdSelectModal = document.getElementById('editContactIdSelectModal');
contactIdSelectModal.classList.add('active'); contactIdSelectModal.classList.add('active');
document.getElementById('editContactIdSearchInput').value = ''; document.getElementById('editContactIdSearchInput').value = '';
editFilteredContacts = [...contacts];
generateEditContactIdOptions(); // 检查contacts数组是否为空,如果为空则尝试重新加载
if (contacts.length === 0) {
console.log('contacts数组为空,尝试重新加载...');
loadContacts().then(() => {
editFilteredContacts = [...contacts];
generateEditContactIdOptions();
}).catch(error => {
console.error('重新加载联系人失败:', error);
// 即使加载失败,也要确保UI更新
editFilteredContacts = [...contacts];
generateEditContactIdOptions();
});
} else {
editFilteredContacts = [...contacts];
generateEditContactIdOptions();
}
} }
// 隐藏编辑联系人选择弹窗 // 隐藏编辑联系人选择弹窗
@ -7203,6 +7524,14 @@
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
// 向WebSocket服务器发送消息,通知其他客户端货源已更新
sendWebSocketMessage({
type: 'supply_update',
supplyId: currentEditSupplyId,
action: 'update',
data: result.data
});
// 如果是直接编辑,显示成功消息;如果是编辑后上架,不显示,由上架函数处理 // 如果是直接编辑,显示成功消息;如果是编辑后上架,不显示,由上架函数处理
if (!window.currentEditPublishMode) { if (!window.currentEditPublishMode) {
alert('编辑成功'); alert('编辑成功');

Loading…
Cancel
Save