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 {