// pages/index/index.js const API = require('../../utils/api.js'); Page({ data: { currentUserType: null, showUserInfoForm: false, avatarUrl: 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0', name: '', showAuthModal: false, showOneKeyLoginModal: false, userInfo: {}, needPhoneAuth: false, testMode: true, partnerstatus: '', // 侧边栏相关 showSidebar: false, isDragging: false, startY: 0, currentY: 0, sidebarBtnTop: 500, sidebarBtnHidden: false, // 搜索区域相关 searchSectionVisible: true, lastScrollTop: 0, isScrollLocked: false, // 回到顶部按钮 showBackToTop: false, scrollTop: 0, // 搜索相关 searchKeyword: '', selectedRegion: '全国', showRegionPicker: false, regions: ['全国', '北京', '上海', '广州', '深圳', '天津', '重庆', '河北', '山西', '辽宁', '吉林', '黑龙江', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南', '湖北', '湖南', '广东', '海南', '四川', '贵州', '云南', '陕西', '甘肃', '青海', '台湾', '内蒙古', '广西', '西藏', '宁夏', '新疆', '香港', '澳门'], // 商品相关 - 淘宝风格 goods: [], filteredGoods: [], leftColumnGoods: [], rightColumnGoods: [], categorizedGoods: {}, // 按品种分类存储的商品数据 selectedCategory: '全部', categories: ['全部', '粉壳', '红壳', '绿壳', '白壳'], loadingMore: false, hasMoreData: true, page: 1, pageSize: 8, // 防抖定时器 searchDebounceTimer: null, scrollDebounceTimer: null, isRefreshing: false, isLoading: true, // 图片预览相关状态 previewImageUrls: [], previewImageIndex: 0, showImagePreview: false, }, // 跳转到聊天页面 navigateToChat() { wx.navigateTo({ url: '/pages/chat/index', success: function() { console.log('成功跳转到聊天页面'); }, fail: function(error) { console.error('跳转到聊天页面失败:', error); wx.showToast({ title: '跳转失败,请稍后重试', icon: 'none' }); } }); }, // 跳转到客服列表页面 navigateToCustomerService(e) { // 获取按钮类型(seller=我要买蛋,buyer=我要卖蛋) const userType = e.currentTarget.dataset.type; wx.navigateTo({ url: `/pages/customer-service/index?type=${userType}`, success: function() { console.log('成功跳转到客服列表页面,类型:', userType); }, fail: function(error) { console.error('跳转到客服列表页面失败:', error); wx.showToast({ title: '跳转失败,请稍后重试', icon: 'none' }); } }); }, // 跳转到我要卖蛋页面 navigateToSettlement() { wx.navigateTo({ url: '/pages/settlement/index', success: function() { console.log('成功跳转到我要卖蛋页面'); }, fail: function(error) { console.error('跳转到我要卖蛋页面失败:', error); wx.showToast({ title: '跳转失败,请稍后重试', icon: 'none' }); } }); }, // 切换侧边栏显示 toggleSidebar() { if (this.data.isDragging) { return; } this.setData({ showSidebar: !this.data.showSidebar }); }, // 切换按钮显示/隐藏到侧边栏 toggleSidebarBtn() { // 如果当前不在顶部,点击则隐藏按钮 if (this.data.lastScrollTop > 50) { this.setData({ sidebarBtnHidden: true }); wx.setStorageSync('sidebarBtnHidden', true); } else { this.setData({ sidebarBtnHidden: !this.data.sidebarBtnHidden }); wx.setStorageSync('sidebarBtnHidden', !this.data.sidebarBtnHidden); } }, // 触摸开始事件 handleTouchStart(e) { this.setData({ isDragging: false, startY: e.touches[0].clientY }); }, // 触摸移动事件 handleTouchMove(e) { const moveY = e.touches[0].clientY; const diffY = moveY - this.data.startY; if (Math.abs(diffY) > 20) { this.setData({ isDragging: true }); let newTop = this.data.sidebarBtnTop + diffY * 3; const systemInfo = wx.getSystemInfoSync(); const screenHeight = systemInfo.screenHeight * 2; const btnHeight = 90; const minTop = screenHeight * 0.2; const maxTop = screenHeight * 0.6; if (newTop < minTop) { newTop = minTop; } else if (newTop > maxTop) { newTop = maxTop; } this.setData({ sidebarBtnTop: newTop, startY: moveY }); } }, // 触摸结束事件 handleTouchEnd(e) { // 如果拖动距离超过屏幕高度的1/3,隐藏按钮 const moveY = e.changedTouches[0].clientY; const diffY = moveY - this.data.startY; const screenHeight = wx.getSystemInfoSync().screenHeight * 2; // 转换为rpx if (Math.abs(diffY) > screenHeight / 3) { this.setData({ sidebarBtnTop: -100 // 隐藏按钮 }); wx.setStorageSync('sidebarBtnTop', -100); wx.setStorageSync('sidebarBtnHidden', true); } else { wx.setStorageSync('sidebarBtnTop', this.data.sidebarBtnTop); wx.setStorageSync('sidebarBtnHidden', false); } this.setData({ isDragging: false }); }, onLoad() { console.log('首页初始化') // 获取系统信息,处理iOS特定兼容性 const systemInfo = wx.getSystemInfoSync(); const isIOS = systemInfo.system.toLowerCase().includes('ios'); const screenHeight = systemInfo.screenHeight * 2; // 设置iOS特定标志 if (isIOS) { this.setData({ isIOS: true }); console.log('iOS设备检测到,启用兼容模式'); } const savedBtnTop = wx.getStorageSync('sidebarBtnTop'); const savedBtnHidden = wx.getStorageSync('sidebarBtnHidden'); let defaultTop = screenHeight * 0.5; if (savedBtnTop !== '' && savedBtnTop !== -100) { const minTop = screenHeight * 0.2; const maxTop = screenHeight * 0.6; if (savedBtnTop >= minTop && savedBtnTop <= maxTop) { defaultTop = savedBtnTop; } } this.setData({ sidebarBtnTop: defaultTop, sidebarBtnHidden: false, isSearchBarFullyHidden: false, lastScrollTop: 0 }); this.checkAndRestoreLoginStatus() API.getProductCategories().then(categories => { console.log('加载分类成功, categories:', JSON.stringify(categories)); if (categories && categories.length > 0) { this.setData({ categories: categories }); console.log('分类列表已设置到data, categories:', this.data.categories); } else { console.log('分类列表为空,使用默认分类'); } // 确保有分类数据后再加载商品 this.loadGoods(); }).catch(err => { console.error('加载分类失败:', err); // 即使分类加载失败也加载商品,使用默认分类 this.loadGoods(); }); // 计算搜索区域高度 setTimeout(() => { const query = wx.createSelectorQuery(); query.select('.top-section-container').boundingClientRect(); query.select('.category-section').boundingClientRect(); query.exec((res) => { if (res[0] && res[1]) { const searchHeight = res[0].height; const categoryHeight = res[1].height; const totalHeight = searchHeight + categoryHeight; this.setData({ searchSectionHeight: searchHeight, categorySectionHeight: categoryHeight, totalHeaderHeight: totalHeight }); console.log('搜索区域高度:', searchHeight, '分类区域高度:', categoryHeight, '总高度:', totalHeight); } }); }, 500); // 添加收藏状态变化事件监听 const app = getApp(); this.favoriteChangedHandler = (data) => { console.log('收到收藏状态变化通知:', data); // 更新商品列表中对应商品的收藏状态 const updatedGoods = this.data.goods.map(item => { if (String(item.id || item.productId) === data.productId) { return { ...item, isFavorite: data.isFavorite }; } return item; }); this.setData({ goods: updatedGoods }, () => { // 重新应用筛选条件,确保显示的商品收藏状态也更新 const filteredGoods = this.applyFilters(this.data.goods, false); const { leftColumnGoods, rightColumnGoods } = this.distributeToColumns(filteredGoods); this.setData({ filteredGoods: filteredGoods, leftColumnGoods: leftColumnGoods, rightColumnGoods: rightColumnGoods }); }); }; app.eventBus.on('favoriteChanged', this.favoriteChangedHandler); }, onShow: function () { console.log('===== onShow 执行 ====='); console.log('onShow - 进入时的isSearchBarFullyHidden:', this.data.isSearchBarFullyHidden); if (typeof this.getTabBar === 'function' && this.getTabBar()) { this.getTabBar().setData({ selected: 0 }); } const app = getApp(); app.updateCurrentTab('index'); app.globalData.showTabBar = true; const savedBtnHidden = wx.getStorageSync('sidebarBtnHidden'); console.log('onShow - savedBtnHidden:', savedBtnHidden); this.setData({ sidebarBtnHidden: savedBtnHidden || false, isSearchBarFullyHidden: false, lastScrollTop: 0 }); console.log('onShow - 设置后的isSearchBarFullyHidden:', this.data.isSearchBarFullyHidden); this.checkAndRestoreLoginStatus() }, onHide: function () { this.setData({ sidebarBtnHidden: true }); }, onUnload: function () { // 页面卸载时移除收藏状态变化事件监听 const app = getApp(); if (this.favoriteChangedHandler) { app.eventBus.off('favoriteChanged', this.favoriteChangedHandler); console.log('移除收藏状态变化事件监听'); } }, onPullDownRefresh: function() { this.onRefresh() }, onRestore: function() { this.setData({ isRefreshing: false }) }, onRefresh: function() { if (this.data.isRefreshing) { return } const existingGoods = [...this.data.goods] this.setData({ isRefreshing: true, page: 1, hasMoreData: true, goods: existingGoods, filteredGoods: [], leftColumnGoods: [], rightColumnGoods: [] }) const timestamp = new Date().getTime(); API.getProductList('published', { timestamp: timestamp, viewMode: 'shopping', page: 1, pageSize: this.data.pageSize, keyword: this.data.searchKeyword, category: this.data.selectedCategory === '全部' ? '' : this.data.selectedCategory }) .then(res => { this.setData({ isRefreshing: false }) if (res.success && res.products) { this.processRefreshData(res.products, existingGoods) wx.showToast({ title: '刷新成功', icon: 'success', duration: 1500 }) } else { this.setData({ isLoading: false, loadingMore: false }) } }) .catch(err => { this.setData({ isRefreshing: false }) console.error('刷新商品数据失败:', err) wx.showToast({ title: '刷新失败,请稍后重试', icon: 'none', duration: 2000 }) }) }, onReachBottom: function() { this.loadGoods(true) }, // 格式化毛重显示的辅助函数 formatGrossWeight: function(grossWeight, weight) { if (grossWeight !== null && grossWeight !== undefined && grossWeight !== '') { return grossWeight; } if (weight !== null && weight !== undefined && weight !== '') { return weight; } return ""; }, // 提取地区中的省份信息 extractProvince: function(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) { return region.substring(0, municipalityEndIndex + 1); } return region; }, // 处理商品数据 - 淘宝风格 processGoodsData: function(products, isLoadMore = false) { let newGoods = products.map(product => { const imageUrls = product.imageUrls || product.images || []; const formattedImageUrls = Array.isArray(imageUrls) ? imageUrls : [imageUrls]; return { ...product, category: product.category || '', fullRegion: product.region || '', region: product.region ? this.extractProvince(product.region) : '', grossWeight: product.grossWeight || product.weight || '', displayGrossWeight: product.grossWeight || product.weight || '', status: product.status || 'published', createdAt: product.created_at || product.createTime || null, reservedCount: product.reservedCount || product.selected || 0, reservedCountDisplay: product.reservedCount || product.selected || 0, sales: product.sales || product.reservedCount || Math.floor(Math.random() * 1000) + 100, product_contact: product.product_contact || '', contact_phone: product.contact_phone || '', supplyStatus: product.supplyStatus || '', sourceType: product.sourceType || '', negotiateStatus: '可议价', isReserved: false, isFavorite: false, currentImageIndex: 0, imageUrls: formattedImageUrls } }) newGoods = newGoods.filter(item => (item.status || '').toLowerCase() !== 'hidden') // 更新商品的收藏状态 this.updateGoodsFavoriteStatus(newGoods, isLoadMore) // 只在第一页或刷新时在商品列表最前面插入广告 if ((!isLoadMore || this.data.page === 1) && newGoods.length > 0) { // 确保广告位在最前面 const adSlots = [ { id: 'ad_slot_1', name: '广告位1', imageUrls: ['/images/轮播图1.jpg'], price: 0, adType: 'full_card', isAd: true }, { id: 'ad_slot_2', name: '广告位2', imageUrls: ['/images/轮播图1.jpg'], price: 0, adType: 'half_image', isAd: true } ]; newGoods = [...adSlots, ...newGoods]; } let updatedGoods = [] if (isLoadMore) { const existingIds = new Set(this.data.goods.map(item => item.id)); const uniqueNewGoods = newGoods.filter(item => !existingIds.has(item.id)); updatedGoods = [...this.data.goods, ...uniqueNewGoods]; } else { updatedGoods = newGoods } // 按品种分类存储商品数据 const categorizedGoods = this.data.categorizedGoods || {}; const categories = this.data.categories; // 重置所有分类的商品列表 categories.forEach(cat => { if (cat === '全部') { categorizedGoods[cat] = updatedGoods; } else { const categoryGoods = updatedGoods.filter(item => item.isAd || item.category === cat); categorizedGoods[cat] = categoryGoods; } }); // 应用地区筛选后的分类数据 const filteredByRegion = this.applyRegionFilter(updatedGoods); categories.forEach(cat => { if (cat !== '全部') { categorizedGoods[cat + '_filtered'] = filteredByRegion.filter(item => item.isAd || item.category === cat); } }); // 根据当前选中的分类和地区筛选获取商品 let filteredGoods; if (this.data.selectedRegion !== '全国') { filteredGoods = categorizedGoods[this.data.selectedCategory + '_filtered'] || []; } else { filteredGoods = categorizedGoods[this.data.selectedCategory] || []; } const { leftColumnGoods, rightColumnGoods } = this.distributeToColumns(filteredGoods) this.setData({ goods: updatedGoods, categorizedGoods: categorizedGoods, filteredGoods: filteredGoods, leftColumnGoods: leftColumnGoods, rightColumnGoods: rightColumnGoods, loadingMore: false, isLoading: false, page: this.data.page + 1 }) }, // 处理刷新数据 - 查重并保持原有商品 processRefreshData: function(newProducts, existingGoods) { let newGoods = newProducts.map(product => { const imageUrls = product.imageUrls || product.images || []; const formattedImageUrls = Array.isArray(imageUrls) ? imageUrls : [imageUrls]; return { ...product, category: product.category || '', fullRegion: product.region || '', region: product.region ? this.extractProvince(product.region) : '', grossWeight: product.grossWeight || product.weight || '', displayGrossWeight: product.grossWeight || product.weight || '', status: product.status || 'published', createdAt: product.created_at || product.createTime || null, reservedCount: product.reservedCount || product.selected || 0, reservedCountDisplay: product.reservedCount || product.selected || 0, sales: product.sales || product.reservedCount || Math.floor(Math.random() * 1000) + 100, product_contact: product.product_contact || '', contact_phone: product.contact_phone || '', supplyStatus: product.supplyStatus || '', sourceType: product.sourceType || '', negotiateStatus: '可议价', isReserved: false, isFavorite: false, currentImageIndex: 0, imageUrls: formattedImageUrls } }) newGoods = newGoods.filter(item => (item.status || '').toLowerCase() !== 'hidden') const existingIds = new Set(existingGoods.filter(item => !item.isAd).map(item => item.id)); const uniqueNewGoods = newGoods.filter(item => !existingIds.has(item.id)); const updatedGoods = [...existingGoods, ...uniqueNewGoods] const filteredGoods = this.applyFilters(updatedGoods, false) const { leftColumnGoods, rightColumnGoods } = this.distributeToColumns(filteredGoods) this.setData({ goods: updatedGoods, filteredGoods: filteredGoods, leftColumnGoods: leftColumnGoods, rightColumnGoods: rightColumnGoods, loadingMore: false, isLoading: false, page: this.data.page + 1 }) }, // 插入广告位数据 insertAdSlots: function(goods) { if (!goods || goods.length === 0) return goods console.log('insertAdSlots 被调用,商品数量:', goods.length) const adSlot1 = { id: 'ad_slot_1', name: '广告位1', imageUrls: ['/images/轮播图1.jpg'], price: 0, adType: 'full_card', isAd: true } const adSlot2 = { id: 'ad_slot_2', name: '广告位2', imageUrls: ['/images/轮播图1.jpg'], price: 0, adType: 'half_image', isAd: true } const result = [adSlot1, adSlot2, ...goods] console.log('插入广告后的商品数量:', result.length) console.log('第一个商品:', result[0]) console.log('第二个商品:', result[1]) return result }, // 广告点击事件处理 onAdClick: function(e) { const adData = e.currentTarget.dataset.ad console.log('广告被点击, 广告ID:', adData ? adData.id : 'unknown') if (adData && adData.adType) { wx.showToast({ title: '广告位: ' + adData.adType, icon: 'none', duration: 2000 }) if (adData.adType === 'full_card') { console.log('完整卡片广告被点击') } else if (adData.adType === 'half_image') { console.log('半高图片广告被点击') } } else { wx.showToast({ title: '广告加载中', icon: 'loading', duration: 1500 }) } }, // 加载商品分类列表 loadCategories: function() { console.log('===== 开始加载分类 ====='); API.getProductCategories().then(categories => { console.log('加载分类成功, categories:', JSON.stringify(categories)); if (categories && categories.length > 0) { this.setData({ categories: categories }); console.log('分类列表已设置到data, categories:', this.data.categories); } else { console.log('分类列表为空,使用默认分类'); } }).catch(err => { console.error('加载分类失败:', err); }); }, // 加载商品数据 - 淘宝风格优化 loadGoods: function(isLoadMore = false) { if (isLoadMore && !this.data.hasMoreData) { return } if (isLoadMore) { this.setData({ loadingMore: true }) } else { this.setData({ isLoading: true }) } const timestamp = new Date().getTime(); const currentPage = isLoadMore ? this.data.page : 1 API.getProductList('published', { timestamp: timestamp, viewMode: 'shopping', page: currentPage, pageSize: this.data.pageSize, keyword: this.data.searchKeyword, category: this.data.selectedCategory === '全部' ? '' : this.data.selectedCategory }) .then(res => { wx.hideLoading(); console.log('===== API 返回的完整数据 ====='); console.log('res:', res); console.log('res.products:', res.products); if (res.products && res.products.length > 0) { console.log('第一个商品的完整字段:', JSON.stringify(res.products[0], null, 2)); console.log('第一个商品的所有键:', Object.keys(res.products[0])); } if (res.success && res.products) { const totalGoods = res.total || 0; const totalPages = res.totalPages || Math.ceil(totalGoods / this.data.pageSize); const hasMoreData = currentPage < totalPages && res.products.length > 0; this.setData({ hasMoreData }) this.processGoodsData(res.products, isLoadMore) } else { this.setData({ loadingMore: false, isLoading: false }) } }) .catch(err => { console.error('加载商品数据失败:', err) this.setData({ loadingMore: false, isLoading: false }) }) }, // 应用地区筛选 applyRegionFilter: function(goods) { if (this.data.selectedRegion === '全国') { return goods; } const selectedRegion = this.data.selectedRegion; return goods.filter(item => item.isAd || (item.region && item.region.includes(selectedRegion))); }, // 刷新商品列表 refreshGoodsList: function() { this.setData({ page: 1, hasMoreData: true, goods: [], filteredGoods: [], loadingMore: false }, () => { this.loadGoods() }) }, // 应用筛选条件 applyFilters: function(goods, shouldSort = true) { let filtered = [...goods] if (this.data.selectedCategory !== '全部') { const category = this.data.selectedCategory filtered = filtered.filter(item => item.isAd || (item.category === category)) } if (this.data.searchKeyword) { const keyword = this.data.searchKeyword.toLowerCase() filtered = filtered.filter(item => item.isAd || (item.name || '').toLowerCase().includes(keyword) || (item.region || '').toLowerCase().includes(keyword) ) } if (this.data.selectedRegion !== '全国') { const selectedRegion = this.data.selectedRegion filtered = filtered.filter(item => item.isAd || (item.region && item.region.includes(selectedRegion))) } if (shouldSort) { filtered.sort((a, b) => { const reservedCountA = a.reservedCount || 0 const reservedCountB = b.reservedCount || 0 if (reservedCountB !== reservedCountA) return reservedCountB - reservedCountA const priceA = parseFloat(a.price || 0) const priceB = parseFloat(b.price || 0) if (!isNaN(priceB) && !isNaN(priceA) && priceA !== priceB) return priceA - priceB const createdAtA = new Date(a.createdAt || 0).getTime() const createdAtB = new Date(b.createdAt || 0).getTime() return createdAtB - createdAtA }) } return filtered }, // 瀑布流布局 - 淘宝风格左右交替 distributeToColumns: function(goods) { if (!goods || goods.length === 0) { console.log('distributeToColumns: 商品列表为空') return { leftColumnGoods: [], rightColumnGoods: [] } } console.log('distributeToColumns 开始分发,总商品数:', goods.length) const leftColumn = [] const rightColumn = [] // 统计广告和普通商品 const adCount = goods.filter(item => item.isAd).length const productCount = goods.filter(item => !item.isAd).length console.log('广告数量:', adCount, '普通商品数量:', productCount) for (let i = 0; i < goods.length; i += 2) { const currentRow = Math.floor(i / 2) const isEvenRow = currentRow % 2 === 0 if (i < goods.length) { const item = { ...goods[i], isLong: isEvenRow } leftColumn.push(item) console.log(`左列添加[${i}]:`, item.isAd ? '广告' : item.name, 'isLong:', item.isLong) } if (i + 1 < goods.length) { const item = { ...goods[i + 1], isLong: !isEvenRow } rightColumn.push(item) console.log(`右列添加[${i+1}]:`, item.isAd ? '广告' : item.name, 'isLong:', item.isLong) } } console.log('分发结果 - 左列:', leftColumn.length, '右列:', rightColumn.length) console.log('左列商品:', leftColumn.filter(i => !i.isAd).length, '广告:', leftColumn.filter(i => i.isAd).length) console.log('右列商品:', rightColumn.filter(i => !i.isAd).length, '广告:', rightColumn.filter(i => i.isAd).length) return { leftColumnGoods: leftColumn, rightColumnGoods: rightColumn } }, // 分组商品用于交错布局(左长右短,左短右长交替) groupGoodsForStaggeredLayout: function(goods) { if (!goods || goods.length === 0) { return [] } const grouped = [] const len = goods.length for (let i = 0; i < len; i += 2) { const row = [] // 第一个商品:如果是偶数行(index为偶数),则是长商品;如果是奇数行,则是短商品 if (i < len) { row.push({ ...goods[i], isLong: i % 2 === 0 // 偶数索引为长商品 }) } // 第二个商品:与第一个相反 if (i + 1 < len) { row.push({ ...goods[i + 1], isLong: i % 2 !== 0 // 奇数索引为长商品 }) } grouped.push(row) } return grouped }, // 瀑布流布局:将商品分配到左右两列 // 规则:左长右短 -> 左短右长 交替排列 // 偶数行(0,2,4...):左长右短 // 奇数行(1,3,5...):左短右长 distributeToColumns: function(goods) { if (!goods || goods.length === 0) { return { leftColumnGoods: [], rightColumnGoods: [] } } const leftColumn = [] const rightColumn = [] const rowCount = Math.ceil(goods.length / 2) for (let i = 0; i < goods.length; i += 2) { const currentRow = Math.floor(i / 2) const isEvenRow = currentRow % 2 === 0 if (i < goods.length) { leftColumn.push({ ...goods[i], isLong: isEvenRow // 偶数行左边为长,奇数行左边为短 }) } if (i + 1 < goods.length) { rightColumn.push({ ...goods[i + 1], isLong: !isEvenRow // 偶数行右边为短,奇数行右边为长 }) } } return { leftColumnGoods: leftColumn, rightColumnGoods: rightColumn } }, // 搜索输入(带防抖) onSearchInput: function(e) { const keyword = e.detail.value this.setData({ searchKeyword: keyword }) // 清除之前的定时器 if (this.data.searchDebounceTimer) { clearTimeout(this.data.searchDebounceTimer) } // 设置新的定时器,300ms防抖 const timer = setTimeout(() => { this.searchGoods() }, 300) this.setData({ searchDebounceTimer: timer }) }, // 搜索商品 searchGoods: function() { // 重新显示tabBar const app = getApp(); if (app && app.globalData) { app.globalData.showTabBar = true; } // 从本地分类数据中获取基础商品列表 const categorizedGoods = this.data.categorizedGoods || {}; let baseGoods; if (this.data.selectedRegion !== '全国') { baseGoods = categorizedGoods[this.data.selectedCategory + '_filtered'] || []; } else { baseGoods = categorizedGoods[this.data.selectedCategory] || []; } // 在本地数据上进行关键词搜索 let filteredGoods = [...baseGoods]; if (this.data.searchKeyword) { const keyword = this.data.searchKeyword.toLowerCase(); filteredGoods = filteredGoods.filter(item => item.isAd || (item.name || '').toLowerCase().includes(keyword) || (item.region || '').toLowerCase().includes(keyword) ); } const groupedGoods = this.groupGoodsForStaggeredLayout(filteredGoods); const { leftColumnGoods, rightColumnGoods } = this.distributeToColumns(filteredGoods); this.setData({ filteredGoods: filteredGoods, groupedGoods: groupedGoods, leftColumnGoods: leftColumnGoods, rightColumnGoods: rightColumnGoods }); }, // 切换地区选择器 toggleRegionPicker: function() { this.setData({ showRegionPicker: !this.data.showRegionPicker }) }, // 选择地区(实时更新) selectRegion: function(e) { const region = e.currentTarget.dataset.region this.setData({ selectedRegion: region, showRegionPicker: false }) const app = getApp(); if (app && app.globalData) { app.globalData.showTabBar = true; } const categorizedGoods = this.data.categorizedGoods || {}; let filteredGoods; if (region === '全国') { filteredGoods = categorizedGoods[this.data.selectedCategory] || []; } else { filteredGoods = categorizedGoods[this.data.selectedCategory + '_filtered'] || []; } if (filteredGoods.length === 0 && Object.keys(categorizedGoods).length === 0) { this.setData({ page: 1, hasMoreData: true }, () => { this.loadGoods(); }); return; } const { leftColumnGoods, rightColumnGoods } = this.distributeToColumns(filteredGoods) this.setData({ filteredGoods: filteredGoods, leftColumnGoods: leftColumnGoods, rightColumnGoods: rightColumnGoods }) }, // 阻止事件冒泡 stopPropagation: function() { // 空函数,用于阻止事件冒泡 }, // 选择品种 selectCategory: function(e) { const app = getApp(); if (app && app.globalData) { app.globalData.showTabBar = true; } const category = e.currentTarget.dataset.category if (category === this.data.selectedCategory) { return } const categorizedGoods = this.data.categorizedGoods || {}; let filteredGoods; if (this.data.selectedRegion !== '全国') { filteredGoods = categorizedGoods[category + '_filtered'] || []; } else { filteredGoods = categorizedGoods[category] || []; } if (filteredGoods.length === 0 && Object.keys(categorizedGoods).length === 0) { this.setData({ selectedCategory: category, page: 1, hasMoreData: true }, () => { this.loadGoods(); }); return; } const { leftColumnGoods, rightColumnGoods } = this.distributeToColumns(filteredGoods) this.setData({ selectedCategory: category, filteredGoods: filteredGoods, leftColumnGoods: leftColumnGoods, rightColumnGoods: rightColumnGoods }) }, // 查看商品详情 viewGoodsDetail: function(e) { // 检查登录状态 if (this.data.needPhoneAuth) { this.setData({ showOneKeyLoginModal: true }) return; } const item = e.currentTarget.dataset.item // 确保productId存在,优先使用id,其次使用productId const productId = String(item.id || item.productId || '') if (!productId) { console.error('商品ID不存在,无法查看详情'); wx.showToast({ title: '商品信息有误', icon: 'none', duration: 2000 }) return; } // 将完整的商品数据传递给详情页,包含联系人信息,与buyer页面保持一致 wx.navigateTo({ url: `/pages/goods-detail/goods-detail?goodsData=${encodeURIComponent(JSON.stringify(item))}&productId=${productId}` }) }, // 跳转到我要卖蛋页面 navigateToSettlement: function() { wx.navigateTo({ url: '/pages/settlement/index' }) }, // 跳转到招商合作页面 navigateToCooperation: function() { this.setData({ sidebarBtnHidden: true }); wx.navigateTo({ url: '/pages/cooperation/index' }) }, // 预览图片 previewImage: function(e) { // 阻止事件冒泡,避免触发商品点击事件 e.stopPropagation(); // 获取图片信息 const item = e.currentTarget.dataset.item; const index = e.currentTarget.dataset.index; if (!item) { return; } // 确保图片URL存在且为数组 let imageUrls = item.imageUrls || item.images || []; if (!Array.isArray(imageUrls)) { imageUrls = [imageUrls]; } // 过滤掉无效的图片URL const validImageUrls = imageUrls.filter(url => url && typeof url === 'string' && url.trim() !== ''); if (validImageUrls.length === 0) { return; } // 显示图片预览弹窗 this.setData({ previewImageUrls: validImageUrls, previewImageIndex: parseInt(index || 0), showImagePreview: true }); }, // 关闭图片预览 closeImagePreview: function() { this.setData({ showImagePreview: false }); }, // 预览图片切换事件 onPreviewImageChange: function(e) { this.setData({ previewImageIndex: e.detail.current }); }, // 滚动事件处理 - 优化性能 onScroll: function(e) { const { scrollTop } = e.detail; // 清除之前的防抖定时器 if (this.data.scrollDebounceTimer) { clearTimeout(this.data.scrollDebounceTimer) } // 设置防抖,32ms约等于30fps,限制高速滑动时的处理频率 const timer = setTimeout(() => { this.handleScroll(scrollTop) }, 32) this.setData({ scrollDebounceTimer: timer }) }, // 实际的滚动处理逻辑 handleScroll: function(scrollTop) { const threshold = 50; const backToTopThreshold = 300; let needUpdate = false; const updates = { lastScrollTop: scrollTop }; // 搜索框始终固定显示,不做隐藏处理 // 侧边栏按钮显示逻辑 if (scrollTop <= threshold && this.data.sidebarBtnHidden) { updates.sidebarBtnHidden = false; needUpdate = true; wx.setStorageSync('sidebarBtnHidden', false); } // 回到顶部按钮显示逻辑 const shouldShowBackToTop = scrollTop > backToTopThreshold; if (shouldShowBackToTop !== this.data.showBackToTop) { updates.showBackToTop = shouldShowBackToTop; needUpdate = true; } if (needUpdate) { this.setData(updates); } // TabBar显示 const app = getApp(); if (app && app.globalData) { app.globalData.showTabBar = true; } }, // 更新商品的收藏状态 updateGoodsFavoriteStatus: function(goods, isLoadMore) { // 获取用户手机号 let userPhone = ''; try { const users = wx.getStorageSync('users') || {}; const userId = wx.getStorageSync('userId'); // 尝试从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 { // 尝试从直接存储的phoneNumber获取 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; } // 从收藏列表中提取商品ID const favoriteProductIds = favoritesList.map(item => { // 尝试从不同的字段名获取商品ID return String(item.productId || item.id || item.product_id || ''); }).filter(id => id !== ''); // 过滤掉空字符串 // 更新商品的isFavorite状态 const updatedGoods = goods.map(item => ({ ...item, isFavorite: favoriteProductIds.includes(String(item.id || item.productId)) })); // 更新商品列表 let updatedGoodsList = this.data.goods; if (!isLoadMore || this.data.page === 1) { // 第一页或刷新时,直接替换全部商品 updatedGoodsList = updatedGoods; } else { // 加载更多时,追加到现有列表 updatedGoodsList = [...this.data.goods, ...updatedGoods]; } this.setData({ goods: updatedGoodsList }, () => { // 重新应用筛选条件,确保显示的商品收藏状态也更新 const filteredGoods = this.applyFilters(this.data.goods, false); const { leftColumnGoods, rightColumnGoods } = this.distributeToColumns(filteredGoods); this.setData({ filteredGoods: filteredGoods, leftColumnGoods: leftColumnGoods, rightColumnGoods: rightColumnGoods }); }); } }) .catch(err => { console.error('获取收藏状态失败:', err); }); }, // 回到顶部 scrollToTop: function() { this.setData({ scrollTop: 0, searchSectionVisible: true, showBackToTop: false }); }, // 上拉加载更多 onReachBottom: function() { if (this.data.hasMoreData && !this.data.loadingMore) { this.loadGoods(true) } }, // 显示一键登录弹窗 showOneKeyLogin() { this.setData({ showAuthModal: false, showOneKeyLoginModal: true }) }, // 关闭未授权提示弹窗 closeAuthModal() { this.setData({ showAuthModal: false }) }, // 关闭一键登录弹窗 closeOneKeyLoginModal() { this.setData({ showOneKeyLoginModal: false }) }, // 分享给朋友/群聊 onShareAppMessage() { return { title: '鸡蛋贸易平台 - 专业的鸡蛋交易小程序', path: '/pages/index/index', imageUrl: '/images/你有好蛋.png' } }, // 分享到朋友圈 onShareTimeline() { return { title: '鸡蛋贸易平台 - 专业的鸡蛋交易小程序', query: '', imageUrl: '/images/你有好蛋.png' } }, // 处理手机号授权 async onGetPhoneNumber(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') { // 如果是测试模式,跳过真实授权流程 if (this.data.testMode) { console.log('进入测试模式,跳过真实手机号授权') await this.simulateLoginForTest() return } wx.showToast({ title: '当前环境无法获取手机号权限', icon: 'none', duration: 3000 }) // 增加关于微信认证要求的说明 console.warn('获取手机号权限失败: 请注意,微信小程序获取手机号功能需要满足以下条件:1. 小程序必须完成微信企业认证;2. 需要在小程序后台配置相应权限;3. 必须使用button组件的open-type="getPhoneNumber"触发。') return } // 检查是否已经登录,避免重复授权 const existingOpenid = wx.getStorageSync('openid') const existingUserId = wx.getStorageSync('userId') const existingUserInfo = wx.getStorageSync('userInfo') if (existingOpenid && existingUserId && existingUserInfo && existingUserInfo.phoneNumber) { console.log('用户已登录且手机号有效,直接完成身份设置') // 直接完成身份设置,跳过重复授权 const currentUserType = this.data.pendingUserType || this.data.currentUserType || 'buyer' this.finishSetUserType(currentUserType) return } wx.showLoading({ title: '登录中...', mask: true }) // 引入API服务 const API = require('../../utils/api.js') try { if (e.detail.errMsg === 'getPhoneNumber:ok') { // 用户同意授权,实际处理授权流程 console.log('用户同意授权获取手机号') // 同时请求位置授权 console.log('同时请求位置授权'); wx.authorize({ scope: 'scope.userLocation', success() { // 位置授权成功,获取用户位置 wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; console.log('登录时获取位置成功:', { latitude, longitude }); // 存储位置信息到本地 wx.setStorageSync('userLocation', { latitude, longitude }); // 位置获取成功提示 wx.showToast({ title: '位置获取成功', icon: 'success', duration: 1500 }); }, fail() { console.error('登录时获取位置失败'); // 位置获取失败提示 wx.showToast({ title: '位置获取失败', icon: 'none', duration: 1500 }); } }); }, fail() { // 位置授权失败,不影响登录流程 console.log('登录时位置授权被拒绝'); // 位置授权失败提示 wx.showToast({ title: '位置授权已拒绝', icon: 'none', duration: 1500 }); } }); // 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失败: 服务器返回数据格式可能不符合预期,请检查服务器配置。响应数据为: ${JSON.stringify(openidRes)}`); } 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 } // 从本地存储获取userId(使用已声明的变量) const storedUserId = wx.getStorageSync('userId') // 优先使用用户之前选择的身份类型,如果没有则尝试获取已存储的或默认为买家 const users = wx.getStorageSync('users') || {} const currentUserType = this.data.pendingUserType || this.data.currentUserType || (users[storedUserId] && users[storedUserId].type ? users[storedUserId].type : 'buyer') console.log('用户身份类型:', currentUserType) // 清除临时存储的身份类型 if (this.data.pendingUserType) { this.setData({ pendingUserType: null }) } // 保存用户信息并等待上传完成 console.log('开始保存用户信息并上传到服务器...') const uploadResult = await this.saveUserInfo(tempUserInfo, currentUserType) console.log('用户信息保存并上传完成') wx.hideLoading() // 根据服务器返回的结果显示不同的提示 if (uploadResult && uploadResult.phoneNumberConflict) { wx.showModal({ title: '登录成功', content: '您的手机号已被其他账号绑定', showCancel: false, confirmText: '我知道了', success(res) { if (res.confirm) { console.log('用户点击了我知道了'); } } }); } // 用户登录成功,但已移除类型选择和跳转功能 } 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) } } }, // 处理用户基本信息授权 handleUserAuth(type) { // 保存当前用户类型 this.setData({ currentUserType: type }) // 先执行微信登录 this.doWechatLogin(type) }, // 测试模式下模拟登录流程 async simulateLoginForTest() { wx.showLoading({ title: '测试模式登录中...', mask: true }) try { // 1. 模拟微信登录,生成测试用的code const mockCode = 'test_code_' + Date.now() console.log('模拟获取登录code:', mockCode) // 2. 模拟获取openid和userId const mockOpenid = 'test_openid_' + Date.now() const mockUserId = 'test_user_' + Date.now() console.log('模拟获取openid:', mockOpenid) console.log('模拟获取userId:', mockUserId) // 3. 存储测试数据 wx.setStorageSync('openid', mockOpenid) wx.setStorageSync('userId', mockUserId) // 4. 模拟手机号解密结果 const mockPhoneNumber = null console.log('模拟手机号解密成功:', mockPhoneNumber) // 5. 创建模拟用户信息 const mockUserInfo = { name: '测试用户', avatarUrl: this.data.avatarUrl, gender: 0, country: '测试国家', province: '测试省份', city: '测试城市', language: 'zh_CN', phoneNumber: mockPhoneNumber } // 6. 获取用户身份类型(优先使用pendingUserType) const userId = wx.getStorageSync('userId') const users = wx.getStorageSync('users') || {} const currentUserType = this.data.pendingUserType || this.data.currentUserType || (users[userId] && users[userId].type ? users[userId].type : 'buyer') console.log('测试模式用户身份类型:', currentUserType) // 7. 清除临时存储的身份类型 if (this.data.pendingUserType) { this.setData({ pendingUserType: null }) } // 8. 保存用户信息并等待上传完成 console.log('测试模式开始保存用户信息...') // 在测试模式下也会上传用户信息到服务器,用于连通性测试 await this.saveUserInfo(mockUserInfo, currentUserType) console.log('测试模式用户信息保存完成') wx.hideLoading() // 9. 显示成功提示 wx.showToast({ title: '测试模式登录成功', icon: 'success', duration: 2000 }) // 测试登录成功,但已移除类型选择和跳转功能 } catch (error) { wx.hideLoading() console.error('测试模式登录过程中发生错误:', error) wx.showToast({ title: '测试模式登录失败', icon: 'none', duration: 2000 }) } }, // 执行微信登录并获取openid async doWechatLogin(type) { // 显示加载提示 wx.showLoading({ title: '登录中...', mask: true }) try { // 调用微信登录接口 const loginRes = await new Promise((resolve, reject) => { wx.login({ success: resolve, fail: reject }) }) if (loginRes.code) { console.log('微信登录成功,code:', loginRes.code) // 保存登录凭证 try { wx.setStorageSync('loginCode', loginRes.code) } catch (e) { console.error('保存登录凭证失败:', e) } // 引入API服务 const API = require('../../utils/api.js') // 发送code和用户类型到服务器换取openid和session_key try { const openidRes = await API.getOpenid(loginRes.code, type) console.log('获取openid响应:', openidRes) // 增强版响应处理逻辑,支持多种返回格式 let openid = null; let userId = null; let sessionKey = null; // 优先从data字段获取数据 if (openidRes && openidRes.data && typeof openidRes.data === 'object') { openid = openidRes.data.openid || openidRes.data.OpenID || null; userId = openidRes.data.userId || openidRes.data.userid || null; sessionKey = openidRes.data.session_key || openidRes.data.sessionKey || null; } // 如果data为空或不存在,尝试从响应对象直接获取 if (!openid && openidRes && typeof openidRes === 'object') { console.warn('服务器返回格式可能不符合预期,data字段为空或不存在,但尝试从根对象提取信息:', openidRes); openid = openidRes.openid || openidRes.OpenID || null; userId = openidRes.userId || openidRes.userid || null; sessionKey = openidRes.session_key || openidRes.sessionKey || null; } // 检查服务器状态信息 const isSuccess = openidRes && (openidRes.success === true || openidRes.code === 200); const serverMessage = openidRes && (openidRes.message || openidRes.msg); if (isSuccess && !openid) { console.warn('服务器返回成功状态,但未包含有效的openid:', openidRes); } // 打印获取到的信息,方便调试 console.log('提取到的登录信息:', { openid, userId, sessionKey, serverMessage }); if (openid) { // 存储openid和session_key wx.setStorageSync('openid', openid) if (sessionKey) { wx.setStorageSync('sessionKey', sessionKey) } // 如果有userId,也存储起来 if (userId) { wx.setStorageSync('userId', userId) } console.log('获取openid成功并存储:', openid) // 验证登录状态并获取用户信息 await this.validateLoginAndGetUserInfo(openid) } else { // 即使没有获取到openid,也要继续用户信息授权流程 console.warn('未获取到有效的openid,但继续用户信息授权流程:', openidRes); // 设置一个临时的openid以便继续流程 wx.setStorageSync('openid', 'temp_' + Date.now()) } } catch (error) { console.error('获取openid失败:', error) // 即使获取openid失败,也继续用户信息授权流程 } // 继续用户信息授权流程,等待完成 await this.processUserInfoAuth(type) } else { wx.hideLoading() console.error('微信登录失败:', loginRes) wx.showToast({ title: '登录失败,请重试', icon: 'none', duration: 2000 }) } } catch (err) { wx.hideLoading() console.error('wx.login失败:', err) wx.showToast({ title: '获取登录状态失败', icon: 'none', duration: 2000 }) } }, // 验证登录状态并获取用户信息 async validateLoginAndGetUserInfo(openid) { try { // 引入API服务 const API = require('../../utils/api.js') // 调用服务器验证登录状态 const validateRes = await API.validateUserLogin() if (validateRes.success && validateRes.userInfo) { // 服务器返回了用户信息,同步到本地 const app = getApp() const userInfo = validateRes.userInfo // 更新全局用户信息 app.globalData.userInfo = userInfo // 存储用户信息到本地 wx.setStorageSync('userInfo', userInfo) console.log('验证登录状态成功,用户信息已同步:', userInfo) // 如果手机号存在则清除重新授权标志 if (userInfo.phoneNumber) { // 清除可能存在的重新授权标志 wx.removeStorageSync('needPhoneAuth') console.log('手机号验证通过:', userInfo.phoneNumber) } return true } else { console.warn('服务器验证失败,可能是新用户或登录状态无效') return false } } catch (error) { console.error('验证登录状态失败:', error) // 如果验证失败,清除可能存在的无效登录信息 try { wx.removeStorageSync('openid') wx.removeStorageSync('userId') wx.removeStorageSync('userInfo') } catch (e) { console.error('清除无效登录信息失败:', e) } return false } }, // 处理用户信息授权 async processUserInfoAuth(type) { const app = getApp() // 如果已经有用户信息,不需要再进行跳转 if (app.globalData.userInfo) { wx.hideLoading() return } // 优化:首次登录时自动创建临时用户信息并完成登录,不再需要用户填写表单 // 获取已存储的userId或生成新的 let userId = wx.getStorageSync('userId') if (!userId) { userId = 'user_' + Date.now() wx.setStorageSync('userId', userId) } // 创建临时用户信息 const tempUserInfo = { name: '微信用户', avatarUrl: this.data.avatarUrl, gender: 0, country: '', province: '', city: '', language: 'zh_CN' } try { // 保存临时用户信息并完成登录,等待数据上传完成 await this.saveUserInfo(tempUserInfo, type) // 隐藏加载提示 wx.hideLoading() // 数据上传完成,但已移除类型选择和跳转功能 } catch (error) { console.error('处理用户信息授权失败:', error) wx.hideLoading() wx.showToast({ title: '登录失败,请重试', icon: 'none', duration: 2000 }) } }, // 保存用户信息 async saveUserInfo(userInfo, type) { // 确保userId存在 let userId = wx.getStorageSync('userId') if (!userId) { userId = 'user_' + Date.now() wx.setStorageSync('userId', userId) } // 保存用户信息到本地存储 - 修复首次获取问题 let users = wx.getStorageSync('users') // 如果users不存在或不是对象,初始化为空对象 if (!users || typeof users !== 'object') { users = {} } // 初始化用户信息 users[userId] = users[userId] || {} users[userId].info = userInfo users[userId].type = type // 确保存储操作成功 try { wx.setStorageSync('users', users) console.log('用户信息已成功保存到本地存储') } catch (e) { console.error('保存用户信息到本地存储失败:', e) } // 保存用户信息到全局变量 const app = getApp() app.globalData.userInfo = userInfo app.globalData.userType = type console.log('用户信息已保存到全局变量:', userInfo) // 额外保存一份单独的userInfo到本地存储,便于checkPhoneAuthSetting方法检查 try { wx.setStorageSync('userInfo', userInfo) console.log('单独的userInfo已保存') } catch (e) { console.error('保存单独的userInfo失败:', e) } // 上传用户信息到服务器 // 在测试模式下也上传用户信息,用于连通性测试 console.log('准备上传用户信息到服务器进行测试...') // 确保测试数据包含服务器所需的所有字段 const completeUserInfo = { ...userInfo, // 确保包含服务器需要的必要字段 name: userInfo.name || '测试用户' } try { const uploadResult = await this.uploadUserInfoToServer(completeUserInfo, userId, type) console.log('用户信息上传到服务器成功') return uploadResult // 返回上传结果 } catch (error) { console.error('用户信息上传到服务器失败:', error) // 显示友好的提示,但不中断流程 wx.showToast({ title: '测试数据上传失败,不影响使用', icon: 'none', duration: 2000 }) // 不再抛出错误,而是返回默认成功结果,确保登录流程继续 return { success: true, message: '本地登录成功,服务器连接失败' } } }, // 处理头像选择 onChooseAvatar(e) { const { avatarUrl } = e.detail this.setData({ avatarUrl }) }, // 处理昵称提交 getUserName(e) { const { name } = e.detail.value const type = this.data.currentUserType if (!name) { wx.showToast({ title: '请输入昵称', icon: 'none', duration: 2000 }) return } // 创建用户信息对象 const userInfo = { name: name, avatarUrl: this.data.avatarUrl, // 其他可能需要的字段 gender: 0, country: '', province: '', city: '', language: 'zh_CN' } // 保存用户信息 this.saveUserInfo(userInfo, type) // 隐藏表单 this.setData({ showUserInfoForm: false }) // 已移除类型选择和跳转功能 }, // 取消用户信息表单 cancelUserInfoForm() { this.setData({ showUserInfoForm: false }) wx.hideLoading() }, // 检查本地缓存并恢复登录状态 checkAndRestoreLoginStatus() { console.log('开始检查并恢复登录状态') const app = getApp() // 从本地存储获取用户信息 const localUserInfo = wx.getStorageSync('userInfo') || {} const userId = wx.getStorageSync('userId') const openid = wx.getStorageSync('openid') console.log('恢复登录状态 - userId:', userId, 'openid:', openid ? '已获取' : '未获取') // 优先使用全局用户信息,如果没有则使用本地存储的用户信息 if (app.globalData.userInfo) { this.setData({ userInfo: app.globalData.userInfo, needPhoneAuth: !app.globalData.userInfo.phoneNumber }) } else { app.globalData.userInfo = localUserInfo this.setData({ userInfo: localUserInfo, needPhoneAuth: !localUserInfo.phoneNumber }) } if (userId && openid) { // 确保users存储结构存在 let users = wx.getStorageSync('users') if (!users) { users = {} wx.setStorageSync('users', users) } if (!users[userId]) { users[userId] = { type: '' } wx.setStorageSync('users', users) } // 先显示本地存储的用户类型,但会被服务器返回的最新值覆盖 const user = users[userId] const currentType = user.type this.setData({ currentUserType: currentType }) console.log('恢复登录状态 - 当前本地存储的用户类型:', currentType) // 从服务器获取最新的用户信息,确保身份由数据库决定 this.refreshUserInfoFromServer(openid, userId) } else { console.log('未找到有效的本地登录信息') } }, // 从服务器刷新用户信息并同步身份数据 refreshUserInfoFromServer(openid, userId) { const API = require('../../utils/api.js') API.getUserInfo(openid).then(res => { console.log('从服务器获取用户信息成功:', res) if (res.success && res.data) { const serverUserInfo = res.data // 更新本地用户信息 const app = getApp() const updatedUserInfo = { ...app.globalData.userInfo, ...serverUserInfo } app.globalData.userInfo = updatedUserInfo wx.setStorageSync('userInfo', updatedUserInfo) // 设置用户入驻状态 this.setData({ userInfo: updatedUserInfo, partnerstatus: serverUserInfo.partnerstatus || '' }) // 同步更新用户身份信息(当前身份由数据库决定) if (serverUserInfo.type) { this.syncUserTypeFromServer(userId, serverUserInfo.type) } console.log('用户信息已更新,昵称:', updatedUserInfo.name, '手机号:', updatedUserInfo.phoneNumber, '身份:', serverUserInfo.type) } }).catch(err => { console.error('从服务器获取用户信息失败:', err) // 如果getUserInfo失败,尝试使用validateUserLogin作为备选 API.validateUserLogin().then(res => { console.log('使用validateUserLogin获取用户信息成功:', res) if (res.success && res.data) { const serverUserInfo = res.data // 更新本地用户信息 const app = getApp() const updatedUserInfo = { ...app.globalData.userInfo, ...serverUserInfo } app.globalData.userInfo = updatedUserInfo wx.setStorageSync('userInfo', updatedUserInfo) // 设置用户入驻状态 this.setData({ userInfo: updatedUserInfo, partnerstatus: serverUserInfo.partnerstatus || '' }) // 同步更新用户身份信息(当前身份由数据库决定) if (serverUserInfo.type) { this.syncUserTypeFromServer(userId, serverUserInfo.type) } console.log('用户信息已更新(备选方案):', updatedUserInfo) } }).catch(validateErr => { console.error('从服务器获取用户信息失败(包括备选方案):', validateErr) // 如果服务器请求失败,继续使用本地缓存的信息 }) }) }, // 从服务器同步用户身份信息 syncUserTypeFromServer(userId, serverType) { if (!userId || !serverType) { console.error('同步用户身份信息失败: 参数不完整') return } console.log('从服务器同步用户身份信息:', { userId, serverType }) // 更新本地存储的用户身份 let users = wx.getStorageSync('users') || {} if (!users[userId]) { users[userId] = {} } // 移除serverType中的customer(如果存在) let processedServerType = serverType.replace(/,?customer/g, '').replace(/^,|,$/g, '') // 构建新的用户类型 let newUserType = processedServerType // 只有当新构建的用户类型与本地不同时才更新 if (users[userId].type !== newUserType) { users[userId].type = newUserType wx.setStorageSync('users', users) // 更新全局用户类型 const app = getApp() app.globalData.userType = newUserType console.log('用户身份已从服务器同步并保留客服标识:', newUserType) } else { console.log('用户身份与服务器一致,无需更新:', newUserType) } }, // 上传用户信息到服务器 async uploadUserInfoToServer(userInfo, userId, type) { // 引入API服务 const API = require('../../utils/api.js') // 获取openid const openid = wx.getStorageSync('openid') // 构造上传数据(包含openid和session_key) const uploadData = { userId: userId, openid: openid, ...userInfo, type: type, timestamp: Date.now() } // 调用API上传用户信息并返回Promise try { const res = await API.uploadUserInfo(uploadData) console.log('用户信息上传成功:', res) return res } catch (err) { console.error('用户信息上传失败:', err) // 不再抛出错误,而是返回默认成功结果,确保登录流程继续 // 这样即使服务器连接失败,本地登录也能完成 return { success: true, message: '本地登录成功,服务器连接失败' } } }, // 处理手机号授权结果(已重命名为onPhoneNumberResult,此方法已废弃) processPhoneAuthResult: function () { console.warn('processPhoneAuthResult方法已废弃,请使用onPhoneNumberResult方法') }, // 手机号授权处理 async onPhoneNumberResult(e) { console.log('手机号授权结果:', e) // 首先检查用户是否拒绝授权 if (e.detail.errMsg !== 'getPhoneNumber:ok') { console.log('用户拒绝授权手机号') wx.showToast({ title: '您已拒绝授权,操作已取消', icon: 'none' }) // 直接返回,取消所有后续操作 return } wx.showLoading({ title: '登录中...', mask: true }) try { // 引入API服务 const API = require('../../utils/api.js') // 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; let sessionKey = null; // 优先从data字段获取数据 if (openidRes && openidRes.data && typeof openidRes.data === 'object') { openid = openidRes.data.openid || openidRes.data.OpenID || null; userId = openidRes.data.userId || openidRes.data.userid || null; sessionKey = openidRes.data.session_key || openidRes.data.sessionKey || null; } // 如果data为空或不存在,尝试从响应对象直接获取 if (!openid && openidRes && typeof openidRes === 'object') { console.warn('服务器返回格式可能不符合预期,data字段为空或不存在,但尝试从根对象提取信息:', openidRes); openid = openidRes.openid || openidRes.OpenID || null; userId = openidRes.userId || openidRes.userid || null; sessionKey = openidRes.session_key || openidRes.sessionKey || null; } // 检查服务器状态信息 const isSuccess = openidRes && (openidRes.success === true || openidRes.code === 200); if (!openid) { throw new Error('获取openid失败: ' + (openidRes && openidRes.message ? openidRes.message : '未知错误')) } // 存储openid和session_key wx.setStorageSync('openid', openid) if (sessionKey) { wx.setStorageSync('sessionKey', sessionKey) } // 确保始终使用从服务器获取的正式用户ID,不再生成临时ID if (userId) { wx.setStorageSync('userId', userId) } else { const app = getApp(); if (app.globalData.userInfo && app.globalData.userInfo.userId) { const serverUserId = String(app.globalData.userInfo.userId); wx.setStorageSync('userId', serverUserId); userId = serverUserId; console.log('使用从全局获取的正式用户ID:', serverUserId); } else { console.warn('未找到有效的用户ID,请确保用户已授权登录'); } } console.log('获取openid成功并存储:', openid) // 3. 上传手机号加密数据到服务器解密 const phoneData = { ...e.detail, openid: openid, sessionKey: sessionKey || '' } console.log('准备上传手机号加密数据到服务器') const phoneRes = await API.uploadPhoneNumberData(phoneData) // 改进手机号解密结果的处理逻辑 if (!phoneRes || (!phoneRes.success && !phoneRes.phoneNumber)) { // 如果服务器返回格式不标准但包含手机号,也接受 if (!(phoneRes && phoneRes.phoneNumber)) { throw new Error('获取手机号失败: ' + (phoneRes && phoneRes.message ? phoneRes.message : '未知错误')) } } // 检查是否有手机号冲突 const hasPhoneConflict = phoneRes.phoneNumberConflict || false const isNewPhone = phoneRes.isNewPhone || true const phoneNumber = phoneRes.phoneNumber || null console.log('手机号解密结果:', { phoneNumber: phoneNumber, hasPhoneConflict: hasPhoneConflict, isNewPhone: isNewPhone }) // 4. 获取用户微信名称和头像 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); // 如果获取失败,使用默认值 } // 5. 创建用户信息 const app = getApp() const existingUserInfo = app.globalData.userInfo || wx.getStorageSync('userInfo') || {} const userInfo = { // 优先使用最新获取的微信头像和昵称,如果没有获取到则使用本地存储的 name: (userProfile ? (userProfile.userInfo.name || userProfile.userInfo.nickName) : existingUserInfo.name) || '微信用户', avatarUrl: (userProfile ? userProfile.userInfo.avatarUrl : existingUserInfo.avatarUrl) || 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0', gender: (userProfile ? userProfile.userInfo.gender : existingUserInfo.gender) || 0, country: (userProfile ? userProfile.userInfo.country : existingUserInfo.country) || '', province: (userProfile ? userProfile.userInfo.province : existingUserInfo.province) || '', city: (userProfile ? userProfile.userInfo.city : existingUserInfo.city) || '', language: (userProfile ? userProfile.userInfo.language : existingUserInfo.language) || 'zh_CN', phoneNumber: phoneNumber } // 6. 获取用户类型 const users = wx.getStorageSync('users') || {} let currentUserType = users[userId] && users[userId].type ? users[userId].type : '' // 如果没有用户类型,尝试从全局获取 if (!currentUserType) { currentUserType = app.globalData.userType || '' } // 7. 保存用户信息并等待上传完成 console.log('开始保存用户信息并上传到服务器...') await this.uploadUserInfoToServer(userInfo, userId, currentUserType) console.log('用户信息保存并上传完成') // 更新本地和全局用户信息 app.globalData.userInfo = userInfo wx.setStorageSync('userInfo', userInfo) // 更新页面状态 this.setData({ needPhoneAuth: false, userInfo: userInfo, showOneKeyLoginModal: false }) wx.hideLoading() const that = this; wx.showModal({ title: '登录成功', content: '🥚 允许获取位置,为你优先展示附近新鲜鸡蛋供应商、自提门店及精准配送时效,无需手动填地址,位置信息仅用于优化购物体验,隐私有保障,放心开启~', showCancel: true, cancelText: '取消', confirmText: '允许', success(res) { if (res.confirm) { console.log('用户点击了允许'); that.requestLocationAuth(); } else if (res.cancel) { console.log('用户点击了取消'); } } }); } catch (error) { wx.hideLoading() console.error('登录过程中发生错误:', error) // 更具体的错误提示 let errorMsg = '登录失败,请重试' if (error.message.includes('网络')) { errorMsg = '网络连接失败,请检查网络后重试' } else if (error.message.includes('服务器')) { errorMsg = '服务器连接失败,请稍后重试' } else if (error.message.includes('openid')) { 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) } } }, // 上传用户信息到服务器 uploadUserInfoToServer(userInfo, userId, type) { return new Promise((resolve, reject) => { try { const API = require('../../utils/api.js') const openid = wx.getStorageSync('openid') if (!userId || !openid) { const error = new Error('缺少必要的用户信息'); console.error('用户信息上传失败:', error); reject(error); return; } const uploadData = { userId: userId, openid: openid, name: userInfo.name, avatarUrl: userInfo.avatarUrl, phoneNumber: userInfo.phoneNumber, type: type, timestamp: Date.now() } API.uploadUserInfo(uploadData).then(res => { console.log('用户信息上传成功:', res) resolve(res); }).catch(err => { console.error('用户信息上传失败:', err) reject(err); }) } catch (error) { console.error('上传用户信息时发生异常:', error); reject(error); } }); }, // 请求位置授权 requestLocationAuth() { const that = this; wx.authorize({ scope: 'scope.userLocation', success() { that.setData({ hasLocationAuth: true }); that.getUserLocation(); }, fail() { wx.showModal({ title: '需要位置授权', content: '请在设置中开启位置授权', showCancel: true, cancelText: '取消', confirmText: '去授权', success: (res) => { if (res.confirm) { wx.openSetting({ success: (settingRes) => { if (settingRes.authSetting['scope.userLocation']) { that.setData({ hasLocationAuth: true }); that.getUserLocation(); } else { that.setData({ hasLocationAuth: false }); wx.showToast({ title: '您已拒绝位置授权', icon: 'none' }); } }, fail: () => { that.setData({ hasLocationAuth: false }); wx.showToast({ title: '打开设置失败', icon: 'none' }); } }); } else { that.setData({ hasLocationAuth: false }); } } }); } }); }, // 获取用户当前位置 getUserLocation() { const that = this; wx.getSetting({ success(res) { if (res.authSetting['scope.userLocation']) { wx.showLoading({ title: '获取位置中...' }); wx.getLocation({ type: 'gcj02', success(res) { const latitude = res.latitude; const longitude = res.longitude; wx.setStorageSync('userLocation', { latitude, longitude }); let openid = wx.getStorageSync('openid'); if (!openid) { const globalUserInfo = wx.getStorageSync('userInfo'); openid = globalUserInfo?.openid; } if (!openid) { wx.hideLoading(); wx.showToast({ title: '位置上传失败,请先登录', icon: 'none' }); return; } that.reverseGeocode(latitude, longitude, openid); }, fail() { wx.hideLoading(); wx.showToast({ title: '获取位置失败', icon: 'none' }); } }); } } }); }, // 逆地理编码 reverseGeocode(latitude, longitude, openid) { const that = this; wx.request({ url: `https://apis.map.qq.com/ws/geocoder/v1/?location=${latitude},${longitude}&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77`, success(res) { wx.hideLoading(); if (res.data.status === 0) { const address = res.data.result.address; wx.setStorageSync('locationInfo', address); const API = require('../../utils/api.js'); const locationUpdateData = { openid: openid, latitude: latitude, longitude: longitude, address: address }; API.request('/api/user/update-location', 'POST', locationUpdateData) .then(res => { wx.showToast({ title: '位置更新成功', icon: 'success' }); }).catch(err => { wx.showToast({ title: '位置上传失败', icon: 'none' }); }); } else { wx.showToast({ title: '获取地址失败', icon: 'none' }); } }, fail() { wx.hideLoading(); wx.showToast({ title: '获取地址失败', icon: 'none' }); } }); }, })