From db73efdd599d35d11df822aa08b5d4047d3bdaad Mon Sep 17 00:00:00 2001 From: DU Date: Wed, 18 Mar 2026 10:58:11 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=89=AB=E7=A0=81=E5=A4=84?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.config.ts | 1 + src/pages/qrHandler/index.scss | 94 +++++++++++++++ src/pages/qrHandler/index.tsx | 119 +++++++++++++++++++ src/pages/scan/index.tsx | 28 ++--- src/pages/scanCode/index.tsx | 6 +- src/utils/qrCodeHandler.ts | 209 +++++++++++++++++++++++++++++++++ 6 files changed, 440 insertions(+), 17 deletions(-) create mode 100644 src/pages/qrHandler/index.scss create mode 100644 src/pages/qrHandler/index.tsx create mode 100644 src/utils/qrCodeHandler.ts diff --git a/src/app.config.ts b/src/app.config.ts index 197be93..f82a178 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -6,6 +6,7 @@ export default defineAppConfig({ 'pages/message/index', 'pages/user/index', 'pages/scanCode/index', // 独立扫码页面(无tabbar) + 'pages/qrHandler/index', // 二维码处理页面(统一跳转逻辑) ], // 分包配置 subpackages: [ diff --git a/src/pages/qrHandler/index.scss b/src/pages/qrHandler/index.scss new file mode 100644 index 0000000..53fff6d --- /dev/null +++ b/src/pages/qrHandler/index.scss @@ -0,0 +1,94 @@ +.qr-handler-page { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background-color: #f5f5f5; + padding: 40px 20px; + + // 加载状态 + &.loading-state { + .loading-content { + display: flex; + flex-direction: column; + align-items: center; + gap: 20px; + + .loading-text { + font-size: 32px; + color: #666; + margin-top: 20px; + } + } + } + + // 错误状态 + &.error-state { + .error-content { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + max-width: 600px; + + .error-icon { + font-size: 120px; + margin-bottom: 30px; + } + + .error-title { + font-size: 36px; + font-weight: bold; + color: #333; + margin-bottom: 20px; + } + + .error-message { + font-size: 28px; + color: #666; + line-height: 1.5; + margin-bottom: 60px; + } + + .error-actions { + display: flex; + gap: 20px; + width: 100%; + + .action-btn { + flex: 1; + height: 88px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 12px; + font-size: 32px; + font-weight: 500; + cursor: pointer; + transition: all 0.3s ease; + + &.primary { + background-color: #667eea; + color: white; + + &:active { + background-color: #5a6fd8; + transform: scale(0.98); + } + } + + &.secondary { + background-color: white; + color: #666; + border: 2px solid #e0e0e0; + + &:active { + background-color: #f5f5f5; + transform: scale(0.98); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/pages/qrHandler/index.tsx b/src/pages/qrHandler/index.tsx new file mode 100644 index 0000000..e793042 --- /dev/null +++ b/src/pages/qrHandler/index.tsx @@ -0,0 +1,119 @@ +/** + * 二维码处理页面 + * 统一处理所有扫码后的跳转逻辑 + * + * 使用场景: + * 1. 微信扫码跳转到小程序时的落地页 + * 2. 小程序内扫码后的统一处理页 + */ + +import { View, Text } from '@tarojs/components' +import Taro, { useLoad, useRouter } from '@tarojs/taro' +import { useState } from 'react' +import { Loading } from '@taroify/core' +import { parseQRCode, handleQRCodeNavigation, QRCodeType } from '@/utils/qrCodeHandler' +import './index.scss' + +// 页面配置 +definePageConfig({ + navigationBarTitleText: '处理中...', + navigationStyle: 'default' +}) + +const QRHandlerPage = () => { + const router = useRouter() + const [loading, setLoading] = useState(true) + const [error, setError] = useState('') + + useLoad(async () => { + console.log('二维码处理页面加载,参数:', router.params) + + try { + // 从路由参数获取二维码内容 + const { q, scene, qrContent } = router.params + + let content = '' + + // 处理不同来源的二维码内容 + if (q) { + // 微信扫码进入小程序的情况,q 参数是 base64 编码的 URL + content = decodeURIComponent(q) + } else if (scene) { + // 小程序码的场景值 + content = scene + } else if (qrContent) { + // 直接传递的二维码内容 + content = decodeURIComponent(qrContent) + } else { + throw new Error('未找到二维码内容') + } + + console.log('解析二维码内容:', content) + + // 延迟一下,给用户看到处理中的状态 + await new Promise(resolve => setTimeout(resolve, 500)) + + // 解析并处理二维码 + const qrData = parseQRCode(content) + console.log('解析结果:', qrData) + + if (qrData.type === QRCodeType.UNKNOWN) { + throw new Error('无法识别的二维码格式') + } + + // 执行跳转逻辑 + await handleQRCodeNavigation(qrData) + + } catch (error: any) { + console.error('二维码处理失败:', error) + setError(error.message || '二维码处理失败') + setLoading(false) + } + }) + + // 返回首页 + const handleBackHome = () => { + Taro.switchTab({ + url: '/pages/index/index' + }) + } + + // 重新扫码 + const handleRescan = () => { + Taro.navigateTo({ + url: '/pages/scanCode/index' + }) + } + + if (error) { + return ( + + + + 处理失败 + {error} + + + + 重新扫码 + + + 返回首页 + + + + + ) + } + + return ( + + + + 正在处理二维码... + + + ) +} + +export default QRHandlerPage \ No newline at end of file diff --git a/src/pages/scan/index.tsx b/src/pages/scan/index.tsx index da553ec..42940c7 100644 --- a/src/pages/scan/index.tsx +++ b/src/pages/scan/index.tsx @@ -6,6 +6,7 @@ import Taro, { useLoad } from '@tarojs/taro' import { observer } from 'mobx-react' import { useState } from 'react' import { Dialog } from '@taroify/core' +import { scanAndHandle } from '@/utils/qrCodeHandler' import './index.scss' const ScanIndex = observer(() => { @@ -16,19 +17,16 @@ const ScanIndex = observer(() => { }) // 扫码 - const handleScan = () => { - Taro.scanCode({ - success: (res) => { - Taro.showToast({ - title: '扫码成功', - icon: 'success', - }) - console.log('扫码结果:', res) - }, - fail: (err) => { - console.error('扫码失败:', err) - } - }) + const handleScan = async () => { + try { + await scanAndHandle({ + onlyFromCamera: true, + scanType: ['qrCode', 'barCode'] + }) + } catch (error) { + // 错误处理已在 scanAndHandle 中完成 + console.log('扫码取消或失败') + } } // 去开台 @@ -92,9 +90,9 @@ const ScanIndex = observer(() => { 扫码开台 - {/* */} + diff --git a/src/pages/scanCode/index.tsx b/src/pages/scanCode/index.tsx index 193bea2..b1dae90 100644 --- a/src/pages/scanCode/index.tsx +++ b/src/pages/scanCode/index.tsx @@ -47,8 +47,10 @@ const ScanCodePage = observer(() => { // 处理扫码结果 const handleScanResult = (result: string) => { console.log('扫码结果:', result) - // TODO: 根据扫码结果处理开台逻辑 - // 例如:调用后端接口验证二维码,然后开台 + // 跳转到统一的二维码处理页面 + Taro.navigateTo({ + url: `/pages/qrHandler/index?qrContent=${encodeURIComponent(result)}` + }) } // 关闭页面返回首页 diff --git a/src/utils/qrCodeHandler.ts b/src/utils/qrCodeHandler.ts new file mode 100644 index 0000000..31fafed --- /dev/null +++ b/src/utils/qrCodeHandler.ts @@ -0,0 +1,209 @@ +/** + * 二维码处理工具 + * 用于解析二维码内容并决定跳转逻辑 + */ + +import Taro from '@tarojs/taro' + +// 二维码类型枚举 +export enum QRCodeType { + TABLE_START = 'table_start', // 开台二维码 + TABLE_CLOSE = 'table_close', // 关台二维码 + CABINET = 'cabinet', // 存杆柜二维码 + COUPON = 'coupon', // 团购券二维码 + UNKNOWN = 'unknown' // 未知类型 +} + +// 二维码数据接口 +export interface QRCodeData { + type: QRCodeType + tableId?: string // 球桌ID + grpSn?: string // 设备编码 + cabinetId?: string // 存杆柜ID + couponCode?: string // 团购券码 + storeId?: string // 门店ID + [key: string]: any // 其他参数 +} + +/** + * 解析二维码内容 + * @param qrContent 二维码原始内容 + * @returns 解析后的二维码数据 + */ +export const parseQRCode = (qrContent: string): QRCodeData => { + try { + // 尝试解析 JSON 格式的二维码 + if (qrContent.startsWith('{')) { + const data = JSON.parse(qrContent) + return { + type: data.type || QRCodeType.UNKNOWN, + ...data + } + } + + // 尝试解析 URL 格式的二维码 + if (qrContent.startsWith('http')) { + const url = new URL(qrContent) + const params = new URLSearchParams(url.search) + + return { + type: (params.get('type') as QRCodeType) || QRCodeType.UNKNOWN, + tableId: params.get('tableId') || undefined, + grpSn: params.get('grpSn') || undefined, + cabinetId: params.get('cabinetId') || undefined, + couponCode: params.get('couponCode') || undefined, + storeId: params.get('storeId') || undefined, + } + } + + // 尝试解析简单格式:type:param1:param2 + const parts = qrContent.split(':') + if (parts.length >= 2) { + const [type, ...params] = parts + + switch (type) { + case 'table_start': + return { + type: QRCodeType.TABLE_START, + grpSn: params[0], + tableId: params[1], + storeId: params[2] + } + case 'table_close': + return { + type: QRCodeType.TABLE_CLOSE, + grpSn: params[0], + tableId: params[1] + } + case 'cabinet': + return { + type: QRCodeType.CABINET, + cabinetId: params[0], + storeId: params[1] + } + case 'coupon': + return { + type: QRCodeType.COUPON, + couponCode: params[0] + } + } + } + + // 无法识别的格式 + return { + type: QRCodeType.UNKNOWN, + rawContent: qrContent + } + } catch (error) { + console.error('二维码解析失败:', error) + return { + type: QRCodeType.UNKNOWN, + rawContent: qrContent + } + } +} + +/** + * 根据二维码数据执行跳转逻辑 + * @param qrData 解析后的二维码数据 + */ +export const handleQRCodeNavigation = async (qrData: QRCodeData) => { + console.log('处理二维码跳转:', qrData) + + try { + switch (qrData.type) { + case QRCodeType.TABLE_START: + // 开台二维码 - 跳转到开台选择页面 + await Taro.navigateTo({ + url: `/pageScan/startTable/index?grpSn=${qrData.grpSn}&tableId=${qrData.tableId}&storeId=${qrData.storeId || ''}` + }) + break + + case QRCodeType.TABLE_CLOSE: + // 关台二维码 - 跳转到关台页面 + await Taro.navigateTo({ + url: `/pageScan/closeTable/index?grpSn=${qrData.grpSn}&tableId=${qrData.tableId}` + }) + break + + case QRCodeType.CABINET: + // 存杆柜二维码 - 跳转到存杆柜页面 + await Taro.navigateTo({ + url: `/pagesOrder/poleStorage/index?cabinetId=${qrData.cabinetId}&storeId=${qrData.storeId || ''}` + }) + break + + case QRCodeType.COUPON: + // 团购券二维码 - 跳转到团购券使用页面 + await Taro.navigateTo({ + url: `/pageScan/couponUse/index?couponCode=${qrData.couponCode}` + }) + break + + default: + // 未知类型 - 显示错误提示 + Taro.showModal({ + title: '提示', + content: '无法识别的二维码,请确认二维码是否正确', + showCancel: false + }) + break + } + } catch (error) { + console.error('页面跳转失败:', error) + Taro.showToast({ + title: '页面跳转失败', + icon: 'none' + }) + } +} + +/** + * 扫码并处理结果的完整流程 + * @param options 扫码选项 + */ +export const scanAndHandle = (options?: { + onlyFromCamera?: boolean + scanType?: ('qrCode' | 'barCode')[] +}) => { + return new Promise((resolve, reject) => { + Taro.scanCode({ + onlyFromCamera: options?.onlyFromCamera ?? true, + scanType: options?.scanType ?? ['qrCode', 'barCode'], + success: async (res) => { + console.log('扫码成功:', res.result) + + // 解析二维码 + const qrData = parseQRCode(res.result) + + // 执行跳转 + await handleQRCodeNavigation(qrData) + + resolve(qrData) + }, + fail: (err) => { + console.error('扫码失败:', err) + + if (err.errMsg?.includes('cancel')) { + // 用户取消扫码 + reject(new Error('用户取消扫码')) + } else { + // 扫码失败 + Taro.showToast({ + title: '扫码失败,请重试', + icon: 'none' + }) + reject(err) + } + } + }) + }) +} + +// 默认导出 +export default { + parseQRCode, + handleQRCodeNavigation, + scanAndHandle, + QRCodeType +} \ No newline at end of file