Skip to content

跳转小程序

小程序码 / 普通二维码扫码进入固定页面并携带业务参数,未登录时强制登录、登录成功后回跳原页面的标准方案(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 更像「页面直接打开时的参数」

正确处理顺序:

  1. options.scene
  2. options.orgCreditCode
  3. 其他兜底参数

八、最终落地规范

方案一:官方小程序码(推荐)

  • 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 走默认页

十、结论

标准微信小程序扫码处理流程,核心两件事:

  1. 官方小程序码优先,参数放在 scene
  2. 入口页解析按 scene > query 优先级,且 scene 必须做标准解析(URLSearchParams

© 2026 开发速查