- 添加游客模式,展示公共内容无需强制登录 - 实现游客模块展示打卡、计划、关于我们等功能介绍 - 添加产品介绍页面显示学习成长工具详情 - 优化登录流程,添加用户协议和隐私政策同意选项 - 更新角色标签将老师改为管理员,学员改为成员 - 添加注销账号功能替代退出登录 - 实现webview页面域名白名单安全验证 - 添加隐私政策和用户协议页面 - 优化页面跳转逻辑和错误提示信息 - 统一项目名称从明缘进阶场改为明缘云道场
109 lines
3.1 KiB
JavaScript
109 lines
3.1 KiB
JavaScript
const api = require('./api');
|
||
|
||
let loginPromise = null;
|
||
|
||
function getToken() {
|
||
return wx.getStorageSync('token') || '';
|
||
}
|
||
|
||
function setToken(token) {
|
||
wx.setStorageSync('token', token);
|
||
}
|
||
|
||
function clearToken() {
|
||
wx.removeStorageSync('token');
|
||
}
|
||
|
||
function wxLogin(retries) {
|
||
const remain = typeof retries === 'number' ? retries : 3;
|
||
return new Promise((resolve, reject) => {
|
||
wx.login({
|
||
success(res) {
|
||
if (res && res.code) {
|
||
resolve(res.code);
|
||
} else {
|
||
reject(new Error((res && res.errMsg) || '登录失败'));
|
||
}
|
||
},
|
||
fail: reject
|
||
});
|
||
}).catch((err) => {
|
||
// 冷启动时微信登录服务可能尚未就绪,指数退避重试
|
||
if (remain > 0) {
|
||
const delay = 400 + (4 - remain) * 200; // 首次 600ms,依次 800ms、1000ms
|
||
return new Promise((resolve) => setTimeout(resolve, delay))
|
||
.then(() => wxLogin(remain - 1));
|
||
}
|
||
throw err;
|
||
});
|
||
}
|
||
|
||
// 整个微信登录流程失败时重新取 code 再试一次,覆盖冷启动首个网络请求偶发失败
|
||
function wechatLogin(retries) {
|
||
const remain = typeof retries === 'number' ? retries : 1;
|
||
return wxLogin()
|
||
.then((code) => api.request({
|
||
url: '/api/v1/auth/wechat-login',
|
||
method: 'POST',
|
||
data: { code },
|
||
auth: false
|
||
}))
|
||
.catch((err) => {
|
||
if (remain > 0) {
|
||
return new Promise((resolve) => setTimeout(resolve, 500)).then(() => wechatLogin(remain - 1));
|
||
}
|
||
throw err;
|
||
});
|
||
}
|
||
|
||
// 单例登录:App.onLaunch 与首屏页面 onShow 可能几乎同时调用,
|
||
// 共享同一个进行中的登录 Promise,避免重复 wx.login / wechat-login 造成竞态。
|
||
// 关键:登录失败时必须清空单例,让下一次调用能重新发起,
|
||
// 否则冷启动 wx.login 偶发失败后,后续页面永远拿不到 token。
|
||
function ensureLogin(refresh) {
|
||
const existing = getToken();
|
||
if (existing && !refresh) {
|
||
return Promise.resolve(existing);
|
||
}
|
||
if (loginPromise) {
|
||
return loginPromise;
|
||
}
|
||
const p = wechatLogin()
|
||
.then((res) => {
|
||
if (res && res.success) {
|
||
setToken(res.token);
|
||
return res.token;
|
||
}
|
||
// need_phone:未绑定手机号时 resolve(null),由页面引导去「我的」绑定
|
||
return null;
|
||
})
|
||
.finally(() => {
|
||
// 无论成功失败,完成后都清掉单例,让下一次调用可以重新发起
|
||
if (loginPromise === p) {
|
||
loginPromise = null;
|
||
}
|
||
});
|
||
loginPromise = p;
|
||
return p;
|
||
}
|
||
|
||
function loginWithPhone(phoneCode) {
|
||
return wxLogin()
|
||
.then((code) => api.request({
|
||
url: '/api/v1/auth/bind-phone',
|
||
method: 'POST',
|
||
data: { code, phone_code: phoneCode },
|
||
auth: false
|
||
}))
|
||
.then((res) => {
|
||
if (res && res.success) {
|
||
setToken(res.token);
|
||
return res;
|
||
}
|
||
const error = new Error((res && res.error) || '手机号登录失败');
|
||
error.error = (res && res.error) || '手机号登录失败';
|
||
throw error;
|
||
});
|
||
}
|
||
|
||
module.exports = { getToken, setToken, clearToken, wxLogin, ensureLogin, loginWithPhone }; |