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; } }