feat: 扫码进去小程序的几种方式兼容支持
This commit is contained in:
1 parent
681ba3d8f1
commit
a4c29569bf
3 files changed
+108
-5
No files matched your search
+17
-2
@@ -1,12 +1,27 @@
|
||||
<script setup>
|
||||
import { onLaunch } from "@dcloudio/uni-app";
|
||||
import { onLaunch, onShow } from "@dcloudio/uni-app";
|
||||
import { useUserStore } from "@/store";
|
||||
import userApi from "@/api/user";
|
||||
import { handleEntry } from "@/utils/nav";
|
||||
|
||||
onLaunch(() => {
|
||||
/**
|
||||
* 启动/唤起参数 → 统一的入口对象。
|
||||
* ⚠️ 只取 options.query:App 层的 options.scene 是「场景值」(数字),
|
||||
* 小程序码真正的 scene 字符串在 options.query.scene 里,不能用前者覆盖后者。
|
||||
*/
|
||||
const entryParams = (options) => ({ ...((options && options.query) || {}) });
|
||||
|
||||
onLaunch((options) => {
|
||||
useUserStore().restore();
|
||||
// 本地若留有注销回执,查一次清理进度(无回执时不发请求),完成即丢弃
|
||||
userApi.fetchErasureStatus();
|
||||
// 扫码进入:冷启动参数在 onLaunch(页面 onLoad 也会带,双保险)
|
||||
handleEntry(entryParams(options));
|
||||
});
|
||||
|
||||
onShow((options) => {
|
||||
// 后台被再次扫码唤起:页面 onLoad 不会重跑,参数只在这里
|
||||
handleEntry(entryParams(options));
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -2,11 +2,21 @@
|
||||
/**
|
||||
* 主包壳页面:主页 / 权益 / 我的 三块面板同屏共存,底部 Tab 栏只渲染一次
|
||||
* ---------------------------------------------------------------------------
|
||||
* 扫码入口:二维码指向本页并带商家 id(`merchant_id` / `scene` / `q` 三种来源都认),
|
||||
* 进来自动切到权益 Tab 再打开商家详情 —— 取值见 utils/nav.js 的 pickMerchantId / handleEntry。
|
||||
*/
|
||||
import { onMounted, ref } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import { nextTick, onMounted, ref } from "vue";
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app";
|
||||
import { TABS } from "@/constant";
|
||||
import { consumePendingTab, registerTabController, setTabQuery } from "@/utils/nav";
|
||||
import {
|
||||
consumePendingMerchant,
|
||||
consumePendingTab,
|
||||
goDetail,
|
||||
pickMerchantId,
|
||||
registerTabController,
|
||||
setPendingMerchant,
|
||||
setTabQuery,
|
||||
} from "@/utils/nav";
|
||||
import HomePanel from "@/views/home/index.vue";
|
||||
import BenefitPanel from "@/views/benefit/index.vue";
|
||||
import MinePanel from "@/views/mine/index.vue";
|
||||
@@ -32,17 +42,39 @@ const goTab = (key, query = "") => {
|
||||
showPanel(key);
|
||||
};
|
||||
|
||||
/** Tab 控制器是否已就绪(onShow 可能早于 onMounted,未就绪时不消费扫码参数) */
|
||||
let shellReady = false;
|
||||
|
||||
/**
|
||||
* 扫码进店:先切权益 Tab 再打开商家详情。
|
||||
* 顺序不能反 —— 先切 Tab,「详情返回」才会落在权益页而不是主页。
|
||||
*/
|
||||
const runEntry = () => {
|
||||
if (!shellReady) return;
|
||||
const id = consumePendingMerchant();
|
||||
if (!id) return;
|
||||
goTab("benefit");
|
||||
nextTick(() => setTimeout(() => goDetail(id), 0));
|
||||
};
|
||||
|
||||
onLoad((options) => {
|
||||
setPendingMerchant(pickMerchantId(options));
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
// 面板的 onMounted 早于本页 onMounted,此时 ref 已就绪
|
||||
registerTabController({ go: goTab, getActive: () => active.value });
|
||||
// 从子页 / 登录页切过来:nav 里可能压着待切 Tab(登录回跳、reLaunch 冷启)
|
||||
const pending = consumePendingTab();
|
||||
goTab(pending?.key || active.value, pending?.query || "");
|
||||
shellReady = true;
|
||||
runEntry();
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
// 从板块列表 / 详情 / 登录页等返回:让当前面板按需刷新(各家内部有幂等守卫)
|
||||
showPanel(active.value);
|
||||
runEntry();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ export const registerTabController = (ctl) => {
|
||||
let tabQuery = "";
|
||||
/** 壳尚未就绪时的待切 Tab */
|
||||
let pendingTab = null;
|
||||
/** 扫码 / 外链进入时待打开的商家 id */
|
||||
let pendingMerchant = "";
|
||||
|
||||
export const setTabQuery = (query) => {
|
||||
tabQuery = query || "";
|
||||
@@ -47,6 +49,60 @@ export const consumePendingTab = () => {
|
||||
return pending;
|
||||
};
|
||||
|
||||
/** 记下待打开的商家 id(空值不覆盖已有的) */
|
||||
export const setPendingMerchant = (id) => {
|
||||
const value = String(id || "").trim();
|
||||
if (value) pendingMerchant = value;
|
||||
};
|
||||
|
||||
/** 取出并清空待打开的商家 id */
|
||||
export const consumePendingMerchant = () => {
|
||||
const value = pendingMerchant;
|
||||
pendingMerchant = "";
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* 从启动/唤起参数里取商家 id:兼容四种来源
|
||||
* - `merchant_id` / `merchantId` / `id`:页面 query 直传(小程序码 path、分享链接)
|
||||
* - `scene`:小程序码 getUnlimited,值可能是 `merchant_id=xxx` 也可能是短码
|
||||
* - `q`:普通链接二维码,值是 URL 编码过的原始链接,解码后再取 query
|
||||
*/
|
||||
export const pickMerchantId = (options) => {
|
||||
const o = options || {};
|
||||
const direct = o.merchant_id || o.merchantId || o.id || "";
|
||||
if (direct) return decodeURIComponent(String(direct)).trim();
|
||||
if (o.scene) {
|
||||
const scene = decodeURIComponent(String(o.scene));
|
||||
return (
|
||||
pickParam(scene, "merchant_id") ||
|
||||
pickParam(scene, "merchantId") ||
|
||||
pickParam(scene, "id") ||
|
||||
scene.trim()
|
||||
);
|
||||
}
|
||||
if (o.q) {
|
||||
const query = decodeURIComponent(String(o.q)).split("?")[1] || "";
|
||||
return pickParam(query, "merchant_id") || pickParam(query, "merchantId") || pickParam(query, "id");
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
/**
|
||||
* 入口参数处理(App.onLaunch / App.onShow 调用):记下商家 id;
|
||||
* 栈顶不是壳页时 reLaunch 回壳 —— 由壳消费 pending 后切权益 Tab 并打开详情。
|
||||
* 冷启动时页面栈还没建立(getCurrentPages 为空),此时只记 pending,不 reLaunch。
|
||||
*/
|
||||
export const handleEntry = (options) => {
|
||||
const id = pickMerchantId(options);
|
||||
if (!id) return;
|
||||
setPendingMerchant(id);
|
||||
const pages = getCurrentPages();
|
||||
if (!pages.length) return;
|
||||
const top = `/${pages[pages.length - 1].route}`;
|
||||
if (top !== ROUTES.HOME) uni.reLaunch({ url: ROUTES.HOME });
|
||||
};
|
||||
|
||||
/** query 串取参数 */
|
||||
const pickParam = (query, key) => {
|
||||
const matched = (query || "").match(new RegExp(`(?:^|&)${key}=([^&]*)`));
|
||||
|
||||
Reference in new issue
Block a user