|
@@ -48,7 +48,7 @@ const getTabBarWrapStyle = computed(() => ({
|
|
|
position: props.fixed ? 'fixed' : 'relative'
|
|
position: props.fixed ? 'fixed' : 'relative'
|
|
|
}));
|
|
}));
|
|
|
|
|
|
|
|
-// 解析扫码内容:支持 s=店铺id&t=桌号id、URL 的 query、scene 参数、JSON 等格式
|
|
|
|
|
|
|
+// 解析扫码内容:支持小程序码 path、scene,以及 s=店铺id&t=桌号id、URL、JSON 等格式
|
|
|
function parseScanResult(str) {
|
|
function parseScanResult(str) {
|
|
|
const trim = (v) => (v == null ? '' : String(v).trim());
|
|
const trim = (v) => (v == null ? '' : String(v).trim());
|
|
|
let storeId = '';
|
|
let storeId = '';
|
|
@@ -76,8 +76,50 @@ function parseScanResult(str) {
|
|
|
});
|
|
});
|
|
|
return out;
|
|
return out;
|
|
|
};
|
|
};
|
|
|
|
|
+ const parseScene = (sceneStr) => {
|
|
|
|
|
+ let decoded = sceneStr;
|
|
|
|
|
+ try {
|
|
|
|
|
+ decoded = decodeURIComponent(decoded);
|
|
|
|
|
+ } catch (_) {}
|
|
|
|
|
+ decoded = trim(decoded);
|
|
|
|
|
+ // scene 为 base64 编码的 JSON(小程序码常见)
|
|
|
|
|
+ if (/^[A-Za-z0-9+/=]+$/.test(decoded) && decoded.length > 20) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const jsonStr = typeof atob === 'function' ? 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 ?? '')
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (_) {}
|
|
|
|
|
+ }
|
|
|
|
|
+ // scene 为 JSON 字符串
|
|
|
|
|
+ if (decoded.startsWith('{') && decoded.endsWith('}')) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ 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 ?? '')
|
|
|
|
|
+ };
|
|
|
|
|
+ } catch (_) {}
|
|
|
|
|
+ }
|
|
|
|
|
+ // scene 为 storeId_tableId 下划线分隔(常见小程序码格式)
|
|
|
|
|
+ if (/^\d+_\d+$/.test(decoded)) {
|
|
|
|
|
+ const [sid, tid] = decoded.split('_');
|
|
|
|
|
+ return { storeId: trim(sid), tableId: trim(tid) };
|
|
|
|
|
+ }
|
|
|
|
|
+ // scene 仅为数字,视为桌号
|
|
|
|
|
+ if (/^\d+$/.test(decoded)) {
|
|
|
|
|
+ return { storeId: '', tableId: decoded };
|
|
|
|
|
+ }
|
|
|
|
|
+ const fromKv = parseKv(decoded);
|
|
|
|
|
+ if (!fromKv.tableId && /^\d+$/.test(decoded)) fromKv.tableId = decoded;
|
|
|
|
|
+ return fromKv;
|
|
|
|
|
+ };
|
|
|
try {
|
|
try {
|
|
|
- // 1. 若为 URL 或含 ?,提取 query 或 scene
|
|
|
|
|
|
|
+ // 1. 小程序码 path 格式:pages/xxx?scene=xxx
|
|
|
const qIdx = s.indexOf('?');
|
|
const qIdx = s.indexOf('?');
|
|
|
const hIdx = s.indexOf('#');
|
|
const hIdx = s.indexOf('#');
|
|
|
if (qIdx >= 0) {
|
|
if (qIdx >= 0) {
|
|
@@ -86,37 +128,27 @@ function parseScanResult(str) {
|
|
|
queryPart.split('&').forEach((pair) => {
|
|
queryPart.split('&').forEach((pair) => {
|
|
|
const eq = pair.indexOf('=');
|
|
const eq = pair.indexOf('=');
|
|
|
if (eq > 0) {
|
|
if (eq > 0) {
|
|
|
- const k = decodeURIComponent(pair.substring(0, eq).trim());
|
|
|
|
|
- const v = decodeURIComponent(pair.substring(eq + 1).trim());
|
|
|
|
|
|
|
+ const k = pair.substring(0, eq).trim();
|
|
|
|
|
+ let v = pair.substring(eq + 1).trim();
|
|
|
|
|
+ try {
|
|
|
|
|
+ v = decodeURIComponent(v);
|
|
|
|
|
+ } catch (_) {}
|
|
|
params.set(k, v);
|
|
params.set(k, v);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
const scene = params.get('scene') || params.get('Scene');
|
|
const scene = params.get('scene') || params.get('Scene');
|
|
|
if (scene) {
|
|
if (scene) {
|
|
|
- const decoded = decodeURIComponent(scene);
|
|
|
|
|
- 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 ?? '')
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
- const fromKv = parseKv(decoded);
|
|
|
|
|
- // scene 仅为数字时,视为桌号
|
|
|
|
|
- if (!fromKv.tableId && /^\d+$/.test(trim(decoded))) {
|
|
|
|
|
- fromKv.tableId = trim(decoded);
|
|
|
|
|
- }
|
|
|
|
|
- return fromKv;
|
|
|
|
|
|
|
+ return parseScene(scene);
|
|
|
}
|
|
}
|
|
|
- const fromQuery = parseKv(queryPart);
|
|
|
|
|
- return fromQuery;
|
|
|
|
|
|
|
+ return parseKv(queryPart);
|
|
|
}
|
|
}
|
|
|
- // 2. 尝试 JSON 格式
|
|
|
|
|
|
|
+ // 2. JSON 格式
|
|
|
if ((s.startsWith('{') && s.endsWith('}')) || (s.startsWith('[') && s.endsWith(']'))) {
|
|
if ((s.startsWith('{') && s.endsWith('}')) || (s.startsWith('[') && s.endsWith(']'))) {
|
|
|
const obj = JSON.parse(s);
|
|
const obj = JSON.parse(s);
|
|
|
- storeId = trim(obj?.storeId ?? obj?.store_id ?? obj?.s ?? '');
|
|
|
|
|
- tableId = trim(obj?.tableId ?? obj?.table_id ?? obj?.tableid ?? obj?.t ?? obj?.tableNo ?? obj?.table ?? '');
|
|
|
|
|
- return { storeId, tableId };
|
|
|
|
|
|
|
+ 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 ?? '')
|
|
|
|
|
+ };
|
|
|
}
|
|
}
|
|
|
// 3. key=value 格式
|
|
// 3. key=value 格式
|
|
|
return parseKv(s);
|
|
return parseKv(s);
|
|
@@ -134,56 +166,43 @@ function handleScanOrder() {
|
|
|
uni.showToast({ title: '请登录', icon: 'none' });
|
|
uni.showToast({ title: '请登录', icon: 'none' });
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- // scanCode 真机需相机权限,先申请再扫码
|
|
|
|
|
- uni.authorize({
|
|
|
|
|
- scope: 'scope.camera',
|
|
|
|
|
- success: () => runScan(),
|
|
|
|
|
- fail: () => {
|
|
|
|
|
- uni.showModal({
|
|
|
|
|
- title: '提示',
|
|
|
|
|
- content: '需要相机权限才能扫描桌号二维码,请在设置中开启',
|
|
|
|
|
- confirmText: '去设置',
|
|
|
|
|
- success: (res) => {
|
|
|
|
|
- if (res.confirm) {
|
|
|
|
|
- uni.openSetting({
|
|
|
|
|
- success: (settingRes) => {
|
|
|
|
|
- if (settingRes?.authSetting?.['scope.camera']) runScan();
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ uni.scanCode({
|
|
|
|
|
+ scanType: ['wxCode', 'qrCode', 'barCode'],
|
|
|
|
|
+ success: (res) => {
|
|
|
|
|
+ const result = (res?.path || res?.result || '').trim();
|
|
|
|
|
+ const { storeId, tableId } = parseScanResult(result);
|
|
|
|
|
+ const payload = { raw: result, storeId, tableId };
|
|
|
|
|
+ uni.setStorageSync(SCAN_QR_CACHE, JSON.stringify(payload));
|
|
|
|
|
+ if (storeId) uni.setStorageSync('currentStoreId', storeId);
|
|
|
|
|
+ if (tableId) uni.setStorageSync('currentTableId', tableId);
|
|
|
|
|
+ const diners = uni.getStorageSync('currentDiners') || '1';
|
|
|
|
|
+ uni.setStorageSync('currentDiners', diners);
|
|
|
|
|
+ if (!tableId) {
|
|
|
|
|
+ uni.showToast({ title: '未识别到桌号,请扫描正确的桌号二维码', icon: 'none' });
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ uni.reLaunch({
|
|
|
|
|
+ url: `/pages/orderFood/index?tableid=${encodeURIComponent(tableId)}&diners=${encodeURIComponent(diners)}`
|
|
|
});
|
|
});
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- function runScan() {
|
|
|
|
|
- uni.scanCode({
|
|
|
|
|
- scanType: ['qrCode', 'barCode'],
|
|
|
|
|
- success: (res) => {
|
|
|
|
|
- const result = (res?.path || res?.result || '').trim();
|
|
|
|
|
- const { storeId, tableId } = parseScanResult(result);
|
|
|
|
|
- const payload = { raw: result, storeId, tableId };
|
|
|
|
|
- uni.setStorageSync(SCAN_QR_CACHE, JSON.stringify(payload));
|
|
|
|
|
- if (storeId) uni.setStorageSync('currentStoreId', storeId);
|
|
|
|
|
- if (tableId) uni.setStorageSync('currentTableId', tableId);
|
|
|
|
|
- const diners = uni.getStorageSync('currentDiners') || '1';
|
|
|
|
|
- uni.setStorageSync('currentDiners', diners);
|
|
|
|
|
- if (!tableId) {
|
|
|
|
|
- uni.showToast({ title: '未识别到桌号,请扫描正确的桌号二维码', icon: 'none' });
|
|
|
|
|
- console.warn('[扫码] 未解析到桌号,原始内容:', result);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- uni.reLaunch({
|
|
|
|
|
- url: `/pages/orderFood/index?tableid=${encodeURIComponent(tableId)}&diners=${encodeURIComponent(diners)}`
|
|
|
|
|
- });
|
|
|
|
|
- },
|
|
|
|
|
- fail: (err) => {
|
|
|
|
|
- if (err?.errMsg && !String(err.errMsg).includes('cancel')) {
|
|
|
|
|
- uni.showToast({ title: err?.errMsg || '扫码失败', icon: 'none' });
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ fail: (err) => {
|
|
|
|
|
+ const msg = err?.errMsg || '';
|
|
|
|
|
+ if (msg && !msg.includes('cancel')) {
|
|
|
|
|
+ if (msg.includes('auth') || msg.includes('camera') || msg.includes('scope')) {
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: '提示',
|
|
|
|
|
+ content: '需要相机权限才能扫码,请在设置中开启',
|
|
|
|
|
+ confirmText: '去设置',
|
|
|
|
|
+ success: (res) => {
|
|
|
|
|
+ if (res.confirm) uni.openSetting();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showToast({ title: msg || '扫码失败', icon: 'none' });
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 底部 tabBar 跳转(需在 pages.json 中配置 tabBar.list)
|
|
// 底部 tabBar 跳转(需在 pages.json 中配置 tabBar.list)
|