From e960e737fd2cf33caa71514662dff9f65d3e1fdf Mon Sep 17 00:00:00 2001 From: Default User Date: Wed, 14 Jan 2026 14:36:28 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=95=86=E5=93=81=E4=BB=B7?= =?UTF-8?q?=E6=A0=BC=E5=8F=98=E6=9B=B4=E6=97=A5=E5=BF=97=EF=BC=8C=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E5=85=B7=E4=BD=93=E8=A7=84=E6=A0=BC=E7=9A=84=E4=BB=B7?= =?UTF-8?q?=E6=A0=BC=E5=8F=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/goods-update/goods-update.js | 186 ++++++++++++++++++++++++++- pages/goods-update/goods-update.wxml | 23 ++++ pages/goods-update/goods-update.wxss | 79 ++++++++++++ server-example/server-mysql.js | 109 +++++++++++++++- 4 files changed, 395 insertions(+), 2 deletions(-) diff --git a/pages/goods-update/goods-update.js b/pages/goods-update/goods-update.js index 8f3998f..e11d10a 100644 --- a/pages/goods-update/goods-update.js +++ b/pages/goods-update/goods-update.js @@ -690,6 +690,32 @@ Page({ } } + // 处理产品日志信息 + let productLog = []; + if (product.product_log) { + if (typeof product.product_log === 'string') { + // 尝试解析JSON字符串 + try { + const parsedLog = JSON.parse(product.product_log); + if (Array.isArray(parsedLog)) { + productLog = parsedLog; + } else { + // 如果解析后不是数组,可能是单个日志字符串 + productLog = [parsedLog]; + } + } catch (e) { + // 如果解析失败,可能是普通字符串,直接作为单个日志 + productLog = [product.product_log]; + } + } else if (Array.isArray(product.product_log)) { + // 已经是数组格式 + productLog = product.product_log; + } else { + // 其他情况,转换为数组 + productLog = [product.product_log]; + } + } + // 转换商品数据格式 const formattedGoods = { id: productIdStr, @@ -724,6 +750,8 @@ Page({ creatorName: creatorName, // 地区信息(先设置,后面会被覆盖) region: finalRegion, + // 产品日志信息 - 确保是数组格式 + product_log: productLog, // 复制原始产品对象中的所有字段,确保不丢失任何数据 ...product, // 重新设置关键字段,防止被product数据覆盖 @@ -749,7 +777,9 @@ Page({ freshness: product.freshness || '', // 确保价格字段使用我们提取的第一个规格价格 price: defaultPrice, - costprice: defaultCostprice + costprice: defaultCostprice, + // 确保产品日志字段使用我们处理后的值 - 确保是数组格式 + product_log: productLog }; console.log('最终formattedGoods.status:', formattedGoods.status); @@ -758,6 +788,14 @@ Page({ console.log('最终formattedGoods.region:', formattedGoods.region); // 调试输出完整的formattedGoods对象 + // 调试日志:检查product_log字段 + console.log('=== 产品日志调试 ==='); + console.log('原始product.product_log:', product.product_log); + console.log('处理后的productLog:', productLog); + console.log('productLog类型:', typeof productLog); + console.log('productLog是否为数组:', Array.isArray(productLog)); + console.log('productLog长度:', productLog.length); + console.log('最终格式化的商品数据:', JSON.stringify(formattedGoods, null, 2)); // 强制将测试商品设置为已下架状态,用于调试 @@ -769,6 +807,7 @@ Page({ console.log('goodsDetail.label:', formattedGoods.label); console.log('按钮是否应该被禁用:', formattedGoods.status === 'sold_out'); console.log('是否应该显示disabled-button类:', formattedGoods.status === 'sold_out'); + console.log('是否应该显示日志区域:', formattedGoods.product_log && formattedGoods.product_log.length > 0); this.setData({ goodsDetail: formattedGoods @@ -883,6 +922,10 @@ Page({ return item.projectName || item.position || item.role || '未知身份'; })); + // 将获取到的personnel数据存储到本地,以便在saveEdit中使用 + wx.setStorageSync('personnel', allPersonnelData); + console.log('【权限验证】已将personnel数据存储到本地'); + // 检查是否有管理员、采购员或销售员身份 // 优先级:管理员 > 采购员/销售员 const isAdmin = allPersonnelData.some(item => @@ -1042,8 +1085,15 @@ Page({ contact_phone: goodsDetail.contact_phone || '' }; + // 保存原始规格和价格信息,用于比较差异 + const originalSpecPriceInfo = { + specArray: [...specArray], + priceArray: [...priceArray] + }; + this.setData({ editSupply: editSupply, + originalSpecPriceInfo: originalSpecPriceInfo, showEditModal: true }); }, @@ -1059,6 +1109,7 @@ Page({ saveEdit: function() { console.log('保存编辑'); const editSupply = this.data.editSupply; + const goodsDetail = this.data.goodsDetail; // 处理价格字段,将价格数组合并成字符串 let priceStr = ''; @@ -1160,6 +1211,139 @@ Page({ wx.hideLoading(); console.log('更新商品成功:', res); if (res && res.code === 200) { + // 获取原始规格和价格信息 + const originalSpecPriceInfo = this.data.originalSpecPriceInfo || {}; + const originalSpecArray = originalSpecPriceInfo.specArray || []; + const originalPriceArray = originalSpecPriceInfo.priceArray || []; + + // 获取新的规格和价格信息 + const newSpecArray = editSupply.specArray || []; + const newPriceArray = editSupply.priceArray || []; + + console.log('【saveEdit】原始规格数组:', originalSpecArray); + console.log('【saveEdit】原始价格数组:', originalPriceArray); + console.log('【saveEdit】新规格数组:', newSpecArray); + console.log('【saveEdit】新价格数组:', newPriceArray); + + // 检查每个规格的价格是否有变化,生成价格变化日志 + const priceChanges = []; + for (let i = 0; i < Math.max(originalSpecArray.length, newSpecArray.length); i++) { + const originalSpec = originalSpecArray[i] || ''; + const newSpec = newSpecArray[i] || ''; + const originalPrice = originalPriceArray[i] || ''; + const newPrice = newPriceArray[i] || ''; + + // 只有当规格存在且价格有变化时,才记录日志 + if ((originalSpec || newSpec) && originalPrice !== newPrice) { + // 使用新规格名称(如果有),否则使用原始规格名称 + const specName = newSpec || originalSpec; + priceChanges.push({ + spec: specName, + oldPrice: originalPrice, + newPrice: newPrice + }); + } + } + + // 如果有价格变化,记录日志 + if (priceChanges.length > 0) { + console.log('【saveEdit】有规格价格变化,记录日志:', priceChanges); + + // 获取当前用户信息 + const userPhone = getLocalPhoneNumber(); + let operatorName = userPhone; + + // 尝试从本地存储获取用户名 + try { + // 尝试从personnel数据中获取姓名 + const personnelData = wx.getStorageSync('personnel') || []; + const personnel = Array.isArray(personnelData) ? personnelData : [personnelData]; + console.log('【saveEdit】本地存储的personnel数据:', personnel); + + // 查找当前用户的personnel信息 + const userInfo = personnel.find(item => + item.phoneNumber === userPhone || + item.phone === userPhone || + (item.phoneNumber && item.phoneNumber.includes(userPhone)) || + (item.phone && item.phone.includes(userPhone)) + ); + + console.log('【saveEdit】找到的userInfo:', userInfo); + + if (userInfo) { + // 使用alias或name作为姓名,优先使用alias,不包含电话号码 + const userName = userInfo.alias || userInfo.name || '未知用户'; + console.log('【saveEdit】获取到的userName:', userName); + operatorName = userName; + console.log('【saveEdit】生成的operatorName:', operatorName); + } else { + // 尝试从users中获取,不包含电话号码 + const users = wx.getStorageSync('users') || {}; + const userId = wx.getStorageSync('userId'); + if (userId && users[userId] && users[userId].name) { + operatorName = users[userId].name; + } else if (userId && users[userId] && users[userId].nickName) { + operatorName = users[userId].nickName; + } else { + // 如果没有找到用户信息,使用默认名称 + operatorName = '未知用户'; + console.log('【saveEdit】未找到用户姓名,使用默认operatorName:', operatorName); + } + } + } catch (e) { + console.error('获取用户名失败:', e); + console.log('【saveEdit】获取用户名异常,使用默认operatorName:', operatorName); + } + + // 获取当前时间 + const currentTime = new Date().toLocaleString('zh-CN'); + + // 获取当前产品日志 + let productLog = []; + if (goodsDetail.product_log) { + if (Array.isArray(goodsDetail.product_log)) { + // 已经是数组格式 + productLog = [...goodsDetail.product_log]; + } else if (typeof goodsDetail.product_log === 'string') { + // 旧数据可能是JSON字符串 + try { + const parsedLog = JSON.parse(goodsDetail.product_log); + if (Array.isArray(parsedLog)) { + productLog = parsedLog; + } else { + // 旧数据可能是单个字符串,转换为数组 + productLog = [parsedLog]; + } + } catch (e) { + // 可能是普通字符串,直接作为第一条日志 + productLog = [goodsDetail.product_log]; + } + } + } + + // 为每个价格变化生成日志记录 + priceChanges.forEach(change => { + // 生成包含具体规格信息的日志字符串 + const logRecord = `${operatorName}在${currentTime}将${change.spec}规格的销售价格从${change.oldPrice}修改为了${change.newPrice}`; + // 添加新日志 + productLog.push(logRecord); + }); + + console.log('【saveEdit】更新后的日志:', productLog); + + // 更新产品日志 + API.request('/api/products/update-log', 'POST', { + productId: productId, + product_log: productLog // 直接传递字符串数组,后端会处理序列化 + }) + .then(logRes => { + console.log('更新产品日志成功:', logRes); + }) + .catch(logErr => { + console.error('更新产品日志失败:', logErr); + }); + } + wx.showToast({ title: '更新成功', icon: 'success', diff --git a/pages/goods-update/goods-update.wxml b/pages/goods-update/goods-update.wxml index 5e78070..7e5fab2 100644 --- a/pages/goods-update/goods-update.wxml +++ b/pages/goods-update/goods-update.wxml @@ -185,6 +185,29 @@ {{goodsDetail.formattedUpdatedAt || '暂无'}} + + + + 价格变更日志 + + + + 日志{{index + 1}} + + {{item}} + + + + + + 日志 + + 暂无价格变更日志 + + + + + diff --git a/pages/goods-update/goods-update.wxss b/pages/goods-update/goods-update.wxss index 77bb9fb..1fb0e21 100644 --- a/pages/goods-update/goods-update.wxss +++ b/pages/goods-update/goods-update.wxss @@ -1582,4 +1582,83 @@ video.slider-media .wx-video-volume-icon { .price-text { color: #ff4d4f; +} + +/* 产品日志信息样式 */ +.product-log-info { + margin: 16rpx; + padding: 24rpx; + background: #ffffff; + border-radius: 12rpx; + box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08); + border: 1rpx solid #f0f0f0; +} + +.log-title { + font-size: 32rpx; + font-weight: 600; + color: #262626; + margin-bottom: 16rpx; + padding-bottom: 12rpx; + border-bottom: 1rpx solid #f0f0f0; + position: relative; +} + +.log-title::after { + content: ''; + position: absolute; + bottom: -1rpx; + left: 0; + width: 60rpx; + height: 4rpx; + background: linear-gradient(135deg, #ff4d4f 0%, #cf1322 100%); + border-radius: 2rpx; +} + +.log-list { + display: flex; + flex-direction: column; + gap: 16rpx; +} + +.log-item { + display: flex; + gap: 20rpx; + padding: 16rpx; + background: #fff7f7; + border-radius: 8rpx; + border-left: 4rpx solid #ff4d4f; + transition: all 0.2s ease; +} + +.log-item:active { + background: #fff1f0; + transform: translateX(4rpx); +} + +.log-index { + font-size: 24rpx; + font-weight: 600; + color: #ff4d4f; + min-width: 80rpx; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 77, 79, 0.1); + border-radius: 4rpx; + padding: 4rpx 12rpx; +} + +.log-content { + flex: 1; + display: flex; + flex-direction: column; + gap: 8rpx; +} + +.log-full-text { + font-size: 28rpx; + color: #595959; + line-height: 1.6; + font-weight: 500; } \ No newline at end of file diff --git a/server-example/server-mysql.js b/server-example/server-mysql.js index dea89df..6ed7675 100644 --- a/server-example/server-mysql.js +++ b/server-example/server-mysql.js @@ -856,6 +856,26 @@ Product.init({ type: DataTypes.TEXT, allowNull: true, comment: '货源描述' + }, + // 产品日志字段,用于记录价格变更等信息 + product_log: { + type: DataTypes.TEXT, + allowNull: true, + comment: '产品日志', + get() { + const value = this.getDataValue('product_log'); + return value ? JSON.parse(value) : []; + }, + set(value) { + // 检查value是否已经是JSON字符串,如果是则直接使用,否则序列化 + if (typeof value === 'string' && (value.startsWith('[') || value.startsWith('{'))) { + // 已经是JSON字符串,直接保存 + this.setDataValue('product_log', value); + } else { + // 需要序列化 + this.setDataValue('product_log', JSON.stringify(value)); + } + } } }, { sequelize, @@ -3969,7 +3989,7 @@ app.post('/api/products/detail', async (req, res) => { // 查询商品详情 - 排除hidden状态商品,直接使用Product表中的reservedCount字段 const product = await Product.findOne({ - attributes: ['productId', 'productName', 'price', 'quantity', 'grossWeight', 'imageUrls', 'created_at', 'specification', 'yolk', 'sourceType', 'supplyStatus', 'producting', 'product_contact', 'contact_phone', 'region', 'freshness', 'costprice','description', 'frequency'], + attributes: ['productId', 'productName', 'price', 'quantity', 'grossWeight', 'imageUrls', 'created_at', 'specification', 'yolk', 'sourceType', 'supplyStatus', 'producting', 'product_contact', 'contact_phone', 'region', 'freshness', 'costprice','description', 'frequency', 'product_log'], where: { productId, status: { [Sequelize.Op.not]: 'hidden' } @@ -4013,6 +4033,35 @@ app.post('/api/products/detail', async (req, res) => { updatedProduct.imageUrls = []; } } + + // 处理产品日志字段,确保返回数组格式 + if (updatedProduct.product_log) { + console.log('【产品日志】原始product_log:', updatedProduct.product_log, '类型:', typeof updatedProduct.product_log); + if (typeof updatedProduct.product_log === 'string') { + try { + updatedProduct.product_log = JSON.parse(updatedProduct.product_log); + console.log('【产品日志】反序列化后的product_log:', updatedProduct.product_log, '类型:', typeof updatedProduct.product_log); + // 确保是数组格式 + if (!Array.isArray(updatedProduct.product_log)) { + updatedProduct.product_log = [updatedProduct.product_log]; + console.log('【产品日志】转换为数组:', updatedProduct.product_log); + } + } catch (parseError) { + console.error('【产品日志】反序列化失败:', parseError); + // 如果解析失败,将字符串作为单个日志条目 + updatedProduct.product_log = [updatedProduct.product_log]; + console.log('【产品日志】转换为单条日志:', updatedProduct.product_log); + } + } else if (!Array.isArray(updatedProduct.product_log)) { + // 如果不是字符串也不是数组,转换为数组 + updatedProduct.product_log = [updatedProduct.product_log]; + console.log('【产品日志】转换为数组:', updatedProduct.product_log); + } + } else { + // 如果没有日志,返回空数组 + updatedProduct.product_log = []; + console.log('【产品日志】没有日志,返回空数组'); + } // 详细分析毛重字段 const grossWeightDetails = { @@ -6977,6 +7026,64 @@ app.post('/api/products/quick-update', async (req, res) => { } }); +// 添加API接口:更新产品日志 +app.post('/api/products/update-log', async (req, res) => { + console.log('【更新产品日志】收到请求:', req.body); + try { + const { productId, product_log } = req.body; + + if (!productId) { + return res.status(400).json({ + success: false, + code: 400, + message: '缺少productId参数' + }); + } + + // 查找商品 + const product = await Product.findOne({ + where: { productId } + }); + + if (!product) { + console.error('【更新产品日志】商品不存在:', productId); + return res.status(404).json({ + success: false, + code: 404, + message: '商品不存在' + }); + } + + console.log('【更新产品日志】找到商品:', product.productName); + + // 更新产品日志 + await Product.update({ + product_log: product_log, + updated_at: getBeijingTime() + }, { + where: { productId } + }); + + console.log('【更新产品日志】商品日志更新成功:', productId); + + res.json({ + success: true, + code: 200, + message: '日志更新成功', + data: { + productId + } + }); + } catch (error) { + console.error('【更新产品日志】更新商品日志失败:', error); + res.status(500).json({ + success: false, + code: 500, + message: '更新商品日志失败: ' + error.message + }); + } +}); + // REST API接口 - 获取用户会话列表 app.get('/api/conversations/user/:userId', async (req, res) => { try {