/** * 扫码 / 小程序 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; }