Browse Source

修复商品详情联系人信息显示、收藏页面功能及登录状态处理问题

pull/1/head
徐飞洋 3 months ago
parent
commit
ee36e8ae15
  1. 20
      custom-tab-bar/index.wxss
  2. 13
      images/logo.svg
  3. BIN
      images/你有好蛋.png
  4. BIN
      images/生成鸡蛋贸易平台图片.png
  5. 440
      pages/buyer/index.js
  6. 35
      pages/buyer/index.wxml
  7. 67
      pages/favorites/index.js
  8. 4
      pages/favorites/index.json
  9. 6
      pages/favorites/index.wxml
  10. 50
      pages/goods-detail/goods-detail.js
  11. 5
      server-example/update-product-contacts.js
  12. 154
      utils/api.js

20
custom-tab-bar/index.wxss

@ -72,26 +72,6 @@
position: relative; position: relative;
} }
/* 未读标记样式 */
.tab-bar-badge {
position: absolute;
top: -5px;
right: -5px;
min-width: 16px;
height: 16px;
padding: 0 4px;
border-radius: 8px;
background-color: #ff4757;
color: white;
font-size: 10px;
line-height: 16px;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tab-bar-text { .tab-bar-text {
font-size: 28rpx; font-size: 28rpx;

13
images/logo.svg

@ -1,13 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
<!-- 背景圆形 -->
<circle cx="100" cy="100" r="90" fill="#f0f2f5"/>
<!-- 鸡蛋图形 -->
<ellipse cx="100" cy="85" rx="40" ry="55" fill="#fff" stroke="#ffd700" stroke-width="3"/>
<ellipse cx="100" cy="85" rx="35" ry="50" fill="#fff"/>
<circle cx="85" cy="75" r="5" fill="#333"/>
<path d="M85 100 Q100 120 115 100" fill="none" stroke="#ffd700" stroke-width="2"/>
<!-- 贸易符号 - 简单的箭头 -->
<path d="M60 140 L140 140 L125 125 L140 140 L125 155" fill="none" stroke="#1677ff" stroke-width="3"/>
</svg>

Before

Width:  |  Height:  |  Size: 627 B

BIN
images/你有好蛋.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

BIN
images/生成鸡蛋贸易平台图片.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

440
pages/buyer/index.js

@ -145,12 +145,11 @@ Page({
// 检查用户登录状态 // 检查用户登录状态
const openid = wx.getStorageSync('openid'); const openid = wx.getStorageSync('openid');
const userInfo = wx.getStorageSync('userInfo');
const userId = wx.getStorageSync('userId'); const userId = wx.getStorageSync('userId');
console.log('检查用户授权状态 - openid:', !!openid, 'userInfo:', !!userInfo, 'userId:', !!userId); console.log('检查用户授权状态 - openid:', !!openid, 'userId:', !!userId);
if (!openid || !userId || !userInfo) { if (!openid || !userId) {
console.log('用户未登录,显示一键登录弹窗'); console.log('用户未登录,显示一键登录弹窗');
// 显示一键登录弹窗,让用户确认是否要登录 // 显示一键登录弹窗,让用户确认是否要登录
this.showOneKeyLogin(); this.showOneKeyLogin();
@ -1250,42 +1249,27 @@ Page({
// 预览图片 // 预览图片
previewImage(e) { previewImage(e) {
// 登录验证 // 登录验证
const userInfo = wx.getStorageSync('userInfo') || null const openid = wx.getStorageSync('openid');
const userId = wx.getStorageSync('userId') || null const userId = wx.getStorageSync('userId');
if (!userInfo || !userId) {
// 未登录,显示授权登录弹窗 if (!openid || !userId) {
this.setData({ console.log('用户未登录,直接显示授权登录弹窗');
showAuthModal: true, // 直接显示授权登录弹窗
pendingUserType: 'buyer' this.showOneKeyLogin();
}) return;
return
} }
// 已登录,执行图片预览 // 已登录,执行图片预览
const { urls, index } = e.currentTarget.dataset const { item, index } = e.currentTarget.dataset;
this.setData({ console.log('预览图片参数:', item, index);
showImagePreview: true,
previewImageUrls: urls,
previewImageIndex: parseInt(index)
})
},
// 预览图片 // 从item中获取图片URL列表
previewImage(e) { const urls = item.imageUrls || [];
// 登录验证 if (urls.length === 0) {
const userInfo = wx.getStorageSync('userInfo') || null console.error('没有图片可预览');
const userId = wx.getStorageSync('userId') || null return;
if (!userInfo || !userId) {
// 未登录,显示授权登录弹窗
this.setData({
showAuthModal: true,
pendingUserType: 'buyer'
})
return
} }
// 已登录,执行图片预览
const { urls, index } = e.currentTarget.dataset
this.setData({ this.setData({
showImagePreview: true, showImagePreview: true,
previewImageUrls: urls, previewImageUrls: urls,
@ -1614,6 +1598,9 @@ Page({
} }
}, 500); }, 500);
} }
// 登录成功后,加载用户收藏列表,更新商品收藏状态
this.loadUserFavorites();
}, },
// 记录用户行为 // 记录用户行为
@ -1687,17 +1674,9 @@ Page({
const userId = wx.getStorageSync('userId'); const userId = wx.getStorageSync('userId');
if (!openid || !userId) { if (!openid || !userId) {
console.log('用户未登录,显示登录提示和弹窗'); console.log('用户未登录,直接显示授权登录弹窗');
// 提示登录后才可查看详情 // 直接显示授权登录弹窗
wx.showToast({ this.showOneKeyLogin();
title: '登录后才可查看详情',
icon: 'none',
duration: 1500
});
// 显示登录弹窗
setTimeout(() => {
this.showOneKeyLogin();
});
return; return;
} }
@ -1728,118 +1707,249 @@ Page({
// 获取手机号并登录 // 获取手机号并登录
onGetPhoneNumber: function (e) { async onGetPhoneNumber(e) {
console.log('获取手机号:', e); console.log('获取手机号:', e);
// 首先检查用户是否拒绝授权
if (e.detail.errMsg !== 'getPhoneNumber:ok') { if (e.detail.errMsg !== 'getPhoneNumber:ok') {
console.log('用户拒绝授权手机号'); console.log('用户拒绝授权手机号');
wx.showToast({
title: '您已拒绝授权,操作已取消',
icon: 'none'
});
this.setData({ this.setData({
showOneKeyLoginModal: false showOneKeyLoginModal: false
}); });
// 直接返回,取消所有后续操作
return; return;
} }
const encryptedData = e.detail.encryptedData; wx.showLoading({
const iv = e.detail.iv; title: '登录中...',
mask: true
});
// 调用API进行登录 try {
API.login(encryptedData, iv) // 引入API服务
.then(res => { const API = require('../../utils/api.js');
console.log('登录成功:', res);
// 1. 先执行微信登录获取code
const loginRes = await new Promise((resolve, reject) => {
wx.login({
success: resolve,
fail: reject
});
});
if (res.success) { if (!loginRes.code) {
// 保存登录信息 throw new Error('获取登录code失败');
wx.setStorageSync('openid', res.data.openid); }
wx.setStorageSync('userId', res.data.userId);
wx.setStorageSync('sessionKey', res.data.sessionKey || ''); 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({ wx.getUserProfile({
desc: '用于完善会员资料', desc: '用于完善会员资料',
success: (userProfile) => { success: resolve,
console.log('获取用户信息成功:', userProfile); fail: reject
});
// 构建用户信息 });
const userInfo = { console.log('获取用户信息成功:', userProfile);
openid: res.data.openid, } catch (err) {
userId: res.data.userId, console.warn('获取用户信息失败:', err);
name: userProfile.userInfo.name || userProfile.userInfo.nickName, // 如果获取失败,使用默认值
avatarUrl: userProfile.userInfo.avatarUrl, }
type: this.data.pendingUserType
};
// 保存用户信息到本地存储 // 5. 创建用户信息
wx.setStorageSync('userInfo', userInfo); const app = getApp();
const existingUserInfo = app.globalData.userInfo || wx.getStorageSync('userInfo') || {};
const userInfo = {
// 优先使用最新获取的微信头像和昵称,如果没有获取到则使用本地存储的
name: (userProfile ? (userProfile.userInfo.name || userProfile.userInfo.nickName) : existingUserInfo.name) || '微信用户',
avatarUrl: (userProfile ? userProfile.userInfo.avatarUrl : existingUserInfo.avatarUrl) || 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0',
gender: (userProfile ? userProfile.userInfo.gender : existingUserInfo.gender) || 0,
country: (userProfile ? userProfile.userInfo.country : existingUserInfo.country) || '',
province: (userProfile ? userProfile.userInfo.province : existingUserInfo.province) || '',
city: (userProfile ? userProfile.userInfo.city : existingUserInfo.city) || '',
language: (userProfile ? userProfile.userInfo.language : existingUserInfo.language) || 'zh_CN',
phoneNumber: phoneNumber
};
// 上传用户信息到服务器 // 6. 获取用户类型
API.uploadUserInfo(userInfo) const users = wx.getStorageSync('users') || {};
.then(uploadRes => { let currentUserType = users[userId] && users[userId].type ? users[userId].type : '';
console.log('用户信息上传成功:', uploadRes);
// 关闭登录弹窗 // 如果没有用户类型,使用pendingUserType或从全局获取
this.setData({ if (!currentUserType) {
showOneKeyLoginModal: false currentUserType = this.data.pendingUserType || app.globalData.userType || '';
}); }
// 登录成功后,重新执行"我想要"操作 // 7. 保存用户信息并等待上传完成
this.handleLoginSuccess(); console.log('开始保存用户信息并上传到服务器...');
}) await this.uploadUserInfoToServer(userInfo, userId, currentUserType);
.catch(uploadErr => { console.log('用户信息保存并上传完成');
console.error('用户信息上传失败:', uploadErr);
// 即使上传失败,也关闭登录弹窗 // 更新本地和全局用户信息
this.setData({ app.globalData.userInfo = userInfo;
showOneKeyLoginModal: false wx.setStorageSync('userInfo', userInfo);
});
// 登录成功后,重新执行"我想要"操作 // 确保users存储结构存在
this.handleLoginSuccess(); let usersStorage = wx.getStorageSync('users') || {};
}); if (!usersStorage[userId]) {
}, usersStorage[userId] = {};
fail: (profileErr) => { }
console.error('获取用户信息失败:', profileErr);
// 即使获取用户信息失败,也构建基本用户信息对象
const userInfo = {
openid: res.data.openid,
userId: res.data.userId,
name: '微信用户',
avatarUrl: '/images/default-avatar.png',
type: this.data.pendingUserType
};
// 保存用户信息到本地存储 // 保存手机号到users对象中
wx.setStorageSync('userInfo', userInfo); usersStorage[userId].phoneNumber = phoneNumber;
usersStorage[userId].type = currentUserType;
wx.setStorageSync('users', usersStorage);
// 关闭登录弹窗 wx.hideLoading();
this.setData({
showOneKeyLoginModal: false
});
// 登录成功后,重新执行"我想要"操作 // 关闭登录弹窗
this.handleLoginSuccess(); this.setData({
} showOneKeyLoginModal: false
}); });
} else {
wx.showToast({ // 登录成功后,重新执行"我想要"操作
title: '登录失败,请重试', this.handleLoginSuccess();
icon: 'none'
}); // 根据服务器返回的结果显示不同的提示
this.setData({ if (hasPhoneConflict) {
showOneKeyLoginModal: false
});
}
})
.catch(err => {
console.error('登录失败:', err);
wx.showToast({ wx.showToast({
title: '网络错误,请重试', title: '登录成功,但手机号已被其他账号绑定',
icon: 'none' icon: 'none',
duration: 3000
}); });
this.setData({ } else {
showOneKeyLoginModal: false wx.showToast({
title: '登录成功,手机号已绑定',
icon: 'success',
duration: 2000
}); });
}
} 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
});
// 关闭登录弹窗
this.setData({
showOneKeyLoginModal: false
}); });
// 清除可能已经保存的不完整信息
try {
wx.removeStorageSync('openid');
wx.removeStorageSync('sessionKey');
wx.removeStorageSync('userId');
} catch (e) {
console.error('清除临时登录信息失败:', e);
}
}
}, },
// 保存用户信息 // 保存用户信息
@ -1869,44 +1979,48 @@ Page({
}, },
// 上传用户信息到服务器 // 上传用户信息到服务器
uploadUserInfoToServer: function (userInfo, userId, type) { uploadUserInfoToServer(userInfo, userId, type) {
console.log('上传用户信息到服务器:', userInfo); // 返回Promise以便调用者可以进行错误处理
return new Promise((resolve, reject) => {
API.saveUserInfo(userInfo, type) try {
.then(res => { // 引入API服务
console.log('上传用户信息成功:', res); const API = require('../../utils/api.js');
this.setData({ // 获取openid
showUserInfoForm: false const openid = wx.getStorageSync('openid');
});
// 验证必要参数
// 登录成功后,重新执行"我想要"操作 if (!userId || !openid) {
if (this.data.currentGoodsId) { const error = new Error('缺少必要的用户信息');
// 模拟点击"我想要"按钮,使用新的商品ID console.error('用户信息上传失败:', error);
const goodsItem = this.findGoodsItemById(String(this.data.currentGoodsId)); reject(error);
if (goodsItem) { return;
// 重新调用onClickWant,但这次用户已登录
this.onClickWant({ currentTarget: { dataset: { id: this.data.currentGoodsId } } });
}
} }
})
.catch(err => {
console.error('上传用户信息失败:', err);
// 即使上传失败,也标记为已登录 // 构造上传数据(包含所有必要字段,包括phoneNumber和头像URL)
this.setData({ const uploadData = {
showUserInfoForm: false userId: userId,
openid: openid,
name: userInfo.name,
avatarUrl: userInfo.avatarUrl, // 添加头像URL字段
phoneNumber: userInfo.phoneNumber, // 添加phoneNumber字段,满足服务器要求
type: type,
timestamp: Date.now()
};
// 调用API上传用户信息
API.uploadUserInfo(uploadData).then(res => {
console.log('用户信息上传成功:', res);
resolve(res);
}).catch(err => {
console.error('用户信息上传失败:', err);
reject(err);
}); });
} catch (error) {
// 尝试执行"我想要"操作 console.error('上传用户信息时发生异常:', error);
if (this.data.currentGoodsId) { reject(error);
const goodsItem = this.findGoodsItemById(String(this.data.currentGoodsId)); }
if (goodsItem) { });
// 重新调用onClickWant,但这次用户已登录
this.onClickWant({ currentTarget: { dataset: { id: this.data.currentGoodsId } } });
}
}
});
}, },
recordBehavior(behaviorType, targetType, targetId) { recordBehavior(behaviorType, targetType, targetId) {

35
pages/buyer/index.wxml

@ -69,7 +69,7 @@
<!-- 下半部分按钮区域(40%高度) --> <!-- 下半部分按钮区域(40%高度) -->
<view style="flex: 0.4; display: flex; justify-content: space-between; align-items: center; padding: 0 20rpx;"> <view style="flex: 0.4; display: flex; justify-content: space-between; align-items: center; padding: 0 20rpx;">
<!-- 收藏人数显示 --> <!-- 收藏人数显示 -->
<text style="color: #52c41a; font-size: 28rpx; font-weight: bold;">已有{{item.reservedCount || 0}}人收藏</text> <text style="color: #2b66f0ff; font-size: 28rpx; font-weight: bold;">已有{{item.reservedCount || 0}}人收藏</text>
<!-- 根据是否已收藏显示不同的按钮 --> <!-- 根据是否已收藏显示不同的按钮 -->
<button <button
wx:if="{{item.isFavorite}}" wx:if="{{item.isFavorite}}"
@ -153,7 +153,7 @@
<view wx:if="{{showOneKeyLoginModal}}" class="auth-modal-overlay"> <view wx:if="{{showOneKeyLoginModal}}" class="auth-modal-overlay">
<view class="auth-modal-container"> <view class="auth-modal-container">
<view class="auth-modal-title">授权登录</view> <view class="auth-modal-title">授权登录</view>
<view class="auth-modal-content">授权获取您的手机号用于登录</view> <view class="auth-modal-content">授权登录后才可查看详情</view>
<view class="auth-modal-buttons"> <view class="auth-modal-buttons">
<button class="auth-primary-button" open-type="getPhoneNumber" bind:getphonenumber="onGetPhoneNumber">授权获取手机号</button> <button class="auth-primary-button" open-type="getPhoneNumber" bind:getphonenumber="onGetPhoneNumber">授权获取手机号</button>
<button class="auth-cancel-button" bindtap="closeOneKeyLoginModal">取消</button> <button class="auth-cancel-button" bindtap="closeOneKeyLoginModal">取消</button>
@ -198,4 +198,35 @@
<!-- 四分之一页空白区域 --> <!-- 四分之一页空白区域 -->
<view class="quarter-page-blank"></view> <view class="quarter-page-blank"></view>
<!-- 图片预览弹窗 -->
<view class="image-preview-mask" wx:if="{{showImagePreview}}" style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.9); display: flex; justify-content: center; align-items: center; z-index: 10001;" catchtouchmove="true">
<view style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center;">
<swiper
style="width: 100%; height: 100%;"
current="{{previewImageIndex}}"
bindchange="onPreviewImageChange"
indicator-dots="true"
indicator-color="rgba(255,255,255,0.5)"
indicator-active-color="#fff">
<block wx:for="{{previewImageUrls}}" wx:key="*this">
<swiper-item>
<image
src="{{item}}"
mode="aspectFit"
style="width: 100%; height: 100%; transform: scale({{scale}}) translate({{offsetX}}px, {{offsetY}}px); transition: {{isScaling ? 'none' : 'transform 0.3s'}};"
bindtap="handleImageTap"
bindtouchstart="handleTouchStart"
bindtouchmove="handleTouchMove"
bindtouchend="handleTouchEnd"
bindtouchcancel="handleTouchEnd"
></image>
</swiper-item>
</block>
</swiper>
<view style="position: absolute; top: 40rpx; right: 40rpx; color: white; font-size: 40rpx;">
<text bindtap="closeImagePreview" style="background: rgba(0,0,0,0.5); padding: 10rpx 20rpx; border-radius: 50%;">×</text>
</view>
</view>
</view>
</view> </view>

67
pages/favorites/index.js

@ -120,9 +120,26 @@ Page({
loading: true loading: true
}); });
// 获取手机号码 // 获取手机号码 - 使用与API.js一致的获取方式
const phoneNumber = wx.getStorageSync('phoneNumber') || '18482694520'; // 默认使用测试手机号 const users = wx.getStorageSync('users') || {};
const userId = wx.getStorageSync('userId');
let phoneNumber = null;
// 尝试从users中获取手机号
if (userId && users[userId] && users[userId].phoneNumber) {
phoneNumber = users[userId].phoneNumber;
} else {
// 尝试从全局用户信息获取
const userInfo = wx.getStorageSync('userInfo');
if (userInfo && userInfo.phoneNumber) {
phoneNumber = userInfo.phoneNumber;
} else {
// 尝试从直接存储的phoneNumber获取
phoneNumber = wx.getStorageSync('phoneNumber');
}
}
// 检查用户是否登录
if (!phoneNumber) { if (!phoneNumber) {
// 用户未登录,显示提示 // 用户未登录,显示提示
wx.showToast({ wx.showToast({
@ -141,12 +158,25 @@ Page({
API.getFavorites(phoneNumber).then(res => { API.getFavorites(phoneNumber).then(res => {
console.log('获取收藏列表成功:', res); console.log('获取收藏列表成功:', res);
const favorites = res.data && res.data.favorites ? res.data.favorites : []; // 检查API返回是否成功
this.setData({ if (res && res.code === 200 && res.data) {
favoritesList: favorites, const favorites = res.data.favorites || [];
hasFavorites: favorites.length > 0, this.setData({
loading: false favoritesList: favorites,
}); hasFavorites: favorites.length > 0,
loading: false
});
} else {
// API返回格式不正确
this.setData({
loading: false,
hasFavorites: false
});
wx.showToast({
title: '数据获取失败',
icon: 'none'
});
}
}).catch(err => { }).catch(err => {
console.error('获取收藏列表失败:', err); console.error('获取收藏列表失败:', err);
wx.showToast({ wx.showToast({
@ -209,9 +239,26 @@ Page({
* 跳转到商品详情页 * 跳转到商品详情页
*/ */
goToGoodsDetail: function (e) { goToGoodsDetail: function (e) {
const productId = e.currentTarget.dataset.productid; // 检查用户是否登录
const openid = wx.getStorageSync('openid');
const userId = wx.getStorageSync('userId');
if (!openid || !userId) {
console.log('用户未登录,需要授权登录');
// 显示授权登录弹窗
wx.showToast({
title: '请先登录',
icon: 'none'
});
return;
}
const goodsItem = e.currentTarget.dataset.item;
// 获取嵌套在Product对象中的商品数据
const productData = goodsItem.Product;
// 跳转到商品详情页面,并传递商品数据,使用encodeURIComponent编码JSON字符串
wx.navigateTo({ wx.navigateTo({
url: '/pages/goods-detail/goods-detail?productId=' + productId url: '/pages/goods-detail/goods-detail?goodsData=' + encodeURIComponent(JSON.stringify(productData))
}); });
}, },

4
pages/favorites/index.json

@ -1,4 +1,6 @@
{ {
"usingComponents": {}, "usingComponents": {},
"navigationBarTitleText": "我的收藏" "navigationBarTitleText": "我的收藏",
"enablePullDownRefresh": true,
"backgroundTextStyle": "dark"
} }

6
pages/favorites/index.wxml

@ -8,13 +8,13 @@
</view> </view>
<!-- 空状态 --> <!-- 空状态 -->
<view wx:elif="{{!hasFavorites}}" class="empty-container"> <view wx:elif="{{!hasFavorites}}" class="empty-container" style="width: 100%; margin: 0 auto; padding: 0;">
<view class="empty-icon">💔</view> <view class="empty-icon">💔</view>
<text class="empty-text">您还没有收藏任何商品</text> <text class="empty-text">您还没有收藏任何商品</text>
</view> </view>
<!-- 收藏列表 --> <!-- 收藏列表 -->
<view wx:else class="goods-list" style="width: 100%; display: flex; flex-direction: column; align-items: flex-start; min-height: 400rpx; margin-top: 120rpx;"> <view wx:else class="goods-list" style="width: 100%; display: flex; flex-direction: column; align-items: flex-start; min-height: 400rpx;">
<view wx:for="{{favoritesList}}" wx:key="productId" class="card" style="width: 100%; margin-top: 0; margin-bottom: 20rpx;"> <view wx:for="{{favoritesList}}" wx:key="productId" class="card" style="width: 100%; margin-top: 0; margin-bottom: 20rpx;">
<!-- 图片和信息2:3比例并排显示,整体高度固定 --> <!-- 图片和信息2:3比例并排显示,整体高度固定 -->
<view style="display: flex; width: 100%; height: 200rpx; border-radius: 8rpx; overflow: hidden; background-color: #f5f5f5;"> <view style="display: flex; width: 100%; height: 200rpx; border-radius: 8rpx; overflow: hidden; background-color: #f5f5f5;">
@ -51,7 +51,7 @@
<!-- 右侧信息区域 60%宽度(3/5),相应调整 --> <!-- 右侧信息区域 60%宽度(3/5),相应调整 -->
<view style="width: 60%; display: flex; flex-direction: column; background-color: white; border-left: 1rpx solid #f0f0f0;"> <view style="width: 60%; display: flex; flex-direction: column; background-color: white; border-left: 1rpx solid #f0f0f0;">
<!-- 上半部分商品信息区域(60%高度),可点击查看详情 --> <!-- 上半部分商品信息区域(60%高度),可点击查看详情 -->
<view style="flex: 0.6; padding: 0rpx 15rpx 15rpx 15rpx; cursor: pointer;" bindtap="goToGoodsDetail" data-productid="{{item.productId}}"> <view style="flex: 0.6; padding: 0rpx 15rpx 15rpx 15rpx; cursor: pointer;" bindtap="goToGoodsDetail" data-item="{{item}}">
<view> <view>
<view style="margin-bottom: 15rpx; margin-top: -5rpx;"> <view style="margin-bottom: 15rpx; margin-top: -5rpx;">
<view style="display: inline-block; margin-right: 10rpx; font-size: 18rpx; color: #fff; background-color: #DAA520; padding: 2rpx 8rpx; border-radius: 10rpx; vertical-align: middle;">金标蛋</view> <view style="display: inline-block; margin-right: 10rpx; font-size: 18rpx; color: #fff; background-color: #DAA520; padding: 2rpx 8rpx; border-radius: 10rpx; vertical-align: middle;">金标蛋</view>

50
pages/goods-detail/goods-detail.js

@ -56,49 +56,14 @@ Page({
const goodsData = JSON.parse(decodeURIComponent(options.goodsData)); const goodsData = JSON.parse(decodeURIComponent(options.goodsData));
console.log('解析后的商品数据:', goodsData); console.log('解析后的商品数据:', goodsData);
// 从本地存储获取已预约商品ID列表
const reservedGoodsIds = wx.getStorageSync('reservedGoodsIds') || [];
const product = goodsData;
// 确保商品ID的一致性 // 确保商品ID的一致性
const productIdStr = String(product.productId || product.id); const productIdStr = String(goodsData.productId || goodsData.id);
// 增强的预约人数计算逻辑 // 无论是否有传递数据,都调用API获取最新的商品详情(特别是联系人信息)
const selectedValue = product.selected; this.loadGoodsDetail(productIdStr);
const reservedCountValue = product.reservedCount;
const reservationCountValue = product.reservationCount; // 设置来源标识
const finalReservationCount = selectedValue !== undefined && selectedValue !== null ? selectedValue :
(reservedCountValue !== undefined && reservedCountValue !== null ? reservedCountValue :
(reservationCountValue || 0));
// 处理grossWeight为null或无效的情况,返回空字符串以支持文字输入
const grossWeightValue = product.grossWeight !== null && product.grossWeight !== undefined ? product.grossWeight : '';
// 转换商品数据格式,与loadGoodsDetail保持一致
const formattedGoods = {
id: productIdStr,
productId: productIdStr,
name: product.productName || product.name,
price: product.price,
minOrder: product.minOrder || product.quantity,
yolk: product.yolk,
spec: product.spec || product.specification,
region: product.region,
contact_phone: product.contact_phone || product.contactPhone,
product_contact: product.product_contact || product.contactName,
imageUrls: product.imageUrls || product.images || [],
displayGrossWeight: formatGrossWeight(grossWeightValue, product.weight),
isReserved: reservedGoodsIds.some(itemId => String(itemId) === productIdStr),
reservedCount: finalReservationCount,
created_at: product.created_at || product.createdAt,
updated_at: product.updated_at || product.updatedAt,
status: product.status || 'published'
};
// 设置商品详情数据和来源标识
this.setData({ this.setData({
goodsDetail: formattedGoods,
fromSeller: fromSeller fromSeller: fromSeller
}); });
} catch (error) { } catch (error) {
@ -180,7 +145,8 @@ Page({
isReserved: reservedGoodsIds.some(itemId => String(itemId) === productIdStr), isReserved: reservedGoodsIds.some(itemId => String(itemId) === productIdStr),
reservedCount: finalReservationCount, reservedCount: finalReservationCount,
created_at: product.created_at || product.createdAt, created_at: product.created_at || product.createdAt,
updated_at: product.updated_at || product.updatedAt updated_at: product.updated_at || product.updatedAt,
status: product.status
}; };
this.setData({ this.setData({

5
server-example/update-product-contacts.js

@ -132,3 +132,8 @@ async function updateProductContacts() {
// 导出函数供其他模块使用 // 导出函数供其他模块使用
module.exports = updateProductContacts; module.exports = updateProductContacts;
// 如果直接运行此文件,则执行更新操作
if (require.main === module) {
updateProductContacts();
}

154
utils/api.js

@ -317,11 +317,29 @@ module.exports = {
// 添加商品到购物车 - 增强版本,即使本地找不到商品也尝试直接请求服务器 // 添加商品到购物车 - 增强版本,即使本地找不到商品也尝试直接请求服务器
addToCart: function (goodsItem) { addToCart: function (goodsItem) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
var openid = wx.getStorageSync('openid'); // 获取用户信息,包含手机号
console.log('API.addToCart - openid:', openid, 'goodsItem:', goodsItem); const users = wx.getStorageSync('users') || {};
const userId = wx.getStorageSync('userId');
let userPhone = null;
// 尝试从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');
}
}
console.log('API.addToCart - userPhone:', userPhone, 'goodsItem:', goodsItem);
// 1. 验证用户登录状态 // 1. 验证用户登录状态
if (!openid) { if (!userPhone) {
return reject(new Error('用户未登录')); return reject(new Error('用户未登录'));
} }
@ -358,7 +376,7 @@ module.exports = {
console.log(`尝试添加到购物车,第${attempts}/${maxAttempts}次尝试`); console.log(`尝试添加到购物车,第${attempts}/${maxAttempts}次尝试`);
// 总是先尝试直接向服务器发送请求(这是修复新创建货源的关键) // 总是先尝试直接向服务器发送请求(这是修复新创建货源的关键)
this.sendAddToCartRequest(openid, basicProduct).then(resolve).catch(err => { this.sendAddToCartRequest(userPhone, basicProduct).then(resolve).catch(err => {
console.error('添加到购物车请求失败:', err.message, '尝试次数:', attempts); console.error('添加到购物车请求失败:', err.message, '尝试次数:', attempts);
// 检查是否为外键约束错误或者服务器找不到商品的情况 // 检查是否为外键约束错误或者服务器找不到商品的情况
@ -386,13 +404,13 @@ module.exports = {
console.log('使用的商品信息:', updatedProduct); console.log('使用的商品信息:', updatedProduct);
// 直接再次尝试,不再经过复杂的判断 // 直接再次尝试,不再经过复杂的判断
this.sendAddToCartRequest(openid, updatedProduct).then(resolve).catch(reject); this.sendAddToCartRequest(userPhone, updatedProduct).then(resolve).catch(reject);
}).catch(innerErr => { }).catch(innerErr => {
console.error('刷新商品列表失败:', innerErr); console.error('刷新商品列表失败:', innerErr);
// 刷新失败也尝试再次发送请求,不轻易放弃 // 刷新失败也尝试再次发送请求,不轻易放弃
if (attempts < maxAttempts) { if (attempts < maxAttempts) {
console.log('刷新失败,但仍尝试再次发送请求'); console.log('刷新失败,但仍尝试再次发送请求');
this.sendAddToCartRequest(openid, basicProduct).then(resolve).catch(reject); this.sendAddToCartRequest(userPhone, basicProduct).then(resolve).catch(reject);
} else { } else {
reject(new Error('商品信息已更新,请稍后重试')); reject(new Error('商品信息已更新,请稍后重试'));
} }
@ -441,12 +459,12 @@ module.exports = {
}, },
// 发送添加到购物车请求的辅助方法 - 完全符合服务器格式版 // 发送添加到购物车请求的辅助方法 - 完全符合服务器格式版
sendAddToCartRequest: function (openid, product) { sendAddToCartRequest: function (userPhone, product) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 重要:直接使用传入的openid参数,不再本地重新获取 // 重要:直接使用传入的userPhone参数
console.log('构建的product对象:', product); console.log('构建的product对象:', product);
console.log('发送添加到购物车请求,productId:', product.productId, '类型:', typeof product.productId); console.log('发送添加到购物车请求,productId:', product.productId, '类型:', typeof product.productId);
console.log('用户openid:', openid); console.log('用户手机号:', userPhone);
console.log('请求URL:', '/api/cart/add'); console.log('请求URL:', '/api/cart/add');
// 前置验证:确保productId存在且类型正确 // 前置验证:确保productId存在且类型正确
@ -483,10 +501,10 @@ module.exports = {
name: product.productName || '未命名商品' name: product.productName || '未命名商品'
}; };
// 根据服务器端代码分析,服务器期望的格式就是 { openid, product: {...} } // 根据服务器端代码分析,服务器期望的格式是 { user_phone, product: {...} }
// 这是最简单直接的格式,服务器会自动从product对象中提取数据 // 这是最简单直接的格式,服务器会自动从product对象中提取数据
const requestData = { const requestData = {
openid: openid, user_phone: userPhone,
product: safeProduct product: safeProduct
}; };
console.log('最终发送的请求数据完整结构:', requestData); console.log('最终发送的请求数据完整结构:', requestData);
@ -1567,9 +1585,14 @@ module.exports = {
}); });
}, },
// 微信登录函数 - 增强版,支持手机号一键登录 // 微信登录函数 - 增强版,强制要求手机号授权登录
login: function (encryptedData = null, iv = null) { login: function (encryptedData = null, iv = null) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 强制要求手机号授权
if (!encryptedData || !iv) {
return reject(new Error('登录必须进行手机号授权'));
}
// 1. 调用微信登录接口获取code // 1. 调用微信登录接口获取code
wx.login({ wx.login({
success: loginRes => { success: loginRes => {
@ -1818,11 +1841,8 @@ module.exports = {
}); });
}).catch(phoneErr => { }).catch(phoneErr => {
console.error('手机号上传失败:', phoneErr); console.error('手机号上传失败:', phoneErr);
// 手机号上传失败不影响登录,仍然返回登录成功 // 手机号上传失败导致登录失败
resolve({ reject(new Error('手机号授权失败: ' + (phoneErr.message || '未知错误')));
success: true,
data: { openid, userId, sessionKey, phoneError: phoneErr }
});
}); });
} else { } else {
// 没有手机号信息,直接返回登录成功 // 没有手机号信息,直接返回登录成功
@ -1974,11 +1994,8 @@ module.exports = {
const sessionKey = wx.getStorageSync('sessionKey'); const sessionKey = wx.getStorageSync('sessionKey');
if (!openid) { if (!openid) {
// 如果没有openid,先执行登录 // 如果没有openid,返回错误,不自动登录
return this.login().then(loginRes => { return Promise.reject(new Error('用户未登录'));
// 重新尝试上传
return tryUpload();
});
} }
// 检查是否包含openid,如果没有则添加 // 检查是否包含openid,如果没有则添加
@ -2023,7 +2040,6 @@ module.exports = {
addFavorite: function (productId) { addFavorite: function (productId) {
console.log('API.addFavorite - productId:', productId); console.log('API.addFavorite - productId:', productId);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const openid = wx.getStorageSync('openid');
const userId = wx.getStorageSync('userId'); const userId = wx.getStorageSync('userId');
// 获取用户信息,包含手机号 // 获取用户信息,包含手机号
@ -2038,18 +2054,15 @@ module.exports = {
const userInfo = wx.getStorageSync('userInfo'); const userInfo = wx.getStorageSync('userInfo');
if (userInfo && userInfo.phoneNumber) { if (userInfo && userInfo.phoneNumber) {
userPhone = userInfo.phoneNumber; userPhone = userInfo.phoneNumber;
} else {
// 尝试从直接存储的phoneNumber获取
userPhone = wx.getStorageSync('phoneNumber');
} }
} }
// 如果没有openid,直接返回未登录错误
if (!openid) {
reject(new Error('用户未登录,无法添加收藏'));
return;
}
// 如果没有手机号,直接返回错误 // 如果没有手机号,直接返回错误
if (!userPhone) { if (!userPhone) {
reject(new Error('无法获取用户手机号,无法添加收藏')); reject(new Error('请先登录并绑定手机号'));
return; return;
} }
@ -2077,8 +2090,6 @@ module.exports = {
cancelFavorite: function (productId) { cancelFavorite: function (productId) {
console.log('API.cancelFavorite - productId:', productId); console.log('API.cancelFavorite - productId:', productId);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const openid = wx.getStorageSync('openid');
// 获取用户信息,包含手机号 // 获取用户信息,包含手机号
const users = wx.getStorageSync('users') || {}; const users = wx.getStorageSync('users') || {};
const userId = wx.getStorageSync('userId'); const userId = wx.getStorageSync('userId');
@ -2092,18 +2103,15 @@ module.exports = {
const userInfo = wx.getStorageSync('userInfo'); const userInfo = wx.getStorageSync('userInfo');
if (userInfo && userInfo.phoneNumber) { if (userInfo && userInfo.phoneNumber) {
userPhone = userInfo.phoneNumber; userPhone = userInfo.phoneNumber;
} else {
// 尝试从直接存储的phoneNumber获取
userPhone = wx.getStorageSync('phoneNumber');
} }
} }
// 如果没有openid,直接返回未登录错误
if (!openid) {
reject(new Error('用户未登录,无法取消收藏'));
return;
}
// 如果没有手机号,直接返回错误 // 如果没有手机号,直接返回错误
if (!userPhone) { if (!userPhone) {
reject(new Error('无法获取用户手机号,无法取消收藏')); reject(new Error('请先登录并绑定手机号'));
return; return;
} }
@ -2125,38 +2133,16 @@ module.exports = {
}, },
// 获取用户收藏列表 // 获取用户收藏列表
getFavorites: function () { getFavorites: function (user_phone) {
console.log('API.getFavorites'); console.log('API.getFavorites - user_phone:', user_phone);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const openid = wx.getStorageSync('openid'); // 必须传入user_phone参数且不能为空
if (!user_phone) {
// 获取用户信息,包含手机号 reject(new Error('参数错误:必须传入有效的手机号'));
const users = wx.getStorageSync('users') || {};
const userId = wx.getStorageSync('userId');
let userPhone = null;
// 尝试从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;
}
}
// 如果没有openid,直接返回未登录错误
if (!openid) {
reject(new Error('用户未登录,无法获取收藏列表'));
return; return;
} }
// 如果没有手机号,直接返回错误 const userPhone = user_phone;
if (!userPhone) {
reject(new Error('无法获取用户手机号,无法获取收藏列表'));
return;
}
const requestData = { const requestData = {
user_phone: userPhone user_phone: userPhone
@ -2164,7 +2150,8 @@ module.exports = {
console.log('获取收藏列表请求数据:', requestData); console.log('获取收藏列表请求数据:', requestData);
request('/api/favorites/list', 'GET', requestData).then(res => { // 将GET请求改为POST请求,因为GET请求通常不处理请求体
request('/api/favorites/list', 'POST', requestData).then(res => {
console.log('获取收藏列表成功:', res); console.log('获取收藏列表成功:', res);
resolve(res); resolve(res);
}).catch(error => { }).catch(error => {
@ -2333,13 +2320,21 @@ module.exports = {
// 验证用户登录状态 // 验证用户登录状态
validateUserLogin: function () { validateUserLogin: function () {
const openid = wx.getStorageSync('openid'); const openid = wx.getStorageSync('openid');
console.log('API.validateUserLogin - openid:', openid); const phoneNumber = wx.getStorageSync('phoneNumber') || '';
console.log('API.validateUserLogin - openid:', openid, 'phoneNumber:', phoneNumber);
// 验证登录状态和手机号
if (!openid) { if (!openid) {
return Promise.reject(new Error('用户未登录')); return Promise.reject(new Error('用户未登录'));
} }
if (!phoneNumber) {
return Promise.reject(new Error('用户未完成手机号授权'));
}
return request('/api/user/validate', 'POST', { return request('/api/user/validate', 'POST', {
openid: openid openid: openid,
phoneNumber: phoneNumber
}); });
}, },
@ -2704,6 +2699,12 @@ module.exports = {
return request('/api/products/update-contacts', 'POST'); return request('/api/products/update-contacts', 'POST');
}, },
// 获取产品详情
getProductDetail: function ({ productId }) {
console.log('API.getProductDetail - productId:', productId);
return request('/api/products/detail', 'POST', { productId: productId });
},
// 预约商品 // 预约商品
reserveProduct: function ({ id }) { reserveProduct: function ({ id }) {
return request('/api/products/reserve', 'POST', { productId: id }); return request('/api/products/reserve', 'POST', { productId: id });
@ -2804,8 +2805,6 @@ module.exports = {
cancelFavorite: function (productId) { cancelFavorite: function (productId) {
console.log('API.cancelFavorite - productId:', productId); console.log('API.cancelFavorite - productId:', productId);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const openid = wx.getStorageSync('openid');
// 获取用户信息,包含手机号 // 获取用户信息,包含手机号
const users = wx.getStorageSync('users') || {}; const users = wx.getStorageSync('users') || {};
const userId = wx.getStorageSync('userId'); const userId = wx.getStorageSync('userId');
@ -2819,18 +2818,15 @@ module.exports = {
const userInfo = wx.getStorageSync('userInfo'); const userInfo = wx.getStorageSync('userInfo');
if (userInfo && userInfo.phoneNumber) { if (userInfo && userInfo.phoneNumber) {
userPhone = userInfo.phoneNumber; userPhone = userInfo.phoneNumber;
} else {
// 尝试从直接存储的phoneNumber获取
userPhone = wx.getStorageSync('phoneNumber');
} }
} }
// 如果没有openid,直接返回未登录错误
if (!openid) {
reject(new Error('用户未登录,无法取消收藏'));
return;
}
// 如果没有手机号,直接返回错误 // 如果没有手机号,直接返回错误
if (!userPhone) { if (!userPhone) {
reject(new Error('无法获取用户手机号,无法取消收藏')); reject(new Error('请先登录并绑定手机号'));
return; return;
} }

Loading…
Cancel
Save