diff --git a/Management.html b/Management.html
index 5326549..93d382c 100644
--- a/Management.html
+++ b/Management.html
@@ -123,7 +123,7 @@
.supplies-grid {
display: grid;
- grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
+ grid-template-columns: repeat(auto-fill, minmax(min(100%, 350px), 1fr));
gap: 20px;
}
@@ -139,12 +139,142 @@
transform: translateY(-2px);
}
- .supply-card img {
+ .supply-card img,
+ .supply-card video {
max-width: 100%;
- height: 200px;
- object-fit: cover;
+ height: 180px;
+ object-fit: contain;
border-radius: 4px;
margin-bottom: 10px;
+ background-color: #f5f5f5;
+ }
+
+ /* 响应式设计:调整小屏幕设备上的样式 */
+ @media (max-width: 768px) {
+ .container {
+ padding: 10px;
+ }
+
+ .title-bar {
+ padding: 12px 15px;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 10px;
+ }
+
+ .title-bar h1 {
+ font-size: 20px;
+ }
+
+ .title-bar-actions {
+ flex-wrap: wrap;
+ gap: 6px;
+ width: 100%;
+ }
+
+ .title-bar-actions button {
+ padding: 6px 12px;
+ font-size: 13px;
+ }
+
+ .title-bar-actions input[type="date"] {
+ padding: 4px 8px;
+ font-size: 13px;
+ }
+
+ .chart-container {
+ height: 300px;
+ }
+
+ .stats-grid {
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
+ gap: 15px;
+ }
+
+ .stat-number {
+ font-size: 28px;
+ }
+
+ .stat-label {
+ font-size: 13px;
+ }
+
+ .supplies-grid {
+ gap: 15px;
+ }
+
+ .supply-card {
+ padding: 12px;
+ }
+
+ .supply-card img,
+ .supply-card video {
+ height: 150px;
+ }
+
+ .supply-title {
+ font-size: 15px;
+ margin-bottom: 8px;
+ }
+
+ .supply-info {
+ font-size: 13px;
+ }
+ }
+
+ @media (max-width: 480px) {
+ .title-bar h1 {
+ font-size: 18px;
+ }
+
+ .title-bar-actions {
+ gap: 4px;
+ }
+
+ .title-bar-actions button {
+ padding: 5px 10px;
+ font-size: 12px;
+ }
+
+ .title-bar-actions input[type="date"] {
+ padding: 3px 6px;
+ font-size: 12px;
+ }
+
+ .chart-container {
+ height: 250px;
+ }
+
+ .stats-grid {
+ grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
+ gap: 12px;
+ }
+
+ .stat-number {
+ font-size: 24px;
+ }
+
+ .stat-label {
+ font-size: 12px;
+ }
+
+ .supplies-grid {
+ grid-template-columns: 1fr;
+ gap: 12px;
+ }
+
+ .supply-card img,
+ .supply-card video {
+ height: 140px;
+ }
+
+ .supply-title {
+ font-size: 14px;
+ }
+
+ .supply-info {
+ font-size: 12px;
+ }
}
.supply-info {
@@ -849,7 +979,7 @@
// 动态计算Y轴最大值,为顶部数字留出空间
max: function(context) {
const max = Math.max(...context.chart.data.datasets[0].data);
- return max + 1;
+ return max + 2; // 增加Y轴最大高度,为数值标签留出更多空间
}
},
x: {
diff --git a/Reject.js b/Reject.js
index f6aa494..4d2640c 100644
--- a/Reject.js
+++ b/Reject.js
@@ -997,7 +997,8 @@ app.post('/api/supplies/create', async (req, res) => {
];
}
- result = await connection.query(insertQuery, insertParams);
+ const queryResult = await connection.query(insertQuery, insertParams);
+ result = queryResult[0];
} catch (insertError) {
await connection.rollback();
connection.release();
@@ -1037,7 +1038,33 @@ app.post('/api/supplies/create', async (req, res) => {
}
});
- sendResponse(res, true, { productId: result.insertId }, '货源创建成功');
+ // 返回完整的货源数据,包括ID和所有字段
+ sendResponse(res, true, {
+ id: result.insertId,
+ productId: productId,
+ productName,
+ category: category || '',
+ freshness: req.body.freshness || '',
+ costprice: costprice || '',
+ quantity,
+ grossWeight,
+ yolk,
+ specification,
+ producting: producting || '',
+ region,
+ status: 'published',
+ supplyStatus: supplyStatus || '',
+ sourceType: sourceType || '',
+ description: description || '',
+ sellerId,
+ imageUrls: uploadedImageUrls,
+ created_at: new Date(),
+ product_contact: productContact,
+ contact_phone: contactPhone,
+ autoOfflineTime: req.body.autoOfflineTime,
+ autoOfflineDays: req.body.autoOfflineDays,
+ autoOfflineHours: req.body.autoOfflineHours
+ }, '货源创建成功');
} catch (error) {
if (connection) {
try {
@@ -1193,6 +1220,9 @@ app.post('/api/supplies/:id/unpublish', async (req, res) => {
['sold_out', productId]
);
+ // 查询更新后的完整货源数据
+ const [updatedProduct] = await connection.query('SELECT * FROM products WHERE id = ?', [productId]);
+
// 提交事务
await connection.commit();
connection.release();
@@ -1205,7 +1235,8 @@ app.post('/api/supplies/:id/unpublish', async (req, res) => {
status: 'sold_out'
});
- sendResponse(res, true, null, '货源下架成功');
+ // 返回更新后的完整货源数据
+ sendResponse(res, true, updatedProduct[0], '货源下架成功');
} catch (error) {
console.error('下架货源失败:', error.message);
console.error('错误详情:', error);
@@ -1298,6 +1329,9 @@ app.post('/api/supplies/:id/delete', async (req, res) => {
['hidden', productId]
);
+ // 查询更新后的完整货源数据
+ const [updatedProduct] = await connection.query('SELECT * FROM products WHERE id = ?', [productId]);
+
// 提交事务
await connection.commit();
connection.release();
@@ -1306,10 +1340,12 @@ app.post('/api/supplies/:id/delete', async (req, res) => {
broadcastMessage({
type: 'supply_update',
supplyId: productId,
- action: 'update'
+ action: 'update',
+ status: 'hidden'
});
- sendResponse(res, true, null, '货源已删除');
+ // 返回更新后的完整货源数据
+ sendResponse(res, true, updatedProduct[0], '货源已删除');
} catch (error) {
console.error('删除货源失败:', error.message);
console.error('错误详情:', error);
@@ -1591,7 +1627,40 @@ app.put('/api/supplies/:id/edit', async (req, res) => {
}
});
- sendResponse(res, true, null, '货源编辑成功');
+ // 查询修改后的完整货源数据(在事务提交前查询,使用同一个连接)
+ const [updatedProduct] = await connection.query('SELECT * FROM products WHERE id = ?', [productId]);
+
+ // 提交事务
+ await connection.commit();
+ connection.release();
+
+ // 发送WebSocket消息通知所有客户端
+ broadcastMessage({
+ type: 'supply_update',
+ supplyId: productId,
+ action: 'update',
+ data: {
+ id: productId,
+ productName,
+ // 不再使用price字段,移除price字段
+ costprice: costprice || '',
+ quantity,
+ grossWeight,
+ yolk,
+ specification,
+ category,
+ sourceType,
+ freshness,
+ producting,
+ supplyStatus,
+ description,
+ region,
+ imageUrls: uploadedImageUrls.length > 0 ? uploadedImageUrls : imageUrls
+ }
+ });
+
+ // 返回完整的修改后货源数据
+ sendResponse(res, true, updatedProduct[0], '货源编辑成功');
} catch (error) {
console.error('编辑货源失败:', error.message);
console.error('错误详情:', error);
@@ -1859,7 +1928,106 @@ app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'login.html'));
});
-
+// 记录货源操作日志API
+app.post('/api/supplies/log', async (req, res) => {
+ try {
+ const { phoneNumber, projectName, operationEvent, productId, originalData, modifiedData, changedFields, userId } = req.body;
+
+ // 验证必填字段
+ if (!phoneNumber || !projectName || !operationEvent || !productId || !userId) {
+ return sendResponse(res, false, null, '缺少必填字段');
+ }
+
+ // 1. 查询personnel表获取完整信息
+ const userLoginConnection = await userLoginPool.getConnection();
+ // 使用SELECT * 确保获取所有字段,包括company和organization
+ const [personnelResult] = await userLoginConnection.query(
+ 'SELECT * FROM Personnel WHERE phoneNumber = ?',
+ [phoneNumber]
+ );
+ userLoginConnection.release();
+
+ // 打印查询结果,查看是否包含company和organization字段
+ console.log('查询到的personnel信息:', personnelResult[0]);
+
+ // 自动计算变更字段
+ let calculatedChangedFields = changedFields;
+ if (operationEvent.includes('编辑') && originalData && modifiedData && !changedFields) {
+ // 比较两个对象的差异,返回变更的字段列表
+ const getChangedFields = (oldObj, newObj) => {
+ if (!oldObj || !newObj) return [];
+ const changed = [];
+ const allKeys = new Set([...Object.keys(oldObj), ...Object.keys(newObj)]);
+ allKeys.forEach(key => {
+ if (JSON.stringify(oldObj[key]) !== JSON.stringify(newObj[key])) {
+ changed.push(key);
+ }
+ });
+ return changed;
+ };
+ calculatedChangedFields = getChangedFields(originalData, modifiedData);
+ }
+
+ // 2. 准备日志数据
+ const logData = {
+ tracompany: '',
+ tradepartment: '',
+ traorganization: '',
+ trarole: projectName || '',
+ trauserName: '',
+ traassistant: '', // 暂无协助人信息
+ userId: userId, // 使用前端传递的正确userId
+ operationEvent: operationEvent,
+ operationTime: new Date(),
+ originalData: originalData ? JSON.stringify(originalData) : null,
+ modifiedData: modifiedData ? JSON.stringify(modifiedData) : null,
+ changedFields: calculatedChangedFields ? JSON.stringify(calculatedChangedFields) : null
+ };
+
+ // 如果找到了员工信息,使用真实的员工数据
+ if (personnelResult.length > 0) {
+ const personnelInfo = personnelResult[0];
+ // 检查personnelInfo中的字段名,确保使用正确的字段名
+ console.log('personnelInfo的所有字段:', Object.keys(personnelInfo));
+
+ // 使用正确的字段名:managercompany(公司)、managerdepartment(部门)、organization(组织)
+ logData.tracompany = personnelInfo.managercompany || personnelInfo.managerCompany || personnelInfo.MANAGERCOMPANY || '';
+ logData.tradepartment = personnelInfo.managerdepartment || personnelInfo.managerDepartment || personnelInfo.MANAGERDEPARTMENT || '';
+ logData.traorganization = personnelInfo.organization || personnelInfo.Organization || personnelInfo.ORGANIZATION || personnelInfo.org || '';
+ logData.trauserName = personnelInfo.name || personnelInfo.Name || personnelInfo.NAME || personnelInfo.alias || personnelInfo.Alias || personnelInfo.ALIAS || '';
+ }
+
+ // 3. 插入日志到informationtra表
+ const connection = await pool.getConnection();
+ const [result] = await connection.query(
+ `INSERT INTO informationtra (
+ tracompany, tradepartment, traorganization, trarole,
+ trauserName, traassistant, userId, operationEvent,
+ operationTime, originalData, modifiedData, changedFields
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
+ [
+ logData.tracompany,
+ logData.tradepartment,
+ logData.traorganization,
+ logData.trarole,
+ logData.trauserName,
+ logData.traassistant,
+ logData.userId,
+ logData.operationEvent,
+ logData.operationTime,
+ logData.originalData,
+ logData.modifiedData,
+ logData.changedFields
+ ]
+ );
+ connection.release();
+
+ sendResponse(res, true, { logId: result.insertId }, '日志记录成功');
+ } catch (error) {
+ console.error('记录日志失败:', error);
+ sendResponse(res, false, null, '记录日志失败');
+ }
+});
// 货源管理页面路由
app.get('/management', (req, res) => {
diff --git a/supply.html b/supply.html
index 6ef5243..9e9f093 100644
--- a/supply.html
+++ b/supply.html
@@ -2071,16 +2071,16 @@
当前选择: 未选择
-
@@ -2112,6 +2112,9 @@
}
};
+ // 操作状态跟踪,防止重复点击
+ let isOperating = false;
+
// 编辑相关全局变量
let currentEditSupplyId = null;
let editSelectedSpec = [];
@@ -2172,6 +2175,7 @@
currentUserInfoEl.innerHTML = `当前登录用户:${parsedUserInfo.name} | 用户ID:${userId} | 职位:${parsedUserInfo.projectName}`;
}
+ // 返回解析后的用户信息
return parsedUserInfo;
}
@@ -2191,6 +2195,89 @@
// 防止loadSupplies并发执行的标志
let isLoadingSupplies = false;
+ // 比较两个对象的差异,返回变更的字段列表
+ function getChangedFields(oldObj, newObj) {
+ if (!oldObj || !newObj) return [];
+
+ const changedFields = [];
+ const allKeys = new Set([...Object.keys(oldObj), ...Object.keys(newObj)]);
+
+ allKeys.forEach(key => {
+ const oldValue = oldObj[key];
+ const newValue = newObj[key];
+
+ // 优化比较逻辑,减少不必要的JSON.stringify调用
+ // 先比较基本类型
+ if (oldValue !== newValue) {
+ // 如果基本类型不同,直接添加到变更字段
+ // 如果是引用类型,再使用JSON.stringify比较
+ if (typeof oldValue !== 'object' || typeof newValue !== 'object' ||
+ oldValue === null || newValue === null ||
+ Array.isArray(oldValue) !== Array.isArray(newValue)) {
+ changedFields.push(key);
+ } else {
+ // 都是对象或都是数组,使用JSON.stringify比较
+ if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) {
+ changedFields.push(key);
+ }
+ }
+ }
+ });
+
+ return changedFields;
+ }
+
+ // 记录货源操作日志
+ async function logSupplyOperation(operationType, productId, originalData = null, modifiedData = null, changedFields = null) {
+ try {
+ console.log('开始记录货源操作日志:', operationType, productId);
+ // 获取当前登录用户信息
+ const userInfo = checkLogin();
+ console.log('获取到的用户信息:', userInfo);
+ if (!userInfo) {
+ console.log('没有用户信息,跳过日志记录');
+ return;
+ }
+
+ // 如果没有提供changedFields,自动计算
+ if (operationType === '修改' && originalData && modifiedData && !changedFields) {
+ changedFields = getChangedFields(originalData, modifiedData);
+ }
+
+ // 准备日志数据
+ const logData = {
+ phoneNumber: userInfo.phoneNumber,
+ projectName: userInfo.projectName,
+ userId: userInfo.userId || userInfo.id, // 确保使用正确的userId
+ operationEvent: `审核系统-${operationType === '创建' ? '创建货源' : operationType === '修改' ? '编辑货源' : operationType === '下架' ? '下架货源' : operationType === '删除' ? '删除货源' : operationType}`,
+ productId: productId,
+ originalData: originalData,
+ modifiedData: modifiedData,
+ changedFields: changedFields
+ };
+
+ console.log('准备发送的日志数据:', logData);
+ // 调用后端API记录日志
+ const response = await fetch('/api/supplies/log', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(logData)
+ });
+
+ const result = await response.json();
+ console.log('日志API返回结果:', result);
+ if (!result.success) {
+ console.warn('记录日志失败:', result.message);
+ } else {
+ console.log('日志记录成功:', result.data.logId);
+ }
+ } catch (error) {
+ console.error('记录日志出错:', error);
+ }
+ }
+
// 初始化WebSocket连接
function initWebSocket() {
// 获取当前页面的协议和主机
@@ -2349,23 +2436,27 @@
startAutoOfflineCheck();
// 启动定期刷新数据,确保label字段变化能实时显示
+ // 优化:减少刷新频率,从5秒改为30秒,降低服务器负载
timers.loadSupplies = setInterval(() => {
- console.log('定期刷新数据,检查label字段变化');
loadSupplies();
- }, 5000); // 每5秒刷新一次
+ }, 30000); // 每30秒刷新一次
+
+ // 添加防抖机制,避免短时间内多次调用loadSupplies
+ let loadSuppliesTimeout;
// 增强WebSocket消息处理,确保所有相关消息都能触发数据刷新
- console.log('增强WebSocket消息处理');
if (ws) {
// 添加额外的消息监听器,确保所有相关消息都能触发数据刷新
ws.addEventListener('message', function(event) {
try {
const data = JSON.parse(event.data);
- console.log('增强的WebSocket消息处理:', data);
// 对于任何类型的WebSocket消息,都重新加载数据,确保label字段实时更新
if (data.type !== 'ping' && data.type !== 'pong') {
- console.log('收到相关WebSocket消息,刷新数据');
- loadSupplies();
+ // 使用防抖机制,避免短时间内多次调用loadSupplies
+ clearTimeout(loadSuppliesTimeout);
+ loadSuppliesTimeout = setTimeout(() => {
+ loadSupplies();
+ }, 1000); // 1秒防抖
}
} catch (error) {
console.error('解析WebSocket消息失败:', error);
@@ -2439,38 +2530,32 @@
// 根据元素类型设置不同的显示格式
if (element.classList.contains('countdown-badge')) {
- element.innerHTML = `⏰ ${countdownText}`;
+ element.textContent = `⏰ ${countdownText}`;
element.style.background = 'linear-gradient(135deg, #ff6b6b, #ee5a6f)';
element.style.boxShadow = '0 2px 4px rgba(255, 107, 107, 0.3)';
} else if (element.classList.contains('countdown')) {
- element.innerHTML = `剩余下架时间: ${countdownText}`;
+ element.textContent = `剩余下架时间: ${countdownText}`;
}
- console.log('更新倒计时显示为:', countdownText);
} else {
// 时间到了
- console.log('时间已到,显示已下架');
if (element.classList.contains('countdown-badge')) {
- element.innerHTML = '⏰ 已下架';
+ element.textContent = '⏰ 已下架';
element.style.background = '#8c8c8c';
element.style.boxShadow = '0 2px 4px rgba(140, 140, 140, 0.3)';
} else if (element.classList.contains('countdown')) {
- element.innerHTML = '剩余下架时间: 已下架';
+ element.textContent = '剩余下架时间: 已下架';
}
}
} else {
// 没有设置自动下架时间,隐藏倒计时或显示无倒计时
- console.log('没有设置自动下架时间,隐藏倒计时');
if (element.classList.contains('countdown-badge')) {
- element.innerHTML = '⏰ 无倒计时';
+ element.textContent = '⏰ 无倒计时';
element.style.background = '#52c41a';
element.style.boxShadow = '0 2px 4px rgba(82, 196, 26, 0.3)';
} else if (element.classList.contains('countdown')) {
- element.innerHTML = '剩余下架时间: 无';
+ element.textContent = '剩余下架时间: 无';
}
}
- } else {
- console.log('未找到对应的货源,supplyId:', supplyId);
- console.log('supplyData.supplies:', supplyData.supplies);
}
});
}
@@ -4074,8 +4159,10 @@
const provinceList = document.getElementById('provinceList');
const cityList = document.getElementById('cityList');
+ // 使用文档片段减少DOM重绘
+ const provinceFragment = document.createDocumentFragment();
+
// 生成省份选项
- provinceList.innerHTML = '';
filteredProvinces.forEach(province => {
const option = document.createElement('div');
option.className = 'select-item';
@@ -4092,15 +4179,21 @@
cityList.scrollTop = 0;
}, 100);
};
- provinceList.appendChild(option);
+ provinceFragment.appendChild(option);
});
+
+ // 一次性添加到DOM,减少重绘
+ provinceList.innerHTML = '';
+ provinceList.appendChild(provinceFragment);
}
// 生成城市选项
function generateCityOptions(cities) {
const cityList = document.getElementById('cityList');
- cityList.innerHTML = '';
+ // 使用文档片段减少DOM重绘
+ const cityFragment = document.createDocumentFragment();
+
cities.forEach(city => {
const option = document.createElement('div');
option.className = 'select-item';
@@ -4122,9 +4215,13 @@
hideRegionSelectModal();
saveFormData(); // 保存选择
};
- cityList.appendChild(option);
+ cityFragment.appendChild(option);
});
+ // 一次性添加到DOM,减少重绘
+ cityList.innerHTML = '';
+ cityList.appendChild(cityFragment);
+
// 使用scrollIntoView滚动到第一个城市
setTimeout(() => {
const firstCity = cityList.querySelector('.select-item');
@@ -6282,6 +6379,13 @@
localStorage.removeItem('supplyFormDraft');
console.log('创建成功,已清除保存的表单数据');
alert('货源创建成功');
+
+ // 记录创建货源日志
+ const userInfo = checkLogin();
+ if (userInfo) {
+ await logSupplyOperation('创建', result.data.id, null, result.data);
+ }
+
// 重置表单状态,确保下次打开时是空白状态
resetForm();
hideAddSupplyModal();
@@ -6716,19 +6820,40 @@
// 下架货源
async function unpublishSupply(id) {
+ // 防止重复点击
+ if (isOperating) {
+ return;
+ }
+
if (confirm('确定要下架该货源吗?')) {
try {
+ isOperating = true;
+
+ // 获取要下架的货源的原始数据
+ const originalSupply = supplyData.supplies.find(s => String(s.id) === String(id));
+ if (!originalSupply) {
+ alert('找不到要下架的货源数据');
+ return;
+ }
+
const response = await fetch(`/api/supplies/${id}/unpublish`, {
method: 'POST'
});
const result = await response.json();
if (result.success) {
+ // 记录下架货源日志
+ const userInfo = checkLogin();
+ if (userInfo) {
+ // 传入修改后的数据,包含状态变化
+ await logSupplyOperation('下架', id, originalSupply, result.data);
+ }
+
// 向WebSocket服务器发送消息,通知其他客户端货源已下架
sendWebSocketMessage({
type: 'supply_status_change',
supplyId: id,
action: 'unpublish',
- status: 'hidden'
+ status: 'sold_out'
});
alert('下架成功');
@@ -6739,6 +6864,8 @@
} catch (error) {
console.error('下架失败:', error);
alert('下架失败: 网络错误');
+ } finally {
+ isOperating = false;
}
}
}
@@ -6746,18 +6873,40 @@
// 显示编辑货源
// 删除货源
async function deleteSupply(id) {
+ // 防止重复点击
+ if (isOperating) {
+ return;
+ }
+
if (confirm('确定要删除该货源吗?')) {
try {
+ isOperating = true;
+
+ // 获取要删除的货源的原始数据
+ const originalSupply = supplyData.supplies.find(s => String(s.id) === String(id));
+ if (!originalSupply) {
+ alert('找不到要删除的货源数据');
+ return;
+ }
+
const response = await fetch(`/api/supplies/${id}/delete`, {
method: 'POST'
});
const result = await response.json();
if (result.success) {
+ // 记录删除货源日志
+ const userInfo = checkLogin();
+ if (userInfo) {
+ // 传入修改后的数据,包含状态变化
+ await logSupplyOperation('删除', id, originalSupply, result.data);
+ }
+
// 向WebSocket服务器发送消息,通知其他客户端货源已删除
sendWebSocketMessage({
type: 'supply_update',
supplyId: id,
- action: 'delete'
+ action: 'delete',
+ status: 'hidden'
});
alert('删除成功');
@@ -6768,6 +6917,8 @@
} catch (error) {
console.error('删除失败:', error);
alert('删除失败: 网络错误');
+ } finally {
+ isOperating = false;
}
}
}
@@ -7103,12 +7254,19 @@
// 上架货源
async function publishSupply(id) {
+ // 防止重复点击
+ if (isOperating) {
+ return false;
+ }
+
if (!id) {
alert('货源ID不存在');
- return;
+ return false;
}
try {
+ isOperating = true;
+
const response = await fetch(`/api/supplies/${id}/publish`, {
method: 'POST'
});
@@ -7133,6 +7291,8 @@
console.error('上架失败:', error);
alert('上架失败: ' + error.message);
return false;
+ } finally {
+ isOperating = false;
}
}
@@ -7554,7 +7714,6 @@
// 生成编辑城市选项
function generateEditCityOptions(cities) {
const cityList = document.getElementById('editCityList');
- const districtList = document.getElementById('editDistrictList');
cityList.innerHTML = '';
cities.forEach(city => {
@@ -7568,28 +7727,29 @@
editSelectedCity = city.city;
updateEditRegionDisplay();
};
+ option.ondblclick = () => {
+ // 双击城市直接确认整个地区选择
+ editSelectedCity = city.city;
+ updateEditRegionDisplay();
+ // 构建完整的地区字符串
+ const regionString = `${editSelectedProvince} ${editSelectedCity}`;
+ // 设置到表单
+ document.getElementById('editRegionDisplayText').textContent = regionString;
+ document.getElementById('editRegionValue').value = regionString;
+ // 隐藏弹窗
+ hideEditRegionSelectModal();
+ saveFormData(); // 保存选择
+ };
cityList.appendChild(option);
});
- }
-
- // 生成编辑区县选项
- function generateEditDistrictOptions(districts) {
- const districtList = document.getElementById('editDistrictList');
- districtList.innerHTML = '';
- districts.forEach(district => {
- const option = document.createElement('div');
- option.className = 'select-item';
- option.textContent = district;
- if (district === editSelectedDistrict) {
- option.classList.add('selected');
+ // 使用scrollIntoView滚动到第一个城市
+ setTimeout(() => {
+ const firstCity = cityList.querySelector('.select-item');
+ if (firstCity) {
+ firstCity.scrollIntoView({ behavior: 'auto', block: 'start' });
}
- option.onclick = () => {
- editSelectedDistrict = district;
- updateEditRegionDisplay();
- };
- districtList.appendChild(option);
- });
+ }, 50);
}
// 更新编辑地区显示
@@ -7615,76 +7775,62 @@
editSelectedProvince = '';
editSelectedCity = '';
- // 先尝试直接查找区县
- let foundDistrict = false;
- for (let i = 0; i < allRegionOptions.length; i++) {
- const province = allRegionOptions[i];
- for (let j = 0; j < province.cities.length; j++) {
- const city = province.cities[j];
- for (let k = 0; k < city.districts.length; k++) {
- const district = city.districts[k];
- if (district.toLowerCase().includes(searchKeyword)) {
- // 找到匹配的区县,自动填充省市区
- editSelectedProvince = province.province;
- editSelectedCity = city.city;
- editSelectedDistrict = district;
- foundDistrict = true;
- break;
- }
- }
- if (foundDistrict) break;
- }
- if (foundDistrict) break;
+ if (!searchKeyword) {
+ // 如果搜索关键词为空,显示所有省份
+ generateEditRegionOptions();
+ updateEditRegionDisplay();
+ return;
}
- if (foundDistrict) {
- // 生成地区选项并选择
- generateEditRegionOptions();
-
- // 自动生成城市选项并选择
- const cityList = document.getElementById('editCityList');
- cityList.innerHTML = '';
- const province = allRegionOptions.find(p => p.province === editSelectedProvince);
- if (province) {
- province.cities.forEach(city => {
- const option = document.createElement('div');
- option.className = 'select-item';
- option.textContent = city.city;
- if (city.city === editSelectedCity) {
- option.classList.add('selected');
- }
- option.onclick = () => {
- editSelectedCity = city.city;
- editSelectedDistrict = '';
- updateEditRegionDisplay();
- generateEditDistrictOptions(city.districts);
- };
- cityList.appendChild(option);
-
- // 如果是选中的城市,生成区县选项
- if (city.city === editSelectedCity) {
- generateEditDistrictOptions(city.districts);
- }
- });
+ // 过滤省份
+ const filteredProvinces = allRegionOptions.filter(province => {
+ // 检查省份名称是否匹配
+ if (province.province.toLowerCase().includes(searchKeyword)) {
+ return true;
}
-
- // 更新区县选项的选中状态
- const districtList = document.getElementById('editDistrictList');
- const districtOptions = districtList.querySelectorAll('.select-item');
- districtOptions.forEach(option => {
- if (option.textContent === editSelectedDistrict) {
- option.classList.add('selected');
- }
+ // 检查该省份下是否有匹配的城市
+ return province.cities.some(city => {
+ return city.city.toLowerCase().includes(searchKeyword);
});
- } else {
- // 否则按原逻辑生成所有省份
- generateEditRegionOptions();
- // 清空城市和区县选项
- document.getElementById('editCityList').innerHTML = '';
- document.getElementById('editDistrictList').innerHTML = '';
- }
+ });
- updateEditRegionDisplay();
+ // 生成省份选项
+ const provinceList = document.getElementById('editProvinceList');
+ provinceList.innerHTML = '';
+ filteredProvinces.forEach(province => {
+ const option = document.createElement('div');
+ option.className = 'select-item';
+ option.textContent = province.province;
+ if (province.province === editSelectedProvince) {
+ option.classList.add('selected');
+ }
+ option.onclick = () => {
+ editSelectedProvince = province.province;
+ editSelectedCity = '';
+ updateEditRegionDisplay();
+ // 生成城市选项
+ generateEditCityOptions(province.cities);
+ // 直接滚动城市列表到顶部
+ setTimeout(() => {
+ document.getElementById('editCityList').scrollTop = 0;
+ }, 100);
+ };
+ provinceList.appendChild(option);
+ });
+
+ // 自动选择第一个匹配的省份和城市
+ if (filteredProvinces.length > 0) {
+ editSelectedProvince = filteredProvinces[0].province;
+ // 查找第一个匹配的城市
+ const matchedCity = filteredProvinces[0].cities.find(city =>
+ city.city.toLowerCase().includes(searchKeyword)
+ );
+ if (matchedCity) {
+ editSelectedCity = matchedCity.city;
+ }
+ updateEditRegionDisplay();
+ generateEditCityOptions(filteredProvinces[0].cities);
+ }
}
// 确认编辑地区选择
@@ -7692,11 +7838,17 @@
if (editSelectedProvince && editSelectedCity) {
const regionDisplayText = document.getElementById('editRegionDisplayText');
const regionValue = document.getElementById('editRegionValue');
- const regionText = `${editSelectedProvince} ${editSelectedCity}`;
+ let regionText = editSelectedProvince;
+ if (editSelectedCity) {
+ regionText = `${editSelectedProvince} ${editSelectedCity}`;
+ }
regionDisplayText.textContent = regionText;
regionValue.value = regionText;
+ saveFormData(); // 保存选择
+ hideEditRegionSelectModal();
+ } else {
+ alert('请选择完整的地区信息');
}
- hideEditRegionSelectModal();
}
// 编辑货源类型选择功能
@@ -8078,12 +8230,27 @@
// 保存编辑货源
async function saveEditSupply() {
+ // 防止重复点击
+ if (isOperating) {
+ return false;
+ }
+
if (!currentEditSupplyId) {
alert('货源ID不存在');
return false;
}
+ // 在编辑操作开始前就获取原始数据,并创建深拷贝,避免引用关系导致原始数据被修改
+ const originalSupplyTemp = supplyData.supplies.find(s => String(s.id) === String(currentEditSupplyId));
+ if (!originalSupplyTemp) {
+ alert('找不到要编辑的货源数据');
+ return false;
+ }
+ // 创建深拷贝,确保原始数据不会被后续操作修改
+ const originalSupply = JSON.parse(JSON.stringify(originalSupplyTemp));
+
try {
+ isOperating = true;
// 获取规格和件数数据
const pairs = document.querySelectorAll('#editSpecQuantityPairs .spec-quantity-pair');
const specifications = [];
@@ -8194,6 +8361,13 @@
const result = await response.json();
if (result.success) {
+ // 记录编辑货源日志
+ const userInfo = checkLogin();
+ if (userInfo) {
+ // 使用之前保存的原始数据(编辑前的数据)
+ await logSupplyOperation('修改', currentEditSupplyId, originalSupply, result.data);
+ }
+
// 向WebSocket服务器发送消息,通知其他客户端货源已更新
sendWebSocketMessage({
type: 'supply_update',
@@ -8232,6 +8406,8 @@
console.error('编辑失败:', error);
alert('编辑失败: 网络错误');
return false;
+ } finally {
+ isOperating = false;
}
}