Files
youlegames/codes/minipro/calculation/miniprogram/pages/profile/profile.ts
2026-02-04 23:47:45 +08:00

680 lines
26 KiB
TypeScript
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.
// profile.ts (Copied from previous index.ts)
import { config } from '../../config';
import { remoteConfig } from '../../utils/remoteConfig';
// 获取应用实例
const app = getApp<IAppOption>()
Component({
data: {
motto: 'Hello World',
loginHint: config.loginHint,
userInfo: {
avatarUrl: config.defaultAvatarUrl,
nickName: '',
gender: 0, // 0:未知, 1:男, 2:女
country: '',
province: '',
city: '',
language: ''
},
openid: '',
unionid: '',
phoneNumber: '',
playerid: '',
verificationCode: '',
hasUserInfo: false,
authMode: config.authMode || 'oa', // 'oa' | 'mp'
isTestEnabled: config.testConfig && config.testConfig.enable,
canIUseGetUserProfile: wx.canIUse('getUserProfile'),
canIUseNicknameComp: wx.canIUse('input.type.nickname'),
},
pageLifetimes: {
show() {
// 检查是否有从公众号授权回来的数据
const oaUserInfo = wx.getStorageSync('oa_user_info');
if (oaUserInfo) {
console.log('检测到公众号授权数据:', oaUserInfo);
this.setData({
"userInfo.nickName": oaUserInfo.nickName,
"userInfo.avatarUrl": oaUserInfo.avatarUrl,
"userInfo.gender": oaUserInfo.gender,
"userInfo.country": oaUserInfo.country,
"userInfo.province": oaUserInfo.province,
"userInfo.city": oaUserInfo.city,
"userInfo.language": oaUserInfo.language,
// 如果公众号也返回了 unionid可以更新
unionid: oaUserInfo.unionid || this.data.unionid
});
// 清除缓存,避免重复读取
wx.removeStorageSync('oa_user_info');
// 如果已经有手机号,说明是“先手机后头像”的流程,直接保存并进入
if (this.data.phoneNumber) {
this.saveUserInfoToBackend();
} else {
// 授权回来后,尝试登录游戏服务器获取信息
this.loginToServer();
}
}
}
},
lifetimes: {
attached() {
// 检查本地登录态
this.checkLocalSession();
}
},
methods: {
// 检查本地 Session
checkLocalSession() {
const session = wx.getStorageSync('USER_SESSION');
const now = Date.now();
// 默认 24 小时过期
const expirationHours = config.loginExpirationHours || 24;
const expirationTime = expirationHours * 60 * 60 * 1000;
if (session && session.timestamp && (now - session.timestamp < expirationTime)) {
console.log('恢复本地登录态');
this.setData({
userInfo: session.userInfo,
openid: session.openid,
unionid: session.unionid,
phoneNumber: session.phoneNumber,
playerid: session.playerid,
// 始终保持 hasUserInfo 为 false以显示用户信息和操作按钮
hasUserInfo: false
});
// 恢复后,尝试静默刷新服务端状态(不阻塞 UI
this.loginToServer();
} else {
console.log('本地登录态不存在或已过期');
wx.removeStorageSync('USER_SESSION');
// 页面加载时自动静默登录获取 OpenID但不自动登录游戏服务器等待用户点击登录按钮授权
this.performSilentLogin(false);
}
},
// 弹出设置菜单
onSettingsTap() {
wx.showActionSheet({
itemList: ['注销账号'],
itemColor: '#FF0000', // 红色显示注销
success: (res) => {
if (res.tapIndex === 0) {
this.handleLogout();
}
}
});
},
// 处理注销
handleLogout() {
wx.showModal({
title: '提示',
content: '确定要注销当前账号吗?',
success: (res) => {
if (res.confirm) {
// 1. 清除本地存储
wx.removeStorageSync('USER_SESSION'); // 清除持久化会话
wx.removeStorageSync('oa_user_info'); // 清除可能的 OA 授权信息
// 2. 重置页面数据
this.setData({
userInfo: {
avatarUrl: config.defaultAvatarUrl,
nickName: '',
gender: 0,
country: '',
province: '',
city: '',
language: ''
},
openid: '',
unionid: '',
phoneNumber: '',
playerid: '',
verificationCode: '',
// 重置其他相关状态...
});
wx.showToast({
title: '已注销',
icon: 'success'
});
// 3. 重新执行静默登录以获取基础 OpenID (保持未登录状态)
this.performSilentLogin(false);
}
}
});
},
// 选择头像 (小程序原生)
onChooseAvatar(e: any) {
const { avatarUrl } = e.detail;
this.setData({
"userInfo.avatarUrl": avatarUrl
});
},
// 填写昵称 (小程序原生)
onNicknameChange(e: any) {
const nickName = e.detail.value;
this.setData({
"userInfo.nickName": nickName
});
},
// 跳转到公众号授权页面
goOfficialAccountAuth() {
// 如果是 mp 模式,或者测试模式下配置了 Mock UnionID则跳过公众号授权直接尝试登录
if (config.authMode === 'mp' || (config.testConfig && config.testConfig.enable && config.testConfig.mockUnionId)) {
console.log('跳过公众号授权 (模式: ' + config.authMode + ', 测试: ' + (config.testConfig && config.testConfig.enable) + ')');
this.loginToServer();
return;
}
wx.navigateTo({
url: '/pages/webview/webview'
});
},
// 事件处理函数
bindViewTap() {
wx.navigateTo({
url: '../logs/logs',
})
},
// 静默登录逻辑
performSilentLogin(autoLoginGameServer: boolean = true) {
wx.login({
success: (loginRes) => {
if (loginRes.code) {
console.log('静默登录 code:', loginRes.code);
// 请求本地后端
wx.request({
url: `${config.baseUrl}/api/login`,
method: 'POST',
data: { code: loginRes.code },
success: (res: any) => {
if (res.data.error) {
console.error('静默登录失败:', res.data.error);
return;
}
this.setData({
openid: res.data.openid,
unionid: res.data.unionid || '未获取到UnionID'
});
console.log('OpenID 静默获取成功',res.data);
// 获取到 openid/unionid 后,尝试登录游戏服务器获取玩家信息
if (autoLoginGameServer) {
this.loginToServer();
}
},
fail: (err) => {
console.error('静默登录连接失败:', err);
}
});
}
}
});
},
// 登录游戏服务器
loginToServer() {
// 检查远程配置是否就绪
if (!remoteConfig.isReady()) {
console.log('RemoteConfig not ready, waiting...');
wx.showLoading({ title: '加载配置中...', mask: true });
let hasHandled = false;
remoteConfig.onUpdate(() => {
if (!hasHandled && remoteConfig.isReady()) {
hasHandled = true;
wx.hideLoading();
this.loginToServer();
}
});
return;
}
const { openid, unionid, userInfo } = this.data;
if (!openid) return;
// --- 测试配置注入 ---
let finalUnionId = unionid;
if (config.testConfig && config.testConfig.enable && config.testConfig.mockUnionId) {
console.log('[Test] Using Mock UnionID in loginToServer:', config.testConfig.mockUnionId);
finalUnionId = config.testConfig.mockUnionId;
}
// -------------------
// 获取版本号
const { agentid, gameid, channelid, marketid } = config.remoteConfig;
const version = remoteConfig.getParaValue("game_version", agentid, gameid, channelid, marketid);
console.log("game_version",version);
wx.request({
url: `${config.baseUrl}/api/playerLogin`,
method: 'POST',
data: {
openid,
unionid: finalUnionId,
nickName: userInfo.nickName,
avatarUrl: userInfo.avatarUrl,
gender: userInfo.gender,
province: userInfo.province,
city: userInfo.city,
version: version || 1 // 默认版本号 1
},
success: (res: any) => {
this.handleLoginSuccess(res);
},
fail: (err) => {
console.error('游戏服务器登录失败:', err);
wx.showToast({ title: '连接服务器失败', icon: 'none' });
}
});
},
// 测试登录(带手机号和验证码)
testLoginWithPhone() {
const { openid, unionid, userInfo, phoneNumber, verificationCode } = this.data;
if (!openid) {
wx.showToast({ title: '未获取OpenID', icon: 'none' });
return;
}
if (!phoneNumber) {
wx.showToast({ title: '未获取手机号', icon: 'none' });
return;
}
if (!verificationCode) {
wx.showToast({ title: '未生成随机数', icon: 'none' });
return;
}
// --- 测试配置注入 ---
let finalUnionId = unionid;
if (config.testConfig && config.testConfig.enable && config.testConfig.mockUnionId) {
finalUnionId = config.testConfig.mockUnionId;
}
// -------------------
const { agentid, gameid, channelid, marketid } = config.remoteConfig;
const version = remoteConfig.getParaValue("game_version", agentid, gameid, channelid, marketid);
wx.showLoading({ title: '测试登录中...' });
wx.request({
url: `${config.baseUrl}/api/playerLogin`,
method: 'POST',
// data: {
// openid,
// unionid: finalUnionId,
// nickName: userInfo.nickName,
// avatarUrl: userInfo.avatarUrl,
// gender: userInfo.gender,
// province: userInfo.province,
// city: userInfo.city,
// version: version || 1,
// phoneNumber: phoneNumber,
// verificationCode: verificationCode
// },
data: {
openid:"onJdG10JeHtS0Dbz8FtdVv7aeVB6",
unionid: "oLVKis6bj3_l8qspMybG60KV2GN6",
nickName: "testname",
avatarUrl: "testavatarurl",
gender: 3,
province: "testprovince",
city: "testcity",
version: version || 1,
phoneNumber: phoneNumber,
verificationCode: 11111,
telphoneAuto:true,
playerid:710873
},
success: (res: any) => {
wx.hideLoading();
console.log('测试登录返回:', res.data);
this.handleLoginSuccess(res);
wx.showToast({ title: '测试登录请求已发送', icon: 'none' });
},
fail: (err) => {
wx.hideLoading();
console.error('测试登录失败:', err);
wx.showToast({ title: '测试登录失败', icon: 'none' });
}
});
},
handleLoginSuccess(res: any) {
console.log('游戏服务器登录返回:', res.data);
// res.data 是 wxserver 返回的 { success, message, data }
// res.data.data 是游戏服务器返回的原始包 { rpc, data }
const packet = res.data;
if (packet) {
// 1. 处理异常 RPC (show_message, kick_server)
if (packet.rpc === 'show_message' || packet.rpc === 'kick_server') {
const msg = (packet.data && packet.data.msg) || '未知错误';
wx.showToast({
title: msg,
icon: 'none',
duration: 3000
});
return;
}
// 2. 处理业务错误
if (packet.data && packet.data.error) {
wx.showToast({
title: packet.data.error,
icon: 'none',
duration: 3000
});
return;
}
// 3. 处理登录成功
const gameData = packet.data;
if (gameData && (gameData.playerid || gameData.state === 0)) {
// 兼容不同的字段名
const serverNickName = gameData.nickname || gameData.nickName;
const serverAvatarUrl = gameData.avatar || gameData.headimg || gameData.headimgurl || gameData.avatarUrl;
const serverGender = gameData.sex || gameData.gender;
const newUserInfo = {
...this.data.userInfo,
nickName: serverNickName || this.data.userInfo.nickName,
avatarUrl: serverAvatarUrl || this.data.userInfo.avatarUrl,
gender: serverGender !== undefined ? serverGender : this.data.userInfo.gender
};
this.setData({
userInfo: newUserInfo,
playerid: gameData.playerid || '',
phoneNumber: gameData.tel || ''
});
// 更新本地缓存
wx.setStorageSync('USER_SESSION', {
timestamp: Date.now(),
userInfo: newUserInfo,
openid: this.data.openid,
unionid: this.data.unionid,
phoneNumber: gameData.tel || '',
playerid: gameData.playerid || ''
});
}
}
},
// 获取验证码
getVerificationCode() {
// if (this.data.isCountingDown) return;
const { phoneNumber } = this.data;
if (!phoneNumber) {
wx.showToast({ title: '请先绑定手机号', icon: 'none' });
return;
}
wx.showLoading({ title: '获取中...' });
wx.request({
url: `${config.baseUrl}/api/getPhoneCode`,
method: 'POST',
data: { phonenum: phoneNumber },
success: (res: any) => {
wx.hideLoading();
console.log('验证码返回:', res.data);
const smmcode = res.data && res.data.data && res.data.data.smmcode;
if (smmcode) {
this.setData({ verificationCode: smmcode });
wx.showToast({ title: '获取成功', icon: 'success' });
} else {
wx.showToast({ title: '获取失败', icon: 'none' });
}
},
fail: (err) => {
wx.hideLoading();
console.error('获取验证码请求失败:', err);
wx.showToast({ title: '网络错误', icon: 'none' });
}
});
},
// 复制验证码
copyVerificationCode() {
const { verificationCode } = this.data;
if (!verificationCode) return;
wx.setClipboardData({
data: String(verificationCode),
success: () => {
wx.showToast({ title: '复制成功', icon: 'success' });
}
});
},
getPhoneNumber(e: any) {
// const { avatarUrl, nickName } = this.data.userInfo;
const { openid } = this.data;
// 1. 检查 OpenID 是否已获取
if (!openid) {
wx.showToast({ title: '正在初始化...', icon: 'none' });
// 尝试重新登录
this.performSilentLogin();
return;
}
// 2. 获取手机号
if (e.detail.errMsg === "getPhoneNumber:ok" || e.detail.errMsg.includes("ok")) {
// --- 测试配置Mock 手机号 ---
// 原因:微信小程序获取手机号接口收费。在测试阶段,如果配置了 mockPhoneNumber
// 则直接使用模拟数据,跳过后端请求,从而避免产生费用。
if (config.testConfig && config.testConfig.enable && config.testConfig.mockPhoneNumber) {
console.log('[Test] Using Mock PhoneNumber directly:', config.testConfig.mockPhoneNumber);
this.handlePhoneNumberSuccess(config.testConfig.mockPhoneNumber);
return;
}
// ---------------------------
const code = e.detail.code; // 动态令牌
wx.showLoading({ title: '获取手机号...' });
// 请求本地后端获取手机号
wx.request({
url: `${config.baseUrl}/api/getPhoneNumber`,
method: 'POST',
data: { code: code },
success: (res: any) => {
wx.hideLoading();
console.log('后端返回:', res.data);
if (res.data.phoneNumber) {
this.handlePhoneNumberSuccess(res.data.phoneNumber);
} else {
wx.showToast({ title: '获取失败: ' + (res.data.error || '未知错误'), icon: 'none' });
}
},
fail: (err) => {
wx.hideLoading();
console.error('请求后端接口失败:', err);
wx.showToast({ title: '连接服务器失败', icon: 'none' });
}
});
} else {
console.error(e.detail);
wx.showModal({
title: '获取失败',
content: '用户拒绝授权或账号无权限。',
showCancel: false
})
}
},
// 抽离处理手机号成功的逻辑
handlePhoneNumberSuccess(phoneNumber: string) {
this.setData({
phoneNumber: phoneNumber
});
// 3. 检查是否需要同步头像或获取真实UnionID
// 如果没有昵称、头像是默认的或者没有有效的UnionID自动跳转去同步
const hasValidUnionId = this.data.unionid && this.data.unionid !== '未获取到UnionID';
// 在 mp 模式下,我们不强制检查 unionid因为 chooseAvatar 不会返回 unionid
// 但如果用户需要 unionid可能需要其他方式。这里主要关注头像昵称。
const isInfoMissing = !this.data.userInfo.nickName ||
this.data.userInfo.avatarUrl === config.defaultAvatarUrl ||
(this.data.authMode === 'oa' && !hasValidUnionId);
if (isInfoMissing) {
if (this.data.authMode === 'oa') {
wx.showToast({ title: '正在同步完善信息...', icon: 'none', duration: 1500 });
setTimeout(() => {
this.goOfficialAccountAuth();
}, 1500);
} else {
// mp 模式,提示用户手动填写
wx.showToast({ title: '请完善头像和昵称', icon: 'none' });
}
} else {
// 已经有头像了,直接保存
this.saveUserInfoToBackend();
wx.showToast({ title: '登录成功' });
}
},
saveUserInfoToBackend() {
const { userInfo, openid, unionid, phoneNumber } = this.data;
// --- 测试配置注入 ---
let finalUnionId = unionid;
let finalPhoneNumber = phoneNumber;
if (config.testConfig && config.testConfig.enable) {
if (config.testConfig.mockUnionId) {
console.log('[Test] Using Mock UnionID:', config.testConfig.mockUnionId);
finalUnionId = config.testConfig.mockUnionId;
}
if (config.testConfig.mockPhoneNumber) {
console.log('[Test] Using Mock PhoneNumber:', config.testConfig.mockPhoneNumber);
finalPhoneNumber = config.testConfig.mockPhoneNumber;
}
}
// -------------------
const save = (finalAvatarUrl: string) => {
wx.request({
url: `${config.baseUrl}/api/saveUserInfo`,
method: 'POST',
data: {
...userInfo, // 包含 nickName, gender, country, province, city, language 等所有字段
avatarUrl: finalAvatarUrl, // 覆盖可能为临时路径的 avatarUrl
openid,
unionid: finalUnionId,
phoneNumber: finalPhoneNumber
},
success: (res: any) => {
console.log('用户信息保存成功:', res.data);
const serverData = res.data;
// 如果游戏服务器明确返回 result: -1 (例如手机号已存在),则阻止跳转
// wxserver 返回结构: { success: boolean, message: string, data: { data: { result: number, msg: string } } }
// 兼容直接返回的情况
const gamePacket = serverData.data;
const gameResult = (gamePacket && gamePacket.data && gamePacket.data.result !== undefined) ? gamePacket.data.result : ((gamePacket && gamePacket.result !== undefined) ? gamePacket.result : serverData.result);
if (serverData.success === false || gameResult === -1) {
// 优先显示 serverData.data.message其次是 msg
const errorMsg = serverData.message || (gamePacket && gamePacket.data && gamePacket.data.msg) || (gamePacket && gamePacket.msg) || '操作失败';
wx.showToast({
title: errorMsg,
icon: 'none',
duration: 4000
});
return;
}
// 优先取游戏服务器返回的 msg (在 data.data.msg 中),其次取 wxserver 的 message
const displayMsg = (gamePacket && gamePacket.data && gamePacket.data.msg) || (gamePacket && gamePacket.msg) || serverData.message;
if (displayMsg) {
wx.showToast({
title: displayMsg,
icon: 'none',
duration: 5000
});
}
// 保存到本地缓存
wx.setStorageSync('USER_SESSION', {
timestamp: Date.now(),
userInfo: this.data.userInfo,
openid: this.data.openid,
unionid: this.data.unionid,
phoneNumber: this.data.phoneNumber,
playerid: this.data.playerid
});
},
fail: (err) => {
console.error('保存用户信息失败:', err);
}
});
};
// 检查是否需要上传头像 (如果是临时路径)
// 只有在开启了上传配置,且头像是临时路径时才上传
const shouldUpload = config.enableAvatarUpload &&
userInfo.avatarUrl &&
(userInfo.avatarUrl.startsWith('http://tmp') || userInfo.avatarUrl.startsWith('wxfile://'));
if (shouldUpload) {
wx.showLoading({ title: '上传头像中...' });
wx.uploadFile({
url: `${config.baseUrl}/api/upload`,
filePath: userInfo.avatarUrl,
name: 'file',
formData: {
unionid: finalUnionId || openid // 优先使用 unionid如果没有则使用 openid
},
success: (res) => {
wx.hideLoading();
try {
const data = JSON.parse(res.data);
if (data.url) {
console.log('头像上传成功:', data.url);
save(data.url);
} else {
console.error('头像上传响应异常:', data);
// 降级处理:上传失败也尝试保存,虽然头像链接可能无效
save(userInfo.avatarUrl);
}
} catch (e) {
console.error('解析上传响应失败:', e);
save(userInfo.avatarUrl);
}
},
fail: (err) => {
wx.hideLoading();
console.error('头像上传失败:', err);
save(userInfo.avatarUrl);
}
});
} else {
// 已经是网络链接或默认头像,直接保存
save(userInfo.avatarUrl);
}
},
},
})