主要的流程如图:
PC部分:
首先通过小程序官方api创建具体页面的二维码
即developers.weixin.qq.com/miniprogram… (ps: scene为下文的actKey)
/**
* 获取小程序码
*/
getQrcode() {
const that = this;
this.setState({
codeUrl: '',
codeStatus: 1,
});
return req.sys
.getQrcode()
.then(res => {
if (res.code === 0) {
const { actKey, picUrl } = res.data;
that.actKey = actKey;
that.setState({
codeUrl: picUrl,
});
that.getQrcodeStatus();
} else {
req.err.show(res.msg);
}
})
.catch(err => {
req.err.show(err);
});
}
在pc端的login页面,获取了扫码的二维码图片和唯一的actKey后,就调个接口轮询获取小程序端是否有setWebSession,如果有就返回sessionId,并把它当为凭证设在http请求头Header中/cookie
轮询登录状态
/**
* 获取小程序状态
*/
getQrcodeStatus() {
const that = this;
const { actKey } = this;
clearTimeout(this.getQrcodeStatusTimer);
return req.sys
.getQrcodeStatus({
actKey,
})
.then(res => {
if (res.code === 0) {
const { status } = res.data;
that.setState({
codeStatus: status,
});
switch (status) {
case -1: // 已过期
break;
case 0: // 登录成功
req.session.set(res.data.sessionId);
setTimeout(() => {
that.getMyCrtCompany();
}, 1000);
break;
case 1: // 等待扫码
that.getQrcodeStatusTimer = setTimeout(() => {
that.getQrcodeStatus();
}, 2000);
break;
default:
break;
}
} else {
req.err.show(res.msg);
}
})
.catch(req.err.show);
}
下面这部分的内容为路由重定向方案实现的知识补充
1、PC用的是react + ant.design
// MDN上的location.search的使用例子
// 声明了一个 <a id="myAnchor" href="https://developer.mozilla.org/en-US/docs/Location.search?q=123"> 元素在文档流中
var anchor = document.getElementById("myAnchor");
var queryString = anchor.search; // Returns:'?q=123'
2、在顶部app.js里,调用http时
if (res.code === req.err.CODE.sessionExpired) {
req.err.goLogin();
return;
}
3、err.js文件
const CODE = {
sessionExpired: xxxx,
};
/**
* 登录
*/
function goLogin() {
const { pathname, search } = history.location;
history.replace(`/login?url=${encodeURIComponent(pathname + search)}`);
}
/**
* 错误信息提示
* @param {any} err 错误对象
*/
function show(err) {
const msg = msgPicker(err);
if (err.code && err.code === CODE.sessionExpired) {
return message.error('登录已过期,请重新登录', 2).then(goLogin);
}
return Modal.warning({
title: '提示',
content: msg,
});
}
路由重定向
/**
* 获取当前公司
*/
getMyCrtCompany() {
console.log('this.props', this.props);
const { history, location } = this.props;
if (location.search) {
const urlData = searchParser(location.search) || {};
// 形如http://193.112.220.108:3001/#/login?url=%2Fdashboard
console.log('urlData', urlData);
if (urlData.url) {
const { url } = urlData;
if (/^http/.test(url)) {
window.location.href = decodeURIComponent(url);
return;
}
history.replace(decodeURIComponent(url));
return;
}
}
/**
* query解析
*/
export function searchParser(search) {
if (!search) {
return null;
}
const urlArr = search.substr(1).split('&');
const urlData = {};
urlArr.map(item => {
const [key, val] = item.split('=');
urlData[key] = val;
return item;
});
return urlData;
}
微信部分:
(ps:微信扫码只能访问线上环境)
若你需要扫体验版/开发版的环境,得在相关小程序内调用wx.scanCode
/**
* web登录
*/
webLogin() {
const appInst = getApp();
wx.scanCode({
success(res) {
console.log('scanCode:', res);
const { path } = res;
const scene = path.split('=')[1];
appInst.globalData.showOption.query.scene = scene;
appInst.$opts.query.scene = scene;
console.log('appInst.globalData:', appInst.globalData);
// 路由跳转
router.push({
name: 'common.webLogin',
});
},
});
},
在最顶部的app.js里
onShow(option) {
console.log('App onShow option:', option);
this.globalData.showOption = option;
// 检查更新
},
globalData: {
showOption: null,
},
/**
* 获取app option
*/
getOpt() {
const { showOption } = this.globalData;
// 这里的this.$opts为路由参数的封装
const appOpts = (showOption && showOption.query && showOption.query.scene) ? showOption : this.$opts;
if (!appOpts.query || !appOpts.query.scene) {
return null;
}
return appOpts;
},
common.webLogin 页面
const appInst = getApp();
/**
* 登录
*/
login() {
if (!appInst || !appInst.$opts) {
return;
}
const { showOption } = appInst.globalData;
const appOpts = (showOption && showOption.query && showOption.query.scene) ? showOption : appInst.$opts;
if (!appOpts.query || !appOpts.query.scene) {
return;
}
req.getSessionId()
.then((sessionId) => {
console.log({
actKey: appOpts.query.scene,
sessionId,
});
if (!sessionId) {
return;
}
req.common.setWebSession({
actKey: appOpts.query.scene,
sessionId,
})
.then((res) => {
if (res.code !== 0) {
req.err.show(res.msg);
return;
}
wx.showToast({
title: '登录成功',
});
setTimeout(() => {
router.push({
name: 'home',
});
}, 1000);
})
.catch(req.err.show);
});
},
最后
这个是我们目前项目采取的方案,也希望能学习更多的相关方案,欢迎大家来讨论