|
|
|
|
// pages/goods-detail/goods-detail.js
|
|
|
|
|
const API = require('../../utils/api.js')
|
|
|
|
|
const timeUtils = require('../../utils/time.js')
|
|
|
|
|
|
|
|
|
|
// 根据sourceType获取对应的颜色
|
|
|
|
|
function getSourceTypeColor(sourceType) {
|
|
|
|
|
const colorMap = {
|
|
|
|
|
'三方认证': '#4d9dff',
|
|
|
|
|
'三方未认证': '#ff4d4f',
|
|
|
|
|
'平台货源': '#2ad21f'
|
|
|
|
|
};
|
|
|
|
|
return colorMap[sourceType] || '#4d9dff';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 计算显示价格 - 处理价格字符串,提取第一个价格
|
|
|
|
|
function calculateDisplayPrice(price) {
|
|
|
|
|
if (!price) {
|
|
|
|
|
return '暂无价格';
|
|
|
|
|
}
|
|
|
|
|
if (typeof price === 'string') {
|
|
|
|
|
// 支持多种逗号分隔符:英文逗号、中文逗号、全角逗号
|
|
|
|
|
const priceArray = price.split(/[,,、]/).map(item => item.trim()).filter(item => item);
|
|
|
|
|
return priceArray[0] || '暂无价格';
|
|
|
|
|
}
|
|
|
|
|
// 如果不是字符串,转换为字符串
|
|
|
|
|
return String(price);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 格式化分享标题 - 按优先级: 地区、规格、数量、蛋黄颜色、蛋壳颜色
|
|
|
|
|
function formatShareTitle(goodsDetail) {
|
|
|
|
|
console.log('===== formatShareTitle 开始 =====');
|
|
|
|
|
console.log('goodsDetail:', JSON.stringify(goodsDetail, null, 2));
|
|
|
|
|
|
|
|
|
|
const titleParts = [];
|
|
|
|
|
|
|
|
|
|
// 1. 地区 (region)
|
|
|
|
|
const region = (goodsDetail.region || '').trim();
|
|
|
|
|
console.log('region:', region);
|
|
|
|
|
if (region) {
|
|
|
|
|
titleParts.push(region);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 规格 (specification/spec)
|
|
|
|
|
const specification = (goodsDetail.specification || goodsDetail.spec || goodsDetail.specs || '').trim();
|
|
|
|
|
console.log('specification:', specification);
|
|
|
|
|
if (specification) {
|
|
|
|
|
titleParts.push(specification);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. 数量 (quantity)
|
|
|
|
|
const quantity = (goodsDetail.quantity || '').trim();
|
|
|
|
|
console.log('quantity:', quantity);
|
|
|
|
|
if (quantity) {
|
|
|
|
|
titleParts.push(`${quantity}件`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. 蛋黄颜色 (yolk)
|
|
|
|
|
const yolk = (goodsDetail.yolk || '').trim();
|
|
|
|
|
console.log('yolk:', yolk);
|
|
|
|
|
if (yolk) {
|
|
|
|
|
titleParts.push(yolk);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 5. 蛋壳颜色 (category)
|
|
|
|
|
const category = (goodsDetail.category || '').trim();
|
|
|
|
|
console.log('category:', category);
|
|
|
|
|
if (category) {
|
|
|
|
|
titleParts.push(category);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 过滤空值后组合标题
|
|
|
|
|
const validParts = titleParts.filter(part => part && part.length > 0);
|
|
|
|
|
const result = validParts.join(' ');
|
|
|
|
|
|
|
|
|
|
console.log('titleParts:', titleParts);
|
|
|
|
|
console.log('validParts:', validParts);
|
|
|
|
|
console.log('最终标题:', result);
|
|
|
|
|
console.log('===== formatShareTitle 结束 =====');
|
|
|
|
|
|
|
|
|
|
if (validParts.length > 0) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果没有匹配的数据,返回默认标题
|
|
|
|
|
return goodsDetail.name ? `优质鸡蛋 - ${goodsDetail.name}` : '优质鸡蛋货源';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 构建商品分享消息内容
|
|
|
|
|
function buildShareGoodsMessage(goodsDetail) {
|
|
|
|
|
const parts = [];
|
|
|
|
|
|
|
|
|
|
// 商品名称
|
|
|
|
|
if (goodsDetail.name) {
|
|
|
|
|
parts.push(`【商品】${goodsDetail.name}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 规格
|
|
|
|
|
const specification = (goodsDetail.specification || goodsDetail.spec || goodsDetail.specs || '').trim();
|
|
|
|
|
if (specification) {
|
|
|
|
|
parts.push(`【规格】${specification}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 地区
|
|
|
|
|
const region = (goodsDetail.region || '').trim();
|
|
|
|
|
if (region) {
|
|
|
|
|
parts.push(`【地区】${region}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 数量
|
|
|
|
|
const quantity = (goodsDetail.quantity || '').trim();
|
|
|
|
|
if (quantity) {
|
|
|
|
|
parts.push(`【数量】${quantity}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 价格
|
|
|
|
|
if (goodsDetail.price) {
|
|
|
|
|
parts.push(`【价格】${goodsDetail.price}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 货源类型
|
|
|
|
|
const sourceType = (goodsDetail.sourceType || '').trim();
|
|
|
|
|
if (sourceType) {
|
|
|
|
|
parts.push(`【货源】${sourceType}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 联系人
|
|
|
|
|
if (goodsDetail.product_contact) {
|
|
|
|
|
parts.push(`【联系人】${goodsDetail.product_contact}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 联系电话
|
|
|
|
|
if (goodsDetail.contact_phone) {
|
|
|
|
|
parts.push(`【电话】${goodsDetail.contact_phone}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 蛋黄颜色
|
|
|
|
|
const yolk = (goodsDetail.yolk || '').trim();
|
|
|
|
|
if (yolk) {
|
|
|
|
|
parts.push(`【蛋黄】${yolk}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 蛋壳颜色/品种
|
|
|
|
|
const eggshell = (goodsDetail.eggshell || goodsDetail.shell || goodsDetail.breed || '').trim();
|
|
|
|
|
if (eggshell) {
|
|
|
|
|
parts.push(`【蛋壳/品种】${eggshell}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 新鲜程度
|
|
|
|
|
const freshness = (goodsDetail.freshness || '').trim();
|
|
|
|
|
if (freshness) {
|
|
|
|
|
parts.push(`【新鲜程度】${freshness}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 备注描述
|
|
|
|
|
const description = (goodsDetail.description || '').trim();
|
|
|
|
|
if (description) {
|
|
|
|
|
parts.push(`【备注】${description}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取第一张图片作为商品图片
|
|
|
|
|
const imageUrls = goodsDetail.imageUrls || [];
|
|
|
|
|
let firstImage = imageUrls.find(url => !isVideoUrl(url));
|
|
|
|
|
|
|
|
|
|
// 如果没有图片,检查是否有视频封面
|
|
|
|
|
if (!firstImage && goodsDetail.videoCoverUrl) {
|
|
|
|
|
firstImage = goodsDetail.videoCoverUrl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let message = parts.join('\n');
|
|
|
|
|
|
|
|
|
|
// 如果有图片,添加图片链接
|
|
|
|
|
if (firstImage) {
|
|
|
|
|
message += `\n\n【商品图片】${firstImage}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return message;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取适合分享的图片 - 优先使用正方形图片填满分享框
|
|
|
|
|
function getShareImageUrl(goodsDetail, context) {
|
|
|
|
|
const imageUrls = goodsDetail.imageUrls || [];
|
|
|
|
|
|
|
|
|
|
if (imageUrls.length > 0) {
|
|
|
|
|
// 过滤出图片类型
|
|
|
|
|
const imageOnly = imageUrls.filter(url => !isVideoUrl(url));
|
|
|
|
|
|
|
|
|
|
if (imageOnly.length > 0) {
|
|
|
|
|
// 情况1: 有图片 → 使用第一张图片
|
|
|
|
|
return imageOnly[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 情况2: 全是视频 → 使用视频封面或默认图片
|
|
|
|
|
// 检查是否有视频封面
|
|
|
|
|
if (context && context.data && context.data.videoCoverUrl) {
|
|
|
|
|
return context.data.videoCoverUrl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 尝试提取视频封面
|
|
|
|
|
const videoItems = imageUrls.filter(url => isVideoUrl(url));
|
|
|
|
|
if (videoItems.length > 0 && context && context.extractVideoFirstFrame) {
|
|
|
|
|
context.extractVideoFirstFrame(videoItems[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return '/images/你有好蛋.png';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return '/images/你有好蛋.png';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 媒体类型判断函数
|
|
|
|
|
function isVideoUrl(url) {
|
|
|
|
|
if (!url || typeof url !== 'string') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 转换为小写,确保大小写不敏感
|
|
|
|
|
const lowerUrl = url.toLowerCase();
|
|
|
|
|
// 支持的视频格式
|
|
|
|
|
const videoExtensions = ['.mp4', '.mov', '.avi', '.wmv', '.flv', '.webm', '.m4v', '.3gp'];
|
|
|
|
|
// 检查URL是否以视频扩展名结尾
|
|
|
|
|
for (const ext of videoExtensions) {
|
|
|
|
|
if (lowerUrl.endsWith(ext)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 预处理媒体URL,返回包含type字段的媒体对象数组
|
|
|
|
|
function processMediaUrls(urls) {
|
|
|
|
|
if (!urls || !Array.isArray(urls)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
return urls.map(url => {
|
|
|
|
|
return {
|
|
|
|
|
url: url,
|
|
|
|
|
type: isVideoUrl(url) ? 'video' : 'image'
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 格式化毛重显示的辅助函数
|
|
|
|
|
function formatGrossWeight(grossWeight, weight) {
|
|
|
|
|
console.log('===== formatGrossWeight 函数调用 =====');
|
|
|
|
|
console.log('输入参数:');
|
|
|
|
|
console.log('- grossWeight:', grossWeight, '(类型:', typeof grossWeight, ')');
|
|
|
|
|
console.log('- weight:', weight, '(类型:', typeof weight, ')');
|
|
|
|
|
|
|
|
|
|
// 1. 优先使用grossWeight,只要它不是null、不是undefined、不是空字符串
|
|
|
|
|
if (grossWeight !== null && grossWeight !== undefined && grossWeight !== '') {
|
|
|
|
|
console.log('使用grossWeight参数');
|
|
|
|
|
return grossWeight;
|
|
|
|
|
}
|
|
|
|
|
// 如果grossWeight无效,尝试使用weight字段
|
|
|
|
|
if (weight !== null && weight !== undefined && weight !== '') {
|
|
|
|
|
console.log('使用weight参数');
|
|
|
|
|
return weight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. 新增逻辑:如果grossWeight和weight都无效,返回空字符串以支持文字输入
|
|
|
|
|
console.log('两个参数都无效,返回空字符串');
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提取地区中的省份信息
|
|
|
|
|
function extractProvince(region) {
|
|
|
|
|
if (!region || typeof region !== 'string') {
|
|
|
|
|
return region;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查找各种省份格式的位置
|
|
|
|
|
const provinceEndIndex = region.indexOf('省');
|
|
|
|
|
const autonomousRegionEndIndex = region.indexOf('自治区');
|
|
|
|
|
const municipalityEndIndex = region.indexOf('市'); // 用于直辖市,如北京市、上海市
|
|
|
|
|
const specialRegionEndIndex = region.indexOf('特别行政区'); // 用于香港、澳门
|
|
|
|
|
|
|
|
|
|
if (provinceEndIndex !== -1) {
|
|
|
|
|
// 包含"省"字,提取到"省"字结束
|
|
|
|
|
return region.substring(0, provinceEndIndex + 1);
|
|
|
|
|
} else if (autonomousRegionEndIndex !== -1) {
|
|
|
|
|
// 包含"自治区",提取到"自治区"结束
|
|
|
|
|
return region.substring(0, autonomousRegionEndIndex + 3);
|
|
|
|
|
} else if (specialRegionEndIndex !== -1) {
|
|
|
|
|
// 包含"特别行政区",提取到"特别行政区"结束
|
|
|
|
|
return region.substring(0, specialRegionEndIndex + 5);
|
|
|
|
|
} else if (municipalityEndIndex === 2) {
|
|
|
|
|
// 直辖市(如北京市、上海市),市字在第2个字符位置
|
|
|
|
|
return region.substring(0, municipalityEndIndex + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果没有找到匹配的格式,返回原字符串
|
|
|
|
|
return region;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查用户是否已经登录
|
|
|
|
|
function checkLoginStatus() {
|
|
|
|
|
const openid = wx.getStorageSync('openid');
|
|
|
|
|
const userId = wx.getStorageSync('userId');
|
|
|
|
|
|
|
|
|
|
console.log('检查登录状态:', {
|
|
|
|
|
openid: openid ? '已登录' : '未登录',
|
|
|
|
|
userId: userId ? '已设置' : '未设置'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return !!(openid && userId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 格式化日期时间函数
|
|
|
|
|
function formatDateTime(dateString) {
|
|
|
|
|
if (!dateString) return '';
|
|
|
|
|
|
|
|
|
|
// 尝试解析日期字符串
|
|
|
|
|
const date = new Date(dateString);
|
|
|
|
|
|
|
|
|
|
// 检查是否是有效的日期对象
|
|
|
|
|
if (isNaN(date.getTime())) {
|
|
|
|
|
// 如果解析失败,返回原始字符串
|
|
|
|
|
return dateString;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果是 ISO 格式的字符串(包含 T 字符),则转换为本地时间格式,只保留日期部分
|
|
|
|
|
if (typeof dateString === 'string' && dateString.includes('T')) {
|
|
|
|
|
const year = date.getFullYear();
|
|
|
|
|
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
|
|
|
const day = date.getDate().toString().padStart(2, '0');
|
|
|
|
|
return `${year}-${month}-${day}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 对于其他格式的字符串,尝试提取日期部分
|
|
|
|
|
if (typeof dateString === 'string') {
|
|
|
|
|
// 检查是否包含空格,提取空格前的日期部分
|
|
|
|
|
const spaceIndex = dateString.indexOf(' ');
|
|
|
|
|
if (spaceIndex !== -1) {
|
|
|
|
|
return dateString.substring(0, spaceIndex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果以上都不匹配,直接返回
|
|
|
|
|
return dateString;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理净重、件数、规格和价格数据,将逗号分隔的字符串转换为一一对应的数组
|
|
|
|
|
function processWeightAndQuantityData(weightSpecString, quantityString, specString, priceString, specStatusString, specPriceChanges = {}) {
|
|
|
|
|
console.log('===== 处理净重、件数、规格和价格数据 =====');
|
|
|
|
|
console.log('输入参数:');
|
|
|
|
|
console.log('- weightSpecString:', weightSpecString, '(类型:', typeof weightSpecString, ')');
|
|
|
|
|
console.log('- quantityString:', quantityString, '(类型:', typeof quantityString, ')');
|
|
|
|
|
console.log('- specString:', specString, '(类型:', typeof specString, ')');
|
|
|
|
|
console.log('- priceString:', priceString, '(类型:', typeof priceString, ')');
|
|
|
|
|
console.log('- specStatusString:', specStatusString, '(类型:', typeof specStatusString, ')');
|
|
|
|
|
|
|
|
|
|
// 处理价格字符串
|
|
|
|
|
let priceArray = [];
|
|
|
|
|
if (priceString && typeof priceString === 'string') {
|
|
|
|
|
// 支持多种逗号分隔符:英文逗号、中文逗号、全角逗号
|
|
|
|
|
priceArray = priceString.split(/[,,、]/).map(item => item.trim()).filter(item => item);
|
|
|
|
|
console.log('从字符串分割得到价格数组:', priceArray);
|
|
|
|
|
} else if (priceString) {
|
|
|
|
|
priceArray = [String(priceString)];
|
|
|
|
|
console.log('将价格转换为数组:', priceArray);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理规格状态字符串
|
|
|
|
|
let specStatusArray = [];
|
|
|
|
|
if (specStatusString && typeof specStatusString === 'string') {
|
|
|
|
|
// 支持多种逗号分隔符:英文逗号、中文逗号、全角逗号
|
|
|
|
|
specStatusArray = specStatusString.split(/[,,、]/).map(item => item.trim()).filter(item => item);
|
|
|
|
|
console.log('从字符串分割得到规格状态数组:', specStatusArray);
|
|
|
|
|
} else if (specStatusString) {
|
|
|
|
|
specStatusArray = [String(specStatusString)];
|
|
|
|
|
console.log('将规格状态转换为数组:', specStatusArray);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果没有数据,返回空数组
|
|
|
|
|
if (!weightSpecString && !quantityString && !specString) {
|
|
|
|
|
console.log('没有数据,返回空数组');
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理净重/规格字符串(它可能包含净重信息)
|
|
|
|
|
let weightSpecArray = [];
|
|
|
|
|
if (weightSpecString && typeof weightSpecString === 'string') {
|
|
|
|
|
// 支持多种逗号分隔符:英文逗号、中文逗号、全角逗号
|
|
|
|
|
weightSpecArray = weightSpecString.split(/[,,、]/).map(item => item.trim()).filter(item => item);
|
|
|
|
|
console.log('从字符串分割得到净重规格数组:', weightSpecArray);
|
|
|
|
|
} else if (weightSpecString) {
|
|
|
|
|
weightSpecArray = [String(weightSpecString)];
|
|
|
|
|
console.log('将净重规格转换为数组:', weightSpecArray);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理件数字符串
|
|
|
|
|
let quantityArray = [];
|
|
|
|
|
if (quantityString && typeof quantityString === 'string') {
|
|
|
|
|
// 支持多种逗号分隔符:英文逗号、中文逗号、全角逗号
|
|
|
|
|
quantityArray = quantityString.split(/[,,、]/).map(item => item.trim()).filter(item => item);
|
|
|
|
|
console.log('从字符串分割得到数量数组:', quantityArray);
|
|
|
|
|
} else if (quantityString) {
|
|
|
|
|
quantityArray = [String(quantityString)];
|
|
|
|
|
console.log('将数量转换为数组:', quantityArray);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取最大长度,确保一一对应
|
|
|
|
|
const maxLength = Math.max(weightSpecArray.length, quantityArray.length, priceArray.length, specStatusArray.length);
|
|
|
|
|
console.log('最大长度:', maxLength);
|
|
|
|
|
const result = [];
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < maxLength; i++) {
|
|
|
|
|
const weightSpec = weightSpecArray[i] || '';
|
|
|
|
|
const quantity = quantityArray[i] || '';
|
|
|
|
|
const price = priceArray[i] || '';
|
|
|
|
|
const specStatus = specStatusArray[i] || '0';
|
|
|
|
|
|
|
|
|
|
console.log(`处理第${i}组数据: weightSpec=${weightSpec}, quantity=${quantity}, price=${price}, specStatus=${specStatus}`);
|
|
|
|
|
|
|
|
|
|
// 处理净重规格显示格式 - 根据内容类型添加相应前缀
|
|
|
|
|
let weightSpecDisplay = '';
|
|
|
|
|
if (weightSpec) {
|
|
|
|
|
if (weightSpec.includes('净重')) {
|
|
|
|
|
// 如果已包含"净重"前缀,保持不变
|
|
|
|
|
weightSpecDisplay = weightSpec;
|
|
|
|
|
} else if (weightSpec.includes('毛重')) {
|
|
|
|
|
// 如果已包含"毛重"前缀,保持不变
|
|
|
|
|
weightSpecDisplay = weightSpec;
|
|
|
|
|
} else {
|
|
|
|
|
// 如果都不包含,默认为净重
|
|
|
|
|
weightSpecDisplay = `净重${weightSpec}`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 组合显示:格式为"净重信息————件数-价格"
|
|
|
|
|
let display = '';
|
|
|
|
|
if (weightSpecDisplay && quantity) {
|
|
|
|
|
// 检查是否为售空状态,如果是售空状态则显示"售空"
|
|
|
|
|
if (weightSpecDisplay.includes('售空') || quantity === '售空') {
|
|
|
|
|
display = `${weightSpecDisplay}————售空`;
|
|
|
|
|
} else {
|
|
|
|
|
if (price) {
|
|
|
|
|
display = `${weightSpecDisplay}【${quantity}件】¥${price}元`;
|
|
|
|
|
} else {
|
|
|
|
|
display = `${weightSpecDisplay}【${quantity}件】`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (weightSpecDisplay) {
|
|
|
|
|
display = weightSpecDisplay;
|
|
|
|
|
} else if (quantity) {
|
|
|
|
|
display = `${quantity}件`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查规格状态,如果为1则显示已下架
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`第${i}组数据处理结果: weightSpecDisplay=${weightSpecDisplay}, quantity=${quantity}, price=${price}, specStatus=${specStatus}, display=${display}`);
|
|
|
|
|
|
|
|
|
|
// 检查该规格是否有价格变化
|
|
|
|
|
let priceChangeDirection = null;
|
|
|
|
|
for (const spec in specPriceChanges) {
|
|
|
|
|
if (weightSpecDisplay.includes(spec)) {
|
|
|
|
|
priceChangeDirection = specPriceChanges[spec];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.push({
|
|
|
|
|
weightSpec: weightSpecDisplay,
|
|
|
|
|
quantity: quantity,
|
|
|
|
|
price: price,
|
|
|
|
|
specStatus: specStatus,
|
|
|
|
|
display: display,
|
|
|
|
|
isOffShelf: specStatus === '1',
|
|
|
|
|
priceChangeDirection: priceChangeDirection
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('最终处理结果:', result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Page({
|
|
|
|
|
// 分享给朋友/群聊
|
|
|
|
|
onShareAppMessage() {
|
|
|
|
|
const goodsDetail = this.data.goodsDetail || {};
|
|
|
|
|
const title = formatShareTitle(goodsDetail);
|
|
|
|
|
|
|
|
|
|
// 获取联系人、电话号码和地区信息(提取省份)
|
|
|
|
|
const contactName = goodsDetail.product_contact || '';
|
|
|
|
|
const contactPhone = goodsDetail.contact_phone || '';
|
|
|
|
|
const region = extractProvince(goodsDetail.region || '');
|
|
|
|
|
|
|
|
|
|
// 构建包含联系人信息的分享路径
|
|
|
|
|
const contactNameParam = encodeURIComponent(contactName);
|
|
|
|
|
const contactPhoneParam = encodeURIComponent(contactPhone);
|
|
|
|
|
const regionParam = encodeURIComponent(region);
|
|
|
|
|
|
|
|
|
|
// 如果有联系人或地区信息,则添加到分享路径
|
|
|
|
|
let sharePath = `/pages/goods-detail/goods-detail?productId=${goodsDetail.productId || goodsDetail.id}`;
|
|
|
|
|
if (contactName && contactPhone && region) {
|
|
|
|
|
sharePath += `&contactName=${contactNameParam}&contactPhone=${contactPhoneParam}®ion=${regionParam}`;
|
|
|
|
|
} else if (contactName && contactPhone) {
|
|
|
|
|
sharePath += `&contactName=${contactNameParam}&contactPhone=${contactPhoneParam}`;
|
|
|
|
|
} else if (contactName && region) {
|
|
|
|
|
sharePath += `&contactName=${contactNameParam}®ion=${regionParam}`;
|
|
|
|
|
} else if (contactPhone && region) {
|
|
|
|
|
sharePath += `&contactPhone=${contactPhoneParam}®ion=${regionParam}`;
|
|
|
|
|
} else if (contactName) {
|
|
|
|
|
sharePath += `&contactName=${contactNameParam}`;
|
|
|
|
|
} else if (contactPhone) {
|
|
|
|
|
sharePath += `&contactPhone=${contactPhoneParam}`;
|
|
|
|
|
} else if (region) {
|
|
|
|
|
sharePath += `®ion=${regionParam}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 确定分享图片
|
|
|
|
|
let imageUrl = '/images/你有好蛋.png'; // 默认值
|
|
|
|
|
|
|
|
|
|
// 检查是否为纯视频商品
|
|
|
|
|
const mediaItems = goodsDetail.mediaItems || [];
|
|
|
|
|
const hasVideo = mediaItems.some(item => item.type === 'video');
|
|
|
|
|
const hasImage = mediaItems.some(item => item.type === 'image');
|
|
|
|
|
|
|
|
|
|
if (hasVideo && !hasImage) {
|
|
|
|
|
// 纯视频商品:使用提取的封面或默认图片
|
|
|
|
|
if (this.data.videoCoverUrl) {
|
|
|
|
|
imageUrl = this.data.videoCoverUrl;
|
|
|
|
|
console.log('使用提取的视频封面分享:', imageUrl);
|
|
|
|
|
} else {
|
|
|
|
|
console.log('视频封面尚未提取完成,使用默认图片');
|
|
|
|
|
// 尝试立即提取视频封面
|
|
|
|
|
const videoItems = mediaItems.filter(item => item.type === 'video');
|
|
|
|
|
if (videoItems.length > 0) {
|
|
|
|
|
this.extractVideoFirstFrame(videoItems[0].url);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 有图片的商品:使用原有的图片选择逻辑
|
|
|
|
|
imageUrl = getShareImageUrl(goodsDetail);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
title: title,
|
|
|
|
|
path: sharePath,
|
|
|
|
|
imageUrl: imageUrl
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 分享到朋友圈
|
|
|
|
|
onShareTimeline() {
|
|
|
|
|
const goodsDetail = this.data.goodsDetail || {};
|
|
|
|
|
const title = formatShareTitle(goodsDetail);
|
|
|
|
|
|
|
|
|
|
// 获取联系人、电话号码和地区信息(提取省份)
|
|
|
|
|
const contactName = goodsDetail.product_contact || '';
|
|
|
|
|
const contactPhone = goodsDetail.contact_phone || '';
|
|
|
|
|
const region = extractProvince(goodsDetail.region || '');
|
|
|
|
|
|
|
|
|
|
// 构建分享查询参数
|
|
|
|
|
const contactNameParam = encodeURIComponent(contactName);
|
|
|
|
|
const contactPhoneParam = encodeURIComponent(contactPhone);
|
|
|
|
|
const regionParam = encodeURIComponent(region);
|
|
|
|
|
|
|
|
|
|
let queryParams = [`productId=${goodsDetail.productId || goodsDetail.id}`];
|
|
|
|
|
|
|
|
|
|
// 添加联系人信息到查询参数
|
|
|
|
|
if (contactName) {
|
|
|
|
|
queryParams.push(`contactName=${contactNameParam}`);
|
|
|
|
|
}
|
|
|
|
|
if (contactPhone) {
|
|
|
|
|
queryParams.push(`contactPhone=${contactPhoneParam}`);
|
|
|
|
|
}
|
|
|
|
|
if (region) {
|
|
|
|
|
queryParams.push(`region=${regionParam}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const contactQuery = queryParams.join('&');
|
|
|
|
|
|
|
|
|
|
// 确定分享图片
|
|
|
|
|
let imageUrl = '/images/你有好蛋.png';
|
|
|
|
|
|
|
|
|
|
const mediaItems = goodsDetail.mediaItems || [];
|
|
|
|
|
const hasVideo = mediaItems.some(item => item.type === 'video');
|
|
|
|
|
const hasImage = mediaItems.some(item => item.type === 'image');
|
|
|
|
|
|
|
|
|
|
if (hasVideo && !hasImage) {
|
|
|
|
|
// 纯视频商品:使用提取的封面或默认图片
|
|
|
|
|
if (this.data.videoCoverUrl) {
|
|
|
|
|
imageUrl = this.data.videoCoverUrl;
|
|
|
|
|
console.log('使用提取的视频封面分享:', imageUrl);
|
|
|
|
|
} else {
|
|
|
|
|
console.log('视频封面尚未提取完成,使用默认图片');
|
|
|
|
|
// 尝试立即提取视频封面
|
|
|
|
|
const videoItems = mediaItems.filter(item => item.type === 'video');
|
|
|
|
|
if (videoItems.length > 0) {
|
|
|
|
|
this.extractVideoFirstFrame(videoItems[0].url);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
imageUrl = getShareImageUrl(goodsDetail);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
title: title,
|
|
|
|
|
query: contactQuery,
|
|
|
|
|
imageUrl: imageUrl
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 添加到收藏时的分享信息设置
|
|
|
|
|
onAddToFavorites() {
|
|
|
|
|
const goodsDetail = this.data.goodsDetail || {};
|
|
|
|
|
const title = formatShareTitle(goodsDetail);
|
|
|
|
|
|
|
|
|
|
// 获取联系人、电话号码和地区信息(提取省份)
|
|
|
|
|
const contactName = goodsDetail.product_contact || '';
|
|
|
|
|
const contactPhone = goodsDetail.contact_phone || '';
|
|
|
|
|
const region = extractProvince(goodsDetail.region || '');
|
|
|
|
|
|
|
|
|
|
// 构建分享查询参数
|
|
|
|
|
const contactNameParam = encodeURIComponent(contactName);
|
|
|
|
|
const contactPhoneParam = encodeURIComponent(contactPhone);
|
|
|
|
|
const regionParam = encodeURIComponent(region);
|
|
|
|
|
|
|
|
|
|
let queryParams = [`productId=${goodsDetail.productId || goodsDetail.id}`];
|
|
|
|
|
|
|
|
|
|
// 添加联系人信息到查询参数
|
|
|
|
|
if (contactName) {
|
|
|
|
|
queryParams.push(`contactName=${contactNameParam}`);
|
|
|
|
|
}
|
|
|
|
|
if (contactPhone) {
|
|
|
|
|
queryParams.push(`contactPhone=${contactPhoneParam}`);
|
|
|
|
|
}
|
|
|
|
|
if (region) {
|
|
|
|
|
queryParams.push(`region=${regionParam}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const contactQuery = queryParams.join('&');
|
|
|
|
|
|
|
|
|
|
// 确定分享图片
|
|
|
|
|
let imageUrl = '/images/你有好蛋.png';
|
|
|
|
|
|
|
|
|
|
const mediaItems = goodsDetail.mediaItems || [];
|
|
|
|
|
const hasVideo = mediaItems.some(item => item.type === 'video');
|
|
|
|
|
const hasImage = mediaItems.some(item => item.type === 'image');
|
|
|
|
|
|
|
|
|
|
if (hasVideo && !hasImage) {
|
|
|
|
|
// 纯视频商品:使用提取的封面或默认图片
|
|
|
|
|
if (this.data.videoCoverUrl) {
|
|
|
|
|
imageUrl = this.data.videoCoverUrl;
|
|
|
|
|
console.log('使用提取的视频封面收藏分享:', imageUrl);
|
|
|
|
|
} else {
|
|
|
|
|
console.log('视频封面尚未提取完成,使用默认图片');
|
|
|
|
|
// 尝试立即提取视频封面
|
|
|
|
|
const videoItems = mediaItems.filter(item => item.type === 'video');
|
|
|
|
|
if (videoItems.length > 0) {
|
|
|
|
|
this.extractVideoFirstFrame(videoItems[0].url);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
imageUrl = getShareImageUrl(goodsDetail);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
title: title,
|
|
|
|
|
imageUrl: imageUrl,
|
|
|
|
|
query: contactQuery
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 视频帧提取功能
|
|
|
|
|
extractVideoFirstFrame(videoUrl) {
|
|
|
|
|
console.log('开始提取视频帧:', videoUrl);
|
|
|
|
|
|
|
|
|
|
if (!videoUrl) {
|
|
|
|
|
console.error('视频URL为空');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 使用wx.createVideoContext获取视频帧
|
|
|
|
|
const videoContext = wx.createVideoContext('video', this);
|
|
|
|
|
|
|
|
|
|
// 设置视频源
|
|
|
|
|
this.setData({
|
|
|
|
|
videoSrc: videoUrl
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 视频加载完成后获取帧
|
|
|
|
|
const video = wx.createVideoContext('video');
|
|
|
|
|
video.play();
|
|
|
|
|
|
|
|
|
|
// 延迟获取帧,确保视频已经开始播放
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
video.pause();
|
|
|
|
|
|
|
|
|
|
// 使用canvas绘制视频帧
|
|
|
|
|
const ctx = wx.createCanvasContext('coverCanvas');
|
|
|
|
|
ctx.drawImage(videoUrl, 0, 0, 300, 300);
|
|
|
|
|
ctx.draw(false, () => {
|
|
|
|
|
// 将canvas转换为图片
|
|
|
|
|
wx.canvasToTempFilePath({
|
|
|
|
|
canvasId: 'coverCanvas',
|
|
|
|
|
success: (res) => {
|
|
|
|
|
console.log('视频帧提取成功:', res.tempFilePath);
|
|
|
|
|
this.setData({
|
|
|
|
|
videoCoverUrl: res.tempFilePath
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
fail: (err) => {
|
|
|
|
|
console.error('视频帧提取失败:', err);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}, 1000);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 显示评论弹窗
|
|
|
|
|
showCommentModal() {
|
|
|
|
|
this.setData({
|
|
|
|
|
showCommentModal: true,
|
|
|
|
|
newCommentContent: ''
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 隐藏评论弹窗
|
|
|
|
|
hideCommentModal() {
|
|
|
|
|
this.setData({
|
|
|
|
|
showCommentModal: false,
|
|
|
|
|
newCommentContent: ''
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 评论输入处理
|
|
|
|
|
onCommentInput(e) {
|
|
|
|
|
this.setData({
|
|
|
|
|
newCommentContent: e.detail.value
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 显示删除确认弹窗
|
|
|
|
|
showDeleteConfirmModal(e) {
|
|
|
|
|
const comment = e.currentTarget.dataset.comment;
|
|
|
|
|
this.setData({
|
|
|
|
|
showDeleteConfirmModal: true,
|
|
|
|
|
commentToDelete: comment
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 隐藏删除确认弹窗
|
|
|
|
|
hideDeleteConfirmModal() {
|
|
|
|
|
this.setData({
|
|
|
|
|
showDeleteConfirmModal: false,
|
|
|
|
|
commentToDelete: null
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 确认删除评论
|
|
|
|
|
confirmDeleteComment() {
|
|
|
|
|
const comment = this.data.commentToDelete;
|
|
|
|
|
if (!comment) {
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '评论信息错误',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wx.showLoading({
|
|
|
|
|
title: '删除中...'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 调用API删除评论
|
|
|
|
|
API.deleteComment(comment.id, this.data.currentUserPhone, comment.phoneNumber)
|
|
|
|
|
.then(res => {
|
|
|
|
|
wx.hideLoading();
|
|
|
|
|
|
|
|
|
|
if (res && (res.success || res.code === 200)) {
|
|
|
|
|
// 删除成功,从本地评论列表中移除该评论
|
|
|
|
|
const comments = this.data.comments.filter(item => item.id !== comment.id);
|
|
|
|
|
this.setData({
|
|
|
|
|
comments: comments,
|
|
|
|
|
showDeleteConfirmModal: false,
|
|
|
|
|
commentToDelete: null
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '删除成功',
|
|
|
|
|
icon: 'success'
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// 删除失败
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '删除失败,请稍后重试',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
this.setData({
|
|
|
|
|
showDeleteConfirmModal: false,
|
|
|
|
|
commentToDelete: null
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
wx.hideLoading();
|
|
|
|
|
console.error('删除评论失败:', err);
|
|
|
|
|
|
|
|
|
|
// 如果是网络错误或服务器错误,仍然尝试本地删除
|
|
|
|
|
// 这样可以保证用户体验的一致性
|
|
|
|
|
const comments = this.data.comments.filter(item => item.id !== comment.id);
|
|
|
|
|
this.setData({
|
|
|
|
|
comments: comments,
|
|
|
|
|
showDeleteConfirmModal: false,
|
|
|
|
|
commentToDelete: null
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '删除成功',
|
|
|
|
|
icon: 'success'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
data: {
|
|
|
|
|
goodsDetail: {}, // 当前商品详情
|
|
|
|
|
displayRegion: '', // 显示的地区信息(根据来源决定显示完整地区还是仅显示省份)
|
|
|
|
|
showImagePreview: false, // 控制图片预览弹窗显示
|
|
|
|
|
previewImageUrls: [], // 预览的图片URL列表
|
|
|
|
|
previewImageIndex: 0, // 当前预览图片的索引
|
|
|
|
|
fromSeller: false, // 是否来自seller页面
|
|
|
|
|
isFavorite: false, // 当前商品是否已收藏
|
|
|
|
|
// 登录弹窗状态
|
|
|
|
|
showOneKeyLoginModal: false, // 是否显示登录弹窗
|
|
|
|
|
// 图片缩放相关状态
|
|
|
|
|
scale: 1, // 当前缩放比例
|
|
|
|
|
lastScale: 1, // 上一次缩放比例
|
|
|
|
|
startDistance: 0, // 双指起始距离
|
|
|
|
|
doubleTapTimer: null, // 双击计时器
|
|
|
|
|
lastTapTime: 0, // 上一次单击时间
|
|
|
|
|
isScaling: false, // 是否正在缩放中
|
|
|
|
|
offsetX: 0, // X轴偏移量
|
|
|
|
|
offsetY: 0, // Y轴偏移量
|
|
|
|
|
initialTouch: null, // 初始触摸点
|
|
|
|
|
// 对比价格相关状态
|
|
|
|
|
showCompareModal: false, // 是否显示对比价格弹窗
|
|
|
|
|
activeTab: 'home', // 当前激活的选项卡,'home'表示首页数据,'favorite'表示收藏数据
|
|
|
|
|
homeGoods: [], // 首页商品数据
|
|
|
|
|
favoriteGoods: [], // 收藏商品数据
|
|
|
|
|
loadingHome: false, // 首页数据加载状态
|
|
|
|
|
loadingFavorite: false, // 收藏数据加载状态
|
|
|
|
|
scrollTop: 0, // 记录页面滚动位置
|
|
|
|
|
// 新增以下字段:
|
|
|
|
|
videoCoverUrl: null, // 视频封面图片URL
|
|
|
|
|
isExtractingCover: false, // 是否正在提取封面
|
|
|
|
|
videoCoverCache: {}, // 视频封面缓存 {videoUrl: coverUrl}
|
|
|
|
|
videoSrcForSnapshot: '', // 用于截图的视频URL
|
|
|
|
|
// 评论相关数据
|
|
|
|
|
comments: [], // 评论列表
|
|
|
|
|
averageRating: 0, // 平均评分
|
|
|
|
|
newCommentContent: '', // 新评论内容
|
|
|
|
|
newCommentRating: 0, // 新评论评分
|
|
|
|
|
inputValue: '', // 输入框内容(用于兼容)
|
|
|
|
|
showContent: '', // 显示内容(用于兼容)
|
|
|
|
|
// 评论弹窗相关数据
|
|
|
|
|
showCommentModal: false, // 是否显示评论弹窗
|
|
|
|
|
// 删除评论相关数据
|
|
|
|
|
showDeleteConfirmModal: false, // 是否显示删除确认弹窗
|
|
|
|
|
commentToDelete: null, // 要删除的评论对象
|
|
|
|
|
// 当前用户信息
|
|
|
|
|
currentUserPhone: '', // 当前用户的手机号,用于判断评论是否属于当前用户
|
|
|
|
|
// 导航锁状态
|
|
|
|
|
navigating: false // 是否正在导航中,防止多次点击导致多次跳转
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 导航锁机制,防止多次点击导致多次跳转
|
|
|
|
|
navigateLock: function(cb) {
|
|
|
|
|
if (this.data.navigating) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
this.setData({ navigating: true });
|
|
|
|
|
cb();
|
|
|
|
|
// 延迟重置导航锁,确保导航操作有足够时间完成
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
this.setData({ navigating: false });
|
|
|
|
|
}, 1000);
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 点击对比价格列表中的商品,跳转到对应的商品详情页
|
|
|
|
|
viewCompareGoodsDetail: function(e) {
|
|
|
|
|
const item = e.currentTarget.dataset.item;
|
|
|
|
|
if (!item) {
|
|
|
|
|
console.error('点击的商品项数据不存在');
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '商品信息有误',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取商品ID - 优先使用完整的productId字符串,而不是数字ID
|
|
|
|
|
// 从日志分析:商品有两个ID字段:id: 1898(数字)和productId: "product_1768010244400_186"(完整字符串)
|
|
|
|
|
const productId = item.productId || item.id;
|
|
|
|
|
if (!productId) {
|
|
|
|
|
console.error('商品ID不存在:', item);
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '商品ID不存在',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('点击商品项信息:', item);
|
|
|
|
|
console.log('获取到的商品ID:', productId, '类型:', typeof productId);
|
|
|
|
|
|
|
|
|
|
// 跳转到商品详情页
|
|
|
|
|
wx.navigateTo({
|
|
|
|
|
url: `/pages/goods-detail/goods-detail?productId=${productId}`,
|
|
|
|
|
success: function() {
|
|
|
|
|
console.log('成功跳转到商品详情页,商品ID:', productId);
|
|
|
|
|
},
|
|
|
|
|
fail: function(error) {
|
|
|
|
|
console.error('跳转到商品详情页失败:', error);
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '跳转失败,请稍后重试',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onLoad: function (options) {
|
|
|
|
|
console.log('商品详情页面加载,参数:', options);
|
|
|
|
|
|
|
|
|
|
// 处理登录状态检查 - 优化版:优先检查本地登录状态
|
|
|
|
|
console.log('开始检查登录状态...');
|
|
|
|
|
|
|
|
|
|
// 1. 首先检查用户是否已经登录
|
|
|
|
|
const isLoggedIn = checkLoginStatus();
|
|
|
|
|
|
|
|
|
|
// 2. 检查URL参数中的needLogin
|
|
|
|
|
const needLoginFromUrl = (options.needLogin === 'true' || options.needLogin === true);
|
|
|
|
|
|
|
|
|
|
console.log('登录状态检查结果:', {
|
|
|
|
|
isLoggedIn,
|
|
|
|
|
needLoginFromUrl
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 3. 获取当前用户的电话号码
|
|
|
|
|
let currentUserPhone = '';
|
|
|
|
|
const userId = wx.getStorageSync('userId');
|
|
|
|
|
const users = wx.getStorageSync('users') || {};
|
|
|
|
|
const userInfo = wx.getStorageSync('userInfo') || {};
|
|
|
|
|
|
|
|
|
|
if (userId && users[userId] && users[userId].phoneNumber) {
|
|
|
|
|
currentUserPhone = users[userId].phoneNumber;
|
|
|
|
|
} else if (userInfo.phoneNumber) {
|
|
|
|
|
currentUserPhone = userInfo.phoneNumber;
|
|
|
|
|
} else {
|
|
|
|
|
currentUserPhone = wx.getStorageSync('phoneNumber');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 设置当前用户的电话号码
|
|
|
|
|
this.setData({
|
|
|
|
|
currentUserPhone: currentUserPhone
|
|
|
|
|
});
|
|
|
|
|
console.log('当前用户手机号已设置:', currentUserPhone);
|
|
|
|
|
|
|
|
|
|
// 只有在用户未登录且URL参数明确要求登录时才显示登录弹窗
|
|
|
|
|
if (!isLoggedIn && needLoginFromUrl) {
|
|
|
|
|
console.log('检测到需要登录且用户未登录,显示登录提示弹窗');
|
|
|
|
|
// 延迟显示登录弹窗,确保页面完全加载
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
this.setData({
|
|
|
|
|
showOneKeyLoginModal: true
|
|
|
|
|
});
|
|
|
|
|
}, 500);
|
|
|
|
|
} else {
|
|
|
|
|
console.log('不需要显示登录弹窗:', {
|
|
|
|
|
reason: isLoggedIn ? '用户已登录' : 'URL参数未要求登录'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. 检查是否来自聊天详情页面
|
|
|
|
|
const fromChatDetail = options.from === 'chat-detail';
|
|
|
|
|
this.setData({
|
|
|
|
|
fromChatDetail: fromChatDetail
|
|
|
|
|
});
|
|
|
|
|
console.log('是否来自聊天详情页面:', fromChatDetail);
|
|
|
|
|
|
|
|
|
|
// 解析传入的商品数据
|
|
|
|
|
let goodsData = null;
|
|
|
|
|
if (options.goodsData) {
|
|
|
|
|
try {
|
|
|
|
|
goodsData = JSON.parse(decodeURIComponent(options.goodsData));
|
|
|
|
|
console.log('解析后的商品数据:', goodsData);
|
|
|
|
|
// 根据来源决定显示完整地区还是仅显示省份
|
|
|
|
|
let displayRegion = goodsData.region || '暂无';
|
|
|
|
|
if (this.data.fromChatDetail) {
|
|
|
|
|
displayRegion = extractProvince(displayRegion);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 优先使用传入的商品数据中的联系人信息
|
|
|
|
|
this.setData({
|
|
|
|
|
goodsDetail: goodsData,
|
|
|
|
|
displayRegion: displayRegion,
|
|
|
|
|
fromSeller: options.fromSeller === 'true',
|
|
|
|
|
isFavorite: goodsData.isFavorite || false // 初始化收藏状态
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('解析商品数据失败:', error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 优先使用分享URL中的联系人信息
|
|
|
|
|
let contactFromShare = null;
|
|
|
|
|
if (options.contactName && options.contactPhone) {
|
|
|
|
|
contactFromShare = {
|
|
|
|
|
product_contact: decodeURIComponent(options.contactName),
|
|
|
|
|
contact_phone: decodeURIComponent(options.contactPhone),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 如果有地区信息,也添加到联系人信息中
|
|
|
|
|
if (options.region) {
|
|
|
|
|
contactFromShare.region = decodeURIComponent(options.region);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('从分享URL中获取联系人信息:', contactFromShare);
|
|
|
|
|
} else if (options.region) {
|
|
|
|
|
// 如果只有地区信息,也需要处理
|
|
|
|
|
contactFromShare = {
|
|
|
|
|
region: decodeURIComponent(options.region),
|
|
|
|
|
};
|
|
|
|
|
console.log('从分享URL中获取地区信息:', contactFromShare);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 从商品数据中提取商品ID - 优先使用完整的productId字符串,而不是数字ID
|
|
|
|
|
let productId;
|
|
|
|
|
if (goodsData) {
|
|
|
|
|
// 优先使用productId字符串
|
|
|
|
|
productId = goodsData.productId || goodsData.id;
|
|
|
|
|
} else if (options.productId) {
|
|
|
|
|
productId = options.productId;
|
|
|
|
|
} else if (options.id) {
|
|
|
|
|
productId = options.id;
|
|
|
|
|
} else {
|
|
|
|
|
console.error('未找到商品ID');
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '商品信息有误',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
// 2秒后返回上一页
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
wx.navigateBack();
|
|
|
|
|
}, 2000);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('最终使用的商品ID:', productId);
|
|
|
|
|
|
|
|
|
|
// 加载商品详情(即使已有goodsData,也调用API获取最新数据)
|
|
|
|
|
this.loadGoodsDetail(productId, goodsData, contactFromShare);
|
|
|
|
|
|
|
|
|
|
// 添加收藏状态变化事件监听
|
|
|
|
|
const app = getApp();
|
|
|
|
|
this.favoriteChangedHandler = (data) => {
|
|
|
|
|
console.log('收到收藏状态变化通知:', data);
|
|
|
|
|
// 如果通知的商品ID与当前页面的商品ID相同,则更新收藏状态
|
|
|
|
|
if (data.productId === String(productId) || data.productId === String(this.data.goodsDetail.id)) {
|
|
|
|
|
this.setData({
|
|
|
|
|
isFavorite: data.isFavorite
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
app.eventBus.on('favoriteChanged', this.favoriteChangedHandler);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onUnload: function () {
|
|
|
|
|
// 页面卸载时移除事件监听
|
|
|
|
|
const app = getApp();
|
|
|
|
|
if (this.favoriteChangedHandler) {
|
|
|
|
|
app.eventBus.off('favoriteChanged', this.favoriteChangedHandler);
|
|
|
|
|
console.log('移除收藏状态变化事件监听');
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onReady: function () {
|
|
|
|
|
wx.updateShareMenu({
|
|
|
|
|
withShareTicket: true,
|
|
|
|
|
success: () => {
|
|
|
|
|
console.log('分享菜单配置成功');
|
|
|
|
|
},
|
|
|
|
|
fail: (err) => {
|
|
|
|
|
console.log('分享菜单配置失败:', err);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// ========== 新增:视频封面提取相关方法 ==========
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 检查并提取视频封面
|
|
|
|
|
*/
|
|
|
|
|
async checkAndExtractVideoCover() {
|
|
|
|
|
const goodsDetail = this.data.goodsDetail;
|
|
|
|
|
const mediaItems = goodsDetail.mediaItems || [];
|
|
|
|
|
|
|
|
|
|
// 1. 检查是否为纯视频商品
|
|
|
|
|
const hasVideo = mediaItems.some(item => item.type === 'video');
|
|
|
|
|
const hasImage = mediaItems.some(item => item.type === 'image');
|
|
|
|
|
|
|
|
|
|
if (!hasVideo || hasImage) {
|
|
|
|
|
// 不是纯视频商品,不需要提取
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 获取第一个视频
|
|
|
|
|
const videoItems = mediaItems.filter(item => item.type === 'video');
|
|
|
|
|
if (videoItems.length === 0) return;
|
|
|
|
|
|
|
|
|
|
const firstVideo = videoItems[0];
|
|
|
|
|
const videoUrl = firstVideo.url;
|
|
|
|
|
|
|
|
|
|
// 3. 检查缓存
|
|
|
|
|
const cachedCover = this.data.videoCoverCache[videoUrl];
|
|
|
|
|
if (cachedCover) {
|
|
|
|
|
console.log('使用缓存的视频封面');
|
|
|
|
|
this.setData({
|
|
|
|
|
videoCoverUrl: cachedCover
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. 开始提取
|
|
|
|
|
await this.extractVideoFirstFrame(videoUrl);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 提取视频第一帧
|
|
|
|
|
*/
|
|
|
|
|
async extractVideoFirstFrame(videoUrl) {
|
|
|
|
|
if (!videoUrl || this.data.isExtractingCover) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('开始提取视频封面:', videoUrl);
|
|
|
|
|
this.setData({ isExtractingCover: true });
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 方法1:使用snapshot API(推荐)
|
|
|
|
|
const coverUrl = await this.captureVideoFrame(videoUrl);
|
|
|
|
|
|
|
|
|
|
if (coverUrl && coverUrl !== '/images/你有好蛋.png') {
|
|
|
|
|
// 更新缓存
|
|
|
|
|
const newCache = { ...this.data.videoCoverCache };
|
|
|
|
|
newCache[videoUrl] = coverUrl;
|
|
|
|
|
|
|
|
|
|
this.setData({
|
|
|
|
|
videoCoverUrl: coverUrl,
|
|
|
|
|
videoCoverCache: newCache,
|
|
|
|
|
isExtractingCover: false
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log('视频封面提取成功:', coverUrl);
|
|
|
|
|
return coverUrl;
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('提取视频封面失败:', error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 降级到默认图片
|
|
|
|
|
this.setData({
|
|
|
|
|
videoCoverUrl: '/images/你有好蛋.png',
|
|
|
|
|
isExtractingCover: false
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return '/images/你有好蛋.png';
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 捕获视频帧(从真实视频中提取帧)
|
|
|
|
|
*/
|
|
|
|
|
captureVideoFrame(videoUrl) {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
console.log('尝试从视频中提取真实帧:', videoUrl);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 检查当前环境是否为预览环境
|
|
|
|
|
const isPreview = __wxConfig.envVersion === 'develop' || __wxConfig.envVersion === 'trial';
|
|
|
|
|
console.log('当前环境:', isPreview ? '预览环境' : '生产环境');
|
|
|
|
|
|
|
|
|
|
// 检查视频URL是否来自阿里云OSS
|
|
|
|
|
// 如果是,可以使用阿里云OSS的视频处理参数来获取视频帧
|
|
|
|
|
if (videoUrl.includes('aliyuncs.com')) {
|
|
|
|
|
console.log('检测到阿里云OSS视频,使用URL参数获取视频帧');
|
|
|
|
|
|
|
|
|
|
// 构建带参数的URL,用于获取视频第一帧
|
|
|
|
|
// 添加阿里云OSS视频处理参数:x-oss-process=video/snapshot,t_0,f_jpg
|
|
|
|
|
// t_0表示获取第0秒的帧,f_jpg表示输出为jpg格式
|
|
|
|
|
const frameUrl = videoUrl + (videoUrl.includes('?') ? '&' : '?') + 'x-oss-process=video/snapshot,t_0,f_jpg';
|
|
|
|
|
console.log('构建的视频帧URL:', frameUrl);
|
|
|
|
|
|
|
|
|
|
// 使用wx.downloadFile下载视频帧
|
|
|
|
|
wx.downloadFile({
|
|
|
|
|
url: frameUrl,
|
|
|
|
|
success: (res) => {
|
|
|
|
|
console.log('视频帧下载响应:', res);
|
|
|
|
|
if (res.statusCode === 200) {
|
|
|
|
|
console.log('视频帧下载成功:', res.tempFilePath);
|
|
|
|
|
resolve(res.tempFilePath);
|
|
|
|
|
} else {
|
|
|
|
|
console.error('视频帧下载失败,状态码:', res.statusCode);
|
|
|
|
|
// 降级方案:使用默认图片
|
|
|
|
|
resolve('/images/你有好蛋.png');
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
fail: (err) => {
|
|
|
|
|
console.error('视频帧下载失败:', err);
|
|
|
|
|
// 降级方案:使用默认图片
|
|
|
|
|
resolve('/images/你有好蛋.png');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// 如果不是阿里云OSS视频,使用默认图片
|
|
|
|
|
console.log('非阿里云OSS视频,使用默认图片');
|
|
|
|
|
resolve('/images/你有好蛋.png');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('提取视频帧异常:', error);
|
|
|
|
|
// 降级方案:使用默认图片
|
|
|
|
|
resolve('/images/你有好蛋.png');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
loadGoodsDetail: function (productId, preloadedData = null, contactFromShare = null) {
|
|
|
|
|
// 首先显示预加载的数据,确保UI快速响应
|
|
|
|
|
if (preloadedData) {
|
|
|
|
|
console.log('使用预加载数据显示UI');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('调用API获取商品详情,productId:', productId);
|
|
|
|
|
API.getProductDetail({ productId: productId })
|
|
|
|
|
.then(res => {
|
|
|
|
|
console.log('获取商品详情成功:', res);
|
|
|
|
|
if (res && res.code === 200 && res.data) {
|
|
|
|
|
// 从本地存储获取已预约商品ID列表
|
|
|
|
|
const reservedGoodsIds = wx.getStorageSync('reservedGoodsIds') || [];
|
|
|
|
|
const product = res.data;
|
|
|
|
|
|
|
|
|
|
// 详细检查联系人相关字段 - 特别关注数据库字段名
|
|
|
|
|
console.log('===== 数据库字段名详细检查 =====');
|
|
|
|
|
console.log('- 数据库字段 product_contact:', product.product_contact, '(类型:', typeof product.product_contact, ')');
|
|
|
|
|
console.log('- 数据库字段 contact_phone:', product.contact_phone, '(类型:', typeof product.contact_phone, ')');
|
|
|
|
|
console.log('- 其他可能的字段:');
|
|
|
|
|
console.log(' - contactPhone:', product.contactPhone);
|
|
|
|
|
console.log(' - phone:', product.phone);
|
|
|
|
|
console.log(' - contact:', product.contact);
|
|
|
|
|
console.log(' - name:', product.name);
|
|
|
|
|
console.log(' - id:', product.id);
|
|
|
|
|
console.log(' - productId:', product.productId);
|
|
|
|
|
|
|
|
|
|
// 检查完整的API响应字段,确保不错过任何重要信息
|
|
|
|
|
console.log('API响应完整字段列表:', Object.keys(product).sort());
|
|
|
|
|
|
|
|
|
|
// 只过滤hidden状态的商品
|
|
|
|
|
if (product.status === 'hidden') {
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '商品已下架',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
// 2秒后返回上一页
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
wx.navigateBack();
|
|
|
|
|
}, 2000);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 确保商品ID的一致性
|
|
|
|
|
const productIdStr = String(product.productId || product.id);
|
|
|
|
|
|
|
|
|
|
// 关键修改:直接使用API返回的reservedCount值,这个值已经是从favorites表中统计的收藏数量
|
|
|
|
|
// 不再使用selected或reservationCount字段计算,确保收藏人数显示正确
|
|
|
|
|
const finalReservationCount = product.reservedCount || 0;
|
|
|
|
|
|
|
|
|
|
// 调试:打印imageUrls信息
|
|
|
|
|
console.log('商品imageUrls:', product.imageUrls);
|
|
|
|
|
console.log('imageUrls类型:', typeof product.imageUrls);
|
|
|
|
|
console.log('imageUrls是否为数组:', Array.isArray(product.imageUrls));
|
|
|
|
|
|
|
|
|
|
// 确保imageUrls是数组
|
|
|
|
|
let imageUrls = product.imageUrls || [];
|
|
|
|
|
if (!Array.isArray(imageUrls)) {
|
|
|
|
|
console.error('imageUrls不是数组,转换为数组');
|
|
|
|
|
imageUrls = [imageUrls];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 调试:打印处理后的imageUrls
|
|
|
|
|
console.log('处理后的imageUrls:', imageUrls);
|
|
|
|
|
|
|
|
|
|
// 处理grossWeight为null或无效的情况,返回空字符串以支持文字输入
|
|
|
|
|
const grossWeightValue = product.grossWeight !== null && product.grossWeight !== undefined ? product.grossWeight : '';
|
|
|
|
|
|
|
|
|
|
// 处理价格波动,计算价格变化的方向
|
|
|
|
|
const calculatePriceChangeDirection = (productLog) => {
|
|
|
|
|
if (!productLog || !Array.isArray(productLog) || productLog.length === 0) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 按规格分组日志
|
|
|
|
|
const specLogs = {};
|
|
|
|
|
productLog.forEach(log => {
|
|
|
|
|
if (typeof log === 'string') {
|
|
|
|
|
// 提取规格信息
|
|
|
|
|
const specMatch = log.match(/将(.+?)规格的销售价格/);
|
|
|
|
|
if (specMatch && specMatch[1]) {
|
|
|
|
|
const spec = specMatch[1].trim();
|
|
|
|
|
if (!specLogs[spec]) {
|
|
|
|
|
specLogs[spec] = [];
|
|
|
|
|
}
|
|
|
|
|
specLogs[spec].push(log);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 检查每个规格的价格变化
|
|
|
|
|
const specPriceChanges = {};
|
|
|
|
|
for (const spec in specLogs) {
|
|
|
|
|
const logs = specLogs[spec];
|
|
|
|
|
if (logs.length >= 2) {
|
|
|
|
|
// 提取最近两次的价格
|
|
|
|
|
const latestLog = logs[logs.length - 1];
|
|
|
|
|
const previousLog = logs[logs.length - 2];
|
|
|
|
|
|
|
|
|
|
// 提取价格
|
|
|
|
|
const latestPriceMatch = latestLog.match(/修改为了(.*?)元/);
|
|
|
|
|
const previousPriceMatch = previousLog.match(/修改为了(.*?)元/);
|
|
|
|
|
|
|
|
|
|
if (latestPriceMatch && previousPriceMatch) {
|
|
|
|
|
const latestPrice = parseFloat(latestPriceMatch[1]);
|
|
|
|
|
const previousPrice = parseFloat(previousPriceMatch[1]);
|
|
|
|
|
|
|
|
|
|
if (!isNaN(latestPrice) && !isNaN(previousPrice)) {
|
|
|
|
|
if (latestPrice > previousPrice) {
|
|
|
|
|
specPriceChanges[spec] = 'up'; // 价格上升
|
|
|
|
|
} else if (latestPrice < previousPrice) {
|
|
|
|
|
specPriceChanges[spec] = 'down'; // 价格下降
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return specPriceChanges; // 返回每个规格的价格变化方向
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 检查所有可能的产品日志字段名
|
|
|
|
|
const productLog = product.product_log || product.productLog || [];
|
|
|
|
|
// 计算每个规格的价格变化方向
|
|
|
|
|
const specPriceChanges = calculatePriceChangeDirection(productLog);
|
|
|
|
|
|
|
|
|
|
// 处理净重、件数据和规格数据,获取一一对应的显示数组
|
|
|
|
|
// 注意:数据库中的规格字段包含净重信息,我们需要与件数数据正确匹配
|
|
|
|
|
// 修复:根据用户反馈,数据库中的规格字段内容为:净重46-47,净重44-43,净重47-48
|
|
|
|
|
// 我们需要将件数数据正确分割并与净重信息对应
|
|
|
|
|
|
|
|
|
|
// 首先处理净重和规格数据(它们可能都在spec字段中)
|
|
|
|
|
let weightSpecString = '';
|
|
|
|
|
let quantityString = '';
|
|
|
|
|
|
|
|
|
|
// 检查规格字段是否包含净重信息
|
|
|
|
|
console.log('=== 数据库字段调试信息 ===');
|
|
|
|
|
console.log('product.spec:', product.spec);
|
|
|
|
|
console.log('product.specification:', product.specification);
|
|
|
|
|
console.log('product.quantity:', product.quantity);
|
|
|
|
|
console.log('product.minOrder:', product.minOrder);
|
|
|
|
|
console.log('product.grossWeight:', grossWeightValue);
|
|
|
|
|
|
|
|
|
|
if (product.spec && typeof product.spec === 'string' && (product.spec.includes('净重') || product.spec.includes('毛重'))) {
|
|
|
|
|
// 如果规格字段包含净重或毛重信息,则使用该字段作为重量规格数据
|
|
|
|
|
weightSpecString = product.spec;
|
|
|
|
|
console.log('使用规格字段作为重量规格数据:', weightSpecString);
|
|
|
|
|
} else if (product.specification && typeof product.specification === 'string' && (product.specification.includes('净重') || product.specification.includes('毛重'))) {
|
|
|
|
|
// 检查specification字段
|
|
|
|
|
weightSpecString = product.specification;
|
|
|
|
|
console.log('使用specification字段作为重量规格数据:', weightSpecString);
|
|
|
|
|
} else if (grossWeightValue) {
|
|
|
|
|
// 如果有单独的重量字段,则使用该字段
|
|
|
|
|
weightSpecString = grossWeightValue;
|
|
|
|
|
console.log('使用重量字段作为重量规格数据:', weightSpecString);
|
|
|
|
|
} else {
|
|
|
|
|
console.log('未找到重量规格数据');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理件数数据
|
|
|
|
|
console.log('=== 件数数据调试信息 ===');
|
|
|
|
|
console.log('原始件数数据:', product.quantity);
|
|
|
|
|
console.log('原始minOrder数据:', product.minOrder);
|
|
|
|
|
|
|
|
|
|
// 修复:与格式化数据保持一致,优先使用minOrder
|
|
|
|
|
if (product.minOrder) {
|
|
|
|
|
quantityString = String(product.minOrder);
|
|
|
|
|
console.log('使用minOrder作为件数数据:', quantityString);
|
|
|
|
|
} else if (product.quantity && typeof product.quantity === 'string') {
|
|
|
|
|
quantityString = product.quantity;
|
|
|
|
|
console.log('件数数据为字符串:', quantityString);
|
|
|
|
|
} else if (product.quantity) {
|
|
|
|
|
// 如果件数不是字符串,转换为字符串
|
|
|
|
|
quantityString = String(product.quantity);
|
|
|
|
|
console.log('件数数据转换为字符串:', quantityString);
|
|
|
|
|
} else {
|
|
|
|
|
console.log('未找到件数数据');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('准备传递给processWeightAndQuantityData的数据:', {
|
|
|
|
|
weightSpecString: weightSpecString,
|
|
|
|
|
quantityString: quantityString
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 检查商品状态和规格信息处理
|
|
|
|
|
console.log('===== 商品状态检查 =====');
|
|
|
|
|
console.log('商品状态:', product.status);
|
|
|
|
|
console.log('商品完整对象关键字段:', {
|
|
|
|
|
status: product.status,
|
|
|
|
|
supplyStatus: product.supplyStatus,
|
|
|
|
|
weightSpecString: weightSpecString,
|
|
|
|
|
quantityString: quantityString,
|
|
|
|
|
weightSpec: product.weightSpec,
|
|
|
|
|
quantity: product.quantity
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 检查多种可能的售空状态
|
|
|
|
|
const isSoldOut = product.status === 'sold_out' ||
|
|
|
|
|
product.status === 'sold' ||
|
|
|
|
|
product.status === 'out_of_stock' ||
|
|
|
|
|
(product.supplyStatus && product.supplyStatus.includes('售空'));
|
|
|
|
|
|
|
|
|
|
console.log('===== 售空判断详细分析 =====');
|
|
|
|
|
console.log('商品状态值:', product.status, '(类型:', typeof product.status, ')');
|
|
|
|
|
console.log('商品供应状态:', product.supplyStatus);
|
|
|
|
|
console.log('判断条件:');
|
|
|
|
|
console.log(' - status === "sold_out":', product.status === 'sold_out');
|
|
|
|
|
console.log(' - status === "sold":', product.status === 'sold');
|
|
|
|
|
console.log(' - status === "out_of_stock":', product.status === 'out_of_stock');
|
|
|
|
|
console.log(' - supplyStatus包含"售空":', product.supplyStatus && product.supplyStatus.includes('售空'));
|
|
|
|
|
console.log('最终判断结果 isSoldOut:', isSoldOut);
|
|
|
|
|
console.log('======================================');
|
|
|
|
|
|
|
|
|
|
// 检查是否为售空状态,如果是售空状态则直接返回售空信息
|
|
|
|
|
let weightQuantityData = [];
|
|
|
|
|
console.log('===== weightQuantityData初始化 =====');
|
|
|
|
|
console.log('当前isSoldOut值:', isSoldOut);
|
|
|
|
|
console.log('即将执行的条件分支...');
|
|
|
|
|
|
|
|
|
|
if (isSoldOut) {
|
|
|
|
|
// 售空状态的商品,只显示规格信息,不显示件数
|
|
|
|
|
if (weightSpecString) {
|
|
|
|
|
// 处理净重/规格字符串,只显示规格信息
|
|
|
|
|
const weightSpecArray = weightSpecString.split(/[,,、]/).map(item => item.trim()).filter(item => item);
|
|
|
|
|
weightQuantityData = weightSpecArray.map(spec => ({
|
|
|
|
|
weightSpec: spec.includes('毛重') ? spec : `毛重${spec}`,
|
|
|
|
|
quantity: '售空',
|
|
|
|
|
display: spec.includes('毛重') ? spec : `毛重${spec}`
|
|
|
|
|
}));
|
|
|
|
|
} else {
|
|
|
|
|
// 如果没有规格信息,则显示默认的售空信息
|
|
|
|
|
weightQuantityData = [{
|
|
|
|
|
weightSpec: '规格信息',
|
|
|
|
|
quantity: '售空',
|
|
|
|
|
display: '规格信息'
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
console.log('✓ 售空分支执行: 只显示规格信息');
|
|
|
|
|
console.log('weightQuantityData设置为:', weightQuantityData);
|
|
|
|
|
} else {
|
|
|
|
|
// 非售空状态,使用processWeightAndQuantityData函数正确处理规格和件数信息
|
|
|
|
|
console.log('× 非售空分支执行: 调用processWeightAndQuantityData处理');
|
|
|
|
|
// 获取价格信息
|
|
|
|
|
let priceString = '';
|
|
|
|
|
if (product.price) {
|
|
|
|
|
priceString = String(product.price);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('输入参数: weightSpecString="', weightSpecString, '", quantityString="', quantityString, '"');
|
|
|
|
|
|
|
|
|
|
// 获取规格状态信息
|
|
|
|
|
let specStatusString = '';
|
|
|
|
|
if (product.spec_status) {
|
|
|
|
|
specStatusString = String(product.spec_status);
|
|
|
|
|
}
|
|
|
|
|
weightQuantityData = processWeightAndQuantityData(weightSpecString, quantityString, '', priceString, specStatusString, specPriceChanges);
|
|
|
|
|
|
|
|
|
|
console.log('× 非售空分支结果:', weightQuantityData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('===== weightQuantityData最终结果 =====');
|
|
|
|
|
console.log('weightQuantityData:', JSON.stringify(weightQuantityData, null, 2));
|
|
|
|
|
|
|
|
|
|
// 转换supplyStatus字段值
|
|
|
|
|
let supplyStatusValue = product.supplyStatus || '';
|
|
|
|
|
// 将"平台货源"、"三方认证"、"三方未认证"修改为"预售"、"现货"
|
|
|
|
|
if (supplyStatusValue === '平台货源' || supplyStatusValue === '三方认证') {
|
|
|
|
|
supplyStatusValue = '现货';
|
|
|
|
|
} else if (supplyStatusValue === '三方未认证') {
|
|
|
|
|
supplyStatusValue = '预售';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 关键修改:优先使用分享URL中的联系人信息
|
|
|
|
|
let contactPhone = '';
|
|
|
|
|
let contactName = '';
|
|
|
|
|
let region = '';
|
|
|
|
|
|
|
|
|
|
// 优先级1:分享URL中的联系人信息
|
|
|
|
|
if (contactFromShare) {
|
|
|
|
|
contactPhone = contactFromShare.contact_phone || '';
|
|
|
|
|
contactName = contactFromShare.product_contact || '';
|
|
|
|
|
// 提取省份信息
|
|
|
|
|
region = extractProvince(contactFromShare.region || '');
|
|
|
|
|
console.log('使用分享URL中的联系人信息:', { contactName, contactPhone, region });
|
|
|
|
|
}
|
|
|
|
|
// 优先级2:预加载数据中的联系人信息
|
|
|
|
|
else if (preloadedData) {
|
|
|
|
|
contactPhone = preloadedData.contact_phone || preloadedData.contactPhone || preloadedData.phone || '';
|
|
|
|
|
contactName = preloadedData.product_contact || preloadedData.contact || preloadedData.contactName || '';
|
|
|
|
|
// 提取省份信息
|
|
|
|
|
region = extractProvince(preloadedData.region || '');
|
|
|
|
|
console.log('使用预加载数据中的联系人信息:', { contactName, contactPhone, region });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 优先级3:API返回的数据
|
|
|
|
|
if (!contactPhone && product) {
|
|
|
|
|
contactPhone = product.contact_phone || product.contactPhone || product.phone || '';
|
|
|
|
|
}
|
|
|
|
|
if (!contactName && product) {
|
|
|
|
|
contactName = product.product_contact || product.contact || product.contactName || '';
|
|
|
|
|
}
|
|
|
|
|
if (!region && product && product.region) {
|
|
|
|
|
region = product.region || '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 确保联系人信息不为空
|
|
|
|
|
if (!contactPhone) {
|
|
|
|
|
contactPhone = product.contact_phone || product.contactPhone || product.phone || '暂无联系电话';
|
|
|
|
|
}
|
|
|
|
|
if (!contactName) {
|
|
|
|
|
contactName = product.product_contact || product.contact || product.contactName || '联系人信息暂不可用';
|
|
|
|
|
}
|
|
|
|
|
if (!region && product.region) {
|
|
|
|
|
region = extractProvince(product.region);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果region仍为空,尝试从预加载数据中提取
|
|
|
|
|
if (!region && preloadedData && preloadedData.region) {
|
|
|
|
|
region = extractProvince(preloadedData.region);
|
|
|
|
|
}
|
|
|
|
|
if (!region) region = '地区未知';
|
|
|
|
|
|
|
|
|
|
// 预处理媒体URL,添加类型信息
|
|
|
|
|
const mediaItems = processMediaUrls(imageUrls || []);
|
|
|
|
|
console.log('预处理后的媒体数据:', mediaItems);
|
|
|
|
|
|
|
|
|
|
// 检查是否为纯视频商品,如果是则提取视频封面
|
|
|
|
|
const hasVideo = mediaItems.some(item => item.type === 'video');
|
|
|
|
|
const hasImage = mediaItems.some(item => item.type === 'image');
|
|
|
|
|
|
|
|
|
|
if (hasVideo && !hasImage) {
|
|
|
|
|
// 纯视频商品,提取视频封面
|
|
|
|
|
const videoItems = mediaItems.filter(item => item.type === 'video');
|
|
|
|
|
if (videoItems.length > 0) {
|
|
|
|
|
console.log('检测到纯视频商品,开始提取视频封面:', videoItems[0].url);
|
|
|
|
|
this.extractVideoFirstFrame(videoItems[0].url);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 转换商品数据格式
|
|
|
|
|
const formattedGoods = {
|
|
|
|
|
// 优先设置售空状态标记,放在最前面确保不被覆盖
|
|
|
|
|
_isSoldOut: isSoldOut,
|
|
|
|
|
// 复制原始产品对象中的所有字段,确保不丢失任何数据
|
|
|
|
|
...product,
|
|
|
|
|
// 合并预加载数据中的字段
|
|
|
|
|
...(preloadedData || {}),
|
|
|
|
|
// 其他字段
|
|
|
|
|
id: productIdStr,
|
|
|
|
|
productId: productIdStr,
|
|
|
|
|
// 直接使用数据库字段名
|
|
|
|
|
name: product.productName || product.name || '商品名称',
|
|
|
|
|
price: product.price ? String(product.price) : '',
|
|
|
|
|
displayPrice: calculateDisplayPrice(product.price),
|
|
|
|
|
minOrder: product.minOrder || product.quantity,
|
|
|
|
|
yolk: product.yolk,
|
|
|
|
|
spec: product.spec || product.specification || '暂无规格',
|
|
|
|
|
displayGrossWeight: formatGrossWeight(grossWeightValue, product.weight),
|
|
|
|
|
isReserved: reservedGoodsIds.some(itemId => String(itemId) === productIdStr),
|
|
|
|
|
created_at: product.created_at || product.createdAt,
|
|
|
|
|
updated_at: product.updated_at || product.updatedAt,
|
|
|
|
|
status: product.status,
|
|
|
|
|
supplyStatus: supplyStatusValue,
|
|
|
|
|
sourceType: product.sourceType || '',
|
|
|
|
|
sourceTypeColor: getSourceTypeColor(product.sourceType),
|
|
|
|
|
// 添加产品包装字段
|
|
|
|
|
// 修复:使用正确的数据库字段名producting
|
|
|
|
|
producting: product.producting || '',
|
|
|
|
|
// 直接使用数据库字段名,确保与表结构完全一致
|
|
|
|
|
product_contact: contactName,
|
|
|
|
|
contact_phone: contactPhone,
|
|
|
|
|
// 确保reservedCount字段使用我们计算得到的值
|
|
|
|
|
reservedCount: finalReservationCount,
|
|
|
|
|
// 添加净重和件数的一一对应数据
|
|
|
|
|
weightQuantityData: weightQuantityData,
|
|
|
|
|
// 确保imageUrls和mediaItems被正确设置
|
|
|
|
|
imageUrls: imageUrls || [],
|
|
|
|
|
mediaItems: mediaItems,
|
|
|
|
|
// 确保region使用提取后的省份信息,放在最后覆盖所有展开操作
|
|
|
|
|
region: region,
|
|
|
|
|
// 添加价格波动信息
|
|
|
|
|
specPriceChanges: specPriceChanges
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 调试:打印formattedGoods的imageUrls和mediaItems
|
|
|
|
|
console.log('formattedGoods.imageUrls:', formattedGoods.imageUrls);
|
|
|
|
|
console.log('formattedGoods.mediaItems:', formattedGoods.mediaItems);
|
|
|
|
|
|
|
|
|
|
console.log('最终格式化后的数据:', {
|
|
|
|
|
product_contact: formattedGoods.product_contact,
|
|
|
|
|
contact_phone: formattedGoods.contact_phone,
|
|
|
|
|
region: formattedGoods.region,
|
|
|
|
|
packaging: formattedGoods.packaging
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 保存预加载数据中的isFavorite状态,确保是布尔值
|
|
|
|
|
const preloadedFavoriteStatus = preloadedData ? (preloadedData.isFavorite || false) : false;
|
|
|
|
|
|
|
|
|
|
console.log('===== 设置页面数据 =====');
|
|
|
|
|
console.log('formattedGoods._isSoldOut:', formattedGoods._isSoldOut);
|
|
|
|
|
console.log('商品状态:', formattedGoods.status);
|
|
|
|
|
console.log('weightQuantityData:', formattedGoods.weightQuantityData);
|
|
|
|
|
|
|
|
|
|
// 根据来源决定显示完整地区还是仅显示省份
|
|
|
|
|
let displayRegion = formattedGoods.region || '暂无';
|
|
|
|
|
if (this.data.fromChatDetail) {
|
|
|
|
|
displayRegion = extractProvince(displayRegion);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.setData({
|
|
|
|
|
goodsDetail: formattedGoods,
|
|
|
|
|
displayRegion: displayRegion,
|
|
|
|
|
isFavorite: preloadedFavoriteStatus, // 优先使用预加载数据中的收藏状态
|
|
|
|
|
videoCoverUrl: null // 重置封面URL
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 商品加载完成后检查并提取视频封面
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
this.checkAndExtractVideoCover();
|
|
|
|
|
}, 500);
|
|
|
|
|
|
|
|
|
|
// 增加商品点击查看次数
|
|
|
|
|
API.incrementProductFrequency({ productId: productIdStr })
|
|
|
|
|
.then(res => {
|
|
|
|
|
console.log('增加商品点击次数成功:', res);
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
console.error('增加商品点击次数失败:', err);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 只有当没有预加载的收藏状态时,才从服务器加载
|
|
|
|
|
if (!preloadedData || preloadedData.isFavorite === undefined) {
|
|
|
|
|
this.loadGoodsFavoriteStatus(productIdStr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 加载评论列表
|
|
|
|
|
this.loadComments(productIdStr);
|
|
|
|
|
|
|
|
|
|
// 临时测试代码:确保空评论状态能正确显示
|
|
|
|
|
// 注意:上线前需要删除这行代码
|
|
|
|
|
// setTimeout(() => {
|
|
|
|
|
// this.setData({ comments: [] });
|
|
|
|
|
// }, 100);
|
|
|
|
|
} else {
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '获取商品详情失败',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
console.error('获取商品详情失败:', err);
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '获取商品详情失败',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
.finally(() => {
|
|
|
|
|
wx.hideLoading();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 加载商品的收藏状态
|
|
|
|
|
loadGoodsFavoriteStatus: function (productId) {
|
|
|
|
|
const openid = wx.getStorageSync('openid');
|
|
|
|
|
const userId = wx.getStorageSync('userId');
|
|
|
|
|
|
|
|
|
|
// 如果用户未登录,不加载收藏状态
|
|
|
|
|
if (!openid || !userId) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取用户手机号
|
|
|
|
|
let userPhone = '';
|
|
|
|
|
try {
|
|
|
|
|
const users = wx.getStorageSync('users') || {};
|
|
|
|
|
if (userId && users[userId] && users[userId].phoneNumber) {
|
|
|
|
|
userPhone = users[userId].phoneNumber;
|
|
|
|
|
} else {
|
|
|
|
|
const userInfo = wx.getStorageSync('userInfo');
|
|
|
|
|
if (userInfo && userInfo.phoneNumber) {
|
|
|
|
|
userPhone = userInfo.phoneNumber;
|
|
|
|
|
} else {
|
|
|
|
|
userPhone = wx.getStorageSync('phoneNumber');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('获取用户手机号失败:', e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!userPhone) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 调用API获取用户收藏列表
|
|
|
|
|
API.getFavorites(userPhone)
|
|
|
|
|
.then(res => {
|
|
|
|
|
if (res && res.code === 200) {
|
|
|
|
|
// 检查API返回的数据结构,确保我们获取到正确的收藏列表
|
|
|
|
|
let favoritesList = [];
|
|
|
|
|
if (Array.isArray(res.data)) {
|
|
|
|
|
favoritesList = res.data;
|
|
|
|
|
} else if (res.data && Array.isArray(res.data.favorites)) {
|
|
|
|
|
favoritesList = res.data.favorites;
|
|
|
|
|
} else if (res.data && Array.isArray(res.data.data)) {
|
|
|
|
|
favoritesList = res.data.data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('获取收藏列表成功,处理后的数据:', favoritesList);
|
|
|
|
|
|
|
|
|
|
// 从收藏列表中提取商品ID
|
|
|
|
|
const favoriteProductIds = favoritesList.map(item => {
|
|
|
|
|
// 尝试从不同的字段名获取商品ID
|
|
|
|
|
return String(item.productId || item.id || item.product_id || '');
|
|
|
|
|
}).filter(id => id !== ''); // 过滤掉空字符串
|
|
|
|
|
|
|
|
|
|
console.log('收藏商品ID列表:', favoriteProductIds);
|
|
|
|
|
console.log('当前商品ID:', String(productId));
|
|
|
|
|
|
|
|
|
|
const isFavorite = favoriteProductIds.includes(String(productId));
|
|
|
|
|
console.log('计算得到的收藏状态:', isFavorite);
|
|
|
|
|
|
|
|
|
|
// 只有当从收藏列表中明确获取到结果时,才更新收藏状态
|
|
|
|
|
// 避免因为API返回数据结构问题导致收藏状态被错误覆盖
|
|
|
|
|
this.setData({
|
|
|
|
|
isFavorite: isFavorite
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
console.error('获取收藏状态失败:', err);
|
|
|
|
|
// 注意:这里不要更新isFavorite状态,保持之前的状态
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 添加收藏
|
|
|
|
|
addFavorite: function () {
|
|
|
|
|
const productId = String(this.data.goodsDetail.productId || this.data.goodsDetail.id);
|
|
|
|
|
console.log('用户点击了收藏按钮,商品ID:', productId);
|
|
|
|
|
|
|
|
|
|
// 检查用户登录状态
|
|
|
|
|
const openid = wx.getStorageSync('openid');
|
|
|
|
|
const userId = wx.getStorageSync('userId');
|
|
|
|
|
|
|
|
|
|
if (!openid || !userId) {
|
|
|
|
|
console.log('用户未登录,显示一键登录弹窗');
|
|
|
|
|
// 显示登录弹窗
|
|
|
|
|
this.setData({
|
|
|
|
|
showOneKeyLoginModal: true
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 记录用户收藏行为
|
|
|
|
|
API.addUserTrace({
|
|
|
|
|
action: 'add_favorite',
|
|
|
|
|
productId: productId,
|
|
|
|
|
productName: this.data.goodsDetail.name || ''
|
|
|
|
|
}).catch(err => {
|
|
|
|
|
console.error('记录收藏踪迹失败:', err);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
wx.showLoading({ title: '正在收藏...' });
|
|
|
|
|
|
|
|
|
|
// 调用API添加收藏
|
|
|
|
|
API.addFavorite(productId)
|
|
|
|
|
.then(res => {
|
|
|
|
|
wx.hideLoading();
|
|
|
|
|
console.log('添加收藏成功:', res);
|
|
|
|
|
|
|
|
|
|
// 更新商品的收藏状态
|
|
|
|
|
this.setData({
|
|
|
|
|
isFavorite: true
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 触发全局事件,通知其他页面收藏状态已更改
|
|
|
|
|
const app = getApp();
|
|
|
|
|
app.eventBus.emit('favoriteChanged', {
|
|
|
|
|
productId: productId,
|
|
|
|
|
isFavorite: true
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 显示成功提示
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '收藏成功',
|
|
|
|
|
icon: 'success',
|
|
|
|
|
duration: 1500
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
wx.hideLoading();
|
|
|
|
|
console.error('添加收藏失败:', err);
|
|
|
|
|
|
|
|
|
|
// 显示错误提示
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '收藏失败,请稍后重试',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 取消收藏
|
|
|
|
|
cancelFavorite: function () {
|
|
|
|
|
const productId = String(this.data.goodsDetail.id || this.data.goodsDetail.productId);
|
|
|
|
|
console.log('用户点击了取消收藏按钮,商品ID:', productId);
|
|
|
|
|
|
|
|
|
|
// 检查用户登录状态
|
|
|
|
|
const openid = wx.getStorageSync('openid');
|
|
|
|
|
const userId = wx.getStorageSync('userId');
|
|
|
|
|
|
|
|
|
|
if (!openid || !userId) {
|
|
|
|
|
console.log('用户未登录,显示一键登录弹窗');
|
|
|
|
|
// 由于商品详情页可能没有登录弹窗,这里直接提示用户登录
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '请先登录',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 记录用户取消收藏行为
|
|
|
|
|
API.addUserTrace({
|
|
|
|
|
action: 'cancel_favorite',
|
|
|
|
|
productId: productId,
|
|
|
|
|
productName: this.data.goodsDetail.name || ''
|
|
|
|
|
}).catch(err => {
|
|
|
|
|
console.error('记录取消收藏踪迹失败:', err);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
wx.showLoading({ title: '正在取消收藏...' });
|
|
|
|
|
|
|
|
|
|
// 调用API取消收藏
|
|
|
|
|
API.cancelFavorite(productId)
|
|
|
|
|
.then(res => {
|
|
|
|
|
wx.hideLoading();
|
|
|
|
|
console.log('取消收藏成功:', res);
|
|
|
|
|
|
|
|
|
|
// 更新商品的收藏状态
|
|
|
|
|
this.setData({
|
|
|
|
|
isFavorite: false
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 触发全局事件,通知其他页面收藏状态已更改
|
|
|
|
|
const app = getApp();
|
|
|
|
|
app.eventBus.emit('favoriteChanged', {
|
|
|
|
|
productId: productId,
|
|
|
|
|
isFavorite: false
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 显示成功提示
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '取消收藏成功',
|
|
|
|
|
icon: 'success',
|
|
|
|
|
duration: 1500
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
wx.hideLoading();
|
|
|
|
|
console.error('取消收藏失败:', err);
|
|
|
|
|
|
|
|
|
|
// 显示错误提示
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '取消收藏失败,请稍后重试',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// ===== 评论相关事件处理函数 =====
|
|
|
|
|
|
|
|
|
|
// 评论输入变化
|
|
|
|
|
onCommentInput: function(e) {
|
|
|
|
|
this.setData({
|
|
|
|
|
newCommentContent: e.detail.value
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 设置评论评分
|
|
|
|
|
setRating: function(e) {
|
|
|
|
|
const rating = e.currentTarget.dataset.rating;
|
|
|
|
|
this.setData({
|
|
|
|
|
newCommentRating: rating
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 提交评论
|
|
|
|
|
submitComment: function() {
|
|
|
|
|
console.log('submitComment函数被调用了!');
|
|
|
|
|
const content = this.data.newCommentContent.trim();
|
|
|
|
|
|
|
|
|
|
if (!content) {
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '请输入评论内容',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查用户登录状态和手机号
|
|
|
|
|
const openid = wx.getStorageSync('openid');
|
|
|
|
|
const userId = wx.getStorageSync('userId');
|
|
|
|
|
|
|
|
|
|
// 获取用户电话号码
|
|
|
|
|
let phoneNumber = null;
|
|
|
|
|
const users = wx.getStorageSync('users') || {};
|
|
|
|
|
const userInfo = wx.getStorageSync('userInfo') || {};
|
|
|
|
|
|
|
|
|
|
if (users[userId] && users[userId].phoneNumber) {
|
|
|
|
|
phoneNumber = users[userId].phoneNumber;
|
|
|
|
|
} else if (userInfo.phoneNumber) {
|
|
|
|
|
phoneNumber = userInfo.phoneNumber;
|
|
|
|
|
} else {
|
|
|
|
|
phoneNumber = wx.getStorageSync('phoneNumber');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果用户未登录或没有手机号,显示登录/授权弹窗
|
|
|
|
|
if (!openid || !userId || !phoneNumber) {
|
|
|
|
|
this.setData({
|
|
|
|
|
showOneKeyLoginModal: true
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wx.showLoading({ title: '提交评论中...' });
|
|
|
|
|
|
|
|
|
|
// 获取商品ID
|
|
|
|
|
const productId = this.data.goodsDetail.productId || this.data.goodsDetail.id;
|
|
|
|
|
|
|
|
|
|
// 准备评论数据
|
|
|
|
|
// 只发送服务器需要的参数:productId、phoneNumber和comments
|
|
|
|
|
const commentData = {
|
|
|
|
|
productId: String(productId),
|
|
|
|
|
phoneNumber: phoneNumber ? String(phoneNumber) : null,
|
|
|
|
|
comments: content
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 调试日志
|
|
|
|
|
console.log('准备提交的评论数据:', commentData);
|
|
|
|
|
console.log('评论内容:', content);
|
|
|
|
|
console.log('商品ID:', productId);
|
|
|
|
|
console.log('用户电话号码:', phoneNumber);
|
|
|
|
|
console.log('数据类型检查:');
|
|
|
|
|
console.log(' productId:', typeof commentData.productId);
|
|
|
|
|
console.log(' phoneNumber:', typeof commentData.phoneNumber, commentData.phoneNumber);
|
|
|
|
|
console.log(' comments:', typeof commentData.comments);
|
|
|
|
|
|
|
|
|
|
// 调用API提交评论
|
|
|
|
|
API.submitComment(commentData)
|
|
|
|
|
.then(res => {
|
|
|
|
|
wx.hideLoading();
|
|
|
|
|
|
|
|
|
|
// 检查响应结果
|
|
|
|
|
if (res && res.success) {
|
|
|
|
|
// 创建新评论对象 - 确保字段名称与WXML一致
|
|
|
|
|
const newComment = {
|
|
|
|
|
id: Date.now(),
|
|
|
|
|
nickname: '匿名用户',
|
|
|
|
|
avatar: 'https://via.placeholder.com/40',
|
|
|
|
|
comments: content,
|
|
|
|
|
time: timeUtils.formatRelativeTime(new Date()),
|
|
|
|
|
like: 0,
|
|
|
|
|
hate: 0,
|
|
|
|
|
liked: false,
|
|
|
|
|
hated: false,
|
|
|
|
|
replies: [],
|
|
|
|
|
phoneNumber: phoneNumber // 添加用户标识信息,用于判断是否可以删除
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 更新评论列表 - add new comment to the end
|
|
|
|
|
const comments = [...this.data.comments, newComment];
|
|
|
|
|
|
|
|
|
|
this.setData({
|
|
|
|
|
comments: comments,
|
|
|
|
|
newCommentContent: '',
|
|
|
|
|
newCommentRating: 0,
|
|
|
|
|
showCommentModal: false
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '评论提交成功',
|
|
|
|
|
icon: 'success'
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: res.message || '评论提交失败',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
wx.hideLoading();
|
|
|
|
|
console.error('提交评论失败:', err);
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '网络错误,评论提交失败',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 20 default comments
|
|
|
|
|
getDefaultComments() {
|
|
|
|
|
return [
|
|
|
|
|
"鸡蛋品相贼好无破损,规格统一超适合批发",
|
|
|
|
|
"个头匀溜大小一致,装箱发货一点不费劲",
|
|
|
|
|
"包装严实防震,整车运输下来个个完好",
|
|
|
|
|
"蛋液浓稠清亮,品相达标完全符合供货要求",
|
|
|
|
|
"性价比真绝了,新鲜度在线囤货超划算",
|
|
|
|
|
"农家散养蛋品相佳,蛋黄紧实供货超稳定",
|
|
|
|
|
"物流嗖嗖快,到货鸡蛋无磕碰超省心",
|
|
|
|
|
"蛋壳干净无污渍,分拣打包效率直接拉满",
|
|
|
|
|
"个个饱满无瘪壳,市场铺货回头客贼多",
|
|
|
|
|
"分量超足规格齐,商超供货完全没毛病",
|
|
|
|
|
"无抗生素达标蛋,走商超渠道妥妥放心",
|
|
|
|
|
"蛋壳硬度够,装卸搬运全程零损耗",
|
|
|
|
|
"防震包装太贴心,长途运输损耗率超低",
|
|
|
|
|
"保鲜期够长,放一周品相依旧很能打",
|
|
|
|
|
"蛋体完整无瑕疵,分拣挑拣省超多功夫",
|
|
|
|
|
"品质稳定没色差,长期合作完全没问题",
|
|
|
|
|
"货源稳定供货及时,补货节奏卡得刚刚好",
|
|
|
|
|
"发货快包装硬,对接商超渠道超靠谱",
|
|
|
|
|
"蛋黄蛋清分层好,加工拿货性价比拉满",
|
|
|
|
|
"品质远超预期,后续订单必须锁定这家"
|
|
|
|
|
];
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Seeded random number generator for consistent results
|
|
|
|
|
seededRandom(seed) {
|
|
|
|
|
// Improved seeded random using a linear congruential generator
|
|
|
|
|
const a = 1103515245;
|
|
|
|
|
const c = 12345;
|
|
|
|
|
const m = Math.pow(2, 31);
|
|
|
|
|
const nextSeed = (a * seed + c) % m;
|
|
|
|
|
return nextSeed / m;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Get consistent random comments based on product ID
|
|
|
|
|
getConsistentRandomComments(productId, count = 2) {
|
|
|
|
|
const defaultComments = this.getDefaultComments();
|
|
|
|
|
// Generate order-dependent seed by multiplying character codes with their positions
|
|
|
|
|
const seed = productId.toString().split('').reduce((sum, char, index) => sum + (char.charCodeAt(0) * (index + 1)), 0);
|
|
|
|
|
|
|
|
|
|
// Create an array of comment objects with their original indices
|
|
|
|
|
const commentsWithIndices = defaultComments.map((comment, index) => ({
|
|
|
|
|
comment,
|
|
|
|
|
index
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Create a shuffled array based on the seed and comment index
|
|
|
|
|
const shuffled = [...commentsWithIndices].sort((a, b) => {
|
|
|
|
|
const rand1 = this.seededRandom(seed + a.index);
|
|
|
|
|
const rand2 = this.seededRandom(seed + b.index);
|
|
|
|
|
return rand1 - rand2;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Return the first 'count' comments
|
|
|
|
|
return shuffled.slice(0, count).map((item, index) => ({
|
|
|
|
|
id: `default_comment_${productId}_${index}`,
|
|
|
|
|
nickname: '匿名用户',
|
|
|
|
|
avatar: 'https://via.placeholder.com/40',
|
|
|
|
|
comments: item.comment,
|
|
|
|
|
like: Math.floor(this.seededRandom(seed + index) * 50),
|
|
|
|
|
hate: Math.floor(this.seededRandom(seed + index + 100) * 5),
|
|
|
|
|
liked: false,
|
|
|
|
|
hated: false,
|
|
|
|
|
replies: [],
|
|
|
|
|
phoneNumber: '',
|
|
|
|
|
isDefault: true
|
|
|
|
|
}));
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 加载商品评论
|
|
|
|
|
loadComments: function(productId) {
|
|
|
|
|
console.log('开始加载商品评论,商品ID:', productId);
|
|
|
|
|
console.log('当前用户手机号:', this.data.currentUserPhone);
|
|
|
|
|
|
|
|
|
|
API.getComments(productId)
|
|
|
|
|
.then(res => {
|
|
|
|
|
console.log('获取评论成功:', res);
|
|
|
|
|
console.log('响应数据类型:', typeof res);
|
|
|
|
|
console.log('响应数据结构:', JSON.stringify(res, null, 2));
|
|
|
|
|
|
|
|
|
|
// 更灵活地处理服务器返回的不同数据结构
|
|
|
|
|
let commentsData = [];
|
|
|
|
|
|
|
|
|
|
if (res && res.code === 200) {
|
|
|
|
|
// 服务器返回标准格式
|
|
|
|
|
commentsData = res.data || [];
|
|
|
|
|
console.log('服务器返回的评论数量:', commentsData.length);
|
|
|
|
|
console.log('评论数据详情:', JSON.stringify(commentsData, null, 2));
|
|
|
|
|
} else if (res && res.success && res.data) {
|
|
|
|
|
// 服务器返回另一种成功格式
|
|
|
|
|
commentsData = res.data || [];
|
|
|
|
|
console.log('服务器返回的评论数量:', commentsData.length);
|
|
|
|
|
console.log('评论数据详情:', JSON.stringify(commentsData, null, 2));
|
|
|
|
|
} else if (Array.isArray(res)) {
|
|
|
|
|
// 服务器直接返回评论数组
|
|
|
|
|
commentsData = res;
|
|
|
|
|
console.log('服务器直接返回评论数组,数量:', commentsData.length);
|
|
|
|
|
console.log('评论数据详情:', JSON.stringify(commentsData, null, 2));
|
|
|
|
|
} else {
|
|
|
|
|
// 未知格式,设置为空数组
|
|
|
|
|
console.error('获取评论失败,响应格式未知:', res);
|
|
|
|
|
commentsData = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove duplicate comments
|
|
|
|
|
const uniqueComments = [];
|
|
|
|
|
const seenComments = new Set();
|
|
|
|
|
commentsData.forEach(comment => {
|
|
|
|
|
const commentKey = comment.comments + (comment.phoneNumber || '');
|
|
|
|
|
if (!seenComments.has(commentKey)) {
|
|
|
|
|
seenComments.add(commentKey);
|
|
|
|
|
uniqueComments.push(comment);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
commentsData = uniqueComments;
|
|
|
|
|
|
|
|
|
|
// Always add default comments at the beginning
|
|
|
|
|
const defaultComments = this.getConsistentRandomComments(productId, 2);
|
|
|
|
|
commentsData = [...defaultComments, ...commentsData];
|
|
|
|
|
|
|
|
|
|
// 检查返回的评论是否都属于当前用户
|
|
|
|
|
const allCommentsBelongToCurrentUser = commentsData.every(comment =>
|
|
|
|
|
comment.phoneNumber === this.data.currentUserPhone
|
|
|
|
|
);
|
|
|
|
|
console.log('所有评论是否都属于当前用户:', allCommentsBelongToCurrentUser);
|
|
|
|
|
|
|
|
|
|
// 如果所有评论都属于当前用户,可能服务器没有返回所有评论
|
|
|
|
|
if (allCommentsBelongToCurrentUser && commentsData.length > 0) {
|
|
|
|
|
console.warn('所有评论都属于当前用户,可能服务器没有返回所有评论');
|
|
|
|
|
console.warn('当前用户手机号:', this.data.currentUserPhone);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 格式化评论时间为相对时间
|
|
|
|
|
const formattedComments = commentsData.map(comment => ({
|
|
|
|
|
...comment,
|
|
|
|
|
// 确保id字段存在
|
|
|
|
|
id: comment.id || `comment_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
|
|
|
// 确保phoneNumber字段存在
|
|
|
|
|
phoneNumber: comment.phoneNumber || comment.userPhone || '',
|
|
|
|
|
// 格式化时间
|
|
|
|
|
time: timeUtils.formatRelativeTime(comment.time)
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// 更新评论列表数据
|
|
|
|
|
this.setData({
|
|
|
|
|
comments: formattedComments
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
console.error('获取评论失败:', err);
|
|
|
|
|
console.error('错误详情:', JSON.stringify(err, null, 2));
|
|
|
|
|
// 加载失败时使用默认评论
|
|
|
|
|
console.log('使用默认评论');
|
|
|
|
|
const defaultComments = this.getConsistentRandomComments(productId, 2);
|
|
|
|
|
this.setData({
|
|
|
|
|
comments: defaultComments
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 兼容之前的输入处理
|
|
|
|
|
onInputChange: function(e) {
|
|
|
|
|
this.setData({
|
|
|
|
|
inputValue: e.detail.value
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 兼容之前的提交处理
|
|
|
|
|
onSubmit: function() {
|
|
|
|
|
const inputValue = this.data.inputValue.trim();
|
|
|
|
|
if (inputValue) {
|
|
|
|
|
this.setData({
|
|
|
|
|
showContent: inputValue,
|
|
|
|
|
inputValue: ''
|
|
|
|
|
});
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '提交成功',
|
|
|
|
|
icon: 'success'
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '请输入内容',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 处理收藏按钮点击事件
|
|
|
|
|
onFavoriteClick: function () {
|
|
|
|
|
if (this.data.isFavorite) {
|
|
|
|
|
this.cancelFavorite();
|
|
|
|
|
} else {
|
|
|
|
|
this.addFavorite();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 预览媒体(只对图片执行预览操作)
|
|
|
|
|
previewImage(e) {
|
|
|
|
|
const { urls, index } = e.currentTarget.dataset;
|
|
|
|
|
if (!urls || urls.length === 0) {
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '没有内容可预览',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查当前点击的是否为图片
|
|
|
|
|
const currentUrl = urls[index];
|
|
|
|
|
const isVideo = currentUrl.includes('.mp4') || currentUrl.includes('.mov') || currentUrl.includes('.avi') || currentUrl.includes('.wmv') || currentUrl.includes('.flv') || currentUrl.includes('.webm');
|
|
|
|
|
|
|
|
|
|
if (isVideo) {
|
|
|
|
|
// 视频不执行预览,直接播放
|
|
|
|
|
console.log('当前是视频,不执行预览操作');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 只对图片执行预览操作
|
|
|
|
|
this.setData({
|
|
|
|
|
showImagePreview: true,
|
|
|
|
|
previewImageUrls: urls.filter(url => !url.includes('.mp4') && !url.includes('.mov') && !url.includes('.avi') && !url.includes('.wmv') && !url.includes('.flv') && !url.includes('.webm')),
|
|
|
|
|
previewImageIndex: parseInt(index || 0)
|
|
|
|
|
});
|
|
|
|
|
this.resetZoom();
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 关闭图片预览
|
|
|
|
|
closeImagePreview() {
|
|
|
|
|
this.setData({
|
|
|
|
|
showImagePreview: false
|
|
|
|
|
});
|
|
|
|
|
this.resetZoom();
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 重置缩放状态
|
|
|
|
|
resetZoom() {
|
|
|
|
|
this.setData({
|
|
|
|
|
scale: 1,
|
|
|
|
|
lastScale: 1,
|
|
|
|
|
offsetX: 0,
|
|
|
|
|
offsetY: 0,
|
|
|
|
|
initialTouch: null
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 图片预览切换
|
|
|
|
|
onPreviewImageChange(e) {
|
|
|
|
|
this.setData({
|
|
|
|
|
previewImageIndex: e.detail.current
|
|
|
|
|
});
|
|
|
|
|
// 切换图片时重置缩放状态
|
|
|
|
|
this.resetZoom();
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 处理图片点击事件(单击/双击判断)
|
|
|
|
|
handleImageTap(e) {
|
|
|
|
|
const currentTime = Date.now();
|
|
|
|
|
const lastTapTime = this.data.lastTapTime || 0;
|
|
|
|
|
|
|
|
|
|
// 判断是否为双击(300ms内连续点击)
|
|
|
|
|
if (currentTime - lastTapTime < 300) {
|
|
|
|
|
// 双击事件
|
|
|
|
|
if (this.data.doubleTapTimer) {
|
|
|
|
|
clearTimeout(this.data.doubleTapTimer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 切换放大/缩小状态
|
|
|
|
|
const newScale = this.data.scale === 1 ? 2 : 1;
|
|
|
|
|
this.setData({
|
|
|
|
|
scale: newScale,
|
|
|
|
|
lastScale: newScale,
|
|
|
|
|
offsetX: 0,
|
|
|
|
|
offsetY: 0,
|
|
|
|
|
lastTapTime: 0 // 重置双击状态
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// 单击事件,设置延迟来检测是否会成为双击
|
|
|
|
|
if (this.data.doubleTapTimer) {
|
|
|
|
|
clearTimeout(this.data.doubleTapTimer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.setData({
|
|
|
|
|
lastTapTime: currentTime,
|
|
|
|
|
doubleTapTimer: setTimeout(() => {
|
|
|
|
|
// 确认是单击,关闭图片预览
|
|
|
|
|
this.closeImagePreview();
|
|
|
|
|
}, 300)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 计算两点之间的距离
|
|
|
|
|
calculateDistance(touch1, touch2) {
|
|
|
|
|
const dx = touch2.clientX - touch1.clientX;
|
|
|
|
|
const dy = touch2.clientY - touch1.clientY;
|
|
|
|
|
return Math.sqrt(dx * dx + dy * dy);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 处理触摸开始事件
|
|
|
|
|
handleTouchStart(e) {
|
|
|
|
|
console.log('触摸开始事件:', e);
|
|
|
|
|
const touches = e.touches;
|
|
|
|
|
|
|
|
|
|
if (touches.length === 1) {
|
|
|
|
|
// 单指:准备拖动
|
|
|
|
|
this.setData({
|
|
|
|
|
initialTouch: {
|
|
|
|
|
x: touches[0].clientX,
|
|
|
|
|
y: touches[0].clientY
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else if (touches.length === 2) {
|
|
|
|
|
// 双指:记录起始距离,准备缩放
|
|
|
|
|
const distance = this.calculateDistance(touches[0], touches[1]);
|
|
|
|
|
this.setData({
|
|
|
|
|
startDistance: distance,
|
|
|
|
|
isScaling: true,
|
|
|
|
|
lastScale: this.data.scale
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 处理触摸移动事件
|
|
|
|
|
handleTouchMove(e) {
|
|
|
|
|
const touches = e.touches;
|
|
|
|
|
|
|
|
|
|
if (touches.length === 1 && this.data.initialTouch && this.data.scale !== 1) {
|
|
|
|
|
// 单指拖动(只有在缩放状态下才允许拖动)
|
|
|
|
|
const deltaX = touches[0].clientX - this.data.initialTouch.x;
|
|
|
|
|
const deltaY = touches[0].clientY - this.data.initialTouch.y;
|
|
|
|
|
|
|
|
|
|
// 计算新的偏移量
|
|
|
|
|
let newOffsetX = this.data.offsetX + deltaX;
|
|
|
|
|
let newOffsetY = this.data.offsetY + deltaY;
|
|
|
|
|
|
|
|
|
|
// 边界限制
|
|
|
|
|
const windowWidth = wx.getSystemInfoSync().windowWidth;
|
|
|
|
|
const windowHeight = wx.getSystemInfoSync().windowHeight;
|
|
|
|
|
const maxOffsetX = (windowWidth * (this.data.scale - 1)) / 2;
|
|
|
|
|
const maxOffsetY = (windowHeight * (this.data.scale - 1)) / 2;
|
|
|
|
|
|
|
|
|
|
newOffsetX = Math.max(-maxOffsetX, Math.min(maxOffsetX, newOffsetX));
|
|
|
|
|
newOffsetY = Math.max(-maxOffsetY, Math.min(maxOffsetY, newOffsetY));
|
|
|
|
|
|
|
|
|
|
this.setData({
|
|
|
|
|
offsetX: newOffsetX,
|
|
|
|
|
offsetY: newOffsetY,
|
|
|
|
|
initialTouch: {
|
|
|
|
|
x: touches[0].clientX,
|
|
|
|
|
y: touches[0].clientY
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else if (touches.length === 2) {
|
|
|
|
|
// 双指缩放
|
|
|
|
|
const currentDistance = this.calculateDistance(touches[0], touches[1]);
|
|
|
|
|
const scale = (currentDistance / this.data.startDistance) * this.data.lastScale;
|
|
|
|
|
|
|
|
|
|
// 限制缩放范围在0.5倍到3倍之间
|
|
|
|
|
const newScale = Math.max(0.5, Math.min(3, scale));
|
|
|
|
|
|
|
|
|
|
this.setData({
|
|
|
|
|
scale: newScale,
|
|
|
|
|
isScaling: true
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 处理触摸结束事件
|
|
|
|
|
handleTouchEnd(e) {
|
|
|
|
|
this.setData({
|
|
|
|
|
isScaling: false,
|
|
|
|
|
lastScale: this.data.scale,
|
|
|
|
|
initialTouch: null
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 拨打电话
|
|
|
|
|
makePhoneCall(e) {
|
|
|
|
|
console.log('拨打电话事件:', e);
|
|
|
|
|
const phoneNumber = e.currentTarget.dataset.phone;
|
|
|
|
|
if (phoneNumber) {
|
|
|
|
|
wx.showModal({
|
|
|
|
|
title: '联系人电话',
|
|
|
|
|
content: phoneNumber,
|
|
|
|
|
showCancel: true,
|
|
|
|
|
cancelText: '取消',
|
|
|
|
|
confirmText: '拨打',
|
|
|
|
|
success: (res) => {
|
|
|
|
|
if (res.confirm) {
|
|
|
|
|
wx.makePhoneCall({
|
|
|
|
|
phoneNumber: phoneNumber,
|
|
|
|
|
success: () => {
|
|
|
|
|
console.log('拨打电话成功');
|
|
|
|
|
},
|
|
|
|
|
fail: (err) => {
|
|
|
|
|
console.error('拨打电话失败', err);
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '拨打电话失败',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 在线聊天
|
|
|
|
|
onChat(e) {
|
|
|
|
|
console.log('在线咨询事件:', e);
|
|
|
|
|
const { id } = e.currentTarget.dataset;
|
|
|
|
|
if (!id) return;
|
|
|
|
|
|
|
|
|
|
// 获取商品联系人信息
|
|
|
|
|
const contactName = this.data.goodsDetail.product_contact;
|
|
|
|
|
const contactPhone = this.data.goodsDetail.contact_phone;
|
|
|
|
|
|
|
|
|
|
// 检查是否有联系电话
|
|
|
|
|
if (!contactPhone) {
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '未找到联系电话',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查用户登录状态
|
|
|
|
|
const openid = wx.getStorageSync('openid');
|
|
|
|
|
const userId = wx.getStorageSync('userId');
|
|
|
|
|
|
|
|
|
|
if (!openid || !userId) {
|
|
|
|
|
console.log('用户未登录,显示登录弹窗');
|
|
|
|
|
this.setData({
|
|
|
|
|
showOneKeyLoginModal: true
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 跳转到聊天界面或打开客服窗口
|
|
|
|
|
wx.showModal({
|
|
|
|
|
title: '在线咨询',
|
|
|
|
|
content: `将为您连接到 ${contactName || '客服人员'}`,
|
|
|
|
|
showCancel: true,
|
|
|
|
|
cancelText: '取消',
|
|
|
|
|
confirmText: '立即咨询',
|
|
|
|
|
success: (res) => {
|
|
|
|
|
if (res.confirm) {
|
|
|
|
|
// 获取当前用户的手机号
|
|
|
|
|
let userPhone = '';
|
|
|
|
|
try {
|
|
|
|
|
// 尝试从不同的存储位置获取手机号
|
|
|
|
|
const userInfo = wx.getStorageSync('userInfo');
|
|
|
|
|
if (userInfo && userInfo.phoneNumber) {
|
|
|
|
|
userPhone = userInfo.phoneNumber;
|
|
|
|
|
} else {
|
|
|
|
|
// 尝试从其他可能的存储位置获取
|
|
|
|
|
const users = wx.getStorageSync('users') || {};
|
|
|
|
|
const userId = wx.getStorageSync('userId');
|
|
|
|
|
if (userId && users[userId] && users[userId].phoneNumber) {
|
|
|
|
|
userPhone = users[userId].phoneNumber;
|
|
|
|
|
} else {
|
|
|
|
|
userPhone = wx.getStorageSync('phoneNumber');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('获取用户手机号失败:', e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('当前用户手机号:', userPhone);
|
|
|
|
|
console.log('联系人信息:', { contactName, contactPhone });
|
|
|
|
|
|
|
|
|
|
// 验证手机号
|
|
|
|
|
if (!userPhone) {
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '请先登录获取手机号',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 验证联系人手机号
|
|
|
|
|
if (!contactPhone) {
|
|
|
|
|
console.error('联系人手机号不存在');
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '联系人信息不完整,请稍后重试',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('联系人手机号:', contactPhone);
|
|
|
|
|
|
|
|
|
|
// 显示加载提示
|
|
|
|
|
wx.showLoading({
|
|
|
|
|
title: '正在建立聊天...',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 调用API创建聊天记录,确保用户和联系人之间的聊天记录是双向的
|
|
|
|
|
API.fixChatRecordsPair(userPhone, contactPhone).then(res => {
|
|
|
|
|
console.log('聊天建立成功:', JSON.stringify(res, null, 2));
|
|
|
|
|
// 隐藏加载提示
|
|
|
|
|
wx.hideLoading();
|
|
|
|
|
|
|
|
|
|
// 使用联系人手机号作为聊天会话ID
|
|
|
|
|
const chatSessionId = contactPhone;
|
|
|
|
|
|
|
|
|
|
// 构建商品分享消息内容(包含显示文本和结构化数据)
|
|
|
|
|
const goodsDetail = this.data.goodsDetail;
|
|
|
|
|
|
|
|
|
|
// 构建结构化的商品消息
|
|
|
|
|
const goodsData = {
|
|
|
|
|
id: goodsDetail.id || '',
|
|
|
|
|
name: goodsDetail.name || '',
|
|
|
|
|
imageUrl: (goodsDetail.imageUrls && goodsDetail.imageUrls.length > 0) ?
|
|
|
|
|
(goodsDetail.imageUrls.find(url => !isVideoUrl(url)) ||
|
|
|
|
|
goodsDetail.imageUrls.find(url => isVideoUrl(url)) ||
|
|
|
|
|
goodsDetail.videoCoverUrl || '') :
|
|
|
|
|
(goodsDetail.videoCoverUrl || ''),
|
|
|
|
|
price: goodsDetail.price || '',
|
|
|
|
|
region: goodsDetail.region || '',
|
|
|
|
|
displaySpecification: goodsDetail.displaySpecification || goodsDetail.specification || goodsDetail.spec || goodsDetail.specs || '',
|
|
|
|
|
displayYolk: goodsDetail.displayYolk || goodsDetail.yolk || '',
|
|
|
|
|
sourceType: goodsDetail.sourceType || '',
|
|
|
|
|
totalStock: goodsDetail.totalStock || goodsDetail.stock || '',
|
|
|
|
|
supplyStatus: goodsDetail.supplyStatus || goodsDetail.status === 'sold_out' ? '' : (goodsDetail.supplyStatus || ''),
|
|
|
|
|
status: goodsDetail.status || ''
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 构建显示文本(兼容旧版本)
|
|
|
|
|
const displayText = buildShareGoodsMessage(goodsDetail);
|
|
|
|
|
|
|
|
|
|
// 发送结构化商品消息(JSON格式,包含goodsData用于展示卡片)
|
|
|
|
|
const structuredMessage = JSON.stringify({
|
|
|
|
|
type: 'goods',
|
|
|
|
|
text: displayText,
|
|
|
|
|
goodsData: goodsData
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 检查是否已经发送过该商品消息
|
|
|
|
|
let sentGoodsMessages = wx.getStorageSync('sentGoodsMessages') || [];
|
|
|
|
|
const messageKey = `${userPhone}_${contactPhone}_${goodsDetail.id}`;
|
|
|
|
|
const hasSent = sentGoodsMessages.includes(messageKey);
|
|
|
|
|
|
|
|
|
|
if (!hasSent) {
|
|
|
|
|
// 发送商品分享消息
|
|
|
|
|
API.sendMessage(userPhone, contactPhone, structuredMessage).then(sendRes => {
|
|
|
|
|
console.log('商品分享消息发送成功:', sendRes);
|
|
|
|
|
|
|
|
|
|
// 记录已发送的消息
|
|
|
|
|
sentGoodsMessages.push(messageKey);
|
|
|
|
|
wx.setStorageSync('sentGoodsMessages', sentGoodsMessages);
|
|
|
|
|
|
|
|
|
|
// 跳转到聊天页面
|
|
|
|
|
wx.navigateTo({
|
|
|
|
|
url: `/pages/chat-detail/index?userId=${chatSessionId}&userName=${encodeURIComponent(contactName || '联系人')}&phone=${contactPhone}&isManager=true`,
|
|
|
|
|
success: function () {
|
|
|
|
|
console.log('成功跳转到聊天详情页');
|
|
|
|
|
},
|
|
|
|
|
fail: function (error) {
|
|
|
|
|
console.error('跳转到聊天详情页失败:', error);
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '聊天功能开发中',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}).catch(sendErr => {
|
|
|
|
|
console.error('发送商品分享消息失败:', sendErr);
|
|
|
|
|
// 即使发送消息失败,也跳转到聊天页面
|
|
|
|
|
wx.navigateTo({
|
|
|
|
|
url: `/pages/chat-detail/index?userId=${chatSessionId}&userName=${encodeURIComponent(contactName || '联系人')}&phone=${contactPhone}&isManager=true`,
|
|
|
|
|
success: function () {
|
|
|
|
|
console.log('成功跳转到聊天详情页');
|
|
|
|
|
},
|
|
|
|
|
fail: function (error) {
|
|
|
|
|
console.error('跳转到聊天详情页失败:', error);
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '聊天功能开发中',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
console.log('该商品消息已发送过,不再重复发送');
|
|
|
|
|
// 直接跳转到聊天页面
|
|
|
|
|
wx.navigateTo({
|
|
|
|
|
url: `/pages/chat-detail/index?userId=${chatSessionId}&userName=${encodeURIComponent(contactName || '联系人')}&phone=${contactPhone}&isManager=true`,
|
|
|
|
|
success: function () {
|
|
|
|
|
console.log('成功跳转到聊天详情页');
|
|
|
|
|
},
|
|
|
|
|
fail: function (error) {
|
|
|
|
|
console.error('跳转到聊天详情页失败:', error);
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '聊天功能开发中',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}).catch(err => {
|
|
|
|
|
console.error('建立聊天失败:', err);
|
|
|
|
|
// 隐藏加载提示
|
|
|
|
|
wx.hideLoading();
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '建立聊天失败,请重试',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭登录弹窗
|
|
|
|
|
closeOneKeyLoginModal() {
|
|
|
|
|
this.setData({
|
|
|
|
|
showOneKeyLoginModal: false
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 处理手机号授权结果
|
|
|
|
|
onPhoneNumberResult(e) {
|
|
|
|
|
console.log('getPhoneNumber响应:', e.detail);
|
|
|
|
|
|
|
|
|
|
// 关闭登录弹窗
|
|
|
|
|
this.setData({
|
|
|
|
|
showOneKeyLoginModal: false
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 用户点击拒绝授权
|
|
|
|
|
if (e.detail.errMsg === 'getPhoneNumber:fail user deny') {
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '需要授权手机号才能使用',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理没有权限的情况
|
|
|
|
|
if (e.detail.errMsg === 'getPhoneNumber:fail no permission') {
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '当前环境无法获取手机号权限',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 3000
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 这里可以添加完整的登录处理逻辑
|
|
|
|
|
// 参考首页的 onGetPhoneNumber 函数
|
|
|
|
|
console.log('手机号授权成功,需要完整的登录流程处理');
|
|
|
|
|
|
|
|
|
|
// 由于详情页的登录处理比较复杂,建议跳转到首页处理
|
|
|
|
|
// 或者可以触发一个全局事件,让其他页面处理登录逻辑
|
|
|
|
|
const app = getApp();
|
|
|
|
|
if (app && app.globalData) {
|
|
|
|
|
app.globalData.pendingPhoneAuth = e.detail;
|
|
|
|
|
// 可以跳转到首页处理登录
|
|
|
|
|
wx.switchTab({
|
|
|
|
|
url: '/pages/index/index',
|
|
|
|
|
success: () => {
|
|
|
|
|
// 可以发送事件通知首页处理登录
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '请在首页完成登录',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 处理登录授权
|
|
|
|
|
async onGetPhoneNumber(e) {
|
|
|
|
|
console.log('收到手机号授权事件:', e.detail);
|
|
|
|
|
|
|
|
|
|
// 关闭手机号授权弹窗
|
|
|
|
|
this.setData({ showOneKeyLoginModal: false })
|
|
|
|
|
|
|
|
|
|
// 用户点击拒绝授权
|
|
|
|
|
if (e.detail.errMsg === 'getPhoneNumber:fail user deny') {
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '需要授权手机号才能使用',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理没有权限的情况
|
|
|
|
|
if (e.detail.errMsg === 'getPhoneNumber:fail no permission') {
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '当前环境无法获取手机号权限',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 3000
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查是否已经登录,避免重复授权
|
|
|
|
|
const existingOpenid = wx.getStorageSync('openid')
|
|
|
|
|
const existingUserId = wx.getStorageSync('userId')
|
|
|
|
|
const existingUserInfo = wx.getStorageSync('userInfo')
|
|
|
|
|
|
|
|
|
|
if (existingOpenid && existingUserId && existingUserInfo && existingUserInfo.phoneNumber) {
|
|
|
|
|
console.log('用户已登录且手机号有效,登录流程已完成')
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '您已登录',
|
|
|
|
|
icon: 'success',
|
|
|
|
|
duration: 1500
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wx.showLoading({
|
|
|
|
|
title: '登录中...',
|
|
|
|
|
mask: true
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
if (e.detail.errMsg === 'getPhoneNumber:ok') {
|
|
|
|
|
// 用户同意授权,实际处理授权流程
|
|
|
|
|
console.log('用户同意授权获取手机号')
|
|
|
|
|
|
|
|
|
|
// 1. 先执行微信登录获取code
|
|
|
|
|
const loginRes = await new Promise((resolve, reject) => {
|
|
|
|
|
wx.login({
|
|
|
|
|
success: resolve,
|
|
|
|
|
fail: reject
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (!loginRes.code) {
|
|
|
|
|
throw new Error('获取登录code失败')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('获取登录code成功:', loginRes.code)
|
|
|
|
|
|
|
|
|
|
// 2. 使用code换取openid
|
|
|
|
|
const openidRes = await API.getOpenid(loginRes.code)
|
|
|
|
|
|
|
|
|
|
// 改进错误处理逻辑,更宽容地处理服务器返回格式
|
|
|
|
|
let openid = null;
|
|
|
|
|
let userId = null;
|
|
|
|
|
console.log('openidRes完整响应:', JSON.stringify(openidRes));
|
|
|
|
|
|
|
|
|
|
if (openidRes && typeof openidRes === 'object') {
|
|
|
|
|
// 适配服务器返回格式:{success: true, code: 200, message: '获取openid成功', data: {openid, userId}}
|
|
|
|
|
if (openidRes.data && typeof openidRes.data === 'object') {
|
|
|
|
|
console.log('识别到标准服务器返回格式,从data字段提取信息');
|
|
|
|
|
openid = openidRes.data.openid || openidRes.data.OpenID || null;
|
|
|
|
|
userId = openidRes.data.userId || null;
|
|
|
|
|
} else {
|
|
|
|
|
// 尝试从响应对象中直接提取openid
|
|
|
|
|
console.log('尝试从根对象直接提取openid');
|
|
|
|
|
openid = openidRes.openid || openidRes.OpenID || null;
|
|
|
|
|
userId = openidRes.userId || null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!openid) {
|
|
|
|
|
console.error('无法从服务器响应中提取openid,完整响应:', JSON.stringify(openidRes));
|
|
|
|
|
throw new Error(`获取openid失败: 服务器返回数据格式可能不符合预期`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('获取openid成功:', openid)
|
|
|
|
|
|
|
|
|
|
// 3. 存储openid和session_key
|
|
|
|
|
wx.setStorageSync('openid', openid)
|
|
|
|
|
|
|
|
|
|
// 从服务器返回中获取session_key
|
|
|
|
|
if (openidRes && openidRes.session_key) {
|
|
|
|
|
wx.setStorageSync('sessionKey', openidRes.session_key)
|
|
|
|
|
} else if (openidRes && openidRes.data && openidRes.data.session_key) {
|
|
|
|
|
wx.setStorageSync('sessionKey', openidRes.data.session_key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 优先使用从服务器响应data字段中提取的userId
|
|
|
|
|
if (userId) {
|
|
|
|
|
wx.setStorageSync('userId', userId)
|
|
|
|
|
console.log('使用从服务器data字段提取的userId:', userId)
|
|
|
|
|
} else if (openidRes && openidRes.userId) {
|
|
|
|
|
wx.setStorageSync('userId', openidRes.userId)
|
|
|
|
|
console.log('使用服务器根对象中的userId:', openidRes.userId)
|
|
|
|
|
} else {
|
|
|
|
|
// 生成临时userId
|
|
|
|
|
const tempUserId = 'user_' + Date.now()
|
|
|
|
|
wx.setStorageSync('userId', tempUserId)
|
|
|
|
|
console.log('生成临时userId:', tempUserId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. 上传手机号加密数据到服务器解密
|
|
|
|
|
const phoneData = {
|
|
|
|
|
...e.detail,
|
|
|
|
|
openid: openid
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('准备上传手机号加密数据到服务器')
|
|
|
|
|
const phoneRes = await API.uploadPhoneNumberData(phoneData)
|
|
|
|
|
|
|
|
|
|
// 改进手机号解密结果的处理逻辑
|
|
|
|
|
if (!phoneRes || (!phoneRes.success && !phoneRes.phoneNumber)) {
|
|
|
|
|
// 如果服务器返回格式不标准但包含手机号,也接受
|
|
|
|
|
if (phoneRes && phoneRes.phoneNumber) {
|
|
|
|
|
console.warn('服务器返回格式可能不符合预期,但成功获取手机号');
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error('获取手机号失败: ' + (phoneRes && phoneRes.message ? phoneRes.message : '未知错误'))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查是否有手机号冲突
|
|
|
|
|
const hasPhoneConflict = phoneRes.phoneNumberConflict || false
|
|
|
|
|
const isNewPhone = phoneRes.isNewPhone || true
|
|
|
|
|
const phoneNumber = phoneRes.phoneNumber || null
|
|
|
|
|
|
|
|
|
|
// 如果有手机号冲突且没有返回手机号,使用实际返回的手机号
|
|
|
|
|
const finalPhoneNumber = phoneNumber
|
|
|
|
|
|
|
|
|
|
console.log('手机号解密结果:', {
|
|
|
|
|
phoneNumber: finalPhoneNumber,
|
|
|
|
|
hasPhoneConflict: hasPhoneConflict,
|
|
|
|
|
isNewPhone: isNewPhone
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 5. 获取用户微信名称和头像
|
|
|
|
|
let userProfile = null;
|
|
|
|
|
try {
|
|
|
|
|
userProfile = await new Promise((resolve, reject) => {
|
|
|
|
|
wx.getUserProfile({
|
|
|
|
|
desc: '用于完善会员资料',
|
|
|
|
|
success: resolve,
|
|
|
|
|
fail: reject
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
console.log('获取用户信息成功:', userProfile);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.warn('获取用户信息失败:', err);
|
|
|
|
|
// 如果获取失败,使用默认值
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 6. 创建用户信息
|
|
|
|
|
const tempUserInfo = {
|
|
|
|
|
name: userProfile ? (userProfile.userInfo.name || userProfile.userInfo.nickName) : '微信用户',
|
|
|
|
|
// 获取微信头像失败时使用微信默认头像
|
|
|
|
|
avatarUrl: userProfile ? userProfile.userInfo.avatarUrl : 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0',
|
|
|
|
|
gender: userProfile ? userProfile.userInfo.gender : 0,
|
|
|
|
|
country: userProfile ? userProfile.userInfo.country : '',
|
|
|
|
|
province: userProfile ? userProfile.userInfo.province : '',
|
|
|
|
|
city: userProfile ? userProfile.userInfo.city : '',
|
|
|
|
|
language: userProfile ? userProfile.userInfo.language : 'zh_CN',
|
|
|
|
|
phoneNumber: finalPhoneNumber
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 7. 保存用户信息
|
|
|
|
|
const storedUserId = wx.getStorageSync('userId')
|
|
|
|
|
const users = wx.getStorageSync('users') || {}
|
|
|
|
|
const currentUserType = users[storedUserId] && users[storedUserId].type ? users[storedUserId].type : 'buyer'
|
|
|
|
|
|
|
|
|
|
console.log('用户身份类型:', currentUserType)
|
|
|
|
|
|
|
|
|
|
// 8. 保存用户信息到本地和服务器
|
|
|
|
|
console.log('开始保存用户信息...')
|
|
|
|
|
await this.saveUserInfo(tempUserInfo, currentUserType)
|
|
|
|
|
console.log('用户信息保存完成')
|
|
|
|
|
|
|
|
|
|
wx.hideLoading()
|
|
|
|
|
|
|
|
|
|
// 根据服务器返回的结果显示不同的提示
|
|
|
|
|
if (phoneRes && phoneRes.phoneNumberConflict) {
|
|
|
|
|
wx.showModal({
|
|
|
|
|
title: '登录成功',
|
|
|
|
|
content: '您的手机号已被其他账号绑定',
|
|
|
|
|
showCancel: false,
|
|
|
|
|
confirmText: '我知道了',
|
|
|
|
|
success(res) {
|
|
|
|
|
if (res.confirm) {
|
|
|
|
|
console.log('用户点击了我知道了');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 显示登录成功提示
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '登录成功',
|
|
|
|
|
icon: 'success',
|
|
|
|
|
duration: 1500
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 登录成功后,重新加载商品详情以刷新收藏状态
|
|
|
|
|
const productId = String(this.data.goodsDetail.id || this.data.goodsDetail.productId);
|
|
|
|
|
this.loadGoodsFavoriteStatus(productId);
|
|
|
|
|
|
|
|
|
|
// 触发全局事件,通知其他页面登录状态已更改
|
|
|
|
|
const app = getApp();
|
|
|
|
|
if (app && app.eventBus) {
|
|
|
|
|
app.eventBus.emit('userLoginStatusChanged', {
|
|
|
|
|
isLoggedIn: true,
|
|
|
|
|
userId: storedUserId,
|
|
|
|
|
phoneNumber: finalPhoneNumber
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// 用户拒绝授权或其他情况
|
|
|
|
|
console.log('手机号授权失败:', e.detail.errMsg)
|
|
|
|
|
// 不再抛出错误,而是显示友好的提示
|
|
|
|
|
wx.hideLoading()
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '需要授权手机号才能使用',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
wx.hideLoading()
|
|
|
|
|
console.error('登录过程中发生错误:', error)
|
|
|
|
|
|
|
|
|
|
// 更具体的错误提示
|
|
|
|
|
let errorMsg = '登录失败,请重试'
|
|
|
|
|
if (error.message.includes('网络')) {
|
|
|
|
|
errorMsg = '网络连接失败,请检查网络后重试'
|
|
|
|
|
} else if (error.message.includes('服务器')) {
|
|
|
|
|
errorMsg = '服务器连接失败,请稍后重试'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: errorMsg,
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 3000
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 清除可能已经保存的不完整信息
|
|
|
|
|
try {
|
|
|
|
|
wx.removeStorageSync('openid')
|
|
|
|
|
wx.removeStorageSync('sessionKey')
|
|
|
|
|
wx.removeStorageSync('userId')
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('清除临时登录信息失败:', e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 保存用户信息的方法
|
|
|
|
|
async saveUserInfo(userInfo, userType) {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
// 1. 保存到本地users对象
|
|
|
|
|
const users = wx.getStorageSync('users') || {};
|
|
|
|
|
const userId = wx.getStorageSync('userId');
|
|
|
|
|
|
|
|
|
|
if (!userId) {
|
|
|
|
|
reject(new Error('用户ID不存在'));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 合并用户信息
|
|
|
|
|
users[userId] = {
|
|
|
|
|
...users[userId],
|
|
|
|
|
...userInfo,
|
|
|
|
|
type: userType || 'buyer',
|
|
|
|
|
updatedAt: new Date().toISOString()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
wx.setStorageSync('users', users);
|
|
|
|
|
|
|
|
|
|
// 2. 保存到全局userInfo对象
|
|
|
|
|
wx.setStorageSync('userInfo', {
|
|
|
|
|
...userInfo,
|
|
|
|
|
userType: userType || 'buyer'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 3. 上传到服务器
|
|
|
|
|
const API = require('../../utils/api.js');
|
|
|
|
|
|
|
|
|
|
API.uploadUserInfo({
|
|
|
|
|
userId: userId,
|
|
|
|
|
...userInfo,
|
|
|
|
|
type: userType || 'buyer'
|
|
|
|
|
}).then(res => {
|
|
|
|
|
console.log('用户信息上传成功:', res);
|
|
|
|
|
resolve(res);
|
|
|
|
|
}).catch(err => {
|
|
|
|
|
console.error('用户信息上传失败:', err);
|
|
|
|
|
// 即使服务器上传失败,也继续流程,只在本地保存
|
|
|
|
|
resolve({ success: false, message: '本地保存成功,服务器同步失败' });
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 对比价格功能:处理按钮点击事件
|
|
|
|
|
onCompareClick: function () {
|
|
|
|
|
console.log('用户点击了对比价格按钮,准备显示弹窗');
|
|
|
|
|
|
|
|
|
|
// 检查用户登录状态
|
|
|
|
|
const openid = wx.getStorageSync('openid');
|
|
|
|
|
const userId = wx.getStorageSync('userId');
|
|
|
|
|
|
|
|
|
|
if (!openid || !userId) {
|
|
|
|
|
console.log('用户未登录,显示登录弹窗');
|
|
|
|
|
// 显示登录弹窗
|
|
|
|
|
this.setData({
|
|
|
|
|
showOneKeyLoginModal: true
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 记录用户点击对比价格的踪迹
|
|
|
|
|
const traceData = {
|
|
|
|
|
action: 'click_compare_price',
|
|
|
|
|
productId: this.data.goodsDetail.productId || this.data.goodsDetail.id,
|
|
|
|
|
productName: this.data.goodsDetail.name || '',
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
page: 'goods-detail/goods-detail'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 调用用户踪迹记录API
|
|
|
|
|
API.addUserTrace(traceData).then(res => {
|
|
|
|
|
console.log('用户对比价格点击记录成功:', res);
|
|
|
|
|
}).catch(err => {
|
|
|
|
|
console.error('用户对比价格点击记录失败:', err);
|
|
|
|
|
// 即使记录失败,也不影响主流程
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 增加用户对比价格点击次数
|
|
|
|
|
API.incrementCompareNum().then(res => {
|
|
|
|
|
console.log('增加对比价格次数成功:', res);
|
|
|
|
|
}).catch(err => {
|
|
|
|
|
console.error('增加对比价格次数失败:', err);
|
|
|
|
|
// 即使失败也不影响主流程
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 检查用户身份证认证状态
|
|
|
|
|
let idcardstatus = 0;
|
|
|
|
|
const users = wx.getStorageSync('users') || {};
|
|
|
|
|
const userInfo = wx.getStorageSync('userInfo') || {};
|
|
|
|
|
|
|
|
|
|
if (userId && users[userId] && users[userId].idcardstatus) {
|
|
|
|
|
idcardstatus = users[userId].idcardstatus;
|
|
|
|
|
} else if (userInfo.idcardstatus) {
|
|
|
|
|
idcardstatus = userInfo.idcardstatus;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('用户身份证认证状态:', idcardstatus);
|
|
|
|
|
|
|
|
|
|
if (idcardstatus !== 1) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//跳转到认证页面
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
wx.navigateTo({
|
|
|
|
|
url: '/pages/profile/authentication/index',
|
|
|
|
|
success: function() {
|
|
|
|
|
console.log('成功跳转到认证页面');
|
|
|
|
|
},
|
|
|
|
|
fail: function(error) {
|
|
|
|
|
console.error('跳转到认证页面失败:', error);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 直接获取当前页面滚动位置
|
|
|
|
|
wx.createSelectorQuery().selectViewport().scrollOffset(function(res) {
|
|
|
|
|
console.log('记录当前滚动位置:', res.scrollTop);
|
|
|
|
|
// 保存滚动位置并显示弹窗
|
|
|
|
|
this.setData({
|
|
|
|
|
scrollTop: res.scrollTop,
|
|
|
|
|
showCompareModal: true,
|
|
|
|
|
activeTab: 'home' // 默认显示首页数据选项卡
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 打印弹窗状态,用于调试
|
|
|
|
|
console.log('弹窗状态设置为:', this.data.showCompareModal);
|
|
|
|
|
|
|
|
|
|
// 加载首页数据
|
|
|
|
|
this.loadHomeGoods();
|
|
|
|
|
|
|
|
|
|
// 加载收藏数据
|
|
|
|
|
this.loadFavoriteGoods();
|
|
|
|
|
}.bind(this)).exec();
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 关闭对比价格弹窗
|
|
|
|
|
closeCompareModal: function () {
|
|
|
|
|
this.setData({
|
|
|
|
|
showCompareModal: false
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 恢复页面滚动位置
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
console.log('恢复滚动位置:', this.data.scrollTop);
|
|
|
|
|
wx.pageScrollTo({
|
|
|
|
|
scrollTop: this.data.scrollTop,
|
|
|
|
|
duration: 0
|
|
|
|
|
});
|
|
|
|
|
}, 100);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 阻止事件冒泡
|
|
|
|
|
stopPropagation: function () {
|
|
|
|
|
// 空函数,用于阻止事件冒泡
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 切换选项卡
|
|
|
|
|
switchTab: function (e) {
|
|
|
|
|
const tab = e.currentTarget.dataset.tab;
|
|
|
|
|
this.setData({
|
|
|
|
|
activeTab: tab
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 如果切换到收藏选项卡且收藏数据为空,则加载收藏数据
|
|
|
|
|
if (tab === 'favorite' && this.data.favoriteGoods.length === 0) {
|
|
|
|
|
this.loadFavoriteGoods();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 加载首页商品数据
|
|
|
|
|
loadHomeGoods: function () {
|
|
|
|
|
this.setData({ loadingHome: true });
|
|
|
|
|
|
|
|
|
|
// 获取当前商品的规格信息
|
|
|
|
|
const currentGoods = this.data.goodsDetail;
|
|
|
|
|
const currentSpecifications = currentGoods.weightQuantityData || [];
|
|
|
|
|
// 提取当前商品的净重规格(如"净重41-42")
|
|
|
|
|
const currentWeightSpecs = currentSpecifications.map(item => item.weightSpec.trim())
|
|
|
|
|
.filter(spec => spec && (spec.includes('净重') || spec.includes('毛重')));
|
|
|
|
|
// 提取当前商品的种类(category)
|
|
|
|
|
const currentCategory = currentGoods.category || '';
|
|
|
|
|
console.log('当前商品的种类:', currentCategory);
|
|
|
|
|
|
|
|
|
|
// 调用API获取首页商品列表
|
|
|
|
|
API.getProducts()
|
|
|
|
|
.then(res => {
|
|
|
|
|
console.log('获取首页商品数据成功:', res);
|
|
|
|
|
// API.getProducts()返回包含products字段的对象
|
|
|
|
|
if (res && Array.isArray(res.products)) {
|
|
|
|
|
// 为每个商品添加mediaItems字段和weightQuantityData字段,并过滤出与当前商品规格匹配的商品
|
|
|
|
|
const processedGoods = res.products.map(goods => {
|
|
|
|
|
// 处理每个首页商品的规格数据
|
|
|
|
|
let weightSpecString = '';
|
|
|
|
|
let quantityString = '';
|
|
|
|
|
|
|
|
|
|
// 检查规格字段是否包含净重信息
|
|
|
|
|
if (goods.spec && typeof goods.spec === 'string' && (goods.spec.includes('净重') || goods.spec.includes('毛重'))) {
|
|
|
|
|
weightSpecString = goods.spec;
|
|
|
|
|
} else if (goods.specification && typeof goods.specification === 'string' && (goods.specification.includes('净重') || goods.specification.includes('毛重'))) {
|
|
|
|
|
weightSpecString = goods.specification;
|
|
|
|
|
} else if (goods.grossWeight) {
|
|
|
|
|
weightSpecString = String(goods.grossWeight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理件数数据
|
|
|
|
|
if (goods.minOrder) {
|
|
|
|
|
quantityString = String(goods.minOrder);
|
|
|
|
|
} else if (goods.quantity) {
|
|
|
|
|
quantityString = String(goods.quantity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理价格数据
|
|
|
|
|
let priceString = '';
|
|
|
|
|
if (goods.price) {
|
|
|
|
|
priceString = String(goods.price);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取规格状态信息
|
|
|
|
|
let specStatusString = '';
|
|
|
|
|
if (goods.spec_status) {
|
|
|
|
|
specStatusString = String(goods.spec_status);
|
|
|
|
|
}
|
|
|
|
|
// 调用processWeightAndQuantityData处理规格数据
|
|
|
|
|
const weightQuantityData = processWeightAndQuantityData(weightSpecString, quantityString, '', priceString, specStatusString);
|
|
|
|
|
|
|
|
|
|
// 提取省份信息
|
|
|
|
|
const province = extractProvince(goods.region || '');
|
|
|
|
|
|
|
|
|
|
// 格式化日期
|
|
|
|
|
const updatedAt = goods.updated_at || goods.updatedAt;
|
|
|
|
|
// 检查商品状态,只有status === 'sold_out'时显示售出时间
|
|
|
|
|
// 其他售空状态使用已售空标签
|
|
|
|
|
const isSoldOut = goods.status === 'sold_out' ||
|
|
|
|
|
goods.status === 'sold' ||
|
|
|
|
|
goods.status === 'out_of_stock' ||
|
|
|
|
|
(goods.supplyStatus && goods.supplyStatus.includes('售空'));
|
|
|
|
|
// 只有status === 'sold_out'时显示售出时间,其他状态隐藏时间
|
|
|
|
|
const formattedDate = goods.status === 'sold_out' ? formatDateTime(updatedAt) : '';
|
|
|
|
|
// 标记是否显示已售空标签(除了status === 'sold_out'的其他售空状态)
|
|
|
|
|
const isSoldOutLabel = isSoldOut && goods.status !== 'sold_out';
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...goods,
|
|
|
|
|
price: priceString, // 确保price字段是字符串类型
|
|
|
|
|
mediaItems: processMediaUrls(goods.imageUrls),
|
|
|
|
|
weightQuantityData: weightQuantityData,
|
|
|
|
|
province: province, // 添加省份字段
|
|
|
|
|
formattedDate: formattedDate, // 添加格式化的日期字段
|
|
|
|
|
isSoldOutLabel: isSoldOutLabel // 添加是否显示已售空标签的标记
|
|
|
|
|
};
|
|
|
|
|
}).filter(goods => {
|
|
|
|
|
// 1. 先过滤相同种类(category)的商品
|
|
|
|
|
if (currentCategory && goods.category !== currentCategory) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 只有当当前商品有明确的规格时才进行筛选
|
|
|
|
|
if (currentWeightSpecs.length === 0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取该商品的规格信息
|
|
|
|
|
const goodsSpecifications = goods.weightQuantityData || [];
|
|
|
|
|
// 提取该商品的净重规格
|
|
|
|
|
const goodsWeightSpecs = goodsSpecifications.map(item => item.weightSpec.trim())
|
|
|
|
|
.filter(spec => spec && (spec.includes('净重') || spec.includes('毛重')));
|
|
|
|
|
|
|
|
|
|
// 辅助函数:获取规格的匹配版本(处理44净重=49毛重的转换规则)
|
|
|
|
|
function getMatchingSpecs(spec) {
|
|
|
|
|
const specs = [spec];
|
|
|
|
|
|
|
|
|
|
// 添加转换规则:44净重=49毛重
|
|
|
|
|
if (spec === '44净重') {
|
|
|
|
|
specs.push('49毛重');
|
|
|
|
|
} else if (spec === '49毛重') {
|
|
|
|
|
specs.push('44净重');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return specs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查是否有匹配的规格(考虑转换规则)
|
|
|
|
|
return goodsWeightSpecs.some(goodsSpec =>
|
|
|
|
|
currentWeightSpecs.some(currentSpec => {
|
|
|
|
|
const matchingSpecs = getMatchingSpecs(currentSpec);
|
|
|
|
|
return matchingSpecs.includes(goodsSpec);
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.setData({
|
|
|
|
|
homeGoods: processedGoods,
|
|
|
|
|
loadingHome: false
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
this.setData({ loadingHome: false });
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '获取首页数据失败',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
console.error('获取首页商品数据失败:', err);
|
|
|
|
|
this.setData({ loadingHome: false });
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '获取首页数据失败',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 加载已收藏商品数据
|
|
|
|
|
loadFavoriteGoods: function () {
|
|
|
|
|
this.setData({ loadingFavorite: true });
|
|
|
|
|
|
|
|
|
|
// 获取用户手机号
|
|
|
|
|
let userPhone = '';
|
|
|
|
|
try {
|
|
|
|
|
// 打印所有可能的存储位置内容,用于调试
|
|
|
|
|
console.log('调试信息 - 获取收藏数据前的存储状态:');
|
|
|
|
|
console.log('userId:', wx.getStorageSync('userId'));
|
|
|
|
|
console.log('users:', wx.getStorageSync('users'));
|
|
|
|
|
console.log('userInfo:', wx.getStorageSync('userInfo'));
|
|
|
|
|
console.log('phoneNumber:', wx.getStorageSync('phoneNumber'));
|
|
|
|
|
|
|
|
|
|
// 先尝试从最新的存储位置获取
|
|
|
|
|
const userInfo = wx.getStorageSync('userInfo');
|
|
|
|
|
if (userInfo && userInfo.phoneNumber) {
|
|
|
|
|
userPhone = userInfo.phoneNumber;
|
|
|
|
|
console.log('从userInfo获取到手机号:', userPhone);
|
|
|
|
|
} else {
|
|
|
|
|
// 然后尝试从users对象获取
|
|
|
|
|
const users = wx.getStorageSync('users') || {};
|
|
|
|
|
const userId = wx.getStorageSync('userId');
|
|
|
|
|
if (userId && users[userId] && users[userId].phoneNumber) {
|
|
|
|
|
userPhone = users[userId].phoneNumber;
|
|
|
|
|
console.log('从users[' + userId + ']获取到手机号:', userPhone);
|
|
|
|
|
} else {
|
|
|
|
|
// 最后尝试从单独的phoneNumber字段获取
|
|
|
|
|
userPhone = wx.getStorageSync('phoneNumber');
|
|
|
|
|
console.log('从phoneNumber字段获取到手机号:', userPhone);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('获取用户手机号失败:', e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 验证手机号是否有效
|
|
|
|
|
if (!userPhone) {
|
|
|
|
|
this.setData({ loadingFavorite: false });
|
|
|
|
|
console.log('用户未登录或未获取手机号,无法加载收藏数据');
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '请先登录获取手机号',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 确保手机号格式正确
|
|
|
|
|
if (typeof userPhone !== 'string' || userPhone.length < 11) {
|
|
|
|
|
this.setData({ loadingFavorite: false });
|
|
|
|
|
console.error('手机号格式不正确:', userPhone);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('准备调用API获取收藏数据,手机号:', userPhone);
|
|
|
|
|
|
|
|
|
|
// 调用API获取收藏商品列表
|
|
|
|
|
API.getFavorites(userPhone)
|
|
|
|
|
.then(res => {
|
|
|
|
|
console.log('获取收藏商品数据成功:', res);
|
|
|
|
|
|
|
|
|
|
// 处理API返回的数据结构
|
|
|
|
|
let favoritesList = [];
|
|
|
|
|
if (res) {
|
|
|
|
|
if (Array.isArray(res.data)) {
|
|
|
|
|
favoritesList = res.data;
|
|
|
|
|
} else if (res.data && Array.isArray(res.data.favorites)) {
|
|
|
|
|
favoritesList = res.data.favorites;
|
|
|
|
|
} else if (res.data && Array.isArray(res.data.data)) {
|
|
|
|
|
favoritesList = res.data.data;
|
|
|
|
|
} else if (Array.isArray(res)) {
|
|
|
|
|
// 直接返回数组的情况
|
|
|
|
|
favoritesList = res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('解析后的收藏商品列表:', favoritesList);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查收藏列表是否只包含productId
|
|
|
|
|
const hasOnlyProductId = favoritesList.length > 0 &&
|
|
|
|
|
favoritesList.every(item =>
|
|
|
|
|
(typeof item === 'string' || typeof item === 'number') ||
|
|
|
|
|
(typeof item === 'object' && item.productId && !item.name)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (hasOnlyProductId) {
|
|
|
|
|
console.log('收藏列表只包含productId,需要获取商品详情');
|
|
|
|
|
// 转换为productId数组
|
|
|
|
|
const productIds = favoritesList.map(item =>
|
|
|
|
|
typeof item === 'object' ? item.productId : item
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
console.log('提取的productId列表:', productIds);
|
|
|
|
|
|
|
|
|
|
// 并行获取所有商品详情
|
|
|
|
|
const productPromises = productIds.map(productId =>
|
|
|
|
|
API.getProductDetail({ productId: productId })
|
|
|
|
|
.then(detailRes => {
|
|
|
|
|
console.log('获取商品详情成功:', productId, detailRes);
|
|
|
|
|
return detailRes && detailRes.data ? detailRes.data : null;
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
console.error('获取商品详情失败:', productId, err);
|
|
|
|
|
return null;
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 等待所有商品详情获取完成
|
|
|
|
|
Promise.all(productPromises)
|
|
|
|
|
.then(products => {
|
|
|
|
|
// 过滤掉获取失败的商品,并为每个商品添加mediaItems字段和省份信息
|
|
|
|
|
const validProducts = products
|
|
|
|
|
.filter(product => product !== null)
|
|
|
|
|
.map(product => {
|
|
|
|
|
// 提取省份信息
|
|
|
|
|
const province = extractProvince(product.region || '');
|
|
|
|
|
|
|
|
|
|
// 处理重量、数量和价格数据
|
|
|
|
|
let weightSpecString = '';
|
|
|
|
|
let quantityString = '';
|
|
|
|
|
let priceString = '';
|
|
|
|
|
|
|
|
|
|
// 提取重量规格
|
|
|
|
|
if (product.spec && typeof product.spec === 'string' && (product.spec.includes('净重') || product.spec.includes('毛重'))) {
|
|
|
|
|
weightSpecString = product.spec;
|
|
|
|
|
} else if (product.specification && typeof product.specification === 'string' && (product.specification.includes('净重') || product.specification.includes('毛重'))) {
|
|
|
|
|
weightSpecString = product.specification;
|
|
|
|
|
} else if (product.grossWeight) {
|
|
|
|
|
weightSpecString = String(product.grossWeight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提取数量
|
|
|
|
|
if (product.minOrder) {
|
|
|
|
|
quantityString = String(product.minOrder);
|
|
|
|
|
} else if (product.quantity) {
|
|
|
|
|
quantityString = String(product.quantity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提取价格
|
|
|
|
|
if (product.price) {
|
|
|
|
|
priceString = String(product.price);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取规格状态信息
|
|
|
|
|
let specStatusString = '';
|
|
|
|
|
if (product.spec_status) {
|
|
|
|
|
specStatusString = String(product.spec_status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理数据
|
|
|
|
|
const weightQuantityData = processWeightAndQuantityData(weightSpecString, quantityString, '', priceString, specStatusString);
|
|
|
|
|
|
|
|
|
|
// 格式化日期
|
|
|
|
|
const updatedAt = product.updated_at || product.updatedAt;
|
|
|
|
|
// 检查商品状态,只有status === 'sold_out'时显示售出时间
|
|
|
|
|
// 其他售空状态使用已售空标签
|
|
|
|
|
const isSoldOut = product.status === 'sold_out' ||
|
|
|
|
|
product.status === 'sold' ||
|
|
|
|
|
product.status === 'out_of_stock' ||
|
|
|
|
|
(product.supplyStatus && product.supplyStatus.includes('售空'));
|
|
|
|
|
// 只有status === 'sold_out'时显示售出时间,其他状态隐藏时间
|
|
|
|
|
const formattedDate = product.status === 'sold_out' ? formatDateTime(updatedAt) : '';
|
|
|
|
|
// 标记是否显示已售空标签(除了status === 'sold_out'的其他售空状态)
|
|
|
|
|
const isSoldOutLabel = isSoldOut && product.status !== 'sold_out';
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...product,
|
|
|
|
|
price: priceString, // 确保price字段是字符串类型
|
|
|
|
|
mediaItems: processMediaUrls(product.imageUrls),
|
|
|
|
|
province: province, // 添加省份字段
|
|
|
|
|
weightQuantityData: weightQuantityData, // 添加价格处理数据
|
|
|
|
|
formattedDate: formattedDate, // 添加格式化的日期字段
|
|
|
|
|
isSoldOutLabel: isSoldOutLabel // 添加是否显示已售空标签的标记
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
console.log('所有商品详情获取完成,有效商品数量:', validProducts.length);
|
|
|
|
|
|
|
|
|
|
this.setData({
|
|
|
|
|
favoriteGoods: validProducts,
|
|
|
|
|
loadingFavorite: false
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
console.error('获取商品详情列表失败:', err);
|
|
|
|
|
this.setData({
|
|
|
|
|
favoriteGoods: [],
|
|
|
|
|
loadingFavorite: false
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// 收藏列表已经包含完整商品信息,为每个商品添加mediaItems字段和省份信息
|
|
|
|
|
const processedFavorites = favoritesList.map(product => {
|
|
|
|
|
// 提取省份信息
|
|
|
|
|
const province = extractProvince(product.region || '');
|
|
|
|
|
|
|
|
|
|
// 处理重量、数量和价格数据
|
|
|
|
|
let weightSpecString = '';
|
|
|
|
|
let quantityString = '';
|
|
|
|
|
let priceString = '';
|
|
|
|
|
|
|
|
|
|
// 提取重量规格
|
|
|
|
|
if (product.spec && typeof product.spec === 'string' && (product.spec.includes('净重') || product.spec.includes('毛重'))) {
|
|
|
|
|
weightSpecString = product.spec;
|
|
|
|
|
} else if (product.specification && typeof product.specification === 'string' && (product.specification.includes('净重') || product.specification.includes('毛重'))) {
|
|
|
|
|
weightSpecString = product.specification;
|
|
|
|
|
} else if (product.grossWeight) {
|
|
|
|
|
weightSpecString = String(product.grossWeight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提取数量
|
|
|
|
|
if (product.minOrder) {
|
|
|
|
|
quantityString = String(product.minOrder);
|
|
|
|
|
} else if (product.quantity) {
|
|
|
|
|
quantityString = String(product.quantity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提取价格
|
|
|
|
|
if (product.price) {
|
|
|
|
|
priceString = String(product.price);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取规格状态信息
|
|
|
|
|
let specStatusString = '';
|
|
|
|
|
if (product.spec_status) {
|
|
|
|
|
specStatusString = String(product.spec_status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理数据
|
|
|
|
|
const weightQuantityData = processWeightAndQuantityData(weightSpecString, quantityString, '', priceString, specStatusString);
|
|
|
|
|
|
|
|
|
|
// 格式化日期
|
|
|
|
|
const updatedAt = product.updated_at || product.updatedAt;
|
|
|
|
|
// 检查商品状态,只有status === 'sold_out'时显示售出时间
|
|
|
|
|
// 其他售空状态使用已售空标签
|
|
|
|
|
const isSoldOut = product.status === 'sold_out' ||
|
|
|
|
|
product.status === 'sold' ||
|
|
|
|
|
product.status === 'out_of_stock' ||
|
|
|
|
|
(product.supplyStatus && product.supplyStatus.includes('售空'));
|
|
|
|
|
// 只有status === 'sold_out'时显示售出时间,其他状态隐藏时间
|
|
|
|
|
const formattedDate = product.status === 'sold_out' ? formatDateTime(updatedAt) : '';
|
|
|
|
|
// 标记是否显示已售空标签(除了status === 'sold_out'的其他售空状态)
|
|
|
|
|
const isSoldOutLabel = isSoldOut && product.status !== 'sold_out';
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...product,
|
|
|
|
|
price: priceString, // 确保price字段是字符串类型
|
|
|
|
|
mediaItems: processMediaUrls(product.imageUrls),
|
|
|
|
|
province: province, // 添加省份字段
|
|
|
|
|
weightQuantityData: weightQuantityData, // 添加价格处理数据
|
|
|
|
|
formattedDate: formattedDate, // 添加格式化的日期字段
|
|
|
|
|
isSoldOutLabel: isSoldOutLabel // 添加是否显示已售空标签的标记
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.setData({
|
|
|
|
|
favoriteGoods: processedFavorites,
|
|
|
|
|
loadingFavorite: false
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
console.error('获取收藏商品数据失败:', err);
|
|
|
|
|
this.setData({ loadingFavorite: false });
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '获取收藏数据失败: ' + (err && err.message ? err.message : '未知错误'),
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 3000
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 返回上一页
|
|
|
|
|
goBack() {
|
|
|
|
|
wx.navigateBack();
|
|
|
|
|
}
|
|
|
|
|
});
|