Przeglądaj źródła

测试小程序码

sunshibo 1 miesiąc temu
rodzic
commit
7153f9633d
2 zmienionych plików z 120 dodań i 78 usunięć
  1. 30 7
      App.vue
  2. 90 71
      components/TabBar.vue

+ 30 - 7
App.vue

@@ -4,20 +4,43 @@ import initConfig from '@/initConfig.js';
 import { SCAN_QR_CACHE } from '@/settings/enums.js';
 // #endif
 
-/** 从 scene 或 query 中解析:二维码 s=店铺id,t=桌号id */
+/** 从 scene 解析:支持小程序码 s=店铺id&t=桌号id、storeId_tableId、纯数字桌号、JSON 等 */
 function parseSceneToStoreTable(sceneStr) {
 	const trim = (v) => (v == null ? '' : String(v).trim());
 	let storeId = '';
 	let tableId = '';
 	if (!sceneStr) return { storeId, tableId };
-	const str = trim(sceneStr);
+	let decoded = sceneStr;
 	try {
-		const parts = str.split('&');
-		parts.forEach((pair) => {
-			const [k, v] = pair.split('=').map((s) => (s ? decodeURIComponent(String(s).trim()) : ''));
-			if (k === 's' || k === 'storeId' || k === 'store_id') storeId = trim(v);
-			if (k === 't' || k === 'tableId' || k === 'table_id' || k === 'tableid') tableId = trim(v);
+		decoded = decodeURIComponent(decoded);
+	} catch (_) {}
+	decoded = trim(decoded);
+	const parseKv = (text) => {
+		const out = { storeId: '', tableId: '' };
+		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;
+			}
 		});
+		return out;
+	};
+	try {
+		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 ?? '') };
+		}
+		if (/^\d+_\d+$/.test(decoded)) {
+			const [s, t] = decoded.split('_');
+			return { storeId: trim(s), tableId: trim(t) };
+		}
+		if (/^\d+$/.test(decoded)) return { storeId: '', tableId: decoded };
+		return parseKv(decoded);
 	} catch (err) {
 		console.warn('parseSceneToStoreTable', err);
 	}

+ 90 - 71
components/TabBar.vue

@@ -48,7 +48,7 @@ const getTabBarWrapStyle = computed(() => ({
 	position: props.fixed ? 'fixed' : 'relative'
 }));
 
-// 解析扫码内容:支持 s=店铺id&t=桌号id、URL 的 query、scene 参数、JSON 等格式
+// 解析扫码内容:支持小程序码 path、scene,以及 s=店铺id&t=桌号id、URL、JSON 等格式
 function parseScanResult(str) {
 	const trim = (v) => (v == null ? '' : String(v).trim());
 	let storeId = '';
@@ -76,8 +76,50 @@ function parseScanResult(str) {
 		});
 		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 {
-		// 1. 若为 URL 或含 ?,提取 query 或 scene
+		// 1. 小程序码 path 格式:pages/xxx?scene=xxx
 		const qIdx = s.indexOf('?');
 		const hIdx = s.indexOf('#');
 		if (qIdx >= 0) {
@@ -86,37 +128,27 @@ function parseScanResult(str) {
 			queryPart.split('&').forEach((pair) => {
 				const eq = pair.indexOf('=');
 				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);
 				}
 			});
 			const scene = params.get('scene') || params.get('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(']'))) {
 			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 格式
 		return parseKv(s);
@@ -134,56 +166,43 @@ function handleScanOrder() {
 		uni.showToast({ title: '请登录', icon: 'none' });
 		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)