Browse Source

添加扫码处理逻辑页面

master
DU 4 months ago
parent
commit
db73efdd59
  1. 1
      src/app.config.ts
  2. 94
      src/pages/qrHandler/index.scss
  3. 119
      src/pages/qrHandler/index.tsx
  4. 24
      src/pages/scan/index.tsx
  5. 6
      src/pages/scanCode/index.tsx
  6. 209
      src/utils/qrCodeHandler.ts

1
src/app.config.ts

@ -6,6 +6,7 @@ export default defineAppConfig({ @@ -6,6 +6,7 @@ export default defineAppConfig({
'pages/message/index',
'pages/user/index',
'pages/scanCode/index', // 独立扫码页面(无tabbar)
'pages/qrHandler/index', // 二维码处理页面(统一跳转逻辑)
],
// 分包配置
subpackages: [

94
src/pages/qrHandler/index.scss

@ -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);
}
}
}
}
}
}
}

119
src/pages/qrHandler/index.tsx

@ -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

24
src/pages/scan/index.tsx

@ -6,6 +6,7 @@ import Taro, { useLoad } from '@tarojs/taro' @@ -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(() => { @@ -16,19 +17,16 @@ const ScanIndex = observer(() => {
})
// 扫码
const handleScan = () => {
Taro.scanCode({
success: (res) => {
Taro.showToast({
title: '扫码成功',
icon: 'success',
const handleScan = async () => {
try {
await scanAndHandle({
onlyFromCamera: true,
scanType: ['qrCode', 'barCode']
})
console.log('扫码结果:', res)
},
fail: (err) => {
console.error('扫码失败:', err)
} catch (error) {
// 错误处理已在 scanAndHandle 中完成
console.log('扫码取消或失败')
}
})
}
// 去开台
@ -92,9 +90,9 @@ const ScanIndex = observer(() => { @@ -92,9 +90,9 @@ const ScanIndex = observer(() => {
<Text className="title"></Text>
</View>
<View className="content">
{/* <Button className="scan-btn" onClick={handleScan}>
<Button className="scan-btn" onClick={handleScan}>
</Button> */}
</Button>
<Button className="start-table-btn" onClick={handleReservation}>
</Button>

6
src/pages/scanCode/index.tsx

@ -47,8 +47,10 @@ const ScanCodePage = observer(() => { @@ -47,8 +47,10 @@ const ScanCodePage = observer(() => {
// 处理扫码结果
const handleScanResult = (result: string) => {
console.log('扫码结果:', result)
// TODO: 根据扫码结果处理开台逻辑
// 例如:调用后端接口验证二维码,然后开台
// 跳转到统一的二维码处理页面
Taro.navigateTo({
url: `/pages/qrHandler/index?qrContent=${encodeURIComponent(result)}`
})
}
// 关闭页面返回首页

209
src/utils/qrCodeHandler.ts

@ -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…
Cancel
Save