Browse Source

修改商品价格变更日志,记录具体规格的价格变化

pull/12/head
Default User 2 months ago
parent
commit
e960e737fd
  1. 186
      pages/goods-update/goods-update.js
  2. 23
      pages/goods-update/goods-update.wxml
  3. 79
      pages/goods-update/goods-update.wxss
  4. 109
      server-example/server-mysql.js

186
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 = { const formattedGoods = {
id: productIdStr, id: productIdStr,
@ -724,6 +750,8 @@ Page({
creatorName: creatorName, creatorName: creatorName,
// 地区信息(先设置,后面会被覆盖) // 地区信息(先设置,后面会被覆盖)
region: finalRegion, region: finalRegion,
// 产品日志信息 - 确保是数组格式
product_log: productLog,
// 复制原始产品对象中的所有字段,确保不丢失任何数据 // 复制原始产品对象中的所有字段,确保不丢失任何数据
...product, ...product,
// 重新设置关键字段,防止被product数据覆盖 // 重新设置关键字段,防止被product数据覆盖
@ -749,7 +777,9 @@ Page({
freshness: product.freshness || '', freshness: product.freshness || '',
// 确保价格字段使用我们提取的第一个规格价格 // 确保价格字段使用我们提取的第一个规格价格
price: defaultPrice, price: defaultPrice,
costprice: defaultCostprice costprice: defaultCostprice,
// 确保产品日志字段使用我们处理后的值 - 确保是数组格式
product_log: productLog
}; };
console.log('最终formattedGoods.status:', formattedGoods.status); console.log('最终formattedGoods.status:', formattedGoods.status);
@ -758,6 +788,14 @@ Page({
console.log('最终formattedGoods.region:', formattedGoods.region); console.log('最终formattedGoods.region:', formattedGoods.region);
// 调试输出完整的formattedGoods对象 // 调试输出完整的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)); console.log('最终格式化的商品数据:', JSON.stringify(formattedGoods, null, 2));
// 强制将测试商品设置为已下架状态,用于调试 // 强制将测试商品设置为已下架状态,用于调试
@ -769,6 +807,7 @@ Page({
console.log('goodsDetail.label:', formattedGoods.label); console.log('goodsDetail.label:', formattedGoods.label);
console.log('按钮是否应该被禁用:', formattedGoods.status === 'sold_out'); console.log('按钮是否应该被禁用:', formattedGoods.status === 'sold_out');
console.log('是否应该显示disabled-button类:', formattedGoods.status === 'sold_out'); console.log('是否应该显示disabled-button类:', formattedGoods.status === 'sold_out');
console.log('是否应该显示日志区域:', formattedGoods.product_log && formattedGoods.product_log.length > 0);
this.setData({ this.setData({
goodsDetail: formattedGoods goodsDetail: formattedGoods
@ -883,6 +922,10 @@ Page({
return item.projectName || item.position || item.role || '未知身份'; return item.projectName || item.position || item.role || '未知身份';
})); }));
// 将获取到的personnel数据存储到本地,以便在saveEdit中使用
wx.setStorageSync('personnel', allPersonnelData);
console.log('【权限验证】已将personnel数据存储到本地');
// 检查是否有管理员、采购员或销售员身份 // 检查是否有管理员、采购员或销售员身份
// 优先级:管理员 > 采购员/销售员 // 优先级:管理员 > 采购员/销售员
const isAdmin = allPersonnelData.some(item => const isAdmin = allPersonnelData.some(item =>
@ -1042,8 +1085,15 @@ Page({
contact_phone: goodsDetail.contact_phone || '' contact_phone: goodsDetail.contact_phone || ''
}; };
// 保存原始规格和价格信息,用于比较差异
const originalSpecPriceInfo = {
specArray: [...specArray],
priceArray: [...priceArray]
};
this.setData({ this.setData({
editSupply: editSupply, editSupply: editSupply,
originalSpecPriceInfo: originalSpecPriceInfo,
showEditModal: true showEditModal: true
}); });
}, },
@ -1059,6 +1109,7 @@ Page({
saveEdit: function() { saveEdit: function() {
console.log('保存编辑'); console.log('保存编辑');
const editSupply = this.data.editSupply; const editSupply = this.data.editSupply;
const goodsDetail = this.data.goodsDetail;
// 处理价格字段,将价格数组合并成字符串 // 处理价格字段,将价格数组合并成字符串
let priceStr = ''; let priceStr = '';
@ -1160,6 +1211,139 @@ Page({
wx.hideLoading(); wx.hideLoading();
console.log('更新商品成功:', res); console.log('更新商品成功:', res);
if (res && res.code === 200) { 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({ wx.showToast({
title: '更新成功', title: '更新成功',
icon: 'success', icon: 'success',

23
pages/goods-update/goods-update.wxml

@ -185,6 +185,29 @@
<text class="create-time">{{goodsDetail.formattedUpdatedAt || '暂无'}}</text> <text class="create-time">{{goodsDetail.formattedUpdatedAt || '暂无'}}</text>
</view> </view>
</view> </view>
<!-- 产品日志信息 -->
<view class="product-log-info">
<view class="log-title">价格变更日志</view>
<view class="log-list">
<block wx:if="{{goodsDetail.product_log && goodsDetail.product_log.length > 0}}">
<view class="log-item" wx:for="{{goodsDetail.product_log}}" wx:key="index">
<view class="log-index">日志{{index + 1}}</view>
<view class="log-content">
<text class="log-full-text">{{item}}</text>
</view>
</view>
</block>
<block wx:else>
<view class="log-item">
<view class="log-index">日志</view>
<view class="log-content">
<text class="log-full-text">暂无价格变更日志</text>
</view>
</view>
</block>
</view>
</view>
</view> </view>
<!-- 操作按钮区域 --> <!-- 操作按钮区域 -->

79
pages/goods-update/goods-update.wxss

@ -1583,3 +1583,82 @@ video.slider-media .wx-video-volume-icon {
.price-text { .price-text {
color: #ff4d4f; 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;
}

109
server-example/server-mysql.js

@ -856,6 +856,26 @@ Product.init({
type: DataTypes.TEXT, type: DataTypes.TEXT,
allowNull: true, allowNull: true,
comment: '货源描述' 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, sequelize,
@ -3969,7 +3989,7 @@ app.post('/api/products/detail', async (req, res) => {
// 查询商品详情 - 排除hidden状态商品,直接使用Product表中的reservedCount字段 // 查询商品详情 - 排除hidden状态商品,直接使用Product表中的reservedCount字段
const product = await Product.findOne({ 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: { where: {
productId, productId,
status: { [Sequelize.Op.not]: 'hidden' } status: { [Sequelize.Op.not]: 'hidden' }
@ -4014,6 +4034,35 @@ app.post('/api/products/detail', async (req, res) => {
} }
} }
// 处理产品日志字段,确保返回数组格式
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 = { const grossWeightDetails = {
type: typeof updatedProduct.grossWeight, type: typeof updatedProduct.grossWeight,
@ -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接口 - 获取用户会话列表 // REST API接口 - 获取用户会话列表
app.get('/api/conversations/user/:userId', async (req, res) => { app.get('/api/conversations/user/:userId', async (req, res) => {
try { try {

Loading…
Cancel
Save