6 changed files with 440 additions and 17 deletions
@ -0,0 +1,94 @@
@@ -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); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,119 @@
@@ -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<string>('') |
||||
|
||||
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 ( |
||||
<View className="qr-handler-page error-state"> |
||||
<View className="error-content"> |
||||
<View className="error-icon">❌</View> |
||||
<Text className="error-title">处理失败</Text> |
||||
<Text className="error-message">{error}</Text> |
||||
|
||||
<View className="error-actions"> |
||||
<View className="action-btn primary" onClick={handleRescan}> |
||||
重新扫码 |
||||
</View> |
||||
<View className="action-btn secondary" onClick={handleBackHome}> |
||||
返回首页 |
||||
</View> |
||||
</View> |
||||
</View> |
||||
</View> |
||||
) |
||||
} |
||||
|
||||
return ( |
||||
<View className="qr-handler-page loading-state"> |
||||
<View className="loading-content"> |
||||
<Loading size="large" /> |
||||
<Text className="loading-text">正在处理二维码...</Text> |
||||
</View> |
||||
</View> |
||||
) |
||||
} |
||||
|
||||
export default QRHandlerPage |
||||
@ -0,0 +1,209 @@
@@ -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<QRCodeData>((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 |
||||
} |
||||
Loading…
Reference in new issue