yingsa/miniprogram/app.js
Ethanfly d07ebb735a fix(env): Update API URL and enhance HTML structure
- Change VITE_API_URL in .env from localhost:3000 to localhost:3001 for backend access.
- Update index.html to replace favicon with logo.png and ensure proper HTML structure.
- Add new dependencies for WangEditor in package.json and package-lock.json to support rich text editing features.
2026-02-06 19:04:16 +08:00

482 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const config = require("./config");
App({
globalData: {
userInfo: null,
token: null,
currentStore: null,
ladderUser: null,
wsConnected: false,
// 微信登录临时信息
wxLoginInfo: null,
// 从配置文件读取
baseUrl: config.baseUrl,
wsUrl: config.wsUrl,
},
onLaunch() {
// 从本地存储读取token
const token = wx.getStorageSync("token");
if (token) {
this.globalData.token = token;
this.getUserInfo();
}
},
// 微信登录第一步获取openid和session_key
wxLogin() {
return new Promise((resolve, reject) => {
wx.login({
success: (res) => {
wx.request({
url: `${this.globalData.baseUrl}/api/user/login`,
method: "POST",
data: { code: res.code },
success: (loginRes) => {
if (loginRes.data.code === 0) {
const data = loginRes.data.data;
// 保存微信登录信息(用于后续手机号授权)
this.globalData.wxLoginInfo = {
openid: data.openid,
unionid: data.unionid,
sessionKey: data.sessionKey,
isNewUser: data.isNewUser,
hasPhone: data.hasPhone,
};
// 如果已有token老用户直接使用
if (data.userInfo && data.hasPhone) {
// 老用户已绑定手机号生成token并登录
this.globalData.userInfo = data.userInfo;
}
resolve(data);
} else {
reject(loginRes.data);
}
},
fail: reject,
});
},
fail: reject,
});
});
},
// 手机号授权登录(第二步:解密手机号完成注册/登录)
phoneLogin(encryptedData, iv, gender) {
return new Promise((resolve, reject) => {
const wxInfo = this.globalData.wxLoginInfo;
if (!wxInfo) {
reject({ message: "请先进行微信登录" });
return;
}
const requestData = {
openid: wxInfo.openid,
unionid: wxInfo.unionid,
sessionKey: wxInfo.sessionKey,
encryptedData,
iv,
};
// 如果提供了性别参数,添加到请求中
if (gender === 1 || gender === 2) {
requestData.gender = gender;
}
wx.request({
url: `${this.globalData.baseUrl}/api/user/phone-login`,
method: "POST",
data: requestData,
success: (loginRes) => {
if (loginRes.data.code === 0) {
this.globalData.token = loginRes.data.data.token;
this.globalData.userInfo = loginRes.data.data.userInfo;
// 处理天梯用户信息
if (
loginRes.data.data.userInfo.ladderUsers &&
loginRes.data.data.userInfo.ladderUsers.length > 0
) {
// 如果有当前门店,优先选择当前门店的天梯用户
if (
this.globalData.currentStore &&
this.globalData.currentStore.storeId
) {
const currentStoreLadderUser =
loginRes.data.data.userInfo.ladderUsers.find(
(lu) => lu.storeId === this.globalData.currentStore.storeId,
);
if (currentStoreLadderUser) {
this.globalData.ladderUser = currentStoreLadderUser;
} else {
// 当前门店没有天梯用户,取第一个
this.globalData.ladderUser =
loginRes.data.data.userInfo.ladderUsers[0];
}
} else {
// 没有当前门店,取第一个天梯用户
this.globalData.ladderUser =
loginRes.data.data.userInfo.ladderUsers[0];
}
} else {
// 没有天梯用户
this.globalData.ladderUser = null;
}
wx.setStorageSync("token", loginRes.data.data.token);
this.connectWebSocket();
resolve(loginRes.data.data);
} else {
reject(loginRes.data);
}
},
fail: reject,
});
});
},
// 旧的登录方法(兼容)
login() {
return this.wxLogin();
},
// 获取用户信息
getUserInfo() {
return new Promise((resolve, reject) => {
this.request("/api/user/info")
.then((res) => {
this.globalData.userInfo = res.data;
// 处理天梯用户信息
if (res.data.ladderUsers && res.data.ladderUsers.length > 0) {
// 如果有当前门店,优先选择当前门店的天梯用户
if (
this.globalData.currentStore &&
this.globalData.currentStore.storeId
) {
const currentStoreLadderUser = res.data.ladderUsers.find(
(lu) => lu.storeId === this.globalData.currentStore.storeId,
);
if (currentStoreLadderUser) {
this.globalData.ladderUser = currentStoreLadderUser;
} else {
// 当前门店没有天梯用户,取第一个
this.globalData.ladderUser = res.data.ladderUsers[0];
}
} else {
// 没有当前门店,取第一个天梯用户
this.globalData.ladderUser = res.data.ladderUsers[0];
}
} else {
// 没有天梯用户
this.globalData.ladderUser = null;
}
this.connectWebSocket();
resolve(res.data);
})
.catch(reject);
});
},
// 获取当前门店
getCurrentStore() {
return new Promise((resolve, reject) => {
wx.getLocation({
type: "gcj02",
success: (loc) => {
this.request("/api/user/current-store", {
latitude: loc.latitude,
longitude: loc.longitude,
})
.then((res) => {
this.globalData.currentStore = res.data;
// 如果当前门店有 ladderUserId获取该门店的天梯用户信息
if (res.data && res.data.ladderUserId) {
this.getLadderUser(res.data.storeId);
} else if (res.data && res.data.storeId) {
// 如果当前门店没有 ladderUserId但用户信息中有该门店的天梯用户使用它
if (
this.globalData.userInfo &&
this.globalData.userInfo.ladderUsers
) {
const currentStoreLadderUser =
this.globalData.userInfo.ladderUsers.find(
(lu) => lu.storeId === res.data.storeId,
);
if (currentStoreLadderUser) {
this.globalData.ladderUser = currentStoreLadderUser;
} else {
// 当前门店没有天梯用户,清空
this.globalData.ladderUser = null;
}
}
}
resolve(res.data);
})
.catch(reject);
},
fail: () => {
// 无法获取位置,使用默认门店
this.request("/api/user/current-store")
.then((res) => {
this.globalData.currentStore = res.data;
// 如果当前门店有 ladderUserId获取该门店的天梯用户信息
if (res.data && res.data.ladderUserId) {
this.getLadderUser(res.data.storeId);
} else if (res.data && res.data.storeId) {
// 如果当前门店没有 ladderUserId但用户信息中有该门店的天梯用户使用它
if (
this.globalData.userInfo &&
this.globalData.userInfo.ladderUsers
) {
const currentStoreLadderUser =
this.globalData.userInfo.ladderUsers.find(
(lu) => lu.storeId === res.data.storeId,
);
if (currentStoreLadderUser) {
this.globalData.ladderUser = currentStoreLadderUser;
} else {
// 当前门店没有天梯用户,清空
this.globalData.ladderUser = null;
}
}
}
resolve(res.data);
})
.catch(reject);
},
});
});
},
// 获取天梯用户信息
getLadderUser(storeId) {
return this.request("/api/user/ladder-info", { store_id: storeId }).then(
(res) => {
if (res.data && res.data.length > 0) {
this.globalData.ladderUser = res.data[0];
} else {
// 没有天梯用户时清空
this.globalData.ladderUser = null;
}
return res.data;
},
);
},
// WebSocket连接
connectWebSocket() {
if (this.globalData.wsConnected || !this.globalData.token) return;
const wsUrl = this.globalData.wsUrl || "ws://localhost:3000/ws";
this.ws = wx.connectSocket({
url: wsUrl,
success: () => {
console.log("WebSocket连接中...");
},
});
wx.onSocketOpen(() => {
console.log("WebSocket已连接");
this.globalData.wsConnected = true;
// 发送认证
wx.sendSocketMessage({
data: JSON.stringify({
type: "auth",
token: this.globalData.token,
}),
});
});
wx.onSocketMessage((res) => {
const data = JSON.parse(res.data);
this.handleWsMessage(data);
});
wx.onSocketClose(() => {
console.log("WebSocket已断开");
this.globalData.wsConnected = false;
// 尝试重连
setTimeout(() => {
this.connectWebSocket();
}, 5000);
});
wx.onSocketError((err) => {
console.error("WebSocket错误:", err);
});
},
// 处理WebSocket消息
handleWsMessage(data) {
switch (data.type) {
case "challenge_request":
// 收到挑战请求 - 使用自定义弹框
const challengeData = data.data;
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
// 如果当前页面有处理挑战请求的方法,调用它
if (
currentPage &&
typeof currentPage.handleChallengeRequest === "function"
) {
currentPage.handleChallengeRequest(challengeData);
} else {
// 否则使用系统弹框
wx.showModal({
title: "收到挑战",
content: `${challengeData.challenger.realName}(Lv${challengeData.challenger.level}, 战力${challengeData.challenger.powerScore}) 向你发起挑战`,
confirmText: "接受",
cancelText: "拒绝",
success: (res) => {
this.request(
"/api/match/challenge/respond",
{
match_id: challengeData.matchId,
accept: res.confirm,
},
"POST",
)
.then(() => {
if (res.confirm) {
wx.showToast({ title: "已接受挑战", icon: "success" });
// 跳转到挑战赛详情
setTimeout(() => {
wx.navigateTo({
url: `/pages/match/challenge-detail/index?id=${challengeData.matchId}`,
});
}, 1500);
} else {
wx.showToast({ title: "已拒绝挑战", icon: "success" });
}
})
.catch((err) => {
console.error("响应挑战失败:", err);
wx.showToast({ title: "操作失败", icon: "none" });
});
},
});
}
break;
case "challenge_accepted":
// 挑战被接受
wx.showToast({ title: "对方已接受挑战", icon: "success" });
// 如果当前在挑战赛详情页面,刷新数据
const pages2 = getCurrentPages();
const currentPage2 = pages2[pages2.length - 1];
if (
currentPage2 &&
currentPage2.route === "pages/match/challenge-detail/index"
) {
if (typeof currentPage2.loadMatchDetail === "function") {
currentPage2.loadMatchDetail();
}
}
break;
case "challenge_rejected":
// 挑战被拒绝
wx.showToast({ title: "对方已拒绝挑战", icon: "none" });
break;
case "score_confirm_request":
// 收到比分确认请求
wx.showModal({
title: "确认比分",
content: `比分: ${data.data.player1Score} : ${data.data.player2Score}`,
confirmText: "确认",
cancelText: "有争议",
success: (res) => {
this.request(
"/api/match/challenge/confirm-score",
{
game_id: data.data.gameId,
confirm: res.confirm,
},
"POST",
);
},
});
break;
case "match_paired":
// 排位赛匹配通知
wx.showModal({
title: "匹配成功",
content: `你的对手是: ${data.data.opponent.realName}`,
showCancel: false,
});
break;
case "ranking_game_updated":
// 排位赛比分/匹配更新:刷新当前排位赛详情页
try {
const pages3 = getCurrentPages();
const currentPage3 = pages3[pages3.length - 1];
if (
currentPage3 &&
currentPage3.route === "pages/match/ranking/index"
) {
const targetCode = data.data && data.data.matchCode;
if (
!targetCode ||
!currentPage3.data ||
currentPage3.data.matchCode === targetCode
) {
if (typeof currentPage3.fetchMatchDetail === "function") {
currentPage3.fetchMatchDetail();
}
}
}
} catch (e) {
console.error("处理 ranking_game_updated 消息失败:", e);
}
break;
}
},
// 封装请求
request(url, data = {}, method = "GET") {
return new Promise((resolve, reject) => {
const showErrorToast = (message) => {
const title =
(message && String(message).trim()) || "网络异常,请稍后重试";
wx.showToast({ title, icon: "none" });
};
wx.request({
url: `${this.globalData.baseUrl}${url}`,
method,
data,
header: {
Authorization: `Bearer ${this.globalData.token}`,
},
success: (res) => {
if (res.data.code === 0) {
resolve(res.data);
} else if (res.data.code === 401) {
// 登录过期
this.globalData.token = null;
wx.removeStorageSync("token");
showErrorToast("登录已过期,请重新登录");
wx.reLaunch({ url: "/pages/user/index" });
reject({ message: res.data.message || "登录已过期", ...res.data });
} else {
showErrorToast(res.data.message || "请求失败");
reject({ message: res.data.message || "请求失败", ...res.data });
}
},
fail: (err) => {
showErrorToast("网络异常,请检查网络后重试");
reject({ message: "网络异常,请检查网络后重试", err });
},
});
});
},
});