You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

3279 lines
104 KiB

// pages/index/index.js
const API = require('../../utils/api.js');
// 媒体类型判断函数
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
function processGoodsMedia(goods) {
if (!goods || !Array.isArray(goods)) {
return [];
}
return goods.map(item => {
// 确保imageUrls是数组
let imageUrls = item.imageUrls || [];
if (!Array.isArray(imageUrls)) {
imageUrls = [imageUrls];
}
// 预处理媒体URL,添加类型信息
const mediaItems = imageUrls.map(url => {
return {
url: url,
type: isVideoUrl(url) ? 'video' : 'image'
};
});
return {
...item,
mediaItems: mediaItems,
// 确保imageUrls是数组
imageUrls: imageUrls
};
});
}
Page({
data: {
isLoggedIn: false,
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,
isPageHidden: false,
hasGoodsManagePermission: false,
// 搜索区域相关
searchSectionVisible: true,
lastScrollTop: 0,
isScrollLocked: false,
// 回到顶部按钮
showBackToTop: false,
scrollTop: 0,
// 搜索相关
searchKeyword: '',
selectedRegion: '全国',
showRegionPicker: false,
regions: ['全国', '北京', '上海', '广州', '深圳', '天津', '重庆', '河北', '山西', '辽宁', '吉林', '黑龙江', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南', '湖北', '湖南', '广东', '海南', '四川', '贵州', '云南', '陕西', '甘肃', '青海', '台湾', '内蒙古', '广西', '西藏', '宁夏', '新疆', '香港', '澳门'],
// 商品相关 - 淘宝风格
goods: [],
filteredGoods: [],
selectedCategory: '全部',
categories: ['全部', '粉壳', '褐壳', '绿壳', '白壳'],
loadingMore: false,
hasMoreData: true,
page: 1,
pageSize: 8,
soldOutPage: 1,
// 商品数据缓存
goodsCache: [],
categoryQueryCache: {},
lastDataTimestamp: 0,
cacheValidDuration: 5 * 60 * 1000, // 缓存有效期5分钟
// sold_out 加载状态
isQueryingSoldOut: false,
soldOutPage: 1,
soldOutPageSize: 8,
publishedHasMore: true,
// 防抖定时器
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;
}
const newShowSidebar = !this.data.showSidebar;
if (newShowSidebar) {
this.checkGoodsManagePermission();
}
this.setData({
showSidebar: newShowSidebar
});
},
// 检查用户是否有货源管理权限
checkGoodsManagePermission() {
const users = wx.getStorageSync('users') || {};
const userId = wx.getStorageSync('userId');
let userPhone = null;
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');
}
}
if (!userPhone) {
console.log('用户未绑定手机号,无货源管理权限');
this.setData({ hasGoodsManagePermission: false });
return;
}
API.checkPersonnelByPhone(userPhone).then(res => {
console.log('货源管理权限检查结果:', res);
this.setData({ hasGoodsManagePermission: res.exists });
}).catch(err => {
console.error('检查货源管理权限失败:', err);
this.setData({ hasGoodsManagePermission: false });
});
},
// 切换按钮显示/隐藏到侧边栏
toggleSidebarBtn() {
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() {
// 获取系统信息,处理iOS特定兼容性
const systemInfo = wx.getSystemInfoSync();
const isIOS = systemInfo.system.toLowerCase().includes('ios');
const screenHeight = systemInfo.screenHeight * 2;
// 设置iOS特定标志
if (isIOS) {
this.setData({
isIOS: true
});
}
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()
this.loadCategories()
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
});
}
});
}, 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);
this.setData({
filteredGoods: filteredGoods,
});
});
};
app.eventBus.on('favoriteChanged', this.favoriteChangedHandler);
},
onShow: function () {
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');
// 只有在返回时才恢复按钮状态(刷新页面不恢复)
if (this.data.isPageHidden) {
this.setData({
sidebarBtnHidden: savedBtnHidden || false,
isPageHidden: false
});
}
this.setData({
isSearchBarFullyHidden: false,
lastScrollTop: 0
});
this.checkAndRestoreLoginStatus()
},
onHide: function () {
this.setData({
sidebarBtnHidden: true,
isPageHidden: 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,
soldOutPage: 1,
hasMoreData: true,
goods: existingGoods,
filteredGoods: [],
// 清除缓存以确保获取最新数据
categoryQueryCache: {},
lastDataTimestamp: 0,
})
const timestamp = new Date().getTime();
const currentCategory = this.data.selectedCategory === '全部' ? '' : this.data.selectedCategory;
const currentKeyword = this.data.searchKeyword;
const pageSize = this.data.pageSize;
// 强制刷新:清除所有缓存并重新从数据库加载
this.setData({
// 清除商品数据缓存
goodsCache: [],
// 重置缓存时间戳
lastDataTimestamp: 0
});
// 优先查询published状态的商品,如果有搜索关键词则同时查询sold_out状态
const statusList = currentKeyword ? ['published', 'sold_out'] : ['published'];
const apiParams = {
timestamp: timestamp,
viewMode: 'shopping',
page: 1,
pageSize: pageSize,
keyword: currentKeyword
}
// 只有非全部分类时才传递category参数
if (currentCategory) {
apiParams.category = currentCategory
}
API.getProductList(statusList, apiParams)
.then(res => {
this.setData({ isRefreshing: false })
if (res.success && res.products) {
this.processRefreshData(res.products, existingGoods)
wx.showToast({
title: '刷新成功',
icon: 'success',
duration: 1500
})
} else if (res.products.length === 0) {
// 如果published状态没有商品,则查询sold_out状态
console.log('没有published状态的商品,查询sold_out状态的商品');
const apiParams = {
timestamp: timestamp,
viewMode: 'shopping',
page: 1,
pageSize: pageSize,
keyword: currentKeyword
}
// 只有非全部分类时才传递category参数
if (currentCategory) {
apiParams.category = currentCategory
}
API.getProductList(['sold_out'], apiParams)
.then(soldOutRes => {
this.setData({ isRefreshing: false });
if (soldOutRes.success && soldOutRes.products && soldOutRes.products.length > 0) {
this.processRefreshData(soldOutRes.products, existingGoods);
wx.showToast({
title: '刷新成功',
icon: 'success',
duration: 1500
});
} else {
this.setData({
isLoading: false,
loadingMore: false
});
}
})
.catch(err => {
this.setData({ isRefreshing: false });
console.error('刷新sold_out商品数据失败:', err);
wx.showToast({
title: '刷新失败,请稍后重试',
icon: 'none',
duration: 2000
});
});
} 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;
},
// 格式化商品规格显示 - 只显示前两个,后面加...
formatSpecification: function(spec, yolk) {
if (!spec || spec === '无') {
return {
displaySpec: '无',
displayYolk: yolk && yolk !== '无' ? yolk : ''
};
}
// 按常见的分隔符分割规格
const separators = [',', ',', '、', '|', ';', ';'];
let parts = [spec];
for (let separator of separators) {
if (spec.includes(separator)) {
parts = spec.split(separator);
break;
}
}
// 清理空值并限制为前两个
const cleanParts = parts
.map(part => part.trim())
.filter(part => part && part !== '无')
.slice(0, 2);
let displaySpec = cleanParts.join(' | ');
// 如果原规格分割后超过两个部分,添加省略号
if (cleanParts.length < parts.filter(part => part.trim() && part.trim() !== '无').length) {
displaySpec += '...';
}
return {
displaySpec,
displayYolk: yolk && yolk !== '无' ? yolk : ''
};
},
// 处理商品数据 - 淘宝风格
processGoodsData: function(products, isLoadMore = false) {
let newGoods = products.map(product => {
const imageUrls = product.imageUrls || product.images || [];
const formattedImageUrls = Array.isArray(imageUrls) ? imageUrls : [imageUrls];
// 确保商品ID的一致性
const productId = product.productId || product.id;
// 预处理媒体URL,添加类型信息
const mediaItems = formattedImageUrls.map(url => {
return {
url: url,
type: isVideoUrl(url) ? 'video' : 'image'
};
});
// 计算库存总数 - 支持逗号分隔的数字字符串
const calculateTotalStock = (value) => {
if (!value) return 0;
if (typeof value === 'string') {
// 支持逗号分隔的数字字符串,如 "23,34,24"
return value.split(/[,,、]/).map(item => parseInt(item.trim()) || 0).reduce((sum, num) => sum + num, 0);
}
return parseInt(value) || 0;
};
// 优化库存计算 - 尝试多个字段
const minOrder = calculateTotalStock(product.minOrder);
const quantity = calculateTotalStock(product.quantity);
const stock = calculateTotalStock(product.stock);
const inventory = calculateTotalStock(product.inventory);
const availableStock = calculateTotalStock(product.availableStock);
const totalAvailable = calculateTotalStock(product.totalAvailable);
// 优先使用最具体的库存字段
const primaryStock = quantity || minOrder || stock || inventory || availableStock || totalAvailable;
const totalStock = primaryStock;
// 智能库存显示 - 库存>=10000显示"库存充足",库存=0显示"暂无",其他显示具体数字
let displayStock;
if (totalStock >= 10000) {
// 库存>=10000时显示"库存充足"
displayStock = '充足';
} else if (totalStock === 0) {
// 库存=0时显示"暂无"
displayStock = '暂无';
} else {
// 其他情况显示具体数字
displayStock = totalStock;
}
// 格式化规格显示
const formattedSpec = this.formatSpecification(product.specification || product.spec || '', product.yolk || '');
return {
...product,
id: productId, // 统一使用id字段
productId: productId, // 同时保留productId字段
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,
// 添加预处理后的媒体数据,包含类型信息
mediaItems: mediaItems,
totalStock: displayStock, // 使用优化后的库存显示值
originalTotalStock: totalStock, // 保留原始计算值用于调试
displaySpecification: formattedSpec.displaySpec, // 格式化后的规格
displayYolk: formattedSpec.displayYolk // 格式化后的蛋黄
}
})
// 过滤隐藏状态的商品
newGoods = newGoods.filter(item => (item.status || '').toLowerCase() !== 'hidden')
// 先对新商品进行内部查重
const uniqueNewGoodsMap = new Map();
newGoods.forEach(item => {
if (!uniqueNewGoodsMap.has(item.id)) {
uniqueNewGoodsMap.set(item.id, item);
}
});
newGoods = Array.from(uniqueNewGoodsMap.values());
// 更新商品的收藏状态
this.updateGoodsFavoriteStatus(newGoods, isLoadMore)
// 只在第一页或刷新时在商品列表最前面插入广告,且只插入一次
let adSlotsAdded = false;
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];
adSlotsAdded = true;
}
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 filteredGoods = this.applyFilters(updatedGoods, false)
const groupedGoods = this.groupGoodsForStaggeredLayout(filteredGoods);
this.setData({
goods: updatedGoods,
filteredGoods: filteredGoods,
groupedGoods: groupedGoods,
loadingMore: false,
isLoading: false,
isRefreshing: false,
page: this.data.page + 1,
lastDataTimestamp: new Date().getTime()
})
},
// 处理刷新数据 - 查重并保持原有商品
processRefreshData: function(newProducts, existingGoods) {
let newGoods = newProducts.map(product => {
const imageUrls = product.imageUrls || product.images || [];
const formattedImageUrls = Array.isArray(imageUrls) ? imageUrls : [imageUrls];
// 确保商品ID的一致性
const productId = product.productId || product.id;
return {
...product,
id: productId, // 统一使用id字段
productId: productId, // 同时保留productId字段
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 newGoodsMap = new Map();
newGoods.forEach(item => {
if (!newGoodsMap.has(item.id)) {
newGoodsMap.set(item.id, item);
}
});
newGoods = Array.from(newGoodsMap.values());
// 移除与现有商品重复的新商品(包括广告和普通商品)
const existingIds = new Set(existingGoods.map(item => item.id));
const uniqueNewGoods = newGoods.filter(item => !existingIds.has(item.id));
// 合并现有商品和去重后的新商品
const updatedGoods = [...existingGoods, ...uniqueNewGoods]
const filteredGoods = this.applyFilters(updatedGoods, false)
const groupedGoods = this.groupGoodsForStaggeredLayout(filteredGoods);
const currentCategory = this.data.selectedCategory === '全部' ? '' : this.data.selectedCategory;
const currentKeyword = this.data.searchKeyword;
const cacheKey = `${currentCategory}_${currentKeyword}`;
this.setData({
goods: updatedGoods,
filteredGoods: filteredGoods,
groupedGoods: groupedGoods,
loadingMore: false,
isLoading: false,
page: this.data.page + 1,
// 更新缓存时间戳
lastDataTimestamp: new Date().getTime()
})
// 更新分类查询缓存
const newCategoryQueryCache = { ...this.data.categoryQueryCache };
newCategoryQueryCache[cacheKey] = updatedGoods;
this.setData({
categoryQueryCache: newCategoryQueryCache
})
},
// 插入广告位数据
insertAdSlots: function(goods) {
if (!goods || goods.length === 0) return goods
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
}
return [adSlot1, adSlot2, ...goods]
},
// 广告点击事件处理
onAdClick: function(e) {
const adSlot = e.currentTarget.dataset.ad;
let imageSrc = e.currentTarget.dataset.src;
// 如果没有从data-src获取到,尝试从图片元素直接获取src
if (!imageSrc) {
imageSrc = e.currentTarget.src;
}
console.log('广告被点击, 广告位:', adSlot);
console.log('广告图片路径:', imageSrc);
// 直接预览广告图片(单击触发)
const validImageUrls = [imageSrc];
if (validImageUrls.length > 0 && validImageUrls[0]) {
this.setData({
previewImageUrls: validImageUrls,
previewImageIndex: 0,
showImagePreview: true
});
console.log('广告图片预览已打开,图片URL:', validImageUrls[0]);
} else {
console.error('无法获取广告图片路径');
wx.showToast({
title: '图片加载失败',
icon: 'none'
});
}
},
// 加载商品分类列表
loadCategories: function() {
API.getProductCategories().then(categories => {
if (categories && categories.length > 0) {
this.setData({
categories: categories
});
}
}).catch(err => {
console.error('加载分类失败:', err);
});
},
// 加载商品数据 - 优化版带缓存,支持状态优先级
loadGoods: function(isLoadMore = false, forceRefresh = false) {
if (isLoadMore && !this.data.hasMoreData) {
return
}
console.log('loadGoods - 开始加载商品,isLoadMore:', isLoadMore, 'forceRefresh:', forceRefresh);
console.log('loadGoods - 当前搜索关键词:', this.data.searchKeyword);
console.log('loadGoods - 当前分类:', this.data.selectedCategory);
const currentCategory = this.data.selectedCategory === '全部' ? '' : this.data.selectedCategory;
const currentKeyword = this.data.searchKeyword;
const cacheKey = `${currentCategory}_${currentKeyword}`;
const now = new Date().getTime();
const timestamp = new Date().getTime();
// 如果正在加载sold_out,继续加载sold_out
if (isLoadMore && this.data.isQueryingSoldOut) {
this.loadSoldOutData(cacheKey, currentKeyword, currentCategory, timestamp, isLoadMore);
return;
}
// 只有非加载更多模式才使用缓存
if (!isLoadMore && !forceRefresh &&
this.data.categoryQueryCache[cacheKey] &&
(now - this.data.lastDataTimestamp) < this.data.cacheValidDuration) {
// 使用缓存数据
const cachedGoods = this.data.categoryQueryCache[cacheKey];
this.setData({ lastDataTimestamp: now });
this.processCachedGoods(cachedGoods, isLoadMore);
return;
}
// 筛选时不显示骨架屏
if (isLoadMore) {
this.setData({ loadingMore: true })
} else if (!forceRefresh) { // 只有非筛选的首次加载才显示骨架屏
this.setData({ isLoading: true, publishedHasMore: true, soldOutPage: 1 })
}
const page = isLoadMore ? this.data.page : 1
const pageSize = this.data.pageSize;
// 构建API请求参数
const apiParams = {
timestamp: timestamp,
viewMode: 'shopping',
page: page,
pageSize: pageSize,
keyword: currentKeyword
}
// 只有非全部分类时才传递category参数
if (currentCategory) {
apiParams.category = currentCategory
}
console.log('loadGoods - API请求参数:', apiParams);
// 在搜索时同时查询published和sold_out状态的商品,否则只查询published
const statusList = this.data.searchKeyword ? ['published', 'sold_out'] : ['published'];
console.log('loadGoods - 查询状态列表:', statusList);
API.getProductList(statusList, apiParams)
.then(res => {
wx.hideLoading();
console.log(`===== ${statusList.join(', ')}状态查询结果 =====`);
console.log('res.success:', res.success);
console.log('res.products:', res.products);
console.log('res.products.length:', res.products ? res.products.length : 'undefined');
console.log('page:', page);
// 如果有published商品,直接处理
if (res.success && res.products && res.products.length > 0) {
console.log('有published商品,处理商品数据');
const totalGoods = res.total || 0;
const totalPages = res.totalPages || Math.ceil(totalGoods / pageSize);
// 只需要判断是否还有下一页,不需要检查当前页数据量是否等于pageSize
const publishedHasMore = page < totalPages;
// 当published商品没有更多数据时,查询sold_out商品
const shouldQuerySoldOut = !publishedHasMore;
// 更新缓存(加载更多时追加数据)
const updatedCache = { ...this.data.categoryQueryCache };
if (isLoadMore && updatedCache[cacheKey]) {
// 追加新数据到缓存
const existingIds = new Set(updatedCache[cacheKey].map(item => item.id));
const newProducts = res.products.filter(item => !existingIds.has(item.id));
updatedCache[cacheKey] = [...updatedCache[cacheKey], ...newProducts];
} else {
// 首次加载或刷新时替换缓存
updatedCache[cacheKey] = res.products;
}
// 计算sold_out起始页码和每页数量
let soldOutPageNum = 1;
let soldOutPageSize = pageSize;
if (shouldQuerySoldOut || (!publishedHasMore && isLoadMore)) {
// 如果published已无更多数据,查询sold_out商品
soldOutPageNum = isLoadMore ? this.data.soldOutPage + 1 : 1;
soldOutPageSize = pageSize;
}
this.setData({
hasMoreData: publishedHasMore || (shouldQuerySoldOut || this.data.isQueryingSoldOut),
isQueryingSoldOut: shouldQuerySoldOut || (!publishedHasMore && isLoadMore),
publishedHasMore: publishedHasMore,
soldOutPage: soldOutPageNum,
soldOutPageSize: soldOutPageSize,
categoryQueryCache: updatedCache,
lastDataTimestamp: now,
goodsCache: updatedCache[cacheKey]
});
this.processGoodsData(res.products, isLoadMore);
// 如果需要查询sold_out
if (this.data.isQueryingSoldOut) {
this.loadSoldOutData(cacheKey, currentKeyword, currentCategory, timestamp, isLoadMore);
}
return;
}
// 如果没有published商品,查询sold_out状态
if (page === 1) {
console.log('没有published商品,查询sold_out状态');
console.log('查询参数 - page:', page, 'pageSize:', pageSize, 'keyword:', currentKeyword, 'category:', currentCategory);
this.setData({ isQueryingSoldOut: true, soldOutPage: 1, soldOutPageSize: pageSize });
this.loadSoldOutData(cacheKey, currentKeyword, currentCategory, timestamp, isLoadMore);
return;
} else {
this.setData({ loadingMore: false, isLoading: false });
}
})
.catch(err => {
console.error('加载商品数据失败:', err);
this.setData({
loadingMore: false,
isLoading: false,
isRefreshing: false
});
})
},
// 加载sold_out数据
loadSoldOutData: function(cacheKey, currentKeyword, currentCategory, timestamp, isLoadMore) {
const pageSize = this.data.soldOutPageSize || 8;
const soldOutPageNum = this.data.soldOutPage || 1;
console.log('加载sold_out数据 - page:', soldOutPageNum, 'pageSize:', pageSize);
console.log('查询参数 - page:', soldOutPageNum, 'pageSize:', pageSize, 'keyword:', currentKeyword, 'category:', currentCategory);
const apiParams = {
timestamp: timestamp,
viewMode: 'shopping',
page: soldOutPageNum,
pageSize: pageSize,
keyword: currentKeyword
}
// 只有非全部分类时才传递category参数
if (currentCategory) {
apiParams.category = currentCategory
}
API.getProductList(['sold_out'], apiParams)
.then(soldOutRes => {
console.log('===== sold_out状态查询结果 =====');
console.log('soldOutRes.success:', soldOutRes.success);
console.log('soldOutRes.products:', soldOutRes.products);
console.log('soldOutRes.products.length:', soldOutRes.products ? soldOutRes.products.length : 'undefined');
if (soldOutRes.success && soldOutRes.products && soldOutRes.products.length > 0) {
const soldOutTotal = soldOutRes.total || 0;
const soldOutTotalPages = soldOutRes.totalPages || Math.ceil(soldOutTotal / pageSize);
const soldOutHasMore = soldOutPageNum < soldOutTotalPages && soldOutRes.products.length >= pageSize;
// 更新缓存
const updatedCache = { ...this.data.categoryQueryCache };
const existingCache = updatedCache[cacheKey] || [];
const existingIds = new Set(existingCache.map(item => item.id));
const uniqueSoldOutProducts = soldOutRes.products.filter(item => !existingIds.has(item.id));
updatedCache[cacheKey] = [...existingCache, ...uniqueSoldOutProducts];
this.setData({
hasMoreData: soldOutHasMore,
isQueryingSoldOut: soldOutHasMore,
soldOutPage: soldOutPageNum + 1,
categoryQueryCache: updatedCache,
lastDataTimestamp: new Date().getTime(),
goodsCache: updatedCache[cacheKey]
});
this.processGoodsData(updatedCache[cacheKey], isLoadMore);
console.log('sold_out数据加载完成,总商品数:', updatedCache[cacheKey].length, 'hasMoreData:', soldOutHasMore);
} else {
console.log('没有找到更多sold_out商品');
this.setData({
isQueryingSoldOut: false,
hasMoreData: false,
loadingMore: false,
isLoading: false
});
}
})
.catch(err => {
console.error('加载sold_out数据失败:', err);
this.setData({
isQueryingSoldOut: false,
loadingMore: false,
isLoading: false,
isRefreshing: false
});
});
},
// 处理缓存的商品数据
processCachedGoods: function(cachedGoods, isLoadMore) {
// 处理商品数据格式并去重
const goodsMap = new Map();
cachedGoods.forEach(product => {
const imageUrls = product.imageUrls || product.images || [];
const formattedImageUrls = Array.isArray(imageUrls) ? imageUrls : [imageUrls];
// 确保商品ID的一致性
const productId = product.productId || product.id;
// 只有当商品ID不存在时才添加,避免重复
if (!goodsMap.has(productId)) {
// 计算库存总数 - 支持逗号分隔的数字字符串
const calculateTotalStock = (value) => {
if (!value) return 0;
if (typeof value === 'string') {
// 支持逗号分隔的数字字符串,如 "23,34,24"
return value.split(/[,,、]/).map(item => parseInt(item.trim()) || 0).reduce((sum, num) => sum + num, 0);
}
return parseInt(value) || 0;
};
// 优化库存计算 - 尝试多个字段
const minOrder = calculateTotalStock(product.minOrder);
const quantity = calculateTotalStock(product.quantity);
const stock = calculateTotalStock(product.stock);
const inventory = calculateTotalStock(product.inventory);
const availableStock = calculateTotalStock(product.availableStock);
const totalAvailable = calculateTotalStock(product.totalAvailable);
// 优先使用最具体的库存字段
const primaryStock = quantity || minOrder || stock || inventory || availableStock || totalAvailable;
const totalStock = primaryStock;
// 智能库存显示 - 库存>=10000显示"库存充足",库存=0显示"暂无",其他显示具体数字
let displayStock;
if (totalStock >= 10000) {
// 库存>=10000时显示"库存充足"
displayStock = '库存充足';
} else if (totalStock === 0) {
// 库存=0时显示"暂无"
displayStock = '暂无';
} else {
// 其他情况显示具体数字
displayStock = totalStock;
}
const processedProduct = {
...product,
id: productId, // 统一使用id字段
productId: productId, // 同时保留productId字段
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,
totalStock: displayStock, // 使用优化后的库存显示值
originalTotalStock: totalStock // 保留原始计算值用于调试
};
goodsMap.set(productId, processedProduct);
}
});
// 转换为数组
const processedGoods = Array.from(goodsMap.values());
// 更新收藏状态
this.updateGoodsFavoriteStatus(processedGoods, false);
// 应用筛选条件
const filteredGoods = this.applyFilters(processedGoods, false);
const groupedGoods = this.groupGoodsForStaggeredLayout(filteredGoods);
this.setData({
goods: processedGoods,
filteredGoods: filteredGoods,
groupedGoods: groupedGoods,
loadingMore: false,
isLoading: false,
isRefreshing: false, // 确保下拉刷新状态被重置
hasMoreData: false // 缓存数据不分页
});
},
// 刷新商品列表 - 强制刷新机制
refreshGoodsList: function() {
this.setData({
page: 1,
hasMoreData: true,
goods: [],
filteredGoods: [],
loadingMore: false,
// 清除所有缓存以获取最新数据
categoryQueryCache: {},
lastDataTimestamp: 0,
goodsCache: []
}, () => {
console.log('refreshGoodsList:清除缓存并重新加载数据');
this.loadGoods(false, true); // 第二个参数true表示强制刷新
})
},
// 统一字段长度
normalizeFieldLength: function(item) {
const normalized = { ...item }
// 统一 name 字段长度为 20 字符
if (normalized.name) {
normalized.name = normalized.name.length > 20 ?
normalized.name.substring(0, 17) + '...' :
normalized.name.padEnd(20, ' ')
}
// 统一 region 字段长度为 15 字符
if (normalized.region) {
normalized.region = normalized.region.length > 15 ?
normalized.region.substring(0, 12) + '...' :
normalized.region.padEnd(15, ' ')
}
// 统一 grossWeight 字段长度为 10 字符
if (normalized.grossWeight) {
normalized.grossWeight = normalized.grossWeight.length > 10 ?
normalized.grossWeight.substring(0, 7) + '...' :
normalized.grossWeight.padEnd(10, ' ')
}
return normalized
},
// 应用筛选条件
applyFilters: function(goods, shouldSort = true) {
let filtered = [...goods]
console.log('applyFilters - 开始过滤,原始商品数量:', goods.length, '关键词:', this.data.searchKeyword);
if (this.data.selectedCategory !== '全部') {
const category = this.data.selectedCategory
filtered = filtered.filter(item => item.isAd || (item.category === category))
console.log('applyFilters - 分类过滤后数量:', filtered.length);
}
if (this.data.searchKeyword) {
const keyword = this.data.searchKeyword.toLowerCase()
const originalLength = filtered.length;
console.log('applyFilters - 搜索关键词:', keyword, '过滤前数量:', originalLength);
// 记录每个商品的匹配情况
filtered = filtered.filter(item => {
const nameMatch = (item.name || '').toLowerCase().includes(keyword);
const productNameMatch = (item.productName || '').toLowerCase().includes(keyword);
const specification = (item.specification || item.spec || '').toLowerCase();
const specMatch = specification.includes(keyword);
const regionMatch = (item.region || '').toLowerCase().includes(keyword);
const grossWeightMatch = (item.grossWeight || '').toLowerCase().includes(keyword);
const yolkMatch = (item.yolk || '').toLowerCase().includes(keyword);
const match = item.isAd || nameMatch || productNameMatch || specMatch || regionMatch || grossWeightMatch || yolkMatch;
// 详细日志,记录每个商品的匹配字段
if (originalLength <= 20 || keyword === '41' || keyword === '43-44') { // 增加特定关键词的日志记录
console.log('商品匹配详情:', {
name: item.name,
productId: item.productId || item.id,
keyword: keyword,
name: item.name,
productName: item.productName,
specification: item.specification,
spec: item.spec,
formattedSpecification: specification,
region: item.region,
grossWeight: item.grossWeight,
yolk: item.yolk,
nameMatch: nameMatch,
productNameMatch: productNameMatch,
specMatch: specMatch,
regionMatch: regionMatch,
grossWeightMatch: grossWeightMatch,
yolkMatch: yolkMatch,
match: match
});
}
return match;
})
console.log('applyFilters - 搜索过滤后数量:', filtered.length);
}
if (this.data.selectedRegion !== '全国') {
const selectedRegion = this.data.selectedRegion
filtered = filtered.filter(item => item.isAd || (item.region && item.region.includes(selectedRegion)))
}
// 去重处理 - 确保返回的商品列表中没有重复的商品
const uniqueGoodsMap = new Map();
filtered.forEach(item => {
// 使用id作为唯一标识,如果id不存在则使用productId
const uniqueId = item.id || item.productId;
if (uniqueId && !uniqueGoodsMap.has(uniqueId)) {
uniqueGoodsMap.set(uniqueId, item);
}
// 对于广告位,使用它们的唯一id
else if (item.isAd && !uniqueGoodsMap.has(item.id)) {
uniqueGoodsMap.set(item.id, item);
}
});
filtered = Array.from(uniqueGoodsMap.values());
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
})
}
// 统一字段长度
filtered = filtered.map(item => this.normalizeFieldLength(item))
return filtered
},
// 瀑布流布局 - 淘宝风格左右交替
distributeToColumns: function(goods) {
if (!goods || goods.length === 0) {
return { leftColumnGoods: [], rightColumnGoods: [] }
}
const leftColumn = []
const rightColumn = []
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)
}
if (i + 1 < goods.length) {
const item = { ...goods[i + 1], isLong: !isEvenRow }
rightColumn.push(item)
}
}
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;
}
console.log('searchGoods - 开始搜索,关键词:', this.data.searchKeyword);
console.log('searchGoods - 本地已有商品数量:', this.data.goods.length);
// 先对本地已加载的商品进行过滤
const filteredGoods = this.applyFilters(this.data.goods, false);
console.log('searchGoods - 本地过滤结果数量:', filteredGoods.length);
// 如果本地过滤结果不足,或者没有匹配的商品,则重新加载数据
if (filteredGoods.length < 3 && this.data.searchKeyword) {
console.log('searchGoods - 本地过滤结果不足3个,将重新加载数据');
this.setData({
page: 1,
hasMoreData: true,
goods: [],
filteredGoods: [],
loadingMore: false,
// 清除相关缓存以获取最新数据
lastDataTimestamp: 0,
categoryQueryCache: {},
isQueryingSoldOut: false,
publishedHasMore: true,
soldOutPage: 1
}, () => {
console.log('searchGoods:本地商品不足,重新加载数据,搜索关键词:', this.data.searchKeyword);
this.loadGoods(false, true); // 第二个参数true表示强制刷新
});
} else {
// 本地商品足够,直接使用本地过滤结果
console.log('searchGoods:使用本地商品过滤结果,数量:', filteredGoods.length);
const groupedGoods = this.groupGoodsForStaggeredLayout(filteredGoods);
this.setData({
filteredGoods: filteredGoods,
groupedGoods: groupedGoods
});
}
},
// 切换地区选择器
toggleRegionPicker: function() {
this.setData({
showRegionPicker: !this.data.showRegionPicker
})
},
// 选择地区(强制刷新机制)
selectRegion: function(e) {
const region = e.currentTarget.dataset.region
// 立即关闭弹窗
this.setData({
selectedRegion: region,
searchKeyword: '', // 清除搜索关键词,筛选框优先级更高
showRegionPicker: false
})
// 重新显示tabBar
const app = getApp();
if (app && app.globalData) {
app.globalData.showTabBar = true;
}
// 清除缓存并重新加载数据 - 仿照下拉刷新机制
this.setData({
page: 1,
hasMoreData: true,
goods: [],
filteredGoods: [],
loadingMore: false,
// 清除所有缓存以获取最新数据
categoryQueryCache: {},
lastDataTimestamp: 0,
goodsCache: []
});
this.loadGoods(false, true); // 强制刷新
},
// 阻止事件冒泡
stopPropagation: function() {
// 空函数,用于阻止事件冒泡
},
// 选择品种 - 使用下拉刷新机制
selectCategory: function(e) {
// 重新显示tabBar
const app = getApp();
if (app && app.globalData) {
app.globalData.showTabBar = true;
}
const category = e.currentTarget.dataset.category
if (category === this.data.selectedCategory) {
return // 如果选择的分类和当前相同,不重复加载
}
// 清除缓存并重新加载数据 - 筛选时不显示骨架屏,直接显示内容
this.setData({
selectedCategory: category,
searchKeyword: '', // 清除搜索关键词,筛选框优先级更高
loadingMore: false,
page: 1,
hasMoreData: true,
goods: [],
filteredGoods: [],
isLoading: false, // 筛选时不显示骨架屏
isRefreshing: false, // 筛选时不显示下拉刷新状态
// 清除所有缓存以获取最新数据
categoryQueryCache: {},
lastDataTimestamp: 0,
goodsCache: []
});
// 强制刷新:从数据库重新加载数据
this.loadGoods(false, true); // 第二个参数true表示强制刷新
},
// 查看商品详情
viewGoodsDetail: function(e) {
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页面保持一致
// 同时传递登录状态信息,让详情页决定是否显示登录弹窗
const needLogin = this.data.needPhoneAuth;
wx.navigateTo({
url: `/pages/goods-detail/goods-detail?goodsData=${encodeURIComponent(JSON.stringify(item))}&productId=${productId}&needLogin=${needLogin}`
})
},
// 跳转到我要卖蛋页面
navigateToSettlement: function() {
wx.switchTab({
url: '/pages/settlement/index'
})
},
// 跳转到招商合作页面
navigateToCooperation: function() {
this.setData({
sidebarBtnHidden: true
});
wx.navigateTo({
url: '/pages/cooperation/index'
})
},
// 跳转到货源管理页面
navigateToGoods: function() {
this.setData({
sidebarBtnHidden: true
});
wx.navigateTo({
url: '/pages/goods/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;
const lastScrollTop = this.data.lastScrollTop || 0;
const isScrollingDown = scrollTop > lastScrollTop;
let needUpdate = false;
const updates = {
lastScrollTop: scrollTop
};
// 搜索框始终固定显示,不做隐藏处理
// 侧边栏按钮显示逻辑
// 下滑时隐藏按钮(除非用户未手动隐藏过且在顶部)
if (isScrollingDown && scrollTop > threshold && !this.data.sidebarBtnHidden) {
updates.sidebarBtnHidden = true;
needUpdate = true;
wx.setStorageSync('sidebarBtnHidden', true);
wx.setStorageSync('userManuallyHidden', true);
}
// 上滑到顶部时显示按钮
else if (scrollTop <= threshold && this.data.sidebarBtnHidden && !this.data.userManuallyHidden) {
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);
this.setData({
filteredGoods: filteredGoods,
});
});
}
})
.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/首页分享照片.jpg'
}
},
// 分享到朋友圈
onShareTimeline() {
return {
title: '鸡蛋贸易平台 - 专业的鸡蛋交易小程序',
query: '',
imageUrl: '/images/首页分享照片.jpg'
}
},
// 处理手机号授权
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) {
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) {
// 直接完成身份设置,跳过重复授权
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') {
// 用户同意授权,实际处理授权流程
// 同时请求位置授权
wx.authorize({
scope: 'scope.userLocation',
success() {
// 位置授权成功,获取用户位置
wx.getLocation({
type: 'gcj02',
success(res) {
const latitude = res.latitude;
const longitude = res.longitude;
// 存储位置信息到本地
wx.setStorageSync('userLocation', { latitude, longitude });
// 位置获取成功提示
wx.showToast({
title: '位置获取成功',
icon: 'success',
duration: 1500
});
},
fail() {
console.error('登录时获取位置失败');
// 位置获取失败提示
wx.showToast({
title: '位置获取失败',
icon: 'none',
duration: 1500
});
}
});
},
fail() {
// 位置授权失败,不影响登录流程
// 位置授权失败提示
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失败')
}
// 2. 使用code换取openid
const openidRes = await API.getOpenid(loginRes.code)
// 改进错误处理逻辑,更宽容地处理服务器返回格式,增加详细日志
let openid = null;
let userId = null;
if (openidRes && typeof openidRes === 'object') {
// 适配服务器返回格式:{success: true, code: 200, message: '获取openid成功', data: {openid, userId}}
if (openidRes.data && typeof openidRes.data === 'object') {
openid = openidRes.data.openid || openidRes.data.OpenID || null;
userId = openidRes.data.userId || null;
} else {
// 尝试从响应对象中直接提取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)}`);
}
// 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)
} else if (openidRes && openidRes.userId) {
wx.setStorageSync('userId', openidRes.userId)
} else {
// 生成临时userId
const tempUserId = 'user_' + Date.now()
wx.setStorageSync('userId', tempUserId)
}
// 4. 上传手机号加密数据到服务器解密
const phoneData = {
...e.detail,
openid: openid
}
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
// 5. 获取用户微信名称和头像
let userProfile = null;
try {
userProfile = await new Promise((resolve, reject) => {
wx.getUserProfile({
desc: '用于完善会员资料',
success: resolve,
fail: reject
});
});
} 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 })
}
// 保存用户信息并等待上传完成
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()
// 2. 模拟获取openid和userId
const mockOpenid = 'test_openid_' + Date.now()
const mockUserId = 'test_user_' + Date.now()
// 3. 存储测试数据
wx.setStorageSync('openid', mockOpenid)
wx.setStorageSync('userId', mockUserId)
// 4. 模拟手机号解密结果
const mockPhoneNumber = null
// 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. 保存用户信息并等待上传完成
// 在测试模式下也会上传用户信息到服务器,用于连通性测试
await this.saveUserInfo(mockUserInfo, currentUserType)
wx.hideLoading()
// 9. 显示成功提示
wx.showToast({
title: '测试模式登录成功',
icon: 'success',
duration: 2000
})
// 更新登录状态
this.setData({
isLoggedIn: true
})
// 测试登录成功,但已移除类型选择和跳转功能
} 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)
}
// 更新登录状态
this.setData({
isLoggedIn: true
})
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)
} catch (e) {
console.error('保存用户信息到本地存储失败:', e)
}
// 保存用户信息到全局变量
const app = getApp()
app.globalData.userInfo = userInfo
app.globalData.userType = type
// 额外保存一份单独的userInfo到本地存储,便于checkPhoneAuthSetting方法检查
try {
wx.setStorageSync('userInfo', userInfo)
} catch (e) {
console.error('保存单独的userInfo失败:', e)
}
// 上传用户信息到服务器
// 在测试模式下也上传用户信息,用于连通性测试
// 确保测试数据包含服务器所需的所有字段
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,
isLoggedIn: !!(userId && openid)
})
} else {
app.globalData.userInfo = localUserInfo
this.setData({
userInfo: localUserInfo,
needPhoneAuth: !localUserInfo.phoneNumber,
isLoggedIn: !!(userId && openid)
})
}
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. 保存用户信息并等待上传完成
await this.uploadUserInfoToServer(userInfo, userId, currentUserType)
console.log('用户信息保存并上传完成')
// 更新本地和全局用户信息
app.globalData.userInfo = userInfo
wx.setStorageSync('userInfo', userInfo)
// 更新页面状态
this.setData({
needPhoneAuth: false,
userInfo: userInfo,
showOneKeyLoginModal: false,
isLoggedIn: true
})
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' });
}
});
},
})