主题
跳转小程序
小程序码 / 普通二维码扫码进入固定页面并携带业务参数,未登录时强制登录、登录成功后回跳原页面的标准方案(uni-app)。
一、总体设计原则
- 小程序扫码入口,优先使用
page + scene scene是微信小程序扫码进入的标准参数载体query作为普通页面打开时的兼容参数- 未登录时,保存原始跳转地址(
redirect),登录成功后回到原页 - 对
scene做标准解析(URLSearchParams),而不是decodeURIComponent后直接做字符串判断
推荐模式:
- 小程序页面:
pages_business/orgMap/index - 扫码传参:
scene=orgCreditCode=xxx - 小程序进入时:
options.scene优先,options.orgCreditCode兜底 - 登录校验后回跳原始页面
二、标准微信小程序码方案(服务端生成,推荐)
PC 端后端调用微信官方接口生成小程序码:
js
// server.js
const axios = require('axios');
const fs = require('fs');
async function getAccessToken(appid, appsecret) {
const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${appsecret}`;
const res = await axios.get(url);
return res.data.access_token;
}
async function createWxaCodeUnlimited(appid, appsecret, scene, page = 'pages_business/orgMap/index') {
const accessToken = await getAccessToken(appid, appsecret);
const url = `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${accessToken}`;
const body = {
scene, // 例如: orgCreditCode=123456
page, // 例如: pages_business/orgMap/index
width: 430,
auto_color: false,
line_color: { r: 0, g: 0, b: 0 },
is_hyaline: false
};
const res = await axios.post(url, body, { responseType: 'arraybuffer' });
return Buffer.from(res.data, 'binary');
}
(async () => {
const appid = 'xxx';
const appsecret = 'xxx';
const qrBuffer = await createWxaCodeUnlimited(appid, appsecret, 'orgCreditCode=123456');
fs.writeFileSync('./org-map-qrcode.png', qrBuffer);
console.log('小程序码生成成功,已保存到 ./org-map-qrcode.png');
})();三、普通二维码兼容方案(无小程序码接口时)
普通二维码不等于微信小程序码,不能完全替代官方小程序码,仅作兼容。
方案 A:内容写小程序页面路径 + query
js
// 适合:有微信识别跳转能力时
const text = `pages_business/orgMap/index?orgCreditCode=123456`;方案 B:内容写 scene 字符串
js
// 适合:后端/小程序统一用 scene 解析
const text = `orgCreditCode=123456`;前端生成普通二维码(兼容旧方案):
js
new QRCode(this.$refs.qrCodeDiv, {
text: `pages_business/orgMap/index?orgCreditCode=${row.orgCreditCode}`,
width: 220,
height: 220
});重点:普通二维码最终也应落到小程序
onLoad里统一解析,不要分散在各处。
四、统一参数解析工具函数
js
// utils/scan.js —— 扫码参数统一解析
export function parseScanParams(options = {}) {
const sceneValue = options.scene || '';
let sceneObj = {};
if (sceneValue) {
try {
const decoded = decodeURIComponent(sceneValue);
const search = new URLSearchParams(decoded);
for (const [k, v] of search.entries()) {
sceneObj[k] = v;
}
} catch (e) {
console.error('scene decode error:', e);
}
}
return {
...options, // query 参数
...sceneObj // scene 参数(优先级更高)
};
}五、小程序入口页:标准参数解析 + 登录拦截
js
// pages_business/orgMap/index.vue
import { parseScanParams } from '@/utils/scan';
export default {
data() {
return {
orgCreditCode: '',
redirect: ''
}
},
onLoad(options) {
// 1. 统一解析:scene 优先,query 兜底
const params = parseScanParams(options);
// 2. 取业务参数
this.orgCreditCode = params.orgCreditCode || '';
// 3. 未登录且带了机构参数 → 跳登录,携带 redirect
if (this.orgCreditCode && !this.isLogin()) {
const redirectUrl = `/pages_business/orgMap/index?orgCreditCode=${encodeURIComponent(this.orgCreditCode)}`;
uni.navigateTo({
url: `/packagePages/login/wxLogin?redirect=${encodeURIComponent(redirectUrl)}`
});
return;
}
// 4. 真正的业务初始化
this.initPage();
},
methods: {
isLogin() {
// 根据项目实际情况返回
return !!uni.getStorageSync('userInfo') || !!uni.getStorageSync('token');
},
initPage() {
if (!this.orgCreditCode) {
console.warn('未带机构参数,按正常页面流程处理');
return;
}
// 调接口加载机构信息/地图数据
// this.getOrgDetailByCreditCode(this.orgCreditCode);
}
}
}关键点说明
不是 decodeURIComponent 直接做字符串判断,而是标准解析:
js
const params = new URLSearchParams(decoded);
const orgCreditCode = params.get('orgCreditCode');六、登录页:接收 redirect,登录成功后回跳
js
// packagePages/login/wxLogin.vue
export default {
data() {
return {
redirect: ''
}
},
onLoad(options) {
// 兼容从扫码页跳转过来的 redirect
this.redirect = options.redirect ? decodeURIComponent(options.redirect) : '';
},
methods: {
async doLogin() {
try {
// 1. 发起微信/手机号登录
// const res = await loginApi();
// 2. 设置登录态
// uni.setStorageSync('token', res.data.token)
// uni.setStorageSync('userInfo', res.data.userInfo)
// 3. 登录成功后回跳原页面
if (this.redirect) {
uni.reLaunch({ url: this.redirect });
return;
}
// 4. 没有 redirect,走默认页
uni.switchTab({ url: '/pages/index/index' });
} catch (e) {
uni.showToast({ title: '登录失败', icon: 'none' });
}
}
}
}七、为什么 scene 优先,而不是直接 query
微信小程序「扫码进入」的官方场景里,scene 才是标准值载体:
page指定打开哪个页面scene传业务参数- 扫码进入时,
options.scene最可靠 ?a=1&b=2这种 query 更像「页面直接打开时的参数」
正确处理顺序:
options.sceneoptions.orgCreditCode- 其他兜底参数
八、最终落地规范
方案一:官方小程序码(推荐)
- PC 端后端调用微信官方接口生成小程序码
- 参数放在
scene - 小程序
onLoad解析options.scene - 未登录时跳登录页,携带
redirect - 登录回跳原页面
方案二:普通二维码兼容
- PC 端生成普通二维码,内容规范化:
pages_business/orgMap/index?orgCreditCode=xxx- 或
orgCreditCode=xxx
- 小程序入口页兼容解析
query/scene - 逻辑统一走同一套鉴权回跳流程
九、代码分层建议
| 端 | 职责 |
|---|---|
| PC 端 | 优先生成「微信小程序码」;兼容生成「普通二维码」,内容统一为 pages_business/orgMap/index?orgCreditCode=xxx |
| 小程序入口页 | 统一解析 options.scene / options.orgCreditCode;统一登录拦截;统一 redirect 回跳 |
| 登录页 | 接收 redirect;登录成功后 reLaunch(redirect);无 redirect 走默认页 |
十、结论
标准微信小程序扫码处理流程,核心两件事:
- 官方小程序码优先,参数放在
scene - 入口页解析按
scene > query优先级,且scene必须做标准解析(URLSearchParams)