// 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) { 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}`); result.push({ weightSpec: weightSpecDisplay, quantity: quantity, price: price, specStatus: specStatus, display: display, isOffShelf: specStatus === '1' }); } 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 } }, // 视频帧提取功能 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: '' // 当前用户的手机号,用于判断评论是否属于当前用户 }, // 点击对比价格列表中的商品,跳转到对应的商品详情页 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 : ''; // 处理净重、件数据和规格数据,获取一一对应的显示数组 // 注意:数据库中的规格字段包含净重信息,我们需要与件数数据正确匹配 // 修复:根据用户反馈,数据库中的规格字段内容为:净重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); 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 }; // 调试:打印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和review const commentData = { productId: String(productId), phoneNumber: phoneNumber ? String(phoneNumber) : null, comments: content, review: 0 // 新提交的评论默认为待审核状态 }; // 调试日志 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, // 添加用户标识信息,用于判断是否可以删除 review: 0 // 新提交的评论默认为待审核状态 }; // 更新评论列表 - 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' }); }); }, // 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, review: 1 // 默认评论默认为审核通过状态 })); }, // 加载商品评论 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; // 应用审核逻辑:审核通过的评论所有人可见,未审核通过的评论仅自己可见 const currentUserPhone = this.data.currentUserPhone; const filteredComments = commentsData.filter(comment => { const reviewStatus = comment.review || 0; // 默认值为0(待审核) // 审核通过的评论(review=1)所有人可见 // 未审核通过的评论(review=0或2)仅评论作者可见 return reviewStatus === 1 || comment.phoneNumber === currentUserPhone; }); console.log('应用审核逻辑后剩余评论数量:', filteredComments.length); // Use only filtered comments without default comments commentsData = filteredComments; // 检查返回的评论是否都属于当前用户 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 || '', // 确保review字段存在 review: comment.review || 0, // 格式化时间 time: timeUtils.formatRelativeTime(comment.time) })); // 更新评论列表数据 this.setData({ comments: formattedComments }); }) .catch(err => { console.error('获取评论失败:', err); console.error('错误详情:', JSON.stringify(err, null, 2)); // 加载失败时使用空数组 console.log('使用空评论数组'); this.setData({ comments: [] }); }); }, // 兼容之前的输入处理 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' }); }); } } }); }, /* 已移除:我想要(预约)功能 onClickWantInDetail(e) { console.log('我想要事件:', e); const { id } = e.currentTarget.dataset; if (!id) return; // 从本地存储获取openid const openid = wx.getStorageSync('openid'); console.log('openid:', openid); // 检查是否已登录 if (!openid) { // 如果未登录,显示授权登录弹窗 this.setData({ showAuthModal: true }); return; } // 获取已预约商品ID列表 let reservedGoodsIds = wx.getStorageSync('reservedGoodsIds') || []; // 检查是否已经预约过 if (reservedGoodsIds.some(itemId => String(itemId) === String(id))) { wx.showToast({ title: '您已经预约过该商品', icon: 'none', duration: 1500 }); return; } // 添加到已预约列表 reservedGoodsIds.push(id); wx.setStorageSync('reservedGoodsIds', reservedGoodsIds); // 更新页面状态 this.setData({ 'goodsDetail.isReserved': true }); // 调用API记录预约 API.reserveProduct({ id: id }) .then(res => { console.log('预约成功:', res); wx.showToast({ title: '预约成功', icon: 'success', duration: 1500 }); }) .catch(err => { console.error('预约失败:', err); // 如果API调用失败,从本地列表中移除 reservedGoodsIds = reservedGoodsIds.filter(itemId => String(itemId) !== String(id)); wx.setStorageSync('reservedGoodsIds', reservedGoodsIds); // 更新页面状态 this.setData({ 'goodsDetail.isReserved': false }); wx.showToast({ title: '预约失败,请重试', icon: 'none', duration: 1500 }); }); }, */ // 关闭登录弹窗 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 userInfo = wx.getStorageSync('userInfo') || {}; const idcard1 = userInfo.idcard1; const idcard2 = userInfo.idcard2; console.log('检查身份证信息:', { idcard1, idcard2 }); if (!idcard1 || !idcard2) { console.log('身份证信息不完整,跳转到认证页面'); wx.showToast({ title: '信息不完整,请先完成身份认证', icon: 'none', duration: 2000 }); // 跳转到认证页面 setTimeout(() => { wx.navigateTo({ url: '/pages/profile/authentication/index' }); }, 1000); 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(); } });