| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- /**
- * 扫码 / 小程序 scene 解析:店铺、桌号、业务线 m。
- * 无 m 或 m=1:美食(原 /store/dining、/store/order 等)。
- * m=2:通用价目,仅文档所列接口改为 generic-dining / generic-order(见 utils/m2GenericApiPath.js)。
- * 其它显式 m:不允许进入扫码点餐链路。
- */
- export function isDiningBusinessMode(m) {
- if (m == null || String(m).trim() === '') return true;
- const s = String(m).trim();
- const n = Number(s);
- if (!Number.isNaN(n)) return n === 1;
- return s === '1';
- }
- /** 是否允许通过扫码进入点餐流程(美食 m=1/无 m,或通用价目 m=2) */
- export function isScanEntryAllowed(m) {
- if (m == null || String(m).trim() === '') return true;
- const s = String(m).trim();
- const n = Number(s);
- if (!Number.isNaN(n)) return n === 1 || n === 2;
- return s === '1' || s === '2';
- }
- function trim(v) {
- return v == null ? '' : String(v).trim();
- }
- /**
- * 解析单段 scene 字符串(已解码或待解码的 query 片段)
- * @returns {{ storeId: string, tableId: string, m: string }}
- */
- export function parseSceneToStoreTable(sceneStr) {
- let storeId = '';
- let tableId = '';
- let m = '';
- if (!sceneStr) return { storeId, tableId, m };
- let decoded = String(sceneStr).trim();
- try {
- decoded = decodeURIComponent(decoded);
- } catch (_) {}
- decoded = trim(decoded);
- const parseKv = (text) => {
- const out = { storeId: '', tableId: '', m: '' };
- text.split('&').forEach((pair) => {
- const eq = pair.indexOf('=');
- if (eq > 0) {
- const k = pair.substring(0, eq).trim().toLowerCase();
- let v = pair.substring(eq + 1).trim();
- try {
- v = decodeURIComponent(v);
- } catch (_) {}
- v = trim(v);
- if (['s', 'storeid', 'store_id'].includes(k)) out.storeId = v;
- if (['t', 'tableid', 'table_id', 'tableno', 'table'].includes(k)) out.tableId = v;
- if (k === 'm' || k === 'mode') out.m = v;
- }
- });
- return out;
- };
- try {
- // 小程序码 scene 为 base64(JSON)
- if (/^[A-Za-z0-9+/=]+$/.test(decoded) && decoded.length > 20) {
- try {
- const jsonStr = typeof atob !== 'undefined' ? atob(decoded) : '';
- if (jsonStr && jsonStr.startsWith('{')) {
- const obj = JSON.parse(jsonStr);
- return {
- storeId: trim(obj?.storeId ?? obj?.store_id ?? obj?.s ?? ''),
- tableId: trim(obj?.tableId ?? obj?.table_id ?? obj?.tableid ?? obj?.t ?? obj?.tableNo ?? obj?.table ?? ''),
- m: trim(obj?.m ?? obj?.mode ?? '')
- };
- }
- } catch (_) {}
- }
- if (decoded.startsWith('{') && decoded.endsWith('}')) {
- const obj = JSON.parse(decoded);
- return {
- storeId: trim(obj?.storeId ?? obj?.store_id ?? obj?.s ?? ''),
- tableId: trim(obj?.tableId ?? obj?.table_id ?? obj?.tableid ?? obj?.t ?? obj?.tableNo ?? obj?.table ?? ''),
- m: trim(obj?.m ?? obj?.mode ?? '')
- };
- }
- if (/^\d+_\d+$/.test(decoded)) {
- const [s, t] = decoded.split('_');
- return { storeId: trim(s), tableId: trim(t), m: '' };
- }
- if (/^\d+$/.test(decoded)) return { storeId: '', tableId: decoded, m: '' };
- const kv = parseKv(decoded);
- return { storeId: kv.storeId, tableId: kv.tableId, m: kv.m };
- } catch (err) {
- console.warn('parseSceneToStoreTable', err);
- }
- return { storeId, tableId, m };
- }
- /**
- * 微信扫一扫 / 小程序码 path 等完整字符串 → 店铺、桌号、m
- */
- export function parseQrScanResult(str) {
- const empty = { storeId: '', tableId: '', m: '' };
- if (!str) return empty;
- const s = trim(str);
- try {
- const qIdx = s.indexOf('?');
- const hIdx = s.indexOf('#');
- if (qIdx >= 0) {
- const queryPart = hIdx >= 0 ? s.substring(qIdx + 1, hIdx) : s.substring(qIdx + 1);
- const params = new Map();
- queryPart.split('&').forEach((pair) => {
- const eq = pair.indexOf('=');
- if (eq > 0) {
- const k = pair.substring(0, eq).trim();
- let v = pair.substring(eq + 1).trim();
- try {
- v = decodeURIComponent(v);
- } catch (_) {}
- params.set(k, v);
- }
- });
- const scene = params.get('scene') || params.get('Scene');
- if (scene) return parseSceneToStoreTable(scene);
- return parseSceneToStoreTable(queryPart);
- }
- if ((s.startsWith('{') && s.endsWith('}')) || (s.startsWith('[') && s.endsWith(']'))) {
- const obj = JSON.parse(s);
- return {
- storeId: trim(obj?.storeId ?? obj?.store_id ?? obj?.s ?? ''),
- tableId: trim(obj?.tableId ?? obj?.table_id ?? obj?.tableid ?? obj?.t ?? obj?.tableNo ?? obj?.table ?? ''),
- m: trim(obj?.m ?? obj?.mode ?? '')
- };
- }
- if (/^[A-Za-z0-9+/=]+$/.test(s) && s.length > 20) {
- try {
- const jsonStr = typeof atob !== 'undefined' ? atob(s) : '';
- if (jsonStr && jsonStr.startsWith('{')) {
- return parseSceneToStoreTable(jsonStr);
- }
- } catch (_) {}
- }
- return parseSceneToStoreTable(s);
- } catch (err) {
- console.warn('parseQrScanResult', err);
- }
- return empty;
- }
|