Browse Source

清理 publish 目录,更新配置

Xfy
徐飞洋 1 month ago
parent
commit
f61934f31d
  1. 1
      app.json
  2. 555
      pages/publish/index.js
  3. 3
      pages/publish/index.json
  4. 54
      pages/publish/index.wxml
  5. 117
      pages/publish/index.wxss

1
app.json

@ -6,7 +6,6 @@
"pages/evaluate2/product-list", "pages/evaluate2/product-list",
"pages/evaluate2/spec-detail", "pages/evaluate2/spec-detail",
"pages/settlement/index", "pages/settlement/index",
"pages/publish/index",
"pages/buyer/index", "pages/buyer/index",
"pages/profile/index", "pages/profile/index",
"pages/profile/authentication/index", "pages/profile/authentication/index",

555
pages/publish/index.js

@ -1,555 +0,0 @@
// 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) {
// 移除登录检查,允许直接发布商品
},
/**
* 品种输入处理
*/
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('发布按钮点击');
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('【上传前检查】存储数据到全局对象');
// 从本地存储获取userId,如果不存在则使用默认值
const userId = wx.getStorageSync('userId') || 'anonymous';
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.name || users[userId].info.nickName) ? (users[userId].info.name || 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();
// 直接显示错误提示,不进行登录验证
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
});
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
return {
title: '发布商品 - 又鸟蛋平台',
path: '/pages/publish/index',
imageUrl: ''
};
},
/**
* 用户点击右上角分享到朋友圈
*/
onShareTimeline: function () {
return {
title: '发布商品 - 又鸟蛋平台',
query: '',
imageUrl: ''
};
}
});

3
pages/publish/index.json

@ -1,3 +0,0 @@
{
"usingComponents": {}
}

54
pages/publish/index.wxml

@ -1,54 +0,0 @@
<!--pages/publish/index.wxml-->
<view class="publish-container">
<view class="publish-header">
<text class="header-title">发布新货源</text>
</view>
<view class="form-container">
<view class="form-item">
<text class="label">品种 *</text>
<input class="input" type="text" placeholder="请输入品种" bindinput="onVarietyInput" value="{{variety}}" />
</view>
<view class="form-item">
<text class="label">价格 (元/斤) *</text>
<input class="input" type="text" placeholder="请输入商品价格(支持文字描述)" bindinput="onPriceInput" value="{{price}}" />
</view>
<view class="form-item">
<text class="label">数量 (斤) *</text>
<input class="input" type="digit" placeholder="请输入商品数量" bindinput="onQuantityInput" value="{{quantity}}" />
</view>
<view class="form-item">
<text class="label">毛重 (斤)</text>
<input class="input" type="text" placeholder="请输入商品毛重(可输入文字,如:十斤)" bindinput="onGrossWeightInput" value="{{grossWeight}}" />
</view>
<view class="form-item">
<text class="label">蛋黄</text>
<input class="input" type="text" placeholder="请输入蛋黄信息" bindinput="onYolkInput" value="{{yolk}}" />
</view>
<view class="form-item">
<text class="label">规格</text>
<input class="input" type="text" placeholder="请输入商品规格" bindinput="onSpecificationInput" value="{{specification}}" />
</view>
<!-- 新增图片上传区域 -->
<view class="image-upload-container">
<text class="label">商品图片(最多5张)</text>
<view class="image-list">
<view class="image-item" wx:for="{{images}}" wx:key="index">
<image src="{{item}}" mode="aspectFill"></image>
<view class="image-delete" bindtap="deleteImage" data-index="{{index}}">×</view>
</view>
<view class="image-upload" wx:if="{{images.length < 5}}" bindtap="chooseImage">
<text>+</text>
</view>
</view>
</view>
<button class="publish-btn" type="primary" bindtap="onPublishTap">发布商品</button>
</view>
</view>

117
pages/publish/index.wxss

@ -1,117 +0,0 @@
/* pages/publish/index.wxss */
.publish-container {
padding: 20rpx;
background-color: #f8f8f8;
min-height: 100vh;
}
.publish-header {
background-color: #fff;
padding: 30rpx;
border-radius: 10rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}
.header-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
.form-container {
background-color: #fff;
padding: 30rpx;
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}
.form-item {
margin-bottom: 30rpx;
}
.label {
display: block;
font-size: 28rpx;
color: #666;
margin-bottom: 10rpx;
}
.input {
width: 100%;
height: 80rpx;
border: 1rpx solid #ddd;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
box-sizing: border-box;
}
/* 图片上传样式 */
.image-upload-container {
margin-bottom: 30rpx;
}
.image-list {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
}
.image-item {
width: 160rpx;
height: 160rpx;
position: relative;
border: 1rpx solid #ddd;
border-radius: 8rpx;
overflow: hidden;
}
.image-item image {
width: 100%;
height: 100%;
}
.image-delete {
position: absolute;
top: 0;
right: 0;
width: 40rpx;
height: 40rpx;
background-color: rgba(0, 0, 0, 0.5);
color: #fff;
text-align: center;
line-height: 40rpx;
font-size: 32rpx;
border-radius: 0 8rpx 0 20rpx;
}
.image-upload {
width: 160rpx;
height: 160rpx;
border: 2rpx dashed #ddd;
border-radius: 8rpx;
display: flex;
justify-content: center;
align-items: center;
background-color: #f8f8f8;
}
.image-upload text {
font-size: 64rpx;
color: #999;
}
.publish-btn {
margin-top: 40rpx;
background-color: #07c160;
color: #fff;
font-size: 32rpx;
height: 90rpx;
line-height: 90rpx;
border-radius: 45rpx;
}
.publish-btn:active {
background-color: #06b356;
}
Loading…
Cancel
Save