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.
1945 lines
60 KiB
1945 lines
60 KiB
// pages/index/index.js
|
|
const API = require('../../utils/api.js');
|
|
Page({
|
|
data: {
|
|
currentUserType: null,
|
|
showUserInfoForm: false,
|
|
avatarUrl: 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0',
|
|
name: '',
|
|
showAuthModal: false,
|
|
showOneKeyLoginModal: false,
|
|
userInfo: {},
|
|
needPhoneAuth: false,
|
|
testMode: true,
|
|
partnerstatus: '',
|
|
|
|
// 侧边栏相关
|
|
showSidebar: false,
|
|
isDragging: false,
|
|
startY: 0,
|
|
currentY: 0,
|
|
sidebarBtnTop: 500,
|
|
sidebarBtnHidden: false,
|
|
|
|
// 搜索区域相关
|
|
searchSectionVisible: true,
|
|
lastScrollTop: 0,
|
|
isScrollLocked: false,
|
|
searchSectionOpacity: 1,
|
|
searchSectionTransform: 0,
|
|
isSearchBarFullyHidden: false,
|
|
|
|
// 回到顶部按钮
|
|
showBackToTop: false,
|
|
scrollTop: 0,
|
|
|
|
// 搜索相关
|
|
searchKeyword: '',
|
|
selectedRegion: '全国',
|
|
showRegionPicker: false,
|
|
regions: ['全国', '北京', '上海', '广州', '深圳', '天津', '重庆', '河北', '山西', '辽宁', '吉林', '黑龙江', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南', '湖北', '湖南', '广东', '海南', '四川', '贵州', '云南', '陕西', '甘肃', '青海', '台湾', '内蒙古', '广西', '西藏', '宁夏', '新疆', '香港', '澳门'],
|
|
|
|
// 商品相关 - 淘宝风格
|
|
goods: [],
|
|
filteredGoods: [],
|
|
leftColumnGoods: [],
|
|
rightColumnGoods: [],
|
|
selectedCategory: '全部',
|
|
loadingMore: false,
|
|
hasMoreData: true,
|
|
page: 1,
|
|
pageSize: 12,
|
|
isRefreshing: false,
|
|
isLoading: true,
|
|
|
|
// 图片预览相关状态
|
|
previewImageUrls: [],
|
|
previewImageIndex: 0,
|
|
showImagePreview: false,
|
|
},
|
|
|
|
// 跳转到聊天页面
|
|
navigateToChat() {
|
|
wx.navigateTo({
|
|
url: '/pages/chat/index',
|
|
success: function() {
|
|
console.log('成功跳转到聊天页面');
|
|
},
|
|
fail: function(error) {
|
|
console.error('跳转到聊天页面失败:', error);
|
|
wx.showToast({
|
|
title: '跳转失败,请稍后重试',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
// 跳转到客服列表页面
|
|
navigateToCustomerService(e) {
|
|
// 获取按钮类型(seller=我要买蛋,buyer=我要卖蛋)
|
|
const userType = e.currentTarget.dataset.type;
|
|
wx.navigateTo({
|
|
url: `/pages/customer-service/index?type=${userType}`,
|
|
success: function() {
|
|
console.log('成功跳转到客服列表页面,类型:', userType);
|
|
},
|
|
fail: function(error) {
|
|
console.error('跳转到客服列表页面失败:', error);
|
|
wx.showToast({
|
|
title: '跳转失败,请稍后重试',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
// 跳转到我要卖蛋页面
|
|
navigateToSettlement() {
|
|
wx.navigateTo({
|
|
url: '/pages/settlement/index',
|
|
success: function() {
|
|
console.log('成功跳转到我要卖蛋页面');
|
|
},
|
|
fail: function(error) {
|
|
console.error('跳转到我要卖蛋页面失败:', error);
|
|
wx.showToast({
|
|
title: '跳转失败,请稍后重试',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
// 切换侧边栏显示
|
|
toggleSidebar() {
|
|
if (this.data.isDragging) {
|
|
return;
|
|
}
|
|
this.setData({
|
|
showSidebar: !this.data.showSidebar
|
|
});
|
|
},
|
|
|
|
// 切换按钮显示/隐藏到侧边栏
|
|
toggleSidebarBtn() {
|
|
this.setData({
|
|
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;
|
|
|
|
// 如果移动距离超过20px,视为拖动
|
|
if (Math.abs(diffY) > 20) {
|
|
this.setData({
|
|
isDragging: true
|
|
});
|
|
|
|
// 更新按钮位置
|
|
let newTop = this.data.sidebarBtnTop + diffY * 2; // 转换为rpx
|
|
|
|
// 限制按钮在屏幕范围内
|
|
const screenHeight = wx.getSystemInfoSync().screenHeight * 2; // 转换为rpx
|
|
const btnHeight = 180; // 按钮高度,单位rpx
|
|
const boundary = 300; // 边界值,限制拖动范围
|
|
|
|
if (newTop < boundary) {
|
|
newTop = boundary;
|
|
} else if (newTop > screenHeight - btnHeight - boundary) {
|
|
newTop = screenHeight - btnHeight - boundary;
|
|
}
|
|
|
|
this.setData({
|
|
sidebarBtnTop: newTop,
|
|
startY: moveY
|
|
});
|
|
}
|
|
},
|
|
|
|
// 触摸结束事件
|
|
handleTouchEnd(e) {
|
|
// 如果拖动距离超过屏幕高度的1/3,隐藏按钮
|
|
const moveY = e.changedTouches[0].clientY;
|
|
const diffY = moveY - this.data.startY;
|
|
const screenHeight = wx.getSystemInfoSync().screenHeight * 2; // 转换为rpx
|
|
|
|
if (Math.abs(diffY) > screenHeight / 3) {
|
|
this.setData({
|
|
sidebarBtnTop: -100 // 隐藏按钮
|
|
});
|
|
wx.setStorageSync('sidebarBtnTop', -100);
|
|
wx.setStorageSync('sidebarBtnHidden', true);
|
|
} else {
|
|
wx.setStorageSync('sidebarBtnTop', this.data.sidebarBtnTop);
|
|
wx.setStorageSync('sidebarBtnHidden', false);
|
|
}
|
|
|
|
this.setData({
|
|
isDragging: false
|
|
});
|
|
},
|
|
|
|
onLoad() {
|
|
console.log('首页初始化')
|
|
const savedBtnTop = wx.getStorageSync('sidebarBtnTop');
|
|
const savedBtnHidden = wx.getStorageSync('sidebarBtnHidden');
|
|
if (savedBtnTop !== '') {
|
|
this.setData({
|
|
sidebarBtnTop: savedBtnTop,
|
|
sidebarBtnHidden: savedBtnHidden || false
|
|
});
|
|
}
|
|
this.checkAndRestoreLoginStatus()
|
|
this.loadGoods()
|
|
},
|
|
|
|
onShow: function () {
|
|
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
|
this.getTabBar().setData({
|
|
selected: 0
|
|
});
|
|
}
|
|
const app = getApp();
|
|
app.updateCurrentTab('index');
|
|
app.globalData.showTabBar = true;
|
|
this.checkAndRestoreLoginStatus()
|
|
},
|
|
|
|
onPullDownRefresh: function() {
|
|
this.onRefresh()
|
|
},
|
|
|
|
onRestore: function() {
|
|
this.setData({
|
|
isRefreshing: false
|
|
})
|
|
},
|
|
|
|
onRefresh: function() {
|
|
if (this.data.isRefreshing) {
|
|
return
|
|
}
|
|
|
|
this.setData({
|
|
isRefreshing: true,
|
|
page: 1,
|
|
hasMoreData: true,
|
|
goods: [],
|
|
filteredGoods: [],
|
|
leftColumnGoods: [],
|
|
rightColumnGoods: []
|
|
})
|
|
|
|
const timestamp = new Date().getTime();
|
|
|
|
API.getProductList('published', {
|
|
timestamp: timestamp,
|
|
viewMode: 'shopping',
|
|
page: 1,
|
|
pageSize: this.data.pageSize,
|
|
keyword: this.data.searchKeyword,
|
|
category: this.data.selectedCategory === '全部' ? '' : this.data.selectedCategory
|
|
})
|
|
.then(res => {
|
|
this.setData({ isRefreshing: false })
|
|
|
|
if (res.success && res.products) {
|
|
this.processGoodsData(res.products, false)
|
|
wx.showToast({
|
|
title: '刷新成功',
|
|
icon: 'success',
|
|
duration: 1500
|
|
})
|
|
} else {
|
|
this.setData({
|
|
isLoading: false,
|
|
loadingMore: false
|
|
})
|
|
}
|
|
})
|
|
.catch(err => {
|
|
this.setData({ isRefreshing: false })
|
|
console.error('刷新商品数据失败:', err)
|
|
wx.showToast({
|
|
title: '刷新失败,请稍后重试',
|
|
icon: 'none',
|
|
duration: 2000
|
|
})
|
|
})
|
|
},
|
|
|
|
onReachBottom: function() {
|
|
this.loadGoods(true)
|
|
},
|
|
|
|
// 格式化毛重显示的辅助函数
|
|
formatGrossWeight: function(grossWeight, weight) {
|
|
if (grossWeight !== null && grossWeight !== undefined && grossWeight !== '') {
|
|
return grossWeight;
|
|
}
|
|
if (weight !== null && weight !== undefined && weight !== '') {
|
|
return weight;
|
|
}
|
|
return "";
|
|
},
|
|
|
|
// 提取地区中的省份信息
|
|
extractProvince: function(region) {
|
|
if (!region || typeof region !== 'string') {
|
|
return region;
|
|
}
|
|
|
|
const provinceEndIndex = region.indexOf('省');
|
|
const autonomousRegionEndIndex = region.indexOf('自治区');
|
|
const municipalityEndIndex = region.indexOf('市');
|
|
const specialRegionEndIndex = region.indexOf('特别行政区');
|
|
|
|
if (provinceEndIndex !== -1) {
|
|
return region.substring(0, provinceEndIndex + 1);
|
|
} else if (autonomousRegionEndIndex !== -1) {
|
|
return region.substring(0, autonomousRegionEndIndex + 3);
|
|
} else if (specialRegionEndIndex !== -1) {
|
|
return region.substring(0, specialRegionEndIndex + 5);
|
|
} else if (municipalityEndIndex === 2) {
|
|
return region.substring(0, municipalityEndIndex + 1);
|
|
}
|
|
|
|
return region;
|
|
},
|
|
|
|
// 处理商品数据 - 淘宝风格
|
|
processGoodsData: function(products, isLoadMore = false) {
|
|
let newGoods = products.map(product => {
|
|
const imageUrls = product.imageUrls || product.images || [];
|
|
const formattedImageUrls = Array.isArray(imageUrls) ? imageUrls : [imageUrls];
|
|
|
|
return {
|
|
...product,
|
|
fullRegion: product.region || '',
|
|
region: product.region ? this.extractProvince(product.region) : '',
|
|
grossWeight: product.grossWeight || product.weight || '',
|
|
displayGrossWeight: product.grossWeight || product.weight || '',
|
|
status: product.status || 'published',
|
|
createdAt: product.created_at || product.createTime || null,
|
|
reservedCount: product.reservedCount || product.selected || 0,
|
|
reservedCountDisplay: product.reservedCount || product.selected || 0,
|
|
sales: product.sales || product.reservedCount || Math.floor(Math.random() * 1000) + 100,
|
|
product_contact: product.product_contact || '',
|
|
contact_phone: product.contact_phone || '',
|
|
supplyStatus: product.supplyStatus || '',
|
|
sourceType: product.sourceType || '',
|
|
negotiateStatus: '可议价',
|
|
isReserved: false,
|
|
isFavorite: false,
|
|
currentImageIndex: 0,
|
|
imageUrls: formattedImageUrls
|
|
}
|
|
})
|
|
|
|
newGoods = newGoods.filter(item => (item.status || '').toLowerCase() !== 'hidden')
|
|
|
|
// 刷新和首次加载都插入广告
|
|
if ((!isLoadMore || this.data.page === 1) && newGoods.length > 0) {
|
|
newGoods = this.insertAdSlots(newGoods)
|
|
}
|
|
|
|
let updatedGoods = []
|
|
if (isLoadMore) {
|
|
const existingIds = new Set(this.data.goods.map(item => item.id));
|
|
const uniqueNewGoods = newGoods.filter(item => !existingIds.has(item.id));
|
|
updatedGoods = [...this.data.goods, ...uniqueNewGoods];
|
|
} else {
|
|
updatedGoods = newGoods
|
|
}
|
|
|
|
const filteredGoods = this.applyFilters(updatedGoods)
|
|
console.log('filteredGoods 数量:', filteredGoods.length)
|
|
console.log('filteredGoods[0]:', filteredGoods[0])
|
|
console.log('filteredGoods[1]:', filteredGoods[1])
|
|
|
|
const { leftColumnGoods, rightColumnGoods } = this.distributeToColumns(filteredGoods)
|
|
console.log('leftColumnGoods 数量:', leftColumnGoods.length)
|
|
console.log('rightColumnGoods 数量:', rightColumnGoods.length)
|
|
console.log('leftColumnGoods[0]:', leftColumnGoods[0])
|
|
console.log('rightColumnGoods[0]:', rightColumnGoods[0])
|
|
|
|
this.setData({
|
|
goods: updatedGoods,
|
|
filteredGoods: filteredGoods,
|
|
leftColumnGoods: leftColumnGoods,
|
|
rightColumnGoods: rightColumnGoods,
|
|
loadingMore: false,
|
|
isLoading: false,
|
|
page: this.data.page + 1
|
|
})
|
|
},
|
|
|
|
// 插入广告位数据
|
|
insertAdSlots: function(goods) {
|
|
if (!goods || goods.length === 0) return goods
|
|
|
|
console.log('insertAdSlots 被调用,商品数量:', goods.length)
|
|
|
|
const adSlot1 = {
|
|
id: 'ad_slot_1',
|
|
name: '广告位1',
|
|
imageUrls: ['/images/轮播图1.jpg'],
|
|
price: 0,
|
|
adType: 'full_card',
|
|
isAd: true
|
|
}
|
|
|
|
const adSlot2 = {
|
|
id: 'ad_slot_2',
|
|
name: '广告位2',
|
|
imageUrls: ['/images/轮播图1.jpg'],
|
|
price: 0,
|
|
adType: 'half_image',
|
|
isAd: true
|
|
}
|
|
|
|
const result = [adSlot1, adSlot2, ...goods]
|
|
console.log('插入广告后的商品数量:', result.length)
|
|
console.log('第一个商品:', result[0])
|
|
console.log('第二个商品:', result[1])
|
|
|
|
return result
|
|
},
|
|
|
|
// 广告点击事件处理
|
|
onAdClick: function(e) {
|
|
const adData = e.currentTarget.dataset.ad
|
|
console.log('广告被点击, 广告ID:', adData ? adData.id : 'unknown')
|
|
|
|
if (adData && adData.adType) {
|
|
wx.showToast({
|
|
title: '广告位: ' + adData.adType,
|
|
icon: 'none',
|
|
duration: 2000
|
|
})
|
|
|
|
if (adData.adType === 'full_card') {
|
|
console.log('完整卡片广告被点击')
|
|
} else if (adData.adType === 'half_image') {
|
|
console.log('半高图片广告被点击')
|
|
}
|
|
} else {
|
|
wx.showToast({
|
|
title: '广告加载中',
|
|
icon: 'loading',
|
|
duration: 1500
|
|
})
|
|
}
|
|
},
|
|
|
|
// 加载商品数据 - 淘宝风格优化
|
|
loadGoods: function(isLoadMore = false) {
|
|
if (isLoadMore && !this.data.hasMoreData) {
|
|
return
|
|
}
|
|
|
|
if (isLoadMore) {
|
|
this.setData({ loadingMore: true })
|
|
} else {
|
|
this.setData({ isLoading: true })
|
|
}
|
|
|
|
const timestamp = new Date().getTime();
|
|
const currentPage = isLoadMore ? this.data.page : 1
|
|
|
|
API.getProductList('published', {
|
|
timestamp: timestamp,
|
|
viewMode: 'shopping',
|
|
page: currentPage,
|
|
pageSize: this.data.pageSize,
|
|
keyword: this.data.searchKeyword,
|
|
category: this.data.selectedCategory === '全部' ? '' : this.data.selectedCategory
|
|
})
|
|
.then(res => {
|
|
wx.hideLoading();
|
|
|
|
if (res.success && res.products) {
|
|
const totalGoods = res.total || 0;
|
|
const totalPages = res.totalPages || Math.ceil(totalGoods / this.data.pageSize);
|
|
const hasMoreData = currentPage < totalPages && res.products.length > 0;
|
|
|
|
this.setData({ hasMoreData })
|
|
this.processGoodsData(res.products, isLoadMore)
|
|
} else {
|
|
this.setData({ loadingMore: false, isLoading: false })
|
|
}
|
|
})
|
|
.catch(err => {
|
|
console.error('加载商品数据失败:', err)
|
|
this.setData({ loadingMore: false, isLoading: false })
|
|
})
|
|
},
|
|
|
|
// 刷新商品列表
|
|
refreshGoodsList: function() {
|
|
this.setData({
|
|
page: 1,
|
|
hasMoreData: true,
|
|
goods: [],
|
|
filteredGoods: [],
|
|
loadingMore: false
|
|
}, () => {
|
|
this.loadGoods()
|
|
})
|
|
},
|
|
|
|
// 应用筛选条件
|
|
applyFilters: function(goods) {
|
|
let filtered = [...goods]
|
|
|
|
if (this.data.selectedCategory !== '全部') {
|
|
const category = this.data.selectedCategory
|
|
let keyword = category
|
|
if (category === '粉壳') keyword = '粉'
|
|
else if (category === '绿壳') keyword = '绿'
|
|
else if (category === '红壳') keyword = '红'
|
|
else if (category === '白壳') keyword = '白'
|
|
|
|
filtered = filtered.filter(item => item.isAd || (item.name || '').includes(keyword))
|
|
}
|
|
|
|
if (this.data.searchKeyword) {
|
|
const keyword = this.data.searchKeyword.toLowerCase()
|
|
filtered = filtered.filter(item =>
|
|
item.isAd ||
|
|
(item.name || '').toLowerCase().includes(keyword) ||
|
|
(item.region || '').toLowerCase().includes(keyword)
|
|
)
|
|
}
|
|
|
|
if (this.data.selectedRegion !== '全国') {
|
|
const selectedRegion = this.data.selectedRegion
|
|
filtered = filtered.filter(item => item.isAd || (item.region && item.region.includes(selectedRegion)))
|
|
}
|
|
|
|
filtered.sort((a, b) => {
|
|
const reservedCountA = a.reservedCount || 0
|
|
const reservedCountB = b.reservedCount || 0
|
|
if (reservedCountB !== reservedCountA) return reservedCountB - reservedCountA
|
|
|
|
const priceA = parseFloat(a.price || 0)
|
|
const priceB = parseFloat(b.price || 0)
|
|
if (!isNaN(priceB) && !isNaN(priceA) && priceA !== priceB) return priceA - priceB
|
|
|
|
const createdAtA = new Date(a.createdAt || 0).getTime()
|
|
const createdAtB = new Date(b.createdAt || 0).getTime()
|
|
return createdAtB - createdAtA
|
|
})
|
|
|
|
return filtered
|
|
},
|
|
|
|
// 瀑布流布局 - 淘宝风格左右交替
|
|
distributeToColumns: function(goods) {
|
|
if (!goods || goods.length === 0) {
|
|
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) {
|
|
leftColumn.push({ ...goods[i], isLong: isEvenRow })
|
|
}
|
|
|
|
if (i + 1 < goods.length) {
|
|
rightColumn.push({ ...goods[i + 1], isLong: !isEvenRow })
|
|
}
|
|
}
|
|
|
|
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) {
|
|
this.setData({
|
|
searchKeyword: e.detail.value
|
|
})
|
|
// 实时应用筛选条件
|
|
this.searchGoods()
|
|
},
|
|
|
|
// 搜索商品
|
|
searchGoods: function() {
|
|
// 重新显示tabBar
|
|
const app = getApp();
|
|
if (app && app.globalData) {
|
|
app.globalData.showTabBar = true;
|
|
}
|
|
|
|
const filteredGoods = this.applyFilters(this.data.goods)
|
|
const groupedGoods = this.groupGoodsForStaggeredLayout(filteredGoods)
|
|
const { leftColumnGoods, rightColumnGoods } = this.distributeToColumns(filteredGoods)
|
|
this.setData({
|
|
filteredGoods: filteredGoods,
|
|
groupedGoods: groupedGoods,
|
|
leftColumnGoods: leftColumnGoods,
|
|
rightColumnGoods: rightColumnGoods
|
|
})
|
|
},
|
|
|
|
// 切换地区选择器
|
|
toggleRegionPicker: function() {
|
|
this.setData({
|
|
showRegionPicker: !this.data.showRegionPicker
|
|
})
|
|
},
|
|
|
|
// 选择地区(实时更新)
|
|
selectRegion: function(e) {
|
|
const region = e.currentTarget.dataset.region
|
|
|
|
// 重新显示tabBar
|
|
const app = getApp();
|
|
if (app && app.globalData) {
|
|
app.globalData.showTabBar = true;
|
|
}
|
|
|
|
// 应用筛选条件
|
|
this.setData({
|
|
selectedRegion: region,
|
|
showRegionPicker: false
|
|
}, () => {
|
|
// 如果从局部地区切换到全国地区,重新加载所有商品
|
|
if (region === '全国' && this.data.selectedCategory === '全部' && !this.data.searchKeyword) {
|
|
// 重新加载商品数据
|
|
this.refreshGoodsList();
|
|
} else {
|
|
// 否则仅对本地商品进行筛选
|
|
const filteredGoods = this.applyFilters(this.data.goods)
|
|
const groupedGoods = this.groupGoodsForStaggeredLayout(filteredGoods)
|
|
const { leftColumnGoods, rightColumnGoods } = this.distributeToColumns(filteredGoods)
|
|
this.setData({
|
|
filteredGoods: filteredGoods,
|
|
groupedGoods: groupedGoods,
|
|
leftColumnGoods: leftColumnGoods,
|
|
rightColumnGoods: rightColumnGoods
|
|
})
|
|
}
|
|
})
|
|
},
|
|
|
|
// 阻止事件冒泡
|
|
stopPropagation: function() {
|
|
// 空函数,用于阻止事件冒泡
|
|
},
|
|
|
|
// 选择品种
|
|
selectCategory: function(e) {
|
|
// 重新显示tabBar
|
|
const app = getApp();
|
|
if (app && app.globalData) {
|
|
app.globalData.showTabBar = true;
|
|
}
|
|
|
|
const category = e.currentTarget.dataset.category
|
|
this.setData({
|
|
selectedCategory: category
|
|
})
|
|
|
|
const filteredGoods = this.applyFilters(this.data.goods)
|
|
const groupedGoods = this.groupGoodsForStaggeredLayout(filteredGoods)
|
|
const { leftColumnGoods, rightColumnGoods } = this.distributeToColumns(filteredGoods)
|
|
this.setData({
|
|
filteredGoods: filteredGoods,
|
|
groupedGoods: groupedGoods,
|
|
leftColumnGoods: leftColumnGoods,
|
|
rightColumnGoods: rightColumnGoods
|
|
})
|
|
},
|
|
|
|
// 查看商品详情
|
|
viewGoodsDetail: function(e) {
|
|
const item = e.currentTarget.dataset.item
|
|
// 确保productId存在,优先使用id,其次使用productId
|
|
const productId = String(item.id || item.productId || '')
|
|
|
|
if (!productId) {
|
|
console.error('商品ID不存在,无法查看详情');
|
|
wx.showToast({
|
|
title: '商品信息有误',
|
|
icon: 'none',
|
|
duration: 2000
|
|
})
|
|
return;
|
|
}
|
|
|
|
// 将完整的商品数据传递给详情页,包含联系人信息,与buyer页面保持一致
|
|
wx.navigateTo({
|
|
url: `/pages/goods-detail/goods-detail?goodsData=${encodeURIComponent(JSON.stringify(item))}&productId=${productId}`
|
|
})
|
|
},
|
|
|
|
// 跳转到我要卖蛋页面
|
|
navigateToSettlement: function() {
|
|
wx.navigateTo({
|
|
url: '/pages/settlement/index'
|
|
})
|
|
},
|
|
|
|
// 跳转到招商合作页面
|
|
navigateToCooperation: function() {
|
|
wx.navigateTo({
|
|
url: '/pages/cooperation/index'
|
|
})
|
|
},
|
|
|
|
// 预览图片
|
|
previewImage: function(e) {
|
|
// 阻止事件冒泡,避免触发商品点击事件
|
|
e.stopPropagation();
|
|
|
|
// 获取图片信息
|
|
const item = e.currentTarget.dataset.item;
|
|
const index = e.currentTarget.dataset.index;
|
|
|
|
if (!item) {
|
|
return;
|
|
}
|
|
|
|
// 确保图片URL存在且为数组
|
|
let imageUrls = item.imageUrls || item.images || [];
|
|
if (!Array.isArray(imageUrls)) {
|
|
imageUrls = [imageUrls];
|
|
}
|
|
|
|
// 过滤掉无效的图片URL
|
|
const validImageUrls = imageUrls.filter(url => url && typeof url === 'string' && url.trim() !== '');
|
|
|
|
if (validImageUrls.length === 0) {
|
|
return;
|
|
}
|
|
|
|
// 显示图片预览弹窗
|
|
this.setData({
|
|
previewImageUrls: validImageUrls,
|
|
previewImageIndex: parseInt(index || 0),
|
|
showImagePreview: true
|
|
});
|
|
},
|
|
|
|
// 关闭图片预览
|
|
closeImagePreview: function() {
|
|
this.setData({
|
|
showImagePreview: false
|
|
});
|
|
},
|
|
|
|
// 预览图片切换事件
|
|
onPreviewImageChange: function(e) {
|
|
this.setData({
|
|
previewImageIndex: e.detail.current
|
|
});
|
|
},
|
|
|
|
// 滚动事件处理
|
|
onScroll: function(e) {
|
|
const { scrollTop } = e.detail;
|
|
|
|
let searchSectionOpacity = 1;
|
|
let searchSectionTransform = 0;
|
|
let isSearchBarFullyHidden = false;
|
|
|
|
if (scrollTop > 50) {
|
|
// 当滚动距离超过50rpx时,开始逐渐隐藏
|
|
const hideProgress = Math.min((scrollTop - 50) / 150, 1); // 0-1的隐藏进度,延长隐藏过程
|
|
searchSectionOpacity = 1 - hideProgress;
|
|
searchSectionTransform = -20 * hideProgress;
|
|
|
|
// 当隐藏进度达到100%时,完全隐藏并收缩高度
|
|
if (hideProgress >= 1) {
|
|
isSearchBarFullyHidden = true;
|
|
}
|
|
}
|
|
|
|
// 当滚动回到顶部时,重置所有状态
|
|
if (scrollTop <= 50) {
|
|
searchSectionOpacity = 1;
|
|
searchSectionTransform = 0;
|
|
isSearchBarFullyHidden = false;
|
|
}
|
|
|
|
// 更新搜索框状态和样式
|
|
this.setData({
|
|
searchSectionOpacity: searchSectionOpacity,
|
|
searchSectionTransform: searchSectionTransform,
|
|
isSearchBarFullyHidden: isSearchBarFullyHidden,
|
|
lastScrollTop: scrollTop
|
|
});
|
|
|
|
// 回到顶部按钮显示逻辑
|
|
if (scrollTop > 300 && !this.data.showBackToTop) {
|
|
this.setData({ showBackToTop: true });
|
|
} else if (scrollTop <= 300 && this.data.showBackToTop) {
|
|
this.setData({ showBackToTop: false });
|
|
}
|
|
|
|
const { scrollHeight, clientHeight } = e.detail;
|
|
const distanceToBottom = scrollHeight - scrollTop - clientHeight;
|
|
|
|
const app = getApp();
|
|
if (!app || !app.globalData) {
|
|
return;
|
|
}
|
|
|
|
app.globalData.showTabBar = true;
|
|
},
|
|
|
|
// 回到顶部
|
|
scrollToTop: function() {
|
|
this.setData({
|
|
scrollTop: 0,
|
|
searchSectionVisible: true,
|
|
showBackToTop: false
|
|
});
|
|
},
|
|
|
|
// 上拉加载更多
|
|
onReachBottom: function() {
|
|
if (this.data.hasMoreData && !this.data.loadingMore) {
|
|
this.loadGoods(true)
|
|
}
|
|
},
|
|
|
|
// 显示一键登录弹窗
|
|
showOneKeyLogin() {
|
|
this.setData({
|
|
showAuthModal: false,
|
|
showOneKeyLoginModal: true
|
|
})
|
|
},
|
|
|
|
// 关闭未授权提示弹窗
|
|
closeAuthModal() {
|
|
this.setData({ showAuthModal: false })
|
|
},
|
|
|
|
// 关闭一键登录弹窗
|
|
closeOneKeyLoginModal() {
|
|
this.setData({ showOneKeyLoginModal: false })
|
|
},
|
|
|
|
// 分享给朋友/群聊
|
|
onShareAppMessage() {
|
|
return {
|
|
title: '鸡蛋贸易平台 - 专业的鸡蛋交易小程序',
|
|
path: '/pages/index/index',
|
|
imageUrl: '/images/你有好蛋.png'
|
|
}
|
|
},
|
|
|
|
// 分享到朋友圈
|
|
onShareTimeline() {
|
|
return {
|
|
title: '鸡蛋贸易平台 - 专业的鸡蛋交易小程序',
|
|
query: '',
|
|
imageUrl: '/images/你有好蛋.png'
|
|
}
|
|
},
|
|
|
|
// 处理手机号授权
|
|
async onGetPhoneNumber(e) {
|
|
// 打印详细错误信息,方便调试
|
|
console.log('getPhoneNumber响应:', e.detail)
|
|
|
|
// 关闭手机号授权弹窗
|
|
this.setData({ showOneKeyLoginModal: false })
|
|
|
|
// 用户点击拒绝授权
|
|
if (e.detail.errMsg === 'getPhoneNumber:fail user deny') {
|
|
wx.showToast({
|
|
title: '需要授权手机号才能使用',
|
|
icon: 'none',
|
|
duration: 2000
|
|
})
|
|
return
|
|
}
|
|
|
|
// 处理没有权限的情况
|
|
if (e.detail.errMsg === 'getPhoneNumber:fail no permission') {
|
|
// 如果是测试模式,跳过真实授权流程
|
|
if (this.data.testMode) {
|
|
console.log('进入测试模式,跳过真实手机号授权')
|
|
await this.simulateLoginForTest()
|
|
return
|
|
}
|
|
|
|
wx.showToast({
|
|
title: '当前环境无法获取手机号权限',
|
|
icon: 'none',
|
|
duration: 3000
|
|
})
|
|
// 增加关于微信认证要求的说明
|
|
console.warn('获取手机号权限失败: 请注意,微信小程序获取手机号功能需要满足以下条件:1. 小程序必须完成微信企业认证;2. 需要在小程序后台配置相应权限;3. 必须使用button组件的open-type="getPhoneNumber"触发。')
|
|
return
|
|
}
|
|
|
|
// 检查是否已经登录,避免重复授权
|
|
const existingOpenid = wx.getStorageSync('openid')
|
|
const existingUserId = wx.getStorageSync('userId')
|
|
const existingUserInfo = wx.getStorageSync('userInfo')
|
|
|
|
if (existingOpenid && existingUserId && existingUserInfo && existingUserInfo.phoneNumber) {
|
|
console.log('用户已登录且手机号有效,直接完成身份设置')
|
|
// 直接完成身份设置,跳过重复授权
|
|
const currentUserType = this.data.pendingUserType || this.data.currentUserType || 'buyer'
|
|
this.finishSetUserType(currentUserType)
|
|
return
|
|
}
|
|
|
|
wx.showLoading({
|
|
title: '登录中...',
|
|
mask: true
|
|
})
|
|
|
|
// 引入API服务
|
|
const API = require('../../utils/api.js')
|
|
|
|
try {
|
|
if (e.detail.errMsg === 'getPhoneNumber:ok') {
|
|
// 用户同意授权,实际处理授权流程
|
|
console.log('用户同意授权获取手机号')
|
|
|
|
// 同时请求位置授权
|
|
console.log('同时请求位置授权');
|
|
wx.authorize({
|
|
scope: 'scope.userLocation',
|
|
success() {
|
|
// 位置授权成功,获取用户位置
|
|
wx.getLocation({
|
|
type: 'gcj02',
|
|
success(res) {
|
|
const latitude = res.latitude;
|
|
const longitude = res.longitude;
|
|
console.log('登录时获取位置成功:', { latitude, longitude });
|
|
// 存储位置信息到本地
|
|
wx.setStorageSync('userLocation', { latitude, longitude });
|
|
// 位置获取成功提示
|
|
wx.showToast({
|
|
title: '位置获取成功',
|
|
icon: 'success',
|
|
duration: 1500
|
|
});
|
|
},
|
|
fail() {
|
|
console.error('登录时获取位置失败');
|
|
// 位置获取失败提示
|
|
wx.showToast({
|
|
title: '位置获取失败',
|
|
icon: 'none',
|
|
duration: 1500
|
|
});
|
|
}
|
|
});
|
|
},
|
|
fail() {
|
|
// 位置授权失败,不影响登录流程
|
|
console.log('登录时位置授权被拒绝');
|
|
// 位置授权失败提示
|
|
wx.showToast({
|
|
title: '位置授权已拒绝',
|
|
icon: 'none',
|
|
duration: 1500
|
|
});
|
|
}
|
|
});
|
|
|
|
// 1. 先执行微信登录获取code
|
|
const loginRes = await new Promise((resolve, reject) => {
|
|
wx.login({
|
|
success: resolve,
|
|
fail: reject
|
|
})
|
|
})
|
|
|
|
if (!loginRes.code) {
|
|
throw new Error('获取登录code失败')
|
|
}
|
|
|
|
console.log('获取登录code成功:', loginRes.code)
|
|
|
|
// 2. 使用code换取openid
|
|
const openidRes = await API.getOpenid(loginRes.code)
|
|
|
|
// 改进错误处理逻辑,更宽容地处理服务器返回格式,增加详细日志
|
|
let openid = null;
|
|
let userId = null;
|
|
console.log('openidRes完整响应:', JSON.stringify(openidRes));
|
|
|
|
if (openidRes && typeof openidRes === 'object') {
|
|
// 适配服务器返回格式:{success: true, code: 200, message: '获取openid成功', data: {openid, userId}}
|
|
if (openidRes.data && typeof openidRes.data === 'object') {
|
|
console.log('识别到标准服务器返回格式,从data字段提取信息');
|
|
openid = openidRes.data.openid || openidRes.data.OpenID || null;
|
|
userId = openidRes.data.userId || null;
|
|
} else {
|
|
// 尝试从响应对象中直接提取openid,适配其他可能的格式
|
|
console.log('尝试从根对象直接提取openid');
|
|
openid = openidRes.openid || openidRes.OpenID || null;
|
|
userId = openidRes.userId || null;
|
|
}
|
|
}
|
|
|
|
if (!openid) {
|
|
console.error('无法从服务器响应中提取openid,完整响应:', JSON.stringify(openidRes));
|
|
// 增加更友好的错误信息,指导用户检查服务器配置
|
|
throw new Error(`获取openid失败: 服务器返回数据格式可能不符合预期,请检查服务器配置。响应数据为: ${JSON.stringify(openidRes)}`);
|
|
}
|
|
|
|
console.log('获取openid成功:', openid)
|
|
|
|
// 3. 存储openid和session_key
|
|
wx.setStorageSync('openid', openid)
|
|
|
|
// 从服务器返回中获取session_key
|
|
if (openidRes && openidRes.session_key) {
|
|
wx.setStorageSync('sessionKey', openidRes.session_key)
|
|
} else if (openidRes && openidRes.data && openidRes.data.session_key) {
|
|
wx.setStorageSync('sessionKey', openidRes.data.session_key)
|
|
}
|
|
|
|
// 优先使用从服务器响应data字段中提取的userId
|
|
if (userId) {
|
|
wx.setStorageSync('userId', userId)
|
|
console.log('使用从服务器data字段提取的userId:', userId)
|
|
} else if (openidRes && openidRes.userId) {
|
|
wx.setStorageSync('userId', openidRes.userId)
|
|
console.log('使用服务器根对象中的userId:', openidRes.userId)
|
|
} else {
|
|
// 生成临时userId
|
|
const tempUserId = 'user_' + Date.now()
|
|
wx.setStorageSync('userId', tempUserId)
|
|
console.log('生成临时userId:', tempUserId)
|
|
}
|
|
|
|
// 4. 上传手机号加密数据到服务器解密
|
|
const phoneData = {
|
|
...e.detail,
|
|
openid: openid
|
|
}
|
|
|
|
console.log('准备上传手机号加密数据到服务器')
|
|
const phoneRes = await API.uploadPhoneNumberData(phoneData)
|
|
|
|
// 改进手机号解密结果的处理逻辑
|
|
if (!phoneRes || (!phoneRes.success && !phoneRes.phoneNumber)) {
|
|
// 如果服务器返回格式不标准但包含手机号,也接受
|
|
if (phoneRes && phoneRes.phoneNumber) {
|
|
console.warn('服务器返回格式可能不符合预期,但成功获取手机号');
|
|
} else {
|
|
throw new Error('获取手机号失败: ' + (phoneRes && phoneRes.message ? phoneRes.message : '未知错误'))
|
|
}
|
|
}
|
|
|
|
// 检查是否有手机号冲突
|
|
const hasPhoneConflict = phoneRes.phoneNumberConflict || false
|
|
const isNewPhone = phoneRes.isNewPhone || true
|
|
const phoneNumber = phoneRes.phoneNumber || null
|
|
|
|
// 如果有手机号冲突且没有返回手机号,使用实际返回的手机号
|
|
const finalPhoneNumber = phoneNumber
|
|
|
|
console.log('手机号解密结果:', {
|
|
phoneNumber: finalPhoneNumber,
|
|
hasPhoneConflict: hasPhoneConflict,
|
|
isNewPhone: isNewPhone
|
|
})
|
|
|
|
// 5. 获取用户微信名称和头像
|
|
let userProfile = null;
|
|
try {
|
|
userProfile = await new Promise((resolve, reject) => {
|
|
wx.getUserProfile({
|
|
desc: '用于完善会员资料',
|
|
success: resolve,
|
|
fail: reject
|
|
});
|
|
});
|
|
console.log('获取用户信息成功:', userProfile);
|
|
} catch (err) {
|
|
console.warn('获取用户信息失败:', err);
|
|
// 如果获取失败,使用默认值
|
|
}
|
|
|
|
// 6. 创建用户信息
|
|
const tempUserInfo = {
|
|
name: userProfile ? (userProfile.userInfo.name || userProfile.userInfo.nickName) : '微信用户',
|
|
// 获取微信头像失败时使用微信默认头像,而不是本地头像
|
|
avatarUrl: userProfile ? userProfile.userInfo.avatarUrl : 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0',
|
|
gender: userProfile ? userProfile.userInfo.gender : 0,
|
|
country: userProfile ? userProfile.userInfo.country : '',
|
|
province: userProfile ? userProfile.userInfo.province : '',
|
|
city: userProfile ? userProfile.userInfo.city : '',
|
|
language: userProfile ? userProfile.userInfo.language : 'zh_CN',
|
|
phoneNumber: finalPhoneNumber
|
|
}
|
|
|
|
// 从本地存储获取userId(使用已声明的变量)
|
|
const storedUserId = wx.getStorageSync('userId')
|
|
// 优先使用用户之前选择的身份类型,如果没有则尝试获取已存储的或默认为买家
|
|
const users = wx.getStorageSync('users') || {}
|
|
const currentUserType = this.data.pendingUserType || this.data.currentUserType ||
|
|
(users[storedUserId] && users[storedUserId].type ? users[storedUserId].type : 'buyer')
|
|
|
|
console.log('用户身份类型:', currentUserType)
|
|
|
|
// 清除临时存储的身份类型
|
|
if (this.data.pendingUserType) {
|
|
this.setData({ pendingUserType: null })
|
|
}
|
|
|
|
// 保存用户信息并等待上传完成
|
|
console.log('开始保存用户信息并上传到服务器...')
|
|
const uploadResult = await this.saveUserInfo(tempUserInfo, currentUserType)
|
|
console.log('用户信息保存并上传完成')
|
|
|
|
wx.hideLoading()
|
|
|
|
// 根据服务器返回的结果显示不同的提示
|
|
if (uploadResult && uploadResult.phoneNumberConflict) {
|
|
wx.showModal({
|
|
title: '登录成功',
|
|
content: '您的手机号已被其他账号绑定',
|
|
showCancel: false,
|
|
confirmText: '我知道了',
|
|
success(res) {
|
|
if (res.confirm) {
|
|
console.log('用户点击了我知道了');
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
wx.showModal({
|
|
title: '登录成功',
|
|
content: '欢迎使用鸡蛋贸易平台',
|
|
showCancel: false,
|
|
confirmText: '开始使用',
|
|
success(res) {
|
|
if (res.confirm) {
|
|
console.log('用户点击了开始使用');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// 用户登录成功,但已移除类型选择和跳转功能
|
|
} else {
|
|
// 用户拒绝授权或其他情况
|
|
console.log('手机号授权失败:', e.detail.errMsg)
|
|
// 不再抛出错误,而是显示友好的提示
|
|
wx.hideLoading()
|
|
wx.showToast({
|
|
title: '需要授权手机号才能使用',
|
|
icon: 'none',
|
|
duration: 2000
|
|
})
|
|
return
|
|
}
|
|
} catch (error) {
|
|
wx.hideLoading()
|
|
console.error('登录过程中发生错误:', error)
|
|
|
|
// 更具体的错误提示
|
|
let errorMsg = '登录失败,请重试'
|
|
if (error.message.includes('网络')) {
|
|
errorMsg = '网络连接失败,请检查网络后重试'
|
|
} else if (error.message.includes('服务器')) {
|
|
errorMsg = '服务器连接失败,请稍后重试'
|
|
}
|
|
|
|
wx.showToast({
|
|
title: errorMsg,
|
|
icon: 'none',
|
|
duration: 3000
|
|
})
|
|
|
|
// 清除可能已经保存的不完整信息
|
|
try {
|
|
wx.removeStorageSync('openid')
|
|
wx.removeStorageSync('sessionKey')
|
|
wx.removeStorageSync('userId')
|
|
} catch (e) {
|
|
console.error('清除临时登录信息失败:', e)
|
|
}
|
|
}
|
|
},
|
|
|
|
// 处理用户基本信息授权
|
|
handleUserAuth(type) {
|
|
// 保存当前用户类型
|
|
this.setData({
|
|
currentUserType: type
|
|
})
|
|
|
|
// 先执行微信登录
|
|
this.doWechatLogin(type)
|
|
},
|
|
|
|
// 测试模式下模拟登录流程
|
|
async simulateLoginForTest() {
|
|
wx.showLoading({
|
|
title: '测试模式登录中...',
|
|
mask: true
|
|
})
|
|
|
|
try {
|
|
// 1. 模拟微信登录,生成测试用的code
|
|
const mockCode = 'test_code_' + Date.now()
|
|
console.log('模拟获取登录code:', mockCode)
|
|
|
|
// 2. 模拟获取openid和userId
|
|
const mockOpenid = 'test_openid_' + Date.now()
|
|
const mockUserId = 'test_user_' + Date.now()
|
|
|
|
console.log('模拟获取openid:', mockOpenid)
|
|
console.log('模拟获取userId:', mockUserId)
|
|
|
|
// 3. 存储测试数据
|
|
wx.setStorageSync('openid', mockOpenid)
|
|
wx.setStorageSync('userId', mockUserId)
|
|
|
|
// 4. 模拟手机号解密结果
|
|
const mockPhoneNumber = null
|
|
console.log('模拟手机号解密成功:', mockPhoneNumber)
|
|
|
|
// 5. 创建模拟用户信息
|
|
const mockUserInfo = {
|
|
name: '测试用户',
|
|
avatarUrl: this.data.avatarUrl,
|
|
gender: 0,
|
|
country: '测试国家',
|
|
province: '测试省份',
|
|
city: '测试城市',
|
|
language: 'zh_CN',
|
|
phoneNumber: mockPhoneNumber
|
|
}
|
|
|
|
// 6. 获取用户身份类型(优先使用pendingUserType)
|
|
const userId = wx.getStorageSync('userId')
|
|
const users = wx.getStorageSync('users') || {}
|
|
const currentUserType = this.data.pendingUserType || this.data.currentUserType ||
|
|
(users[userId] && users[userId].type ? users[userId].type : 'buyer')
|
|
|
|
console.log('测试模式用户身份类型:', currentUserType)
|
|
|
|
// 7. 清除临时存储的身份类型
|
|
if (this.data.pendingUserType) {
|
|
this.setData({ pendingUserType: null })
|
|
}
|
|
|
|
// 8. 保存用户信息并等待上传完成
|
|
console.log('测试模式开始保存用户信息...')
|
|
// 在测试模式下也会上传用户信息到服务器,用于连通性测试
|
|
await this.saveUserInfo(mockUserInfo, currentUserType)
|
|
console.log('测试模式用户信息保存完成')
|
|
|
|
wx.hideLoading()
|
|
|
|
// 9. 显示成功提示
|
|
wx.showToast({
|
|
title: '测试模式登录成功',
|
|
icon: 'success',
|
|
duration: 2000
|
|
})
|
|
|
|
// 测试登录成功,但已移除类型选择和跳转功能
|
|
} catch (error) {
|
|
wx.hideLoading()
|
|
console.error('测试模式登录过程中发生错误:', error)
|
|
|
|
wx.showToast({
|
|
title: '测试模式登录失败',
|
|
icon: 'none',
|
|
duration: 2000
|
|
})
|
|
}
|
|
},
|
|
|
|
// 执行微信登录并获取openid
|
|
async doWechatLogin(type) {
|
|
// 显示加载提示
|
|
wx.showLoading({
|
|
title: '登录中...',
|
|
mask: true
|
|
})
|
|
|
|
try {
|
|
// 调用微信登录接口
|
|
const loginRes = await new Promise((resolve, reject) => {
|
|
wx.login({
|
|
success: resolve,
|
|
fail: reject
|
|
})
|
|
})
|
|
|
|
if (loginRes.code) {
|
|
console.log('微信登录成功,code:', loginRes.code)
|
|
|
|
// 保存登录凭证
|
|
try {
|
|
wx.setStorageSync('loginCode', loginRes.code)
|
|
} catch (e) {
|
|
console.error('保存登录凭证失败:', e)
|
|
}
|
|
|
|
// 引入API服务
|
|
const API = require('../../utils/api.js')
|
|
|
|
// 发送code和用户类型到服务器换取openid和session_key
|
|
try {
|
|
const openidRes = await API.getOpenid(loginRes.code, type)
|
|
console.log('获取openid响应:', openidRes)
|
|
|
|
// 增强版响应处理逻辑,支持多种返回格式
|
|
let openid = null;
|
|
let userId = null;
|
|
let sessionKey = null;
|
|
|
|
// 优先从data字段获取数据
|
|
if (openidRes && openidRes.data && typeof openidRes.data === 'object') {
|
|
openid = openidRes.data.openid || openidRes.data.OpenID || null;
|
|
userId = openidRes.data.userId || openidRes.data.userid || null;
|
|
sessionKey = openidRes.data.session_key || openidRes.data.sessionKey || null;
|
|
}
|
|
|
|
// 如果data为空或不存在,尝试从响应对象直接获取
|
|
if (!openid && openidRes && typeof openidRes === 'object') {
|
|
console.warn('服务器返回格式可能不符合预期,data字段为空或不存在,但尝试从根对象提取信息:', openidRes);
|
|
openid = openidRes.openid || openidRes.OpenID || null;
|
|
userId = openidRes.userId || openidRes.userid || null;
|
|
sessionKey = openidRes.session_key || openidRes.sessionKey || null;
|
|
}
|
|
|
|
// 检查服务器状态信息
|
|
const isSuccess = openidRes && (openidRes.success === true || openidRes.code === 200);
|
|
const serverMessage = openidRes && (openidRes.message || openidRes.msg);
|
|
|
|
if (isSuccess && !openid) {
|
|
console.warn('服务器返回成功状态,但未包含有效的openid:', openidRes);
|
|
}
|
|
|
|
// 打印获取到的信息,方便调试
|
|
console.log('提取到的登录信息:', { openid, userId, sessionKey, serverMessage });
|
|
|
|
if (openid) {
|
|
// 存储openid和session_key
|
|
wx.setStorageSync('openid', openid)
|
|
if (sessionKey) {
|
|
wx.setStorageSync('sessionKey', sessionKey)
|
|
}
|
|
|
|
// 如果有userId,也存储起来
|
|
if (userId) {
|
|
wx.setStorageSync('userId', userId)
|
|
}
|
|
console.log('获取openid成功并存储:', openid)
|
|
|
|
// 验证登录状态并获取用户信息
|
|
await this.validateLoginAndGetUserInfo(openid)
|
|
} else {
|
|
// 即使没有获取到openid,也要继续用户信息授权流程
|
|
console.warn('未获取到有效的openid,但继续用户信息授权流程:', openidRes);
|
|
// 设置一个临时的openid以便继续流程
|
|
wx.setStorageSync('openid', 'temp_' + Date.now())
|
|
}
|
|
} catch (error) {
|
|
console.error('获取openid失败:', error)
|
|
// 即使获取openid失败,也继续用户信息授权流程
|
|
}
|
|
|
|
// 继续用户信息授权流程,等待完成
|
|
await this.processUserInfoAuth(type)
|
|
} else {
|
|
wx.hideLoading()
|
|
console.error('微信登录失败:', loginRes)
|
|
wx.showToast({
|
|
title: '登录失败,请重试',
|
|
icon: 'none',
|
|
duration: 2000
|
|
})
|
|
}
|
|
} catch (err) {
|
|
wx.hideLoading()
|
|
console.error('wx.login失败:', err)
|
|
wx.showToast({
|
|
title: '获取登录状态失败',
|
|
icon: 'none',
|
|
duration: 2000
|
|
})
|
|
}
|
|
},
|
|
|
|
// 验证登录状态并获取用户信息
|
|
async validateLoginAndGetUserInfo(openid) {
|
|
try {
|
|
// 引入API服务
|
|
const API = require('../../utils/api.js')
|
|
|
|
// 调用服务器验证登录状态
|
|
const validateRes = await API.validateUserLogin()
|
|
|
|
if (validateRes.success && validateRes.userInfo) {
|
|
// 服务器返回了用户信息,同步到本地
|
|
const app = getApp()
|
|
const userInfo = validateRes.userInfo
|
|
|
|
// 更新全局用户信息
|
|
app.globalData.userInfo = userInfo
|
|
|
|
// 存储用户信息到本地
|
|
wx.setStorageSync('userInfo', userInfo)
|
|
console.log('验证登录状态成功,用户信息已同步:', userInfo)
|
|
|
|
// 如果手机号存在则清除重新授权标志
|
|
if (userInfo.phoneNumber) {
|
|
// 清除可能存在的重新授权标志
|
|
wx.removeStorageSync('needPhoneAuth')
|
|
console.log('手机号验证通过:', userInfo.phoneNumber)
|
|
}
|
|
|
|
return true
|
|
} else {
|
|
console.warn('服务器验证失败,可能是新用户或登录状态无效')
|
|
return false
|
|
}
|
|
} catch (error) {
|
|
console.error('验证登录状态失败:', error)
|
|
// 如果验证失败,清除可能存在的无效登录信息
|
|
try {
|
|
wx.removeStorageSync('openid')
|
|
wx.removeStorageSync('userId')
|
|
wx.removeStorageSync('userInfo')
|
|
} catch (e) {
|
|
console.error('清除无效登录信息失败:', e)
|
|
}
|
|
return false
|
|
}
|
|
},
|
|
|
|
// 处理用户信息授权
|
|
async processUserInfoAuth(type) {
|
|
const app = getApp()
|
|
|
|
// 如果已经有用户信息,不需要再进行跳转
|
|
if (app.globalData.userInfo) {
|
|
wx.hideLoading()
|
|
return
|
|
}
|
|
|
|
// 优化:首次登录时自动创建临时用户信息并完成登录,不再需要用户填写表单
|
|
// 获取已存储的userId或生成新的
|
|
let userId = wx.getStorageSync('userId')
|
|
if (!userId) {
|
|
userId = 'user_' + Date.now()
|
|
wx.setStorageSync('userId', userId)
|
|
}
|
|
|
|
// 创建临时用户信息
|
|
const tempUserInfo = {
|
|
name: '微信用户',
|
|
avatarUrl: this.data.avatarUrl,
|
|
gender: 0,
|
|
country: '',
|
|
province: '',
|
|
city: '',
|
|
language: 'zh_CN'
|
|
}
|
|
|
|
try {
|
|
// 保存临时用户信息并完成登录,等待数据上传完成
|
|
await this.saveUserInfo(tempUserInfo, type)
|
|
|
|
// 隐藏加载提示
|
|
wx.hideLoading()
|
|
|
|
// 数据上传完成,但已移除类型选择和跳转功能
|
|
} catch (error) {
|
|
console.error('处理用户信息授权失败:', error)
|
|
wx.hideLoading()
|
|
wx.showToast({
|
|
title: '登录失败,请重试',
|
|
icon: 'none',
|
|
duration: 2000
|
|
})
|
|
}
|
|
},
|
|
|
|
// 保存用户信息
|
|
async saveUserInfo(userInfo, type) {
|
|
// 确保userId存在
|
|
let userId = wx.getStorageSync('userId')
|
|
if (!userId) {
|
|
userId = 'user_' + Date.now()
|
|
wx.setStorageSync('userId', userId)
|
|
}
|
|
|
|
// 保存用户信息到本地存储 - 修复首次获取问题
|
|
let users = wx.getStorageSync('users')
|
|
// 如果users不存在或不是对象,初始化为空对象
|
|
if (!users || typeof users !== 'object') {
|
|
users = {}
|
|
}
|
|
|
|
// 初始化用户信息
|
|
users[userId] = users[userId] || {}
|
|
users[userId].info = userInfo
|
|
users[userId].type = type
|
|
|
|
// 确保存储操作成功
|
|
try {
|
|
wx.setStorageSync('users', users)
|
|
console.log('用户信息已成功保存到本地存储')
|
|
} catch (e) {
|
|
console.error('保存用户信息到本地存储失败:', e)
|
|
}
|
|
|
|
// 保存用户信息到全局变量
|
|
const app = getApp()
|
|
app.globalData.userInfo = userInfo
|
|
app.globalData.userType = type
|
|
console.log('用户信息已保存到全局变量:', userInfo)
|
|
|
|
// 额外保存一份单独的userInfo到本地存储,便于checkPhoneAuthSetting方法检查
|
|
try {
|
|
wx.setStorageSync('userInfo', userInfo)
|
|
console.log('单独的userInfo已保存')
|
|
} catch (e) {
|
|
console.error('保存单独的userInfo失败:', e)
|
|
}
|
|
|
|
// 上传用户信息到服务器
|
|
// 在测试模式下也上传用户信息,用于连通性测试
|
|
console.log('准备上传用户信息到服务器进行测试...')
|
|
|
|
// 确保测试数据包含服务器所需的所有字段
|
|
const completeUserInfo = {
|
|
...userInfo,
|
|
// 确保包含服务器需要的必要字段
|
|
name: userInfo.name || '测试用户'
|
|
}
|
|
|
|
try {
|
|
const uploadResult = await this.uploadUserInfoToServer(completeUserInfo, userId, type)
|
|
console.log('用户信息上传到服务器成功')
|
|
return uploadResult // 返回上传结果
|
|
} catch (error) {
|
|
console.error('用户信息上传到服务器失败:', error)
|
|
// 显示友好的提示,但不中断流程
|
|
wx.showToast({
|
|
title: '测试数据上传失败,不影响使用',
|
|
icon: 'none',
|
|
duration: 2000
|
|
})
|
|
// 不再抛出错误,而是返回默认成功结果,确保登录流程继续
|
|
return {
|
|
success: true,
|
|
message: '本地登录成功,服务器连接失败'
|
|
}
|
|
}
|
|
},
|
|
|
|
// 处理头像选择
|
|
onChooseAvatar(e) {
|
|
const { avatarUrl } = e.detail
|
|
this.setData({
|
|
avatarUrl
|
|
})
|
|
},
|
|
|
|
// 处理昵称提交
|
|
getUserName(e) {
|
|
const { name } = e.detail.value
|
|
const type = this.data.currentUserType
|
|
|
|
if (!name) {
|
|
wx.showToast({
|
|
title: '请输入昵称',
|
|
icon: 'none',
|
|
duration: 2000
|
|
})
|
|
return
|
|
}
|
|
|
|
// 创建用户信息对象
|
|
const userInfo = {
|
|
name: name,
|
|
avatarUrl: this.data.avatarUrl,
|
|
// 其他可能需要的字段
|
|
gender: 0,
|
|
country: '',
|
|
province: '',
|
|
city: '',
|
|
language: 'zh_CN'
|
|
}
|
|
|
|
// 保存用户信息
|
|
this.saveUserInfo(userInfo, type)
|
|
|
|
// 隐藏表单
|
|
this.setData({
|
|
showUserInfoForm: false
|
|
})
|
|
|
|
// 已移除类型选择和跳转功能
|
|
},
|
|
|
|
// 取消用户信息表单
|
|
cancelUserInfoForm() {
|
|
this.setData({
|
|
showUserInfoForm: false
|
|
})
|
|
wx.hideLoading()
|
|
},
|
|
|
|
// 检查本地缓存并恢复登录状态
|
|
checkAndRestoreLoginStatus() {
|
|
console.log('开始检查并恢复登录状态')
|
|
const app = getApp()
|
|
|
|
// 从本地存储获取用户信息
|
|
const localUserInfo = wx.getStorageSync('userInfo') || {}
|
|
const userId = wx.getStorageSync('userId')
|
|
const openid = wx.getStorageSync('openid')
|
|
console.log('恢复登录状态 - userId:', userId, 'openid:', openid ? '已获取' : '未获取')
|
|
|
|
// 优先使用全局用户信息,如果没有则使用本地存储的用户信息
|
|
if (app.globalData.userInfo) {
|
|
this.setData({
|
|
userInfo: app.globalData.userInfo,
|
|
needPhoneAuth: !app.globalData.userInfo.phoneNumber
|
|
})
|
|
} else {
|
|
app.globalData.userInfo = localUserInfo
|
|
this.setData({
|
|
userInfo: localUserInfo,
|
|
needPhoneAuth: !localUserInfo.phoneNumber
|
|
})
|
|
}
|
|
|
|
if (userId && openid) {
|
|
// 确保users存储结构存在
|
|
let users = wx.getStorageSync('users')
|
|
if (!users) {
|
|
users = {}
|
|
wx.setStorageSync('users', users)
|
|
}
|
|
|
|
if (!users[userId]) {
|
|
users[userId] = { type: '' }
|
|
wx.setStorageSync('users', users)
|
|
}
|
|
|
|
// 先显示本地存储的用户类型,但会被服务器返回的最新值覆盖
|
|
const user = users[userId]
|
|
const currentType = user.type
|
|
this.setData({ currentUserType: currentType })
|
|
console.log('恢复登录状态 - 当前本地存储的用户类型:', currentType)
|
|
|
|
// 从服务器获取最新的用户信息,确保身份由数据库决定
|
|
this.refreshUserInfoFromServer(openid, userId)
|
|
} else {
|
|
console.log('未找到有效的本地登录信息')
|
|
}
|
|
},
|
|
|
|
// 从服务器刷新用户信息并同步身份数据
|
|
refreshUserInfoFromServer(openid, userId) {
|
|
const API = require('../../utils/api.js')
|
|
|
|
API.getUserInfo(openid).then(res => {
|
|
console.log('从服务器获取用户信息成功:', res)
|
|
|
|
if (res.success && res.data) {
|
|
const serverUserInfo = res.data
|
|
|
|
// 更新本地用户信息
|
|
const app = getApp()
|
|
const updatedUserInfo = {
|
|
...app.globalData.userInfo,
|
|
...serverUserInfo
|
|
}
|
|
|
|
app.globalData.userInfo = updatedUserInfo
|
|
wx.setStorageSync('userInfo', updatedUserInfo)
|
|
|
|
// 设置用户入驻状态
|
|
this.setData({
|
|
userInfo: updatedUserInfo,
|
|
partnerstatus: serverUserInfo.partnerstatus || ''
|
|
})
|
|
|
|
// 同步更新用户身份信息(当前身份由数据库决定)
|
|
if (serverUserInfo.type) {
|
|
this.syncUserTypeFromServer(userId, serverUserInfo.type)
|
|
}
|
|
|
|
console.log('用户信息已更新,昵称:', updatedUserInfo.name, '手机号:', updatedUserInfo.phoneNumber, '身份:', serverUserInfo.type)
|
|
}
|
|
}).catch(err => {
|
|
console.error('从服务器获取用户信息失败:', err)
|
|
// 如果getUserInfo失败,尝试使用validateUserLogin作为备选
|
|
API.validateUserLogin().then(res => {
|
|
console.log('使用validateUserLogin获取用户信息成功:', res)
|
|
|
|
if (res.success && res.data) {
|
|
const serverUserInfo = res.data
|
|
|
|
// 更新本地用户信息
|
|
const app = getApp()
|
|
const updatedUserInfo = {
|
|
...app.globalData.userInfo,
|
|
...serverUserInfo
|
|
}
|
|
|
|
app.globalData.userInfo = updatedUserInfo
|
|
wx.setStorageSync('userInfo', updatedUserInfo)
|
|
|
|
// 设置用户入驻状态
|
|
this.setData({
|
|
userInfo: updatedUserInfo,
|
|
partnerstatus: serverUserInfo.partnerstatus || ''
|
|
})
|
|
|
|
// 同步更新用户身份信息(当前身份由数据库决定)
|
|
if (serverUserInfo.type) {
|
|
this.syncUserTypeFromServer(userId, serverUserInfo.type)
|
|
}
|
|
|
|
console.log('用户信息已更新(备选方案):', updatedUserInfo)
|
|
}
|
|
}).catch(validateErr => {
|
|
console.error('从服务器获取用户信息失败(包括备选方案):', validateErr)
|
|
// 如果服务器请求失败,继续使用本地缓存的信息
|
|
})
|
|
})
|
|
},
|
|
|
|
// 从服务器同步用户身份信息
|
|
syncUserTypeFromServer(userId, serverType) {
|
|
if (!userId || !serverType) {
|
|
console.error('同步用户身份信息失败: 参数不完整')
|
|
return
|
|
}
|
|
|
|
console.log('从服务器同步用户身份信息:', { userId, serverType })
|
|
|
|
// 更新本地存储的用户身份
|
|
let users = wx.getStorageSync('users') || {}
|
|
if (!users[userId]) {
|
|
users[userId] = {}
|
|
}
|
|
|
|
// 移除serverType中的customer(如果存在)
|
|
let processedServerType = serverType.replace(/,?customer/g, '').replace(/^,|,$/g, '')
|
|
|
|
// 构建新的用户类型
|
|
let newUserType = processedServerType
|
|
|
|
// 只有当新构建的用户类型与本地不同时才更新
|
|
if (users[userId].type !== newUserType) {
|
|
users[userId].type = newUserType
|
|
wx.setStorageSync('users', users)
|
|
|
|
// 更新全局用户类型
|
|
const app = getApp()
|
|
app.globalData.userType = newUserType
|
|
|
|
console.log('用户身份已从服务器同步并保留客服标识:', newUserType)
|
|
} else {
|
|
console.log('用户身份与服务器一致,无需更新:', newUserType)
|
|
}
|
|
},
|
|
|
|
// 上传用户信息到服务器
|
|
async uploadUserInfoToServer(userInfo, userId, type) {
|
|
// 引入API服务
|
|
const API = require('../../utils/api.js')
|
|
|
|
// 获取openid
|
|
const openid = wx.getStorageSync('openid')
|
|
|
|
// 构造上传数据(包含openid和session_key)
|
|
const uploadData = {
|
|
userId: userId,
|
|
openid: openid,
|
|
...userInfo,
|
|
type: type,
|
|
timestamp: Date.now()
|
|
}
|
|
|
|
// 调用API上传用户信息并返回Promise
|
|
try {
|
|
const res = await API.uploadUserInfo(uploadData)
|
|
console.log('用户信息上传成功:', res)
|
|
return res
|
|
} catch (err) {
|
|
console.error('用户信息上传失败:', err)
|
|
// 不再抛出错误,而是返回默认成功结果,确保登录流程继续
|
|
// 这样即使服务器连接失败,本地登录也能完成
|
|
return {
|
|
success: true,
|
|
message: '本地登录成功,服务器连接失败'
|
|
}
|
|
}
|
|
},
|
|
|
|
// 处理手机号授权结果(已重命名为onPhoneNumberResult,此方法已废弃)
|
|
processPhoneAuthResult: function () {
|
|
console.warn('processPhoneAuthResult方法已废弃,请使用onPhoneNumberResult方法')
|
|
},
|
|
|
|
// 手机号授权处理
|
|
async onPhoneNumberResult(e) {
|
|
console.log('手机号授权结果:', e)
|
|
if (e.detail.errMsg === 'getPhoneNumber:ok') {
|
|
// 用户同意授权,获取加密数据
|
|
const phoneData = e.detail
|
|
|
|
wx.showLoading({ title: '获取手机号中...' })
|
|
|
|
try {
|
|
// 引入API服务
|
|
const API = require('../../utils/api.js')
|
|
|
|
// 上传到服务器解密
|
|
const res = await API.uploadPhoneNumberData(phoneData)
|
|
|
|
wx.hideLoading()
|
|
|
|
if (res.success && res.phoneNumber) {
|
|
console.log('获取手机号成功:', res.phoneNumber)
|
|
|
|
// 保存手机号到用户信息
|
|
const app = getApp()
|
|
const userInfo = app.globalData.userInfo || wx.getStorageSync('userInfo') || {}
|
|
userInfo.phoneNumber = res.phoneNumber
|
|
|
|
// 更新本地和全局用户信息
|
|
app.globalData.userInfo = userInfo
|
|
wx.setStorageSync('userInfo', userInfo)
|
|
|
|
// 获取userId
|
|
const userId = wx.getStorageSync('userId')
|
|
const users = wx.getStorageSync('users') || {}
|
|
const currentUserType = users[userId] && users[userId].type ? users[userId].type : ''
|
|
|
|
// 同时更新服务器用户信息,确保上传完成
|
|
console.log('开始更新服务器用户信息...')
|
|
if (!this.data.testMode) {
|
|
await this.uploadUserInfoToServer(userInfo, userId, currentUserType)
|
|
console.log('服务器用户信息更新完成')
|
|
} else {
|
|
console.log('测试模式下跳过服务器用户信息更新')
|
|
}
|
|
|
|
wx.showToast({ title: '手机号绑定成功', icon: 'success' })
|
|
} else {
|
|
console.error('获取手机号失败:', res)
|
|
wx.showToast({ title: '获取手机号失败', icon: 'none' })
|
|
}
|
|
} catch (err) {
|
|
wx.hideLoading()
|
|
console.error('获取手机号失败:', err)
|
|
wx.showToast({ title: '获取手机号失败', icon: 'none' })
|
|
}
|
|
} else {
|
|
console.log('用户拒绝授权手机号')
|
|
}
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
})
|
|
|