You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
588 lines
19 KiB
588 lines
19 KiB
// pages/publish/index.js
|
|
// 引入API工具
|
|
const API = require('../../utils/api.js');
|
|
|
|
// 【终极修复】创建全局上传管理器,完全独立于页面生命周期
|
|
if (!global.ImageUploadManager) {
|
|
global.ImageUploadManager = {
|
|
// 存储所有活动的上传任务
|
|
activeTasks: {},
|
|
// 深度克隆工具函数
|
|
deepClone: function(obj) {
|
|
return JSON.parse(JSON.stringify(obj));
|
|
},
|
|
// 核心上传方法
|
|
upload: function(formData, images, successCallback, failCallback) {
|
|
console.log('【全局上传管理器】开始上传,图片数量:', images.length);
|
|
|
|
// 创建唯一的上传任务ID
|
|
const taskId = `upload_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
|
|
// 创建深度克隆,完全隔离数据
|
|
const clonedFormData = this.deepClone(formData);
|
|
const clonedImages = this.deepClone(images);
|
|
|
|
// 将任务保存到全局状态中,防止页面重新编译时丢失
|
|
this.activeTasks[taskId] = {
|
|
id: taskId,
|
|
formData: clonedFormData,
|
|
images: clonedImages,
|
|
status: 'started',
|
|
startTime: Date.now(),
|
|
uploadedCount: 0,
|
|
totalCount: clonedImages.length
|
|
};
|
|
|
|
console.log(`【全局上传管理器】创建任务 ${taskId},已保存到全局状态`);
|
|
|
|
// 使用setTimeout完全隔离执行上下文,避免与页面生命周期耦合
|
|
const self = this;
|
|
setTimeout(() => {
|
|
try {
|
|
console.log('【全局上传管理器】准备调用API.publishProduct');
|
|
console.log('准备的商品数据:', clonedFormData);
|
|
console.log('准备的图片数量:', clonedImages.length);
|
|
|
|
// 关键修改:使用API.publishProduct方法,这是正确的调用链
|
|
// 包含所有必要字段
|
|
const productData = {
|
|
...clonedFormData,
|
|
images: clonedImages, // 直接传递图片数组
|
|
imageUrls: clonedImages, // 同时设置imageUrls字段
|
|
// 生成会话ID,确保所有图片关联同一商品
|
|
sessionId: taskId,
|
|
uploadSessionId: taskId
|
|
};
|
|
|
|
console.log('最终传递给publishProduct的数据:', Object.keys(productData));
|
|
|
|
API.publishProduct(productData)
|
|
.then(res => {
|
|
console.log(`【全局上传管理器】任务 ${taskId} 上传完成,响应:`, res);
|
|
|
|
// 更新任务状态
|
|
if (self.activeTasks[taskId]) {
|
|
self.activeTasks[taskId].status = 'completed';
|
|
self.activeTasks[taskId].endTime = Date.now();
|
|
self.activeTasks[taskId].result = res;
|
|
}
|
|
|
|
// 使用setTimeout隔离成功回调的执行
|
|
setTimeout(() => {
|
|
if (successCallback) {
|
|
successCallback(res);
|
|
}
|
|
}, 0);
|
|
})
|
|
.catch(err => {
|
|
console.error(`【全局上传管理器】任务 ${taskId} 上传失败:`, err);
|
|
|
|
// 更新任务状态
|
|
if (self.activeTasks[taskId]) {
|
|
self.activeTasks[taskId].status = 'failed';
|
|
self.activeTasks[taskId].error = err;
|
|
self.activeTasks[taskId].endTime = Date.now();
|
|
}
|
|
|
|
// 使用setTimeout隔离失败回调的执行
|
|
setTimeout(() => {
|
|
if (failCallback) {
|
|
failCallback(err);
|
|
}
|
|
}, 0);
|
|
})
|
|
.finally(() => {
|
|
// 延迟清理任务,确保所有操作完成
|
|
setTimeout(() => {
|
|
if (self.activeTasks[taskId]) {
|
|
delete self.activeTasks[taskId];
|
|
console.log(`【全局上传管理器】任务 ${taskId} 已清理`);
|
|
}
|
|
}, 10000);
|
|
});
|
|
} catch (e) {
|
|
console.error(`【全局上传管理器】任务 ${taskId} 发生异常:`, e);
|
|
setTimeout(() => {
|
|
if (failCallback) {
|
|
failCallback(e);
|
|
}
|
|
}, 0);
|
|
}
|
|
}, 0);
|
|
|
|
return taskId;
|
|
},
|
|
|
|
// 获取任务状态的方法
|
|
getTaskStatus: function(taskId) {
|
|
return this.activeTasks[taskId] || null;
|
|
},
|
|
|
|
// 获取所有活动任务
|
|
getActiveTasks: function() {
|
|
return Object.values(this.activeTasks);
|
|
}
|
|
};
|
|
}
|
|
|
|
Page({
|
|
/**
|
|
* 页面的初始数据
|
|
*/
|
|
data: {
|
|
variety: '', // 品种
|
|
price: '',
|
|
quantity: '',
|
|
grossWeight: '',
|
|
yolk: '', // 蛋黄
|
|
specification: '',
|
|
images: [] // 新增图片数组
|
|
},
|
|
|
|
/**
|
|
* 生命周期函数--监听页面加载
|
|
*/
|
|
onLoad(options) {
|
|
// 检查用户是否已登录
|
|
this.checkLoginStatus();
|
|
},
|
|
|
|
/**
|
|
* 检查用户登录状态
|
|
*/
|
|
checkLoginStatus() {
|
|
const openid = wx.getStorageSync('openid');
|
|
if (!openid) {
|
|
wx.showModal({
|
|
title: '提示',
|
|
content: '请先登录后再发布商品',
|
|
showCancel: false,
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
wx.navigateTo({ url: '/pages/index/index' });
|
|
}
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 品种输入处理
|
|
*/
|
|
onVarietyInput(e) {
|
|
this.setData({
|
|
variety: e.detail.value
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 蛋黄输入处理
|
|
*/
|
|
onYolkInput(e) {
|
|
this.setData({
|
|
yolk: e.detail.value
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 价格输入处理
|
|
*/
|
|
onPriceInput(e) {
|
|
// 保存原始字符串值,不进行数字转换
|
|
this.setData({
|
|
price: e.detail.value
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 数量输入处理
|
|
*/
|
|
onQuantityInput(e) {
|
|
const value = parseFloat(e.detail.value);
|
|
this.setData({
|
|
quantity: isNaN(value) ? '' : value
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 毛重输入处理
|
|
*/
|
|
onGrossWeightInput(e) {
|
|
// 直接保存原始字符串值,不进行数字转换
|
|
this.setData({
|
|
grossWeight: e.detail.value
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 规格输入处理
|
|
*/
|
|
onSpecificationInput(e) {
|
|
this.setData({
|
|
specification: e.detail.value
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 表单验证
|
|
*/
|
|
validateForm() {
|
|
const { variety, price, quantity } = this.data;
|
|
|
|
console.log('表单验证数据 - variety:', variety, 'price:', price, 'quantity:', quantity);
|
|
console.log('数据类型 - variety:', typeof variety, 'price:', typeof price, 'quantity:', typeof quantity);
|
|
|
|
if (!variety || !variety.trim()) {
|
|
wx.showToast({ title: '请输入品种', icon: 'none' });
|
|
return false;
|
|
}
|
|
|
|
if (!price || price.trim() === '') {
|
|
wx.showToast({ title: '请输入有效价格', icon: 'none' });
|
|
return false;
|
|
}
|
|
|
|
if (quantity === '' || quantity === undefined || quantity === null || quantity <= 0) {
|
|
wx.showToast({ title: '请输入有效数量', icon: 'none' });
|
|
return false;
|
|
}
|
|
|
|
console.log('表单验证通过');
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* 发布商品按钮点击事件
|
|
*/
|
|
onPublishTap() {
|
|
console.log('发布按钮点击');
|
|
|
|
// 检查用户登录状态
|
|
const openid = wx.getStorageSync('openid');
|
|
const userInfo = wx.getStorageSync('userInfo');
|
|
const userId = wx.getStorageSync('userId');
|
|
|
|
console.log('检查用户授权状态 - openid:', !!openid, 'userInfo:', !!userInfo, 'userId:', !!userId);
|
|
|
|
if (!openid || !userId || !userInfo) {
|
|
console.log('用户未登录或未授权,引导重新登录');
|
|
wx.showModal({
|
|
title: '登录过期',
|
|
content: '请先授权登录后再发布商品',
|
|
showCancel: false,
|
|
confirmText: '去登录',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
wx.navigateTo({ url: '/pages/index/index' });
|
|
}
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!this.validateForm()) {
|
|
console.log('表单验证失败');
|
|
return;
|
|
}
|
|
|
|
const { variety, price, quantity, grossWeight, yolk, specification } = this.data;
|
|
const images = this.data.images;
|
|
|
|
// 构建商品数据,确保价格和数量为字符串类型
|
|
const productData = {
|
|
productName: variety.trim(), // 使用品种作为商品名称
|
|
price: price.toString(),
|
|
quantity: quantity.toString(),
|
|
grossWeight: grossWeight !== '' && grossWeight !== null && grossWeight !== undefined ? grossWeight : "",
|
|
yolk: yolk || '',
|
|
specification: specification || '',
|
|
images: images,
|
|
imageUrls: images,
|
|
allImageUrls: images,
|
|
hasMultipleImages: images.length > 1,
|
|
totalImages: images.length
|
|
};
|
|
|
|
console.log('【关键日志】商品数据:', productData);
|
|
console.log('【关键日志】图片数量:', images.length);
|
|
|
|
// 【终极修复】在上传开始前立即清空表单
|
|
// 先深度克隆所有数据
|
|
console.log('【上传前检查】准备克隆数据');
|
|
const formDataCopy = JSON.parse(JSON.stringify(productData));
|
|
const imagesCopy = JSON.parse(JSON.stringify(images));
|
|
|
|
console.log('【上传前检查】克隆后图片数量:', imagesCopy.length);
|
|
console.log('【上传前检查】克隆后图片数据:', imagesCopy);
|
|
|
|
// 立即清空表单,避免任何状态变化触发重新编译
|
|
console.log('【上传前检查】清空表单');
|
|
this.setData({
|
|
variety: '',
|
|
price: '',
|
|
quantity: '',
|
|
grossWeight: '',
|
|
yolk: '',
|
|
specification: '',
|
|
images: []
|
|
});
|
|
|
|
// 显示加载提示
|
|
wx.showLoading({ title: '正在上传图片...' });
|
|
|
|
// 【终极修复】使用全局上传管理器处理上传,完全脱离页面生命周期
|
|
// 将所有数据存储到全局对象中,防止被回收
|
|
console.log('【上传前检查】存储数据到全局对象');
|
|
global.tempUploadData = {
|
|
formData: formDataCopy,
|
|
images: imagesCopy,
|
|
userId: userId,
|
|
timestamp: Date.now()
|
|
};
|
|
|
|
// 预先生成会话ID,确保所有图片关联同一个商品
|
|
const uploadSessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
formDataCopy.sessionId = uploadSessionId;
|
|
formDataCopy.uploadSessionId = uploadSessionId;
|
|
|
|
console.log(`【关键修复】预先生成会话ID:`, uploadSessionId);
|
|
console.log(`【上传前检查】准备调用全局上传管理器,图片数量:`, imagesCopy.length);
|
|
console.log(`【上传前检查】传递的formData结构:`, Object.keys(formDataCopy));
|
|
|
|
// 【核心修复】直接使用wx.uploadFile API,确保与服务器端测试脚本格式一致
|
|
console.log(`【核心修复】使用wx.uploadFile API直接上传`);
|
|
|
|
// 预先生成会话ID
|
|
const sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
formDataCopy.sessionId = sessionId;
|
|
formDataCopy.uploadSessionId = sessionId;
|
|
|
|
console.log(`【核心修复】使用会话ID:`, sessionId);
|
|
console.log(`【核心修复】上传图片数量:`, imagesCopy.length);
|
|
|
|
// 使用Promise处理上传
|
|
const uploadPromise = new Promise((resolve, reject) => {
|
|
// 构建formData,与服务器测试脚本一致
|
|
const formData = {
|
|
productData: JSON.stringify(formDataCopy),
|
|
sessionId: sessionId,
|
|
uploadSessionId: sessionId,
|
|
totalImages: imagesCopy.length.toString(),
|
|
isSingleUpload: 'false' // 关键参数:标记为多图片上传
|
|
};
|
|
|
|
console.log(`【核心修复】准备上传,formData结构:`, Object.keys(formData));
|
|
console.log(`【核心修复】上传URL:`, API.BASE_URL + '/api/products/upload');
|
|
|
|
// 直接使用wx.uploadFile上传第一张图片
|
|
wx.uploadFile({
|
|
url: API.BASE_URL + '/api/products/upload',
|
|
filePath: imagesCopy[0], // 先上传第一张
|
|
name: 'images',
|
|
formData: formData,
|
|
timeout: 180000,
|
|
success: (res) => {
|
|
console.log('【核心修复】上传成功,状态码:', res.statusCode);
|
|
console.log('【核心修复】原始响应:', res.data);
|
|
try {
|
|
const data = JSON.parse(res.data);
|
|
resolve(data);
|
|
} catch (e) {
|
|
resolve({data: res.data});
|
|
}
|
|
},
|
|
fail: (err) => {
|
|
console.error('【核心修复】上传失败:', err);
|
|
reject(err);
|
|
}
|
|
});
|
|
});
|
|
|
|
uploadPromise.then((res) => {
|
|
// 上传成功回调
|
|
console.log('【核心修复】上传成功,响应:', res);
|
|
|
|
// 使用setTimeout完全隔离回调执行上下文
|
|
setTimeout(() => {
|
|
wx.hideLoading();
|
|
|
|
// 从全局临时存储获取数据
|
|
const tempData = global.tempUploadData || {};
|
|
const localFormData = tempData.formData;
|
|
const userId = tempData.userId;
|
|
|
|
// 【关键修复】从多个来源提取图片URL,确保不丢失
|
|
let allUploadedImageUrls = [];
|
|
|
|
// 尝试从多个位置提取图片URLs
|
|
if (res.imageUrls && Array.isArray(res.imageUrls) && res.imageUrls.length > 0) {
|
|
allUploadedImageUrls = [...res.imageUrls];
|
|
console.log('【全局上传】从res.imageUrls提取到图片:', allUploadedImageUrls.length);
|
|
}
|
|
|
|
if (res.product && res.product.imageUrls && Array.isArray(res.product.imageUrls) && res.product.imageUrls.length > 0) {
|
|
allUploadedImageUrls = [...res.product.imageUrls];
|
|
console.log('【全局上传】从res.product.imageUrls提取到图片:', allUploadedImageUrls.length);
|
|
}
|
|
|
|
if (res.data && res.data.imageUrls && Array.isArray(res.data.imageUrls) && res.data.imageUrls.length > 0) {
|
|
allUploadedImageUrls = [...res.data.imageUrls];
|
|
console.log('【全局上传】从res.data.imageUrls提取到图片:', allUploadedImageUrls.length);
|
|
}
|
|
|
|
// 去重处理,确保URL不重复
|
|
allUploadedImageUrls = [...new Set(allUploadedImageUrls)];
|
|
|
|
console.log('【全局上传】最终去重后的图片URL列表:', allUploadedImageUrls);
|
|
console.log('【全局上传】最终图片数量:', allUploadedImageUrls.length);
|
|
|
|
// 获取卖家信息
|
|
const users = wx.getStorageSync('users') || {};
|
|
const sellerName = users[userId] && users[userId].info && users[userId].info.nickName ? users[userId].info.nickName : '未知卖家';
|
|
|
|
// 保存到本地存储
|
|
setTimeout(() => {
|
|
// 获取当前已有的货源列表
|
|
const supplies = wx.getStorageSync('supplies') || [];
|
|
const newId = supplies.length > 0 ? Math.max(...supplies.map(s => s.id)) + 1 : 1;
|
|
const serverProductId = res.product && res.product.productId ? res.product.productId : '';
|
|
|
|
// 创建新的货源记录
|
|
const newSupply = {
|
|
id: newId,
|
|
productId: serverProductId,
|
|
serverProductId: serverProductId,
|
|
name: localFormData.productName,
|
|
productName: localFormData.productName,
|
|
price: localFormData.price,
|
|
minOrder: localFormData.quantity,
|
|
yolk: localFormData.yolk,
|
|
spec: localFormData.specification,
|
|
grossWeight: localFormData.grossWeight !== null ? localFormData.grossWeight : '',
|
|
seller: sellerName,
|
|
status: res.product && res.product.status ? res.product.status : 'pending_review',
|
|
imageUrls: allUploadedImageUrls,
|
|
reservedCount: 0,
|
|
isReserved: false
|
|
};
|
|
|
|
// 保存到supplies和goods本地存储
|
|
supplies.push(newSupply);
|
|
wx.setStorageSync('supplies', supplies);
|
|
|
|
const goods = wx.getStorageSync('goods') || [];
|
|
const newGoodForBuyer = {
|
|
id: String(newId),
|
|
productId: String(serverProductId),
|
|
name: localFormData.productName,
|
|
productName: localFormData.productName,
|
|
price: localFormData.price,
|
|
minOrder: localFormData.quantity,
|
|
yolk: localFormData.yolk,
|
|
spec: localFormData.specification,
|
|
grossWeight: localFormData.grossWeight !== null ? localFormData.grossWeight : '',
|
|
displayGrossWeight: localFormData.grossWeight !== null ? localFormData.grossWeight : '',
|
|
seller: sellerName,
|
|
status: res.product && res.product.status ? res.product.status : 'pending_review',
|
|
imageUrls: allUploadedImageUrls,
|
|
reservedCount: 0,
|
|
isReserved: false
|
|
};
|
|
goods.push(newGoodForBuyer);
|
|
wx.setStorageSync('goods', goods);
|
|
|
|
// 显示成功提示
|
|
setTimeout(() => {
|
|
wx.showModal({
|
|
title: '发布成功',
|
|
content: `所有${allUploadedImageUrls.length}张图片已成功上传!\n请手动返回查看您的商品。\n\n重要:请勿关闭小程序,等待3-5秒确保所有数据处理完成。`,
|
|
showCancel: false,
|
|
confirmText: '我知道了',
|
|
success: function() {
|
|
// 延迟清理全局临时数据,确保所有操作完成
|
|
setTimeout(() => {
|
|
if (global.tempUploadData) {
|
|
delete global.tempUploadData;
|
|
}
|
|
}, 5000);
|
|
}
|
|
});
|
|
}, 500);
|
|
}, 500);
|
|
}, 100);
|
|
})
|
|
.catch((err) => {
|
|
// 上传失败回调
|
|
console.error('【核心修复】上传失败:', err);
|
|
|
|
// 使用setTimeout隔离错误处理
|
|
setTimeout(() => {
|
|
wx.hideLoading();
|
|
|
|
if (err.needRelogin) {
|
|
wx.showModal({
|
|
title: '登录状态失效',
|
|
content: '请重新授权登录',
|
|
showCancel: false,
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
wx.removeStorageSync('openid');
|
|
wx.removeStorageSync('userId');
|
|
wx.navigateTo({ url: '/pages/login/index' });
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
wx.showToast({ title: err.message || '发布失败,请重试', icon: 'none' });
|
|
}
|
|
|
|
// 清理全局临时数据
|
|
if (global.tempUploadData) {
|
|
delete global.tempUploadData;
|
|
}
|
|
}, 100);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 生命周期函数--监听页面显示
|
|
*/
|
|
onShow() {
|
|
// 页面显示时可以刷新数据
|
|
},
|
|
|
|
/**
|
|
* 选择图片 - 修复版本
|
|
*/
|
|
chooseImage: function () {
|
|
const that = this;
|
|
wx.chooseMedia({
|
|
count: 5 - that.data.images.length,
|
|
mediaType: ['image'],
|
|
sourceType: ['album', 'camera'],
|
|
success: function (res) {
|
|
console.log('选择图片成功,返回数据:', res);
|
|
const tempFiles = res.tempFiles.map(file => file.tempFilePath);
|
|
that.setData({
|
|
images: [...that.data.images, ...tempFiles]
|
|
});
|
|
console.log('更新后的图片数组:', that.data.images);
|
|
},
|
|
fail: function (err) {
|
|
console.error('选择图片失败:', err);
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 删除图片
|
|
*/
|
|
deleteImage: function (e) {
|
|
const index = e.currentTarget.dataset.index;
|
|
const images = this.data.images;
|
|
images.splice(index, 1);
|
|
this.setData({
|
|
images: images
|
|
});
|
|
}
|
|
});
|