- Added a new method to fetch the default store for users without a login, allowing them to browse store rankings. - Updated the wxLogin function to streamline the login process for users with existing phone numbers, enabling direct token retrieval. - Refactored various page components to utilize the new getDefaultStore method for better user experience when accessing store information. - Enhanced error handling and data synchronization for user and store information across multiple pages.
595 lines
17 KiB
JavaScript
595 lines
17 KiB
JavaScript
const app = getApp();
|
||
const util = require("../../utils/util");
|
||
|
||
Page({
|
||
data: {
|
||
userInfo: null,
|
||
ladderUser: null,
|
||
currentStore: null,
|
||
showQrcode: false,
|
||
qrcodeImage: "",
|
||
qrcodeLoading: false,
|
||
// 完善资料弹框
|
||
showProfileModal: false,
|
||
profileForm: {
|
||
avatar: "",
|
||
nickname: "",
|
||
gender: 0,
|
||
},
|
||
isEditMode: false, // true: 编辑模式,false: 完善模式(登录时)
|
||
|
||
showGenderModal: false,
|
||
registerGender: 0,
|
||
|
||
// 活动公告(最近 10 条)
|
||
announcements: [],
|
||
},
|
||
|
||
normalizeLadderUser(ladderUser) {
|
||
if (!ladderUser) return null;
|
||
const matchCount = Number(ladderUser.matchCount || 0);
|
||
const winCount = Number(ladderUser.winCount || 0);
|
||
const loseCount = Math.max(matchCount - winCount, 0);
|
||
const winRate = matchCount > 0 ? Math.round((winCount / matchCount) * 100) : 0;
|
||
return Object.assign({}, ladderUser, {
|
||
matchCount,
|
||
winCount,
|
||
loseCount,
|
||
winRate,
|
||
});
|
||
},
|
||
|
||
onLoad() {
|
||
this.initData();
|
||
},
|
||
|
||
onShow() {
|
||
// 检查门店是否切换
|
||
if (app.globalData.storeChanged) {
|
||
app.globalData.storeChanged = false;
|
||
this.refreshData();
|
||
} else {
|
||
// 同步最新数据
|
||
this.setData({
|
||
userInfo: app.globalData.userInfo,
|
||
ladderUser: this.normalizeLadderUser(app.globalData.ladderUser),
|
||
currentStore: app.globalData.currentStore,
|
||
});
|
||
this.fetchAnnouncements();
|
||
}
|
||
},
|
||
|
||
async onPullDownRefresh() {
|
||
try {
|
||
await this.refreshData();
|
||
} finally {
|
||
wx.stopPullDownRefresh();
|
||
}
|
||
},
|
||
|
||
async initData() {
|
||
// 微信登录:已有手机号用户会直接完成登录,无手机号用户需授权手机号
|
||
if (!app.globalData.wxLoginInfo) {
|
||
try {
|
||
await app.wxLogin();
|
||
} catch (e) {
|
||
console.error("微信登录失败:", e);
|
||
}
|
||
}
|
||
|
||
if (app.globalData.token) {
|
||
await this.refreshData();
|
||
} else {
|
||
// 无 token 时同步当前状态(如 wxLoginInfo)到页面,用于判断是否显示手机号登录
|
||
this.setData({
|
||
userInfo: app.globalData.userInfo,
|
||
ladderUser: null,
|
||
currentStore: app.globalData.currentStore,
|
||
});
|
||
}
|
||
},
|
||
|
||
async refreshData() {
|
||
if (!app.globalData.token) return;
|
||
|
||
try {
|
||
const store = app.globalData.currentStore;
|
||
// 传入当前门店 ID,获取该门店的积分和天梯信息
|
||
await app.getUserInfo(store?.storeId);
|
||
|
||
// 有当前门店时,拉取该门店的天梯用户信息(确保战力数据与当前门店一致)
|
||
if (store && store.storeId) {
|
||
try {
|
||
await app.getLadderUser(store.storeId);
|
||
} catch (e) {
|
||
console.error("获取天梯用户信息失败:", e);
|
||
}
|
||
}
|
||
|
||
this.setData({
|
||
userInfo: app.globalData.userInfo,
|
||
ladderUser: this.normalizeLadderUser(app.globalData.ladderUser),
|
||
currentStore: app.globalData.currentStore,
|
||
announcements: [], // 先清空,避免切换门店时短暂显示旧门店公告
|
||
});
|
||
|
||
await this.fetchAnnouncements();
|
||
} catch (e) {
|
||
console.error("获取用户信息失败:", e);
|
||
}
|
||
},
|
||
|
||
// 获取当前门店的活动公告(最近 10 条)
|
||
async fetchAnnouncements() {
|
||
if (!app.globalData.token) return;
|
||
const store = app.globalData.currentStore;
|
||
if (!store || !store.storeId) return;
|
||
try {
|
||
const res = await app.request("/api/article", {
|
||
limit: 10,
|
||
store_id: store.storeId,
|
||
});
|
||
const raw = res.data || [];
|
||
const list = raw.map((a) => ({
|
||
id: a.id,
|
||
title: a.title || "",
|
||
isTop: !!a.isTop,
|
||
timeText: a.createdAt ? util.formatDate(a.createdAt) : "",
|
||
}));
|
||
this.setData({ announcements: list });
|
||
} catch (e) {
|
||
console.error("获取活动公告失败:", e);
|
||
}
|
||
},
|
||
|
||
// 获取手机号授权
|
||
async onGetPhoneNumber(e) {
|
||
if (e.detail.errMsg !== "getPhoneNumber:ok") {
|
||
wx.showToast({ title: "需要授权手机号才能登录", icon: "none" });
|
||
return;
|
||
}
|
||
|
||
try {
|
||
wx.showLoading({ title: "登录中..." });
|
||
|
||
// 如果没有微信登录信息,先登录
|
||
if (!app.globalData.wxLoginInfo) {
|
||
await app.wxLogin();
|
||
}
|
||
|
||
const needGender =
|
||
app.globalData.wxLoginInfo && app.globalData.wxLoginInfo.isNewUser;
|
||
|
||
if (
|
||
needGender &&
|
||
!(this.data.registerGender === 1 || this.data.registerGender === 2)
|
||
) {
|
||
this._pendingPhoneLogin = {
|
||
encryptedData: e.detail.encryptedData,
|
||
iv: e.detail.iv,
|
||
};
|
||
wx.hideLoading();
|
||
this.setData({ showGenderModal: true });
|
||
return;
|
||
}
|
||
|
||
await this.doPhoneLogin(
|
||
e.detail.encryptedData,
|
||
e.detail.iv,
|
||
needGender ? this.data.registerGender : undefined,
|
||
);
|
||
|
||
// 获取门店信息
|
||
await app.ensureCurrentStore();
|
||
|
||
const userInfo = app.globalData.userInfo;
|
||
|
||
this.setData({
|
||
userInfo: userInfo,
|
||
ladderUser: this.normalizeLadderUser(app.globalData.ladderUser),
|
||
currentStore: app.globalData.currentStore,
|
||
});
|
||
await this.fetchAnnouncements();
|
||
|
||
// 检查是否需要完善资料(没有头像或昵称为默认值)
|
||
const needProfile =
|
||
!userInfo.avatar ||
|
||
userInfo.avatar === "" ||
|
||
!userInfo.nickname ||
|
||
userInfo.nickname === "新用户" ||
|
||
userInfo.nickname === "";
|
||
|
||
if (needProfile) {
|
||
// 弹出完善资料弹框
|
||
this.setData({
|
||
showProfileModal: true,
|
||
isEditMode: false,
|
||
profileForm: {
|
||
avatar: userInfo.avatar || "/images/avatar-default.svg",
|
||
nickname:
|
||
userInfo.nickname === "新用户" ? "" : userInfo.nickname || "",
|
||
},
|
||
});
|
||
wx.showToast({ title: "登录成功,请完善资料", icon: "none" });
|
||
} else {
|
||
wx.showToast({ title: "登录成功", icon: "success" });
|
||
}
|
||
} catch (e) {
|
||
console.error("登录失败:", e);
|
||
wx.showToast({ title: e.message || "登录失败", icon: "none" });
|
||
} finally {
|
||
wx.hideLoading();
|
||
}
|
||
},
|
||
|
||
async doPhoneLogin(encryptedData, iv, gender) {
|
||
const g = gender === 1 || gender === 2 ? gender : undefined;
|
||
await app.phoneLogin(encryptedData, iv, g);
|
||
this._pendingPhoneLogin = null;
|
||
this.setData({ showGenderModal: false });
|
||
},
|
||
|
||
onSelectRegisterGender(e) {
|
||
const gender = Number(e.currentTarget.dataset.gender);
|
||
if (gender !== 1 && gender !== 2) return;
|
||
this.setData({ registerGender: gender });
|
||
},
|
||
|
||
async onConfirmRegisterGender() {
|
||
if (!(this.data.registerGender === 1 || this.data.registerGender === 2)) {
|
||
wx.showToast({ title: "请选择性别", icon: "none" });
|
||
return;
|
||
}
|
||
|
||
const pending = this._pendingPhoneLogin;
|
||
if (!pending) {
|
||
this.setData({ showGenderModal: false });
|
||
return;
|
||
}
|
||
|
||
wx.showLoading({ title: "登录中..." });
|
||
try {
|
||
await this.doPhoneLogin(
|
||
pending.encryptedData,
|
||
pending.iv,
|
||
this.data.registerGender,
|
||
);
|
||
|
||
await app.ensureCurrentStore();
|
||
const userInfo = app.globalData.userInfo;
|
||
this.setData({
|
||
userInfo: userInfo,
|
||
ladderUser: this.normalizeLadderUser(app.globalData.ladderUser),
|
||
currentStore: app.globalData.currentStore,
|
||
});
|
||
await this.fetchAnnouncements();
|
||
|
||
const needProfile =
|
||
!userInfo.avatar ||
|
||
userInfo.avatar === "" ||
|
||
!userInfo.nickname ||
|
||
userInfo.nickname === "新用户" ||
|
||
userInfo.nickname === "";
|
||
|
||
if (needProfile) {
|
||
this.setData({
|
||
showProfileModal: true,
|
||
isEditMode: false,
|
||
profileForm: {
|
||
avatar: userInfo.avatar || "/images/avatar-default.svg",
|
||
nickname:
|
||
userInfo.nickname === "新用户" ? "" : userInfo.nickname || "",
|
||
gender: userInfo.gender || 0,
|
||
},
|
||
});
|
||
wx.showToast({ title: "登录成功,请完善资料", icon: "none" });
|
||
} else {
|
||
wx.showToast({ title: "登录成功", icon: "success" });
|
||
}
|
||
} catch (e) {
|
||
console.error("登录失败:", e);
|
||
wx.showToast({ title: e.message || "登录失败", icon: "none" });
|
||
} finally {
|
||
wx.hideLoading();
|
||
}
|
||
},
|
||
|
||
// 查看更多活动公告
|
||
goAnnouncementMore() {
|
||
if (!app.globalData.token) {
|
||
wx.showToast({ title: "请先登录", icon: "none" });
|
||
return;
|
||
}
|
||
wx.navigateTo({
|
||
url: "/pages/article/list/index",
|
||
});
|
||
},
|
||
|
||
// 查看公告详情
|
||
goAnnouncementDetail(e) {
|
||
const id = e.currentTarget.dataset.id;
|
||
if (!id) return;
|
||
wx.navigateTo({
|
||
url: `/pages/article/detail/index?id=${id}`,
|
||
});
|
||
},
|
||
|
||
onCancelRegisterGender() {
|
||
this._pendingPhoneLogin = null;
|
||
this.setData({ showGenderModal: false, registerGender: 0 });
|
||
},
|
||
|
||
// 点击头像,打开编辑资料弹框
|
||
onTapAvatar() {
|
||
if (!this.data.userInfo || !this.data.userInfo.phone) return;
|
||
|
||
this.setData({
|
||
showProfileModal: true,
|
||
isEditMode: true,
|
||
profileForm: {
|
||
avatar: this.data.userInfo.avatar || "/images/avatar-default.svg",
|
||
nickname: this.data.userInfo.nickname || "",
|
||
gender: this.data.userInfo.gender || 0,
|
||
},
|
||
});
|
||
},
|
||
|
||
// 选择头像(新API:button open-type="chooseAvatar")
|
||
async onChooseAvatarNew(e) {
|
||
const avatarUrl = e.detail.avatarUrl;
|
||
console.log('选择头像:', avatarUrl);
|
||
|
||
// 检查是否是临时文件
|
||
if (avatarUrl && (avatarUrl.startsWith("wxfile://") ||
|
||
avatarUrl.startsWith("http://tmp") ||
|
||
avatarUrl.includes("/tmp/"))) {
|
||
console.log('检测到临时头像,立即上传');
|
||
wx.showLoading({ title: "上传头像中..." });
|
||
|
||
try {
|
||
const uploadedUrl = await this.uploadAvatar(avatarUrl);
|
||
console.log('头像上传成功:', uploadedUrl);
|
||
|
||
this.setData({
|
||
"profileForm.avatar": uploadedUrl,
|
||
});
|
||
|
||
wx.hideLoading();
|
||
wx.showToast({ title: "头像上传成功", icon: "success", duration: 1500 });
|
||
} catch (uploadErr) {
|
||
wx.hideLoading();
|
||
console.error("头像上传失败:", uploadErr);
|
||
wx.showToast({
|
||
title: uploadErr.message || "头像上传失败",
|
||
icon: "none",
|
||
duration: 2000
|
||
});
|
||
// 上传失败,不设置头像
|
||
}
|
||
} else {
|
||
// 不是临时文件,直接使用
|
||
this.setData({
|
||
"profileForm.avatar": avatarUrl,
|
||
});
|
||
}
|
||
},
|
||
|
||
// 选择头像(使用 wx.chooseMedia)
|
||
onChooseAvatar() {
|
||
wx.chooseMedia({
|
||
count: 1,
|
||
mediaType: ['image'],
|
||
sourceType: ['album', 'camera'],
|
||
sizeType: ['compressed'],
|
||
success: (res) => {
|
||
console.log('选择图片成功:', res);
|
||
const tempFilePath = res.tempFiles[0].tempFilePath;
|
||
this.setData({
|
||
"profileForm.avatar": tempFilePath,
|
||
});
|
||
},
|
||
fail: (err) => {
|
||
console.error('选择图片失败:', err);
|
||
// 如果 chooseMedia 不支持,降级使用 chooseImage
|
||
wx.chooseImage({
|
||
count: 1,
|
||
sourceType: ['album', 'camera'],
|
||
sizeType: ['compressed'],
|
||
success: (res) => {
|
||
console.log('选择图片成功(降级):', res);
|
||
const tempFilePath = res.tempFilePaths[0];
|
||
this.setData({
|
||
"profileForm.avatar": tempFilePath,
|
||
});
|
||
},
|
||
fail: (err) => {
|
||
console.error('选择图片失败:', err);
|
||
wx.showToast({ title: '选择图片失败', icon: 'none' });
|
||
}
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
// 输入昵称
|
||
onNicknameInput(e) {
|
||
this.setData({
|
||
"profileForm.nickname": e.detail.value,
|
||
});
|
||
},
|
||
|
||
onProfileGenderSelect(e) {
|
||
const gender = Number(e.currentTarget.dataset.gender);
|
||
if (gender !== 1 && gender !== 2) return;
|
||
this.setData({
|
||
"profileForm.gender": gender,
|
||
});
|
||
},
|
||
|
||
// 确认保存资料
|
||
async saveProfile() {
|
||
const { avatar, nickname, gender } = this.data.profileForm;
|
||
|
||
if (!nickname || nickname.trim() === "") {
|
||
wx.showToast({ title: "请输入昵称", icon: "none" });
|
||
return;
|
||
}
|
||
|
||
wx.showLoading({ title: "保存中..." });
|
||
|
||
try {
|
||
// 头像已经在选择时上传,这里直接使用
|
||
const avatarUrl = avatar;
|
||
|
||
// 调用更新资料接口
|
||
const payload = {
|
||
nickname: nickname.trim(),
|
||
avatar: avatarUrl,
|
||
};
|
||
if (gender === 1 || gender === 2) {
|
||
payload.gender = gender;
|
||
}
|
||
|
||
console.log("保存资料请求:", payload);
|
||
const res = await app.request("/api/user/profile", payload, "PUT");
|
||
|
||
// 更新本地数据(服务端已返回完整URL)
|
||
const userInfo = Object.assign({}, this.data.userInfo, {
|
||
nickname: (res.data && res.data.nickname) || nickname.trim(),
|
||
avatar: (res.data && res.data.avatar) || avatarUrl,
|
||
gender: (res.data && res.data.gender) || this.data.userInfo.gender || 0,
|
||
});
|
||
app.globalData.userInfo = userInfo;
|
||
|
||
this.setData({
|
||
userInfo: userInfo,
|
||
showProfileModal: false,
|
||
profileForm: { avatar: "", nickname: "", gender: 0 },
|
||
});
|
||
|
||
wx.hideLoading();
|
||
wx.showToast({ title: "保存成功", icon: "success" });
|
||
} catch (e) {
|
||
wx.hideLoading();
|
||
console.error("保存资料失败:", e);
|
||
wx.showToast({ title: e.message || "保存失败", icon: "none" });
|
||
}
|
||
},
|
||
|
||
// 上传头像
|
||
async uploadAvatar(filePath) {
|
||
return new Promise((resolve, reject) => {
|
||
console.log('开始上传头像');
|
||
console.log('文件路径:', filePath);
|
||
console.log('上传URL:', `${app.globalData.baseUrl}/api/upload/avatar`);
|
||
console.log('Token:', app.globalData.token);
|
||
|
||
if (!app.globalData.token) {
|
||
reject(new Error('未登录,无法上传头像'));
|
||
return;
|
||
}
|
||
|
||
wx.uploadFile({
|
||
url: `${app.globalData.baseUrl}/api/upload/avatar`,
|
||
filePath: filePath,
|
||
name: "file",
|
||
header: {
|
||
Authorization: `Bearer ${app.globalData.token}`,
|
||
},
|
||
success: (res) => {
|
||
try {
|
||
console.log("上传头像响应状态:", res.statusCode);
|
||
console.log("上传头像响应数据:", res.data);
|
||
|
||
if (res.statusCode !== 200) {
|
||
reject(new Error(`上传失败,状态码: ${res.statusCode}`));
|
||
return;
|
||
}
|
||
|
||
const data = JSON.parse(res.data);
|
||
if (data.code === 0 && data.data && data.data.url) {
|
||
console.log("头像上传成功:", data.data.url);
|
||
resolve(data.data.url);
|
||
} else {
|
||
console.error("上传头像失败:", data);
|
||
reject(new Error(data.message || "上传头像失败"));
|
||
}
|
||
} catch (e) {
|
||
console.error("解析上传响应失败:", e);
|
||
reject(new Error("上传头像失败"));
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.error("上传头像请求失败:", err);
|
||
reject(new Error("上传头像失败,请检查网络"));
|
||
},
|
||
});
|
||
});
|
||
},
|
||
|
||
// 关闭资料弹框
|
||
closeProfileModal() {
|
||
// 如果是完善模式,提示用户
|
||
if (!this.data.isEditMode) {
|
||
wx.showModal({
|
||
title: "提示",
|
||
content: "完善资料后可以让好友更容易找到你,确定跳过?",
|
||
confirmText: "跳过",
|
||
cancelText: "继续完善",
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
this.setData({ showProfileModal: false });
|
||
}
|
||
},
|
||
});
|
||
} else {
|
||
this.setData({ showProfileModal: false });
|
||
}
|
||
},
|
||
|
||
async showMemberCode() {
|
||
if (!this.data.userInfo || !this.data.userInfo.memberCode) return;
|
||
|
||
this.setData({
|
||
showQrcode: true,
|
||
qrcodeLoading: true,
|
||
});
|
||
|
||
try {
|
||
// 调用接口获取二维码
|
||
const res = await app.request("/api/user/qrcode");
|
||
if (res.data && res.data.qrcode) {
|
||
this.setData({
|
||
qrcodeImage: res.data.qrcode,
|
||
qrcodeLoading: false,
|
||
});
|
||
}
|
||
} catch (e) {
|
||
console.error("获取二维码失败:", e);
|
||
this.setData({ qrcodeLoading: false });
|
||
wx.showToast({ title: "获取二维码失败", icon: "none" });
|
||
}
|
||
},
|
||
|
||
hideQrcode() {
|
||
this.setData({
|
||
showQrcode: false,
|
||
qrcodeImage: "",
|
||
});
|
||
},
|
||
|
||
goTo(e) {
|
||
const url = e.currentTarget.dataset.url;
|
||
if (!app.globalData.token) {
|
||
wx.showToast({ title: "请先登录", icon: "none" });
|
||
return;
|
||
}
|
||
wx.navigateTo({ url });
|
||
},
|
||
|
||
// 阻止事件冒泡
|
||
preventBubble() {
|
||
// 空函数,仅用于阻止事件冒泡
|
||
},
|
||
});
|