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.
4345 lines
142 KiB
4345 lines
142 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://my-supplier-photos.oss-cn-chengdu.aliyuncs.com/products/%E6%B5%B7%E8%93%9D%E7%81%B0/image/7a2a8a17a83ba4d3d4270828531e2041.jpeg',
|
|
name: '',
|
|
showAuthModal: false,
|
|
showOneKeyLoginModal: false,
|
|
showAddToHomeModal: false,
|
|
showUserAgreementModal: false, // 用户协议与隐私政策弹窗
|
|
userInfo: {},
|
|
needPhoneAuth: false,
|
|
testMode: true,
|
|
partnerstatus: '',
|
|
viewedGoods: [], // 已浏览商品ID列表
|
|
isInPersonnel: false, // 用户手机号是否在personnel表中
|
|
videoPlaybackStates: {}, // 视频播放状态,键为videoId,值为是否在视口内
|
|
|
|
// 广告轮播图数据
|
|
adCarouselList: [
|
|
{
|
|
id: 1,
|
|
imageUrl: '/images/1.jpg',
|
|
link: ''
|
|
}
|
|
],
|
|
// 首页分享图片URL
|
|
shareImageUrl: '/images/首页分享照片.jpg',
|
|
|
|
// 侧边栏相关
|
|
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,
|
|
|
|
// 广告轮播图和功能按钮的显示状态
|
|
headerElementsHidden: false,
|
|
headerTranslateY: 0,
|
|
headerOpacity: 1,
|
|
|
|
// 滚动相关
|
|
lastScrollTop: 0,
|
|
scrollDirection: '',
|
|
scrollThreshold: 20,
|
|
|
|
// 搜索相关
|
|
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,
|
|
filterDebounceTimer: null,
|
|
// 请求任务管理
|
|
currentRequestTask: null,
|
|
isRefreshing: false,
|
|
isLoading: true,
|
|
|
|
// 图片预览相关状态
|
|
previewImageUrls: [],
|
|
previewImageIndex: 0,
|
|
showImagePreview: false,
|
|
|
|
// 高级筛选相关状态
|
|
showAdvancedFilter: false,
|
|
advancedFilters: {
|
|
shellColor: 'all', // 蛋壳颜色
|
|
yolkType: 'all', // 蛋黄类型
|
|
specification: 'all', // 规格
|
|
productName: 'all', // 商品名称
|
|
region: 'all', // 地区
|
|
priceRange: [0, 100], // 价格范围
|
|
sortBy: 'default' // 排序方式
|
|
},
|
|
// 自定义下拉菜单显示状态
|
|
showShellColorPicker: false,
|
|
showYolkTypePicker: false,
|
|
showSpecificationPicker: false,
|
|
showProductNamePicker: false,
|
|
showAdvancedRegionPicker: false,
|
|
|
|
// 筛选分类数据(左侧)
|
|
filterCategories: [
|
|
{ label: '地区', value: 'region' },
|
|
{ label: '蛋壳颜色', value: 'shellColor' },
|
|
{ label: '商品名称', value: 'productName' },
|
|
{ label: '规格', value: 'specification' },
|
|
{ label: '蛋黄类型', value: 'yolkType' }
|
|
],
|
|
// 当前选中的筛选分类
|
|
selectedFilterCategory: 'region',
|
|
// 蛋壳颜色选项
|
|
shellColorOptions: [
|
|
{ label: '绿壳', value: '绿壳' },
|
|
{ label: '粉壳', value: '粉壳' },
|
|
{ label: '褐壳', value: '褐壳' },
|
|
{ label: '土鸡蛋', value: '土鸡蛋' }
|
|
],
|
|
// 蛋黄类型选项
|
|
yolkTypeOptions: [
|
|
{ label: '红心', value: '红心' },
|
|
{ label: '黄心', value: '黄心' },
|
|
{ label: '双色', value: '双色' }
|
|
],
|
|
// 规格选项
|
|
specificationOptions: [
|
|
{ label: '格子装', value: '格子装' },
|
|
{ label: '散托', value: '散托' },
|
|
{ label: '不限规格', value: '不限规格' },
|
|
{ label: '净重47+', value: '净重47+' },
|
|
{ label: '净重46-47', value: '净重46-47' },
|
|
{ label: '净重45-46', value: '净重45-46' },
|
|
{ label: '净重44-45', value: '净重44-45' },
|
|
{ label: '净重43-44', value: '净重43-44' },
|
|
{ label: '净重42-43', value: '净重42-43' },
|
|
{ label: '净重41-42', value: '净重41-42' },
|
|
{ label: '净重40-41', value: '净重40-41' },
|
|
{ label: '净重39-40', value: '净重39-40' },
|
|
{ label: '净重38-39', value: '净重38-39' },
|
|
{ label: '净重37-39', value: '净重37-39' },
|
|
{ label: '净重37-38', value: '净重37-38' },
|
|
{ label: '净重36-38', value: '净重36-38' },
|
|
{ label: '净重36-37', value: '净重36-37' },
|
|
{ label: '净重35-36', value: '净重35-36' },
|
|
{ label: '净重34-35', value: '净重34-35' },
|
|
{ label: '净重33-34', value: '净重33-34' },
|
|
{ label: '净重32-33', value: '净重32-33' },
|
|
{ label: '净重32-34', value: '净重32-34' },
|
|
{ label: '净重31-32', value: '净重31-32' },
|
|
{ label: '净重30-35', value: '净重30-35' },
|
|
{ label: '净重30-34', value: '净重30-34' },
|
|
{ label: '净重30-32', value: '净重30-32' },
|
|
{ label: '净重30-31', value: '净重30-31' },
|
|
{ label: '净重29-31', value: '净重29-31' },
|
|
{ label: '净重29-30', value: '净重29-30' },
|
|
{ label: '净重28-29', value: '净重28-29' },
|
|
{ label: '净重28以下', value: '净重28以下' },
|
|
{ label: '毛重52以上', value: '毛重52以上' },
|
|
{ label: '毛重50-51', value: '毛重50-51' },
|
|
{ label: '毛重48-49', value: '毛重48-49' },
|
|
{ label: '毛重47-48', value: '毛重47-48' },
|
|
{ label: '毛重46-47', value: '毛重46-47' },
|
|
{ label: '毛重45-47', value: '毛重45-47' },
|
|
{ label: '毛重45-46', value: '毛重45-46' },
|
|
{ label: '毛重44-45', value: '毛重44-45' },
|
|
{ label: '毛重43-44', value: '毛重43-44' },
|
|
{ label: '毛重42-43', value: '毛重42-43' },
|
|
{ label: '毛重41-42', value: '毛重41-42' },
|
|
{ label: '毛重40-41', value: '毛重40-41' },
|
|
{ label: '毛重38-39', value: '毛重38-39' },
|
|
{ label: '毛重36-37', value: '毛重36-37' },
|
|
{ label: '毛重34-35', value: '毛重34-35' },
|
|
{ label: '毛重32-33', value: '毛重32-33' },
|
|
{ label: '毛重30-31', value: '毛重30-31' },
|
|
{ label: '毛重30以下', value: '毛重30以下' }
|
|
],
|
|
// 地区选项
|
|
regionOptions: [
|
|
{ label: '北京', value: '北京' },
|
|
{ label: '上海', value: '上海' },
|
|
{ label: '广州', value: '广州' },
|
|
{ label: '深圳', value: '深圳' },
|
|
{ label: '天津', value: '天津' },
|
|
{ label: '重庆', value: '重庆' },
|
|
{ label: '河北', value: '河北' },
|
|
{ label: '山西', value: '山西' },
|
|
{ label: '辽宁', value: '辽宁' },
|
|
{ label: '吉林', value: '吉林' },
|
|
{ label: '黑龙江', value: '黑龙江' },
|
|
{ label: '江苏', value: '江苏' },
|
|
{ label: '浙江', value: '浙江' },
|
|
{ label: '安徽', value: '安徽' },
|
|
{ label: '福建', value: '福建' },
|
|
{ label: '江西', value: '江西' },
|
|
{ label: '山东', value: '山东' },
|
|
{ label: '河南', value: '河南' },
|
|
{ label: '湖北', value: '湖北' },
|
|
{ label: '湖南', value: '湖南' },
|
|
{ label: '广东', value: '广东' },
|
|
{ label: '海南', value: '海南' },
|
|
{ label: '四川', value: '四川' },
|
|
{ label: '贵州', value: '贵州' },
|
|
{ label: '云南', value: '云南' },
|
|
{ label: '陕西', value: '陕西' },
|
|
{ label: '甘肃', value: '甘肃' },
|
|
{ label: '青海', value: '青海' },
|
|
{ label: '台湾', value: '台湾' },
|
|
{ label: '内蒙古', value: '内蒙古' },
|
|
{ label: '广西', value: '广西' },
|
|
{ label: '西藏', value: '西藏' },
|
|
{ label: '宁夏', value: '宁夏' },
|
|
{ label: '新疆', value: '新疆' },
|
|
{ label: '香港', value: '香港' },
|
|
{ label: '澳门', value: '澳门' }
|
|
],
|
|
|
|
// 商品名称选项
|
|
productNameOptions: [
|
|
{ label: '罗曼粉', value: '罗曼粉' },
|
|
{ label: '伊莎粉', value: '伊莎粉' },
|
|
{ label: '罗曼灰', value: '罗曼灰' },
|
|
{ label: '海蓝灰', value: '海蓝灰' },
|
|
{ label: '海蓝褐', value: '海蓝褐' },
|
|
{ label: '绿壳', value: '绿壳' },
|
|
{ label: '粉一', value: '粉一' },
|
|
{ label: '粉二', value: '粉二' },
|
|
{ label: '粉八', value: '粉八' },
|
|
{ label: '京粉1号', value: '京粉1号' },
|
|
{ label: '京红', value: '京红' },
|
|
{ label: '京粉6号', value: '京粉6号' },
|
|
{ label: '京粉3号', value: '京粉3号' },
|
|
{ label: '农大系列', value: '农大系列' },
|
|
{ label: '黑鸡土蛋', value: '黑鸡土蛋' },
|
|
{ label: '双黄蛋', value: '双黄蛋' },
|
|
{ label: '大午金凤', value: '大午金凤' },
|
|
{ label: '黑凤', value: '黑凤' }
|
|
],
|
|
// 价格范围选项
|
|
priceOptions: [
|
|
{ label: '全部价格', value: [0, 100] },
|
|
{ label: '0-20元', value: [0, 20] },
|
|
{ label: '20-50元', value: [20, 50] },
|
|
{ label: '50-100元', value: [50, 100] },
|
|
{ label: '100元以上', value: [100, 9999] }
|
|
],
|
|
// 排序方式选项
|
|
sortOptions: [
|
|
{ label: '默认排序', value: 'default' },
|
|
{ label: '价格从低到高', value: 'price_asc' },
|
|
{ label: '价格从高到低', value: 'price_desc' },
|
|
{ label: '最新发布', value: 'newest' }
|
|
],
|
|
},
|
|
|
|
// 跳转到聊天页面
|
|
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'
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
// 标记商品为已浏览
|
|
markGoodsAsViewed: function(productId) {
|
|
let viewedGoods = this.data.viewedGoods;
|
|
if (!viewedGoods.includes(productId)) {
|
|
viewedGoods.push(productId);
|
|
this.setData({ viewedGoods });
|
|
}
|
|
|
|
// 更新商品列表,添加已浏览标记
|
|
let updatedGoods = this.data.goods.map(item => {
|
|
if (String(item.productId || item.id) === productId) {
|
|
return {
|
|
...item,
|
|
isViewed: true
|
|
};
|
|
}
|
|
return item;
|
|
});
|
|
|
|
this.setData({ goods: updatedGoods }, () => {
|
|
// 重新应用筛选条件,确保显示的商品也更新
|
|
const filteredGoods = this.applyFilters(updatedGoods, true);
|
|
this.setData({ filteredGoods });
|
|
});
|
|
},
|
|
|
|
// 跳转到我要卖蛋页面
|
|
navigateToSettlement() {
|
|
wx.navigateTo({
|
|
url: '/pages/settlement/index',
|
|
success: function () {
|
|
console.log('成功跳转到我要卖蛋页面');
|
|
},
|
|
fail: function (error) {
|
|
console.error('跳转到我要卖蛋页面失败:', error);
|
|
wx.showToast({
|
|
title: '跳转失败,请稍后重试',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
// 跳转到货源管理页面
|
|
navigateToGoodsManage() {
|
|
wx.navigateTo({
|
|
url: '/pages/goods/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 });
|
|
});
|
|
},
|
|
|
|
// 检查用户手机号是否在personnel表中
|
|
checkPhoneInPersonnel() {
|
|
// 优先从本地存储获取最新的用户信息
|
|
const localUserInfo = wx.getStorageSync('userInfo') || {};
|
|
const app = getApp();
|
|
const globalUserInfo = app.globalData.userInfo || {};
|
|
|
|
// 按优先级获取用户信息:本地存储 > 全局变量 > 页面数据
|
|
const userInfo = localUserInfo.phoneNumber ? localUserInfo : (globalUserInfo.phoneNumber ? globalUserInfo : this.data.userInfo);
|
|
const phoneNumber = userInfo.phoneNumber;
|
|
|
|
if (!phoneNumber) {
|
|
this.setData({ isInPersonnel: false });
|
|
return;
|
|
}
|
|
|
|
console.log('开始检查手机号是否在personnel表中:', phoneNumber);
|
|
|
|
API.checkPhoneInPersonnel(phoneNumber).then(isInPersonnel => {
|
|
console.log('用户手机号是否在personnel表中:', isInPersonnel);
|
|
this.setData({ isInPersonnel });
|
|
}).catch(err => {
|
|
console.error('检查personnel表失败:', err);
|
|
this.setData({ isInPersonnel: 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,
|
|
headerElementsHidden: false
|
|
});
|
|
|
|
// 检查用户是否已同意协议
|
|
// this.checkUserAgreement();
|
|
|
|
this.checkAndRestoreLoginStatus()
|
|
this.loadCategories()
|
|
this.loadGoods()
|
|
this.checkPhoneInPersonnel()
|
|
this.loadAdCarouselList()
|
|
|
|
// 页面加载完成后检查视频是否在视口内
|
|
setTimeout(() => {
|
|
this.checkVideosInViewport();
|
|
}, 1000);
|
|
|
|
// 计算搜索区域高度
|
|
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.productId || item.id) === data.productId) {
|
|
return {
|
|
...item,
|
|
isFavorite: data.isFavorite
|
|
};
|
|
}
|
|
return item;
|
|
});
|
|
|
|
this.setData({
|
|
goods: updatedGoods
|
|
}, () => {
|
|
// 重新应用筛选条件,确保显示的商品收藏状态也更新
|
|
const filteredGoods = this.applyFilters(this.data.goods, true);
|
|
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()
|
|
this.checkPhoneInPersonnel()
|
|
},
|
|
|
|
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 currentCategory = this.data.selectedCategory;
|
|
const currentKeyword = this.data.searchKeyword;
|
|
const currentRegion = this.data.selectedRegion;
|
|
|
|
// 合并setData调用,减少iOS卡顿
|
|
this.setData({
|
|
isRefreshing: true,
|
|
page: 1,
|
|
soldOutPage: 1,
|
|
hasMoreData: true,
|
|
goods: [], // 下拉刷新时清空商品数组,避免旧数据影响排序
|
|
filteredGoods: [],
|
|
// 清除缓存以确保获取最新数据
|
|
categoryQueryCache: {},
|
|
goodsCache: [],
|
|
lastDataTimestamp: 0,
|
|
// 保存当前筛选条件,用于验证请求结果
|
|
refreshCategory: currentCategory,
|
|
refreshKeyword: currentKeyword,
|
|
refreshRegion: currentRegion,
|
|
// 下拉刷新时确保从published状态开始加载
|
|
isQueryingSoldOut: false,
|
|
publishedHasMore: true,
|
|
// 清除已浏览商品标记
|
|
viewedGoods: [],
|
|
isLoading: true,
|
|
loadingMore: false
|
|
})
|
|
|
|
const timestamp = new Date().getTime();
|
|
const categoryParam = currentCategory === '全部' ? '' : currentCategory;
|
|
const pageSize = this.data.pageSize;
|
|
|
|
// 先只查询published状态的商品
|
|
const statusList = ['published'];
|
|
const apiParams = {
|
|
timestamp: timestamp,
|
|
viewMode: 'shopping',
|
|
page: 1,
|
|
pageSize: pageSize,
|
|
keyword: currentKeyword
|
|
}
|
|
// 只有非全部分类时才传递category参数
|
|
if (categoryParam) {
|
|
apiParams.category = categoryParam
|
|
}
|
|
API.getProductList(statusList, apiParams)
|
|
.then(res => {
|
|
// 验证当前筛选条件是否与刷新开始时一致
|
|
if (currentCategory !== this.data.selectedCategory ||
|
|
currentKeyword !== this.data.searchKeyword ||
|
|
currentRegion !== this.data.selectedRegion) {
|
|
console.log('筛选条件已变更,忽略此次刷新结果');
|
|
this.setData({ isRefreshing: false });
|
|
return;
|
|
}
|
|
|
|
if (res.success && res.products) {
|
|
// 使用setTimeout延迟处理数据,避免阻塞主线程
|
|
setTimeout(() => {
|
|
this.processRefreshData(res.products)
|
|
this.setData({ isRefreshing: false });
|
|
wx.showToast({
|
|
title: '刷新成功',
|
|
icon: 'success',
|
|
duration: 400
|
|
})
|
|
}, 0);
|
|
} 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 (categoryParam) {
|
|
apiParams.category = categoryParam
|
|
}
|
|
API.getProductList(['sold_out'], apiParams)
|
|
.then(soldOutRes => {
|
|
// 验证当前筛选条件是否与刷新开始时一致
|
|
if (currentCategory !== this.data.selectedCategory ||
|
|
currentKeyword !== this.data.searchKeyword ||
|
|
currentRegion !== this.data.selectedRegion) {
|
|
console.log('筛选条件已变更,忽略此次刷新结果(sold_out)');
|
|
this.setData({ isRefreshing: false });
|
|
return;
|
|
}
|
|
|
|
if (soldOutRes.success && soldOutRes.products && soldOutRes.products.length > 0) {
|
|
// 使用setTimeout延迟处理数据,避免阻塞主线程
|
|
setTimeout(() => {
|
|
this.processRefreshData(soldOutRes.products);
|
|
this.setData({ isRefreshing: false });
|
|
wx.showToast({
|
|
title: '刷新成功',
|
|
icon: 'success',
|
|
duration: 1500
|
|
});
|
|
}, 0);
|
|
} else {
|
|
this.setData({
|
|
isLoading: false,
|
|
loadingMore: false,
|
|
isRefreshing: 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 => {
|
|
// 验证当前筛选条件是否与刷新开始时一致
|
|
if (currentCategory !== this.data.selectedCategory ||
|
|
currentKeyword !== this.data.searchKeyword ||
|
|
currentRegion !== this.data.selectedRegion) {
|
|
console.log('筛选条件已变更,忽略此次刷新错误');
|
|
this.setData({ isRefreshing: false });
|
|
return;
|
|
}
|
|
|
|
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;
|
|
},
|
|
|
|
// 检查用户是否已同意协议
|
|
checkUserAgreement: function() {
|
|
const hasAgreed = wx.getStorageSync('userAgreementAgreed');
|
|
if (!hasAgreed) {
|
|
this.setData({
|
|
showUserAgreementModal: true
|
|
});
|
|
}
|
|
},
|
|
|
|
// 同意用户协议
|
|
agreeUserAgreement: function() {
|
|
wx.setStorageSync('userAgreementAgreed', true);
|
|
this.setData({
|
|
showUserAgreementModal: false
|
|
});
|
|
},
|
|
|
|
// 不同意用户协议
|
|
disagreeUserAgreement: function() {
|
|
// 点击不同意按钮,关闭用户协议弹窗
|
|
this.setData({
|
|
showUserAgreementModal: false
|
|
});
|
|
},
|
|
|
|
// 跳转到用户服务协议页面
|
|
navigateToUserServiceAgreement: function() {
|
|
// 这里可以跳转到具体的协议页面,暂时使用提示
|
|
wx.showToast({
|
|
title: '跳转至用户服务协议',
|
|
icon: 'none'
|
|
});
|
|
},
|
|
|
|
// 跳转到隐私政策页面
|
|
navigateToPrivacyPolicy: function() {
|
|
// 这里可以跳转到具体的隐私政策页面,暂时使用提示
|
|
wx.showToast({
|
|
title: '跳转至隐私政策',
|
|
icon: 'none'
|
|
});
|
|
},
|
|
|
|
// 格式化商品规格显示 - 只显示前两个,后面加...
|
|
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) {
|
|
// 检查第一个商品是否包含description字段
|
|
if (products.length > 0) {
|
|
console.log('商品数据包含的所有字段:', Object.keys(products[0]));
|
|
console.log('description字段是否存在:', 'description' in products[0]);
|
|
if ('description' in products[0]) {
|
|
console.log('description字段的值:', products[0].description);
|
|
}
|
|
}
|
|
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, specStatusString) => {
|
|
if (!value) return 0;
|
|
if (typeof value === 'string') {
|
|
// 支持逗号分隔的数字字符串,如 "23,34,24"
|
|
const quantityArray = value.split(/[,,、]/).map(item => parseInt(item.trim()) || 0);
|
|
|
|
// 检查是否有规格状态字符串
|
|
if (specStatusString && typeof specStatusString === 'string') {
|
|
const specStatusArray = specStatusString.split(/[,,、]/).map(item => item.trim());
|
|
|
|
// 只计算未下架的规格库存(specStatus !== '1')
|
|
// 确保规格状态数组与数量数组长度匹配
|
|
return quantityArray.reduce((sum, num, index) => {
|
|
// 如果规格状态数组长度不足,默认为未下架状态
|
|
const specStatus = index < specStatusArray.length ? specStatusArray[index] : '0';
|
|
if (specStatus !== '1') {
|
|
return sum + num;
|
|
}
|
|
return sum;
|
|
}, 0);
|
|
}
|
|
|
|
// 如果没有规格状态,计算所有库存
|
|
return quantityArray.reduce((sum, num) => sum + num, 0);
|
|
}
|
|
return parseInt(value) || 0;
|
|
};
|
|
|
|
// 优化库存计算 - 尝试多个字段,同时考虑规格状态
|
|
// 尝试不同的字段名来获取规格状态
|
|
const specStatusString = product.specStatusString || product.specStatus || product.statusString || product.spec_status || '';
|
|
const minOrder = calculateTotalStock(product.minOrder, specStatusString);
|
|
const quantity = calculateTotalStock(product.quantity, specStatusString);
|
|
const stock = calculateTotalStock(product.stock, specStatusString);
|
|
const inventory = calculateTotalStock(product.inventory, specStatusString);
|
|
const availableStock = calculateTotalStock(product.availableStock, specStatusString);
|
|
const totalAvailable = calculateTotalStock(product.totalAvailable, specStatusString);
|
|
|
|
// 优先使用最具体的库存字段
|
|
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 || '');
|
|
|
|
// 处理价格波动
|
|
const processPriceChange = (productLog) => {
|
|
if (!productLog || !Array.isArray(productLog) || productLog.length === 0) {
|
|
return {
|
|
hasPriceChange: false,
|
|
priceChangeDirection: null
|
|
};
|
|
}
|
|
|
|
// 检查是否有同规格的第二次日志
|
|
const specLogs = {};
|
|
productLog.forEach(log => {
|
|
if (typeof log === 'string') {
|
|
// 提取规格信息
|
|
const specMatch = log.match(/将(.+?)规格的销售价格/);
|
|
if (specMatch && specMatch[1]) {
|
|
const spec = specMatch[1].trim();
|
|
if (!specLogs[spec]) {
|
|
specLogs[spec] = [];
|
|
}
|
|
specLogs[spec].push(log);
|
|
}
|
|
}
|
|
});
|
|
|
|
// 检查是否有规格有多次价格调整
|
|
let hasPriceChange = false;
|
|
for (const spec in specLogs) {
|
|
if (specLogs[spec].length >= 2) {
|
|
hasPriceChange = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return {
|
|
hasPriceChange,
|
|
priceChangeDirection: null // 默认为null,在商品详情页中计算具体方向
|
|
};
|
|
};
|
|
|
|
// 检查所有可能的产品日志字段名
|
|
const productLog = product.product_log || product.productLog || [];
|
|
const priceChangeInfo = processPriceChange(productLog);
|
|
|
|
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,
|
|
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, // 格式化后的蛋黄
|
|
price: product.price !== undefined ? product.price : '', // 确保price字段存在
|
|
// 添加价格波动信息
|
|
hasPriceChange: priceChangeInfo.hasPriceChange,
|
|
priceChangeDirection: priceChangeInfo.priceChangeDirection
|
|
}
|
|
})
|
|
|
|
// 过滤隐藏状态的商品,并确保商品对象有效
|
|
newGoods = newGoods.filter(item => {
|
|
// 过滤隐藏状态的商品
|
|
if ((item.status || '').toLowerCase() === 'hidden') {
|
|
return false;
|
|
}
|
|
// 确保商品对象有必要的字段
|
|
return item && item.id && item.name;
|
|
});
|
|
|
|
// 先对新商品进行内部查重
|
|
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'],
|
|
mediaItems: [{ url: '/images/轮播图1.jpg', type: 'image' }],
|
|
price: 0,
|
|
adType: 'full_card',
|
|
isAd: true
|
|
},
|
|
{
|
|
id: 'ad_slot_2',
|
|
name: '广告位2',
|
|
imageUrls: ['/images/轮播图1.jpg'],
|
|
mediaItems: [{ url: '/images/轮播图1.jpg', type: 'image' }],
|
|
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, true)
|
|
const groupedGoods = this.groupGoodsForStaggeredLayout(filteredGoods);
|
|
|
|
this.setData({
|
|
goods: updatedGoods,
|
|
filteredGoods: filteredGoods,
|
|
groupedGoods: groupedGoods,
|
|
loadingMore: false,
|
|
isLoading: false,
|
|
isRefreshing: false,
|
|
// 根据当前是否在查询售空商品来更新对应的页码
|
|
// soldOutPage 已经在 loadSoldOutData 函数中直接更新
|
|
page: this.data.isQueryingSoldOut ? this.data.page : this.data.page + 1,
|
|
lastDataTimestamp: new Date().getTime()
|
|
}, () => {
|
|
// 商品数据加载完成后检查视频是否在视口内
|
|
setTimeout(() => {
|
|
this.checkVideosInViewport();
|
|
}, 500);
|
|
})
|
|
|
|
// 检查过滤后的非广告商品数量,如果不足6个,则自动触发上拉加载更多
|
|
// 对于已发布商品,在非加载更多模式下触发;对于售空商品,在加载更多模式下触发
|
|
const nonAdGoodsCount = filteredGoods.filter(item => !item.isAd).length;
|
|
if (nonAdGoodsCount < 6 && !this.data.loadingMore && !this.data.isLoading) {
|
|
if (!isLoadMore || this.data.isQueryingSoldOut) {
|
|
console.log('商品数量不足6个,自动触发上拉加载更多');
|
|
// 使用setTimeout确保当前任务完成后再触发加载更多
|
|
setTimeout(() => {
|
|
this.loadGoods(true);
|
|
}, 100);
|
|
}
|
|
}
|
|
},
|
|
|
|
// 处理刷新数据 - 查重并保持原有商品
|
|
processRefreshData: function (newProducts) {
|
|
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;
|
|
|
|
// 预处理媒体URL,添加类型信息
|
|
const mediaItems = formattedImageUrls.map(url => {
|
|
return {
|
|
url: url,
|
|
type: isVideoUrl(url) ? 'video' : 'image'
|
|
};
|
|
});
|
|
|
|
// 计算库存总数 - 支持逗号分隔的数字字符串,同时考虑规格状态
|
|
const calculateTotalStock = (value, specStatusString) => {
|
|
if (!value) return 0;
|
|
if (typeof value === 'string') {
|
|
// 支持逗号分隔的数字字符串,如 "23,34,24"
|
|
const quantityArray = value.split(/[,,、]/).map(item => parseInt(item.trim()) || 0);
|
|
|
|
// 检查是否有规格状态字符串
|
|
if (specStatusString && typeof specStatusString === 'string') {
|
|
const specStatusArray = specStatusString.split(/[,,、]/).map(item => item.trim());
|
|
|
|
// 只计算未下架的规格库存(specStatus !== '1')
|
|
// 确保规格状态数组与数量数组长度匹配
|
|
return quantityArray.reduce((sum, num, index) => {
|
|
// 如果规格状态数组长度不足,默认为未下架状态
|
|
const specStatus = index < specStatusArray.length ? specStatusArray[index] : '0';
|
|
if (specStatus !== '1') {
|
|
return sum + num;
|
|
}
|
|
return sum;
|
|
}, 0);
|
|
}
|
|
|
|
// 如果没有规格状态,计算所有库存
|
|
return quantityArray.reduce((sum, num) => sum + num, 0);
|
|
}
|
|
return parseInt(value) || 0;
|
|
};
|
|
|
|
// 优化库存计算 - 尝试多个字段,同时考虑规格状态
|
|
// 尝试不同的字段名来获取规格状态
|
|
const specStatusString = product.specStatusString || product.specStatus || product.statusString || product.spec_status || '';
|
|
const minOrder = calculateTotalStock(product.minOrder, specStatusString);
|
|
const quantity = calculateTotalStock(product.quantity, specStatusString);
|
|
const stock = calculateTotalStock(product.stock, specStatusString);
|
|
const inventory = calculateTotalStock(product.inventory, specStatusString);
|
|
const availableStock = calculateTotalStock(product.availableStock, specStatusString);
|
|
const totalAvailable = calculateTotalStock(product.totalAvailable, specStatusString);
|
|
|
|
// 优先使用最具体的库存字段
|
|
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 || '');
|
|
|
|
// 处理价格波动
|
|
const processPriceChange = (productLog) => {
|
|
if (!productLog || !Array.isArray(productLog) || productLog.length === 0) {
|
|
return {
|
|
hasPriceChange: false,
|
|
priceChangeDirection: null
|
|
};
|
|
}
|
|
|
|
// 检查是否有同规格的第二次日志
|
|
const specLogs = {};
|
|
productLog.forEach(log => {
|
|
if (typeof log === 'string') {
|
|
// 提取规格信息
|
|
const specMatch = log.match(/将(.+?)规格的销售价格/);
|
|
if (specMatch && specMatch[1]) {
|
|
const spec = specMatch[1].trim();
|
|
if (!specLogs[spec]) {
|
|
specLogs[spec] = [];
|
|
}
|
|
specLogs[spec].push(log);
|
|
}
|
|
}
|
|
});
|
|
|
|
// 检查是否有规格有多次价格调整
|
|
let hasPriceChange = false;
|
|
for (const spec in specLogs) {
|
|
if (specLogs[spec].length >= 2) {
|
|
hasPriceChange = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return {
|
|
hasPriceChange,
|
|
priceChangeDirection: null // 默认为null,在商品详情页中计算具体方向
|
|
};
|
|
};
|
|
|
|
// 检查所有可能的产品日志字段名
|
|
const productLog = product.product_log || product.productLog || [];
|
|
const priceChangeInfo = processPriceChange(productLog);
|
|
|
|
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,
|
|
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, // 格式化后的蛋黄
|
|
price: product.price !== undefined ? product.price : '', // 确保price字段存在
|
|
// 添加价格波动信息
|
|
hasPriceChange: priceChangeInfo.hasPriceChange,
|
|
priceChangeDirection: priceChangeInfo.priceChangeDirection
|
|
}
|
|
})
|
|
|
|
// 过滤隐藏状态的商品,并确保商品对象有效
|
|
newGoods = newGoods.filter(item => {
|
|
// 过滤隐藏状态的商品
|
|
if ((item.status || '').toLowerCase() === 'hidden') {
|
|
return false;
|
|
}
|
|
// 确保商品对象有必要的字段
|
|
return item && item.id && item.name;
|
|
});
|
|
|
|
// 对新商品进行内部查重
|
|
const newGoodsMap = new Map();
|
|
newGoods.forEach(item => {
|
|
if (!newGoodsMap.has(item.id)) {
|
|
newGoodsMap.set(item.id, item);
|
|
}
|
|
});
|
|
// 下拉刷新时完全使用新获取的商品数据,不再与旧数据合并
|
|
const newGoodsOnly = Array.from(newGoodsMap.values());
|
|
|
|
// 对新数据进行过滤和排序
|
|
const filteredGoods = this.applyFilters(newGoodsOnly, true)
|
|
const groupedGoods = this.groupGoodsForStaggeredLayout(filteredGoods);
|
|
|
|
const currentCategory = this.data.selectedCategory === '全部' ? '' : this.data.selectedCategory;
|
|
const currentKeyword = this.data.searchKeyword;
|
|
const cacheKey = `${currentCategory}_${currentKeyword}`;
|
|
|
|
// 下拉刷新后检查返回的商品数量,决定是否还有更多published商品
|
|
const hasMorePublished = newGoodsOnly.length >= this.data.pageSize;
|
|
|
|
this.setData({
|
|
goods: newGoodsOnly,
|
|
filteredGoods: filteredGoods,
|
|
groupedGoods: groupedGoods,
|
|
loadingMore: false,
|
|
isLoading: false,
|
|
page: this.data.page + 1,
|
|
// 确保下拉刷新后继续加载published状态商品
|
|
isQueryingSoldOut: !hasMorePublished,
|
|
publishedHasMore: hasMorePublished,
|
|
// 如果切换到售空商品,重置售空商品页码
|
|
soldOutPage: !hasMorePublished ? 1 : this.data.soldOutPage,
|
|
// 更新缓存时间戳
|
|
lastDataTimestamp: new Date().getTime()
|
|
})
|
|
|
|
// 更新分类查询缓存
|
|
const newCategoryQueryCache = { ...this.data.categoryQueryCache };
|
|
newCategoryQueryCache[cacheKey] = newGoodsOnly;
|
|
this.setData({
|
|
categoryQueryCache: newCategoryQueryCache
|
|
})
|
|
|
|
// 检查过滤后的非广告商品数量,如果不足6个,则自动触发上拉加载更多
|
|
// 对于已发布商品,在非加载更多模式下触发;对于售空商品,在加载更多模式下触发
|
|
const nonAdGoodsCount = filteredGoods.filter(item => !item.isAd).length;
|
|
if (nonAdGoodsCount < 6 && !this.data.loadingMore && !this.data.isLoading) {
|
|
console.log('刷新后商品数量不足6个,自动触发上拉加载更多');
|
|
// 使用setTimeout确保当前任务完成后再触发加载更多
|
|
setTimeout(() => {
|
|
this.loadGoods(true);
|
|
}, 100);
|
|
}
|
|
},
|
|
|
|
// 插入广告位数据
|
|
insertAdSlots: function (goods) {
|
|
if (!goods || goods.length === 0) return goods
|
|
|
|
const adSlot1 = {
|
|
id: 'ad_slot_1',
|
|
name: '广告位1',
|
|
imageUrls: ['/images/轮播图1.jpg'],
|
|
mediaItems: [{ url: '/images/轮播图1.jpg', type: 'image' }],
|
|
price: 0,
|
|
adType: 'full_card',
|
|
isAd: true
|
|
}
|
|
|
|
const adSlot2 = {
|
|
id: 'ad_slot_2',
|
|
name: '广告位2',
|
|
imageUrls: ['/images/轮播图1.jpg'],
|
|
mediaItems: [{ url: '/images/轮播图1.jpg', type: 'image' }],
|
|
price: 0,
|
|
adType: 'half_image',
|
|
isAd: true
|
|
}
|
|
|
|
return [adSlot1, adSlot2, ...goods]
|
|
},
|
|
|
|
// 广告点击事件处理
|
|
onAdClick: function (e) {
|
|
const ad = e.currentTarget.dataset.ad;
|
|
const index = e.currentTarget.dataset.index;
|
|
console.log('广告被点击:', ad, '索引:', index);
|
|
|
|
// 如果广告有链接,跳转到链接
|
|
if (ad.link) {
|
|
wx.navigateTo({
|
|
url: ad.link
|
|
});
|
|
} else {
|
|
// 否则预览广告图片,支持左右切换
|
|
const { adCarouselList } = this.data;
|
|
const validImageUrls = adCarouselList.map(item => item.imageUrl).filter(url => url);
|
|
if (validImageUrls.length > 0) {
|
|
this.setData({
|
|
previewImageUrls: validImageUrls,
|
|
previewImageIndex: index,
|
|
showImagePreview: true
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
// 加载广告轮播图数据
|
|
loadAdCarouselList: function () {
|
|
API.getCovers().then(res => {
|
|
if (res.success && res.covers) {
|
|
// 过滤出轮播图数据(id为1和2)
|
|
const carouselCovers = res.covers.filter(cover => cover.id === 1 || cover.id === 2);
|
|
// 处理轮播图数据,解析coverurl字段
|
|
const adCarouselList = carouselCovers.map(cover => {
|
|
// 解析coverurl字段,提取图片URL
|
|
let imageUrl = '';
|
|
try {
|
|
// coverurl是JSON字符串,格式为["url"]
|
|
const urls = JSON.parse(cover.coverurl);
|
|
if (Array.isArray(urls) && urls.length > 0) {
|
|
// 移除字符串中的空格和反引号
|
|
imageUrl = urls[0].replace(/[`\s]/g, '');
|
|
}
|
|
} catch (err) {
|
|
console.error('解析轮播图URL失败:', err);
|
|
}
|
|
return {
|
|
id: cover.id,
|
|
imageUrl: imageUrl,
|
|
link: ''
|
|
};
|
|
});
|
|
|
|
// 查找首页分享图片(id为6)
|
|
const shareCover = res.covers.find(cover => cover.id === 6);
|
|
let shareImageUrl = '/images/首页分享照片.jpg'; // 默认图片
|
|
if (shareCover) {
|
|
try {
|
|
const urls = JSON.parse(shareCover.coverurl);
|
|
if (Array.isArray(urls) && urls.length > 0) {
|
|
shareImageUrl = urls[0].replace(/[`\s]/g, '');
|
|
}
|
|
} catch (err) {
|
|
console.error('解析首页分享图片URL失败:', err);
|
|
}
|
|
}
|
|
|
|
this.setData({
|
|
adCarouselList: adCarouselList,
|
|
shareImageUrl: shareImageUrl
|
|
});
|
|
}
|
|
}).catch(err => {
|
|
console.error('加载轮播图失败:', err);
|
|
});
|
|
},
|
|
|
|
// 加载商品分类列表
|
|
loadCategories: function () {
|
|
API.getProductCategories().then(categories => {
|
|
if (categories && categories.length > 0) {
|
|
// 将"白壳"替换为"土鸡蛋"
|
|
const updatedCategories = categories.map(category => {
|
|
return category === '白壳' ? '土鸡蛋' : category;
|
|
});
|
|
// 确保"全部"在第一位
|
|
if (!updatedCategories.includes('全部')) {
|
|
updatedCategories.unshift('全部');
|
|
}
|
|
// 去除重复项
|
|
const uniqueCategories = [...new Set(updatedCategories)];
|
|
this.setData({
|
|
categories: uniqueCategories
|
|
});
|
|
}
|
|
}).catch(err => {
|
|
console.error('加载分类失败:', err);
|
|
});
|
|
},
|
|
|
|
// 加载商品数据 - 优化版带缓存,支持状态优先级
|
|
loadGoods: function (isLoadMore = false, forceRefresh = false) {
|
|
// 当正在加载售空商品时,即使hasMoreData为false也继续加载
|
|
if (isLoadMore && !this.data.hasMoreData && !this.data.isQueryingSoldOut) {
|
|
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状态的商品
|
|
const statusList = ['published'];
|
|
console.log('loadGoods - 查询状态列表:', statusList);
|
|
|
|
// 取消正在进行的请求
|
|
if (this.data.currentRequestTask) {
|
|
this.data.currentRequestTask.abort();
|
|
console.log('已取消旧的API请求');
|
|
}
|
|
|
|
// 直接使用API.getProductList,并在请求完成后清除请求任务标记
|
|
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 publishedHasMore = res.products.length === pageSize;
|
|
|
|
// 更新缓存(加载更多时追加数据)
|
|
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;
|
|
}
|
|
|
|
this.setData({
|
|
hasMoreData: true, // 保持hasMoreData为true,以便在已发布商品加载完毕后继续加载售空商品
|
|
isQueryingSoldOut: !publishedHasMore, // 当已发布商品加载完毕时,切换到加载售空商品
|
|
publishedHasMore: publishedHasMore,
|
|
categoryQueryCache: updatedCache,
|
|
lastDataTimestamp: now,
|
|
goodsCache: updatedCache[cacheKey],
|
|
// 当切换到加载售空商品时,重置售空商品的页码为1
|
|
soldOutPage: !publishedHasMore ? 1 : this.data.soldOutPage
|
|
});
|
|
|
|
this.processGoodsData(res.products, isLoadMore);
|
|
return;
|
|
}
|
|
|
|
// 如果没有published商品,查询sold_out状态
|
|
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;
|
|
})
|
|
.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 soldOutHasMore = 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.processGoodsData(uniqueSoldOutProducts, isLoadMore);
|
|
|
|
this.setData({
|
|
hasMoreData: soldOutHasMore,
|
|
isQueryingSoldOut: soldOutHasMore,
|
|
categoryQueryCache: updatedCache,
|
|
lastDataTimestamp: new Date().getTime(),
|
|
goodsCache: updatedCache[cacheKey],
|
|
// 直接更新售空商品的页码
|
|
soldOutPage: soldOutPageNum + 1
|
|
});
|
|
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,
|
|
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, // 保留原始计算值用于调试
|
|
price: product.price !== undefined ? product.price : '' // 确保price字段存在
|
|
};
|
|
|
|
goodsMap.set(productId, processedProduct);
|
|
}
|
|
});
|
|
|
|
// 转换为数组
|
|
const processedGoods = Array.from(goodsMap.values());
|
|
|
|
// 确保商品对象有效
|
|
const validGoods = processedGoods.filter(item => {
|
|
// 确保商品对象有必要的字段
|
|
return item && item.id && item.name;
|
|
});
|
|
|
|
// 更新收藏状态
|
|
this.updateGoodsFavoriteStatus(validGoods, false);
|
|
|
|
// 应用筛选条件
|
|
const filteredGoods = this.applyFilters(validGoods, true);
|
|
const groupedGoods = this.groupGoodsForStaggeredLayout(filteredGoods);
|
|
|
|
this.setData({
|
|
goods: validGoods,
|
|
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);
|
|
|
|
// 过滤商品:广告位不受影响,只显示有价格的商品
|
|
filtered = filtered.filter(item => {
|
|
// 广告位不受影响
|
|
if (item.isAd) {
|
|
return true;
|
|
}
|
|
// 只显示有价格的商品
|
|
return item.price && item.price !== '';
|
|
});
|
|
|
|
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.advancedFilters.region !== 'all') {
|
|
const selectedRegion = this.data.advancedFilters.region
|
|
filtered = filtered.filter(item => item.isAd || (item.region && item.region.includes(selectedRegion)))
|
|
}
|
|
// 同时支持原有的地区筛选(为了向后兼容)
|
|
else if (this.data.selectedRegion !== '全国') {
|
|
const selectedRegion = this.data.selectedRegion
|
|
filtered = filtered.filter(item => item.isAd || (item.region && item.region.includes(selectedRegion)))
|
|
}
|
|
|
|
// 应用高级筛选中的蛋壳颜色筛选(仅当selectedCategory为"全部"时)
|
|
if (this.data.advancedFilters.shellColor !== 'all' && this.data.selectedCategory === '全部') {
|
|
const selectedShellColor = this.data.advancedFilters.shellColor
|
|
filtered = filtered.filter(item => item.isAd || (item.shellColor && item.shellColor.includes(selectedShellColor)))
|
|
}
|
|
|
|
// 应用高级筛选中的蛋黄类型筛选
|
|
if (this.data.advancedFilters.yolkType !== 'all') {
|
|
const selectedYolkType = this.data.advancedFilters.yolkType
|
|
filtered = filtered.filter(item => item.isAd || (item.yolk && item.yolk.includes(selectedYolkType)))
|
|
}
|
|
|
|
// 应用高级筛选中的规格筛选
|
|
if (this.data.advancedFilters.specification !== 'all') {
|
|
const selectedSpecification = this.data.advancedFilters.specification
|
|
filtered = filtered.filter(item => item.isAd || ((item.specification || item.spec) && (item.specification || item.spec).includes(selectedSpecification)))
|
|
}
|
|
|
|
// 应用高级筛选中的商品名称筛选
|
|
if (this.data.advancedFilters.productName !== 'all') {
|
|
const selectedProductName = this.data.advancedFilters.productName
|
|
filtered = filtered.filter(item => item.isAd || (item.productName && item.productName.includes(selectedProductName)))
|
|
}
|
|
|
|
// 去重处理 - 确保返回的商品列表中没有重复的商品
|
|
const uniqueGoodsMap = new Map();
|
|
filtered.forEach(item => {
|
|
// 使用id作为唯一标识,如果id不存在则使用productId
|
|
const uniqueId = item.productId || item.id;
|
|
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) {
|
|
// First, separate items by status
|
|
const publishedItems = []
|
|
const soldOutItems = []
|
|
|
|
filtered.forEach(item => {
|
|
const status = (item.status || '').toLowerCase()
|
|
if (status === 'published') {
|
|
publishedItems.push(item)
|
|
} else if (status === 'sold_out') {
|
|
soldOutItems.push(item)
|
|
}
|
|
})
|
|
|
|
// Define the sorting function: only by createdAt descending order
|
|
const sortByCreatedAtDesc = (a, b) => {
|
|
const createdAtA = new Date(a.createdAt || 0).getTime()
|
|
const createdAtB = new Date(b.createdAt || 0).getTime()
|
|
return createdAtB - createdAtA
|
|
}
|
|
|
|
// Sort each group by createdAt descending
|
|
publishedItems.sort(sortByCreatedAtDesc)
|
|
soldOutItems.sort(sortByCreatedAtDesc)
|
|
|
|
// Combine groups: all published first, then all sold_out
|
|
filtered = [...publishedItems, ...soldOutItems]
|
|
}
|
|
|
|
// 统一字段长度
|
|
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);
|
|
|
|
// 记录用户搜索行为
|
|
API.addUserTrace({
|
|
action: 'search',
|
|
keyword: this.data.searchKeyword,
|
|
region: this.data.selectedRegion
|
|
}).catch(err => {
|
|
console.error('记录搜索踪迹失败:', err);
|
|
});
|
|
|
|
// 先对本地已加载的商品进行过滤
|
|
const filteredGoods = this.applyFilters(this.data.goods, true);
|
|
|
|
console.log('searchGoods - 本地过滤结果数量:', filteredGoods.length);
|
|
|
|
// 如果本地过滤结果不足,或者没有匹配的商品,则重新加载数据
|
|
if (filteredGoods.length < 3 && this.data.searchKeyword) {
|
|
console.log('searchGoods - 本地过滤结果不足3个,将重新加载数据');
|
|
// 调用下拉刷新函数实现自动刷新
|
|
this.onRefresh();
|
|
} 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;
|
|
}
|
|
|
|
// 防抖处理:取消之前的定时器
|
|
if (this.data.filterDebounceTimer) {
|
|
clearTimeout(this.data.filterDebounceTimer);
|
|
}
|
|
|
|
// 设置新的定时器,延迟500ms执行刷新
|
|
this.data.filterDebounceTimer = setTimeout(() => {
|
|
// 调用下拉刷新函数实现自动刷新
|
|
this.onRefresh();
|
|
}, 500);
|
|
},
|
|
|
|
// 选择品种 - 使用下拉刷新机制,添加防抖
|
|
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: '', // 清除搜索关键词,筛选框优先级更高
|
|
});
|
|
|
|
// 如果选择的不是"全部"分类,则同步更新高级筛选中的蛋壳颜色
|
|
if (category !== '全部') {
|
|
this.setData({
|
|
'advancedFilters.shellColor': category
|
|
});
|
|
}
|
|
// 如果选择的是"全部"分类,则重置高级筛选条件
|
|
else {
|
|
this.resetAdvancedFilter();
|
|
}
|
|
|
|
// 防抖处理:取消之前的定时器
|
|
if (this.data.filterDebounceTimer) {
|
|
clearTimeout(this.data.filterDebounceTimer);
|
|
}
|
|
|
|
// 设置新的定时器,延迟500ms执行刷新
|
|
this.data.filterDebounceTimer = setTimeout(() => {
|
|
// 调用下拉刷新函数实现自动刷新
|
|
this.onRefresh();
|
|
}, 500);
|
|
},
|
|
|
|
// 打开高级筛选
|
|
openAdvancedFilter: function () {
|
|
this.setData({
|
|
showAdvancedFilter: true
|
|
});
|
|
},
|
|
|
|
// 关闭高级筛选
|
|
closeAdvancedFilter: function () {
|
|
this.setData({
|
|
showAdvancedFilter: false
|
|
});
|
|
},
|
|
|
|
// 选择筛选分类(左侧)
|
|
selectFilterCategory: function (e) {
|
|
const category = e.currentTarget.dataset.category;
|
|
this.setData({
|
|
selectedFilterCategory: category
|
|
});
|
|
},
|
|
|
|
// 应用高级筛选
|
|
applyAdvancedFilter: function () {
|
|
// 这里可以实现应用筛选条件的逻辑
|
|
console.log('应用高级筛选条件:', this.data.advancedFilters);
|
|
|
|
// 关闭侧边栏
|
|
this.setData({
|
|
showAdvancedFilter: false
|
|
});
|
|
|
|
// 刷新商品列表
|
|
this.onRefresh();
|
|
},
|
|
|
|
// 重置高级筛选
|
|
resetAdvancedFilter: function () {
|
|
this.setData({
|
|
advancedFilters: {
|
|
shellColor: 'all',
|
|
yolkType: 'all',
|
|
specification: 'all',
|
|
productName: 'all',
|
|
region: 'all',
|
|
priceRange: [0, 100],
|
|
sortBy: 'default'
|
|
}
|
|
});
|
|
},
|
|
|
|
// 选择蛋壳颜色并关闭下拉菜单
|
|
selectShellColor: function (e) {
|
|
const shellColor = e.currentTarget.dataset.color;
|
|
|
|
// 如果选择的不是"全部颜色",则同步更新品种筛选区域的选择
|
|
if (shellColor !== 'all') {
|
|
this.setData({
|
|
selectedCategory: shellColor,
|
|
'advancedFilters.shellColor': shellColor,
|
|
showShellColorPicker: false
|
|
});
|
|
}
|
|
// 如果选择的是"全部颜色",则只更新高级筛选中的蛋壳颜色
|
|
else {
|
|
this.setData({
|
|
'advancedFilters.shellColor': shellColor,
|
|
showShellColorPicker: false
|
|
});
|
|
}
|
|
},
|
|
|
|
// 选择蛋黄类型并关闭下拉菜单
|
|
selectYolkType: function (e) {
|
|
const yolkType = e.currentTarget.dataset.yolk;
|
|
this.setData({
|
|
'advancedFilters.yolkType': yolkType,
|
|
showYolkTypePicker: false
|
|
});
|
|
},
|
|
|
|
// 选择规格并关闭下拉菜单
|
|
selectSpecification: function (e) {
|
|
const specification = e.currentTarget.dataset.spec;
|
|
this.setData({
|
|
'advancedFilters.specification': specification,
|
|
showSpecificationPicker: false
|
|
});
|
|
},
|
|
|
|
// 选择商品名称并关闭下拉菜单
|
|
selectProductName: function (e) {
|
|
const productName = e.currentTarget.dataset.name;
|
|
this.setData({
|
|
'advancedFilters.productName': productName,
|
|
showProductNamePicker: false
|
|
});
|
|
},
|
|
|
|
// 选择地区并关闭下拉菜单
|
|
selectAdvancedRegion: function (e) {
|
|
const region = e.currentTarget.dataset.region;
|
|
this.setData({
|
|
'advancedFilters.region': region,
|
|
showAdvancedRegionPicker: false
|
|
});
|
|
},
|
|
|
|
// 选择价格范围
|
|
selectPriceRange: function (e) {
|
|
const priceRange = e.currentTarget.dataset.price;
|
|
this.setData({
|
|
'advancedFilters.priceRange': priceRange
|
|
});
|
|
},
|
|
|
|
// 选择排序方式
|
|
selectSortBy: function (e) {
|
|
const sortBy = e.currentTarget.dataset.sort;
|
|
this.setData({
|
|
'advancedFilters.sortBy': sortBy
|
|
});
|
|
},
|
|
|
|
// 切换蛋壳颜色下拉菜单
|
|
toggleShellColorPicker: function () {
|
|
this.setData({
|
|
showShellColorPicker: !this.data.showShellColorPicker,
|
|
showYolkTypePicker: false,
|
|
showSpecificationPicker: false,
|
|
showProductNamePicker: false,
|
|
showAdvancedRegionPicker: false
|
|
});
|
|
},
|
|
|
|
// 切换蛋黄类型下拉菜单
|
|
toggleYolkTypePicker: function () {
|
|
this.setData({
|
|
showYolkTypePicker: !this.data.showYolkTypePicker,
|
|
showShellColorPicker: false,
|
|
showSpecificationPicker: false,
|
|
showProductNamePicker: false,
|
|
showAdvancedRegionPicker: false
|
|
});
|
|
},
|
|
|
|
// 切换规格下拉菜单
|
|
toggleSpecificationPicker: function () {
|
|
this.setData({
|
|
showSpecificationPicker: !this.data.showSpecificationPicker,
|
|
showShellColorPicker: false,
|
|
showYolkTypePicker: false,
|
|
showProductNamePicker: false,
|
|
showAdvancedRegionPicker: false
|
|
});
|
|
},
|
|
|
|
// 切换商品名称下拉菜单
|
|
toggleProductNamePicker: function () {
|
|
this.setData({
|
|
showProductNamePicker: !this.data.showProductNamePicker,
|
|
showShellColorPicker: false,
|
|
showYolkTypePicker: false,
|
|
showSpecificationPicker: false,
|
|
showAdvancedRegionPicker: false
|
|
});
|
|
},
|
|
|
|
// 切换地区下拉菜单
|
|
toggleRegionPicker: function () {
|
|
this.setData({
|
|
showAdvancedRegionPicker: !this.data.showAdvancedRegionPicker,
|
|
showShellColorPicker: false,
|
|
showYolkTypePicker: false,
|
|
showSpecificationPicker: false,
|
|
showProductNamePicker: false
|
|
});
|
|
},
|
|
|
|
// 查看商品详情
|
|
viewGoodsDetail: function (e) {
|
|
const item = e.currentTarget.dataset.item
|
|
|
|
// 确保productId存在,优先使用id,其次使用productId
|
|
const productId = String(item.productId || item.id || '')
|
|
|
|
if (!productId) {
|
|
console.error('商品ID不存在,无法查看详情');
|
|
wx.showToast({
|
|
title: '商品信息有误',
|
|
icon: 'none',
|
|
duration: 2000
|
|
})
|
|
return;
|
|
}
|
|
|
|
// 记录已浏览商品
|
|
this.markGoodsAsViewed(productId);
|
|
|
|
// 记录用户轨迹
|
|
API.addUserTrace(item).catch(err => {
|
|
console.error('记录用户踪迹失败:', err);
|
|
});
|
|
|
|
// 将完整的商品数据传递给详情页,包含联系人信息,与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.navigateTo({
|
|
url: '/pages/settlement/index'
|
|
})
|
|
},
|
|
|
|
// 跳转到招商合作页面
|
|
navigateToCooperation: function () {
|
|
this.setData({
|
|
sidebarBtnHidden: true
|
|
});
|
|
wx.navigateTo({
|
|
url: '/pages/cooperation/index'
|
|
})
|
|
},
|
|
|
|
// 跳转到公司页面
|
|
navigateToCompany: function () {
|
|
this.setData({
|
|
sidebarBtnHidden: true
|
|
});
|
|
wx.navigateTo({
|
|
url: '/pages/company/company'
|
|
})
|
|
},
|
|
|
|
// 显示添加桌面引导框
|
|
showAddToHomeModal: function () {
|
|
this.setData({
|
|
showAddToHomeModal: true
|
|
});
|
|
},
|
|
|
|
// 隐藏添加桌面引导框
|
|
hideAddToHomeModal: function () {
|
|
this.setData({
|
|
showAddToHomeModal: false
|
|
});
|
|
},
|
|
|
|
// 阻止事件冒泡和默认行为,防止iOS下拉拖动
|
|
stopPropagation: function (e) {
|
|
// 安全检查,防止TypeError
|
|
if (e) {
|
|
if (typeof e.stopPropagation === 'function') {
|
|
e.stopPropagation();
|
|
}
|
|
if (typeof e.preventDefault === 'function') {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
},
|
|
|
|
// iOS专用的触摸事件处理,防止下拉拖动
|
|
handleTouchMove: function (e) {
|
|
// 在iOS上,当scrollTop为0时,阻止下拉拖动
|
|
const scrollTop = e.detail.scrollTop || 0;
|
|
if (scrollTop === 0) {
|
|
// 阻止默认行为,防止iOS弹性滚动
|
|
if (e && typeof e.preventDefault === 'function') {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
},
|
|
|
|
// 跳转到货源管理页面
|
|
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 headerHideThreshold = 20;
|
|
|
|
const lastScrollTop = this.data.lastScrollTop || 0;
|
|
const isScrollingDown = scrollTop > lastScrollTop;
|
|
const scrollDelta = Math.abs(scrollTop - lastScrollTop);
|
|
|
|
let needUpdate = false;
|
|
const updates = {
|
|
lastScrollTop: scrollTop
|
|
};
|
|
|
|
// 广告轮播图和功能按钮的显示/隐藏逻辑
|
|
if (scrollDelta > this.data.scrollThreshold) {
|
|
if (isScrollingDown && scrollTop > headerHideThreshold) {
|
|
// 下滑时隐藏广告轮播图和功能按钮
|
|
updates.headerElementsHidden = true;
|
|
needUpdate = true;
|
|
} else if (!isScrollingDown) {
|
|
// 上滑时显示广告轮播图和功能按钮
|
|
updates.headerElementsHidden = false;
|
|
needUpdate = true;
|
|
}
|
|
}
|
|
|
|
// 动态调整已通过CSS过渡和动态类实现,此处不再需要直接操作DOM
|
|
|
|
// 搜索框始终固定显示,不做隐藏处理
|
|
|
|
// 检查视频是否在视口内并控制播放
|
|
this.checkVideosInViewport();
|
|
|
|
// 侧边栏按钮显示逻辑
|
|
// 下滑时隐藏按钮(除非用户未手动隐藏过且在顶部)
|
|
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, true);
|
|
this.setData({
|
|
filteredGoods: filteredGoods,
|
|
});
|
|
});
|
|
}
|
|
})
|
|
.catch(err => {
|
|
console.error('获取收藏状态失败:', err);
|
|
});
|
|
},
|
|
|
|
// 回到顶部
|
|
scrollToTop: function () {
|
|
// 使用数据绑定的方式回到顶部,这是小程序中最可靠的方式
|
|
this.setData({
|
|
scrollTop: 0,
|
|
searchSectionVisible: true,
|
|
showBackToTop: false
|
|
});
|
|
|
|
// 作为备选方案,确保滚动到顶部
|
|
const scrollView = this.selectComponent('#goodsScrollView');
|
|
if (scrollView) {
|
|
// 添加微小延迟确保数据绑定生效后再调用scrollTo
|
|
setTimeout(() => {
|
|
scrollView.scrollTo({
|
|
scrollTop: 0,
|
|
duration: 300
|
|
});
|
|
}, 50);
|
|
}
|
|
},
|
|
|
|
// 检查视频是否在视口内并控制播放
|
|
checkVideosInViewport: function () {
|
|
const filteredGoods = this.data.filteredGoods;
|
|
if (!filteredGoods || filteredGoods.length === 0) return;
|
|
|
|
const query = wx.createSelectorQuery();
|
|
|
|
// 为每个视频元素创建单独的查询
|
|
filteredGoods.forEach((item, index) => {
|
|
if (item.mediaItems && item.mediaItems.length > 0 && item.mediaItems[0].type === 'video') {
|
|
query.select(`#video-${item.id}`).boundingClientRect();
|
|
}
|
|
});
|
|
|
|
query.exec((res) => {
|
|
if (!res) return;
|
|
|
|
const viewportHeight = wx.getSystemInfoSync().windowHeight;
|
|
const videoPlaybackStates = {};
|
|
const visibleVideos = [];
|
|
let videoIndex = 0;
|
|
|
|
// 遍历所有商品,收集可见的视频
|
|
filteredGoods.forEach((item, itemIndex) => {
|
|
if (!item.mediaItems || !item.mediaItems.length || item.mediaItems[0].type !== 'video') {
|
|
return;
|
|
}
|
|
|
|
// 获取对应的视频矩形
|
|
const videoRect = res[videoIndex];
|
|
videoIndex++;
|
|
|
|
if (!videoRect) return;
|
|
|
|
const videoId = `video-${item.id}`;
|
|
|
|
// 计算视频在视口中的可见比例
|
|
const videoHeight = videoRect.height;
|
|
const visibleTop = Math.max(0, videoRect.top);
|
|
const visibleBottom = Math.min(viewportHeight, videoRect.bottom);
|
|
const visibleHeight = Math.max(0, visibleBottom - visibleTop);
|
|
const visibilityRatio = visibleHeight / videoHeight;
|
|
|
|
// 检查视频是否在视口内
|
|
const isInViewport = visibilityRatio > 0.25;
|
|
|
|
if (isInViewport) {
|
|
visibleVideos.push({ videoId, visibilityRatio });
|
|
}
|
|
|
|
videoPlaybackStates[videoId] = isInViewport;
|
|
});
|
|
|
|
// 按可见比例排序,选择前2个视频播放
|
|
visibleVideos.sort((a, b) => b.visibilityRatio - a.visibilityRatio);
|
|
const videosToPlay = visibleVideos.slice(0, 2).map(v => v.videoId);
|
|
|
|
// 控制视频播放/暂停
|
|
filteredGoods.forEach((item, itemIndex) => {
|
|
if (!item.mediaItems || !item.mediaItems.length || item.mediaItems[0].type !== 'video') {
|
|
return;
|
|
}
|
|
|
|
const videoId = `video-${item.id}`;
|
|
const videoContext = wx.createVideoContext(videoId);
|
|
|
|
if (videosToPlay.includes(videoId)) {
|
|
videoContext.play();
|
|
} else {
|
|
videoContext.pause();
|
|
}
|
|
});
|
|
|
|
// 更新视频播放状态
|
|
this.setData({
|
|
videoPlaybackStates: videoPlaybackStates
|
|
});
|
|
});
|
|
},
|
|
|
|
// 图片加载完成事件
|
|
onImageLoad(e) {
|
|
console.log('图片加载完成:', e);
|
|
// 可以在这里添加图片加载完成后的处理逻辑,比如统计加载时间、显示动画等
|
|
},
|
|
|
|
// 上拉加载更多
|
|
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: this.data.shareImageUrl
|
|
}
|
|
},
|
|
|
|
// 分享到朋友圈
|
|
onShareTimeline() {
|
|
return {
|
|
title: '专业的鸡蛋批发平台',
|
|
query: '',
|
|
imageUrl: this.data.shareImageUrl
|
|
}
|
|
},
|
|
|
|
// 处理手机号授权
|
|
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://my-supplier-photos.oss-cn-chengdu.aliyuncs.com/products/%E6%B5%B7%E8%93%9D%E7%81%B0/image/7a2a8a17a83ba4d3d4270828531e2041.jpeg',
|
|
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()
|
|
console.log('验证登录状态响应:', validateRes)
|
|
console.log('验证登录状态响应完整数据:', JSON.stringify(validateRes))
|
|
|
|
if (validateRes.success && validateRes.userInfo) {
|
|
// 服务器返回了用户信息,同步到本地
|
|
const app = getApp()
|
|
const userInfo = validateRes.userInfo
|
|
console.log('登录时服务器返回的用户信息:', JSON.stringify(userInfo))
|
|
console.log('登录时是否包含idcardstatus:', 'idcardstatus' in userInfo)
|
|
console.log('登录时是否包含reason:', 'reason' in 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://my-supplier-photos.oss-cn-chengdu.aliyuncs.com/products/%E6%B5%B7%E8%93%9D%E7%81%B0/image/7a2a8a17a83ba4d3d4270828531e2041.jpeg',
|
|
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' });
|
|
}
|
|
});
|
|
},
|
|
|
|
|
|
})
|
|
|