Files
mp-10550/src/user/store/verification.tsx
赵忠林 9d9762ef17 feat(theme): 实现主题切换系统并优化经销商相关页面
- 新增主题切换系统,支持智能主题和手动选择
- 更新经销商首页、团队、订单、提现等页面样式
- 添加主题相关的Hook和样式工具函数
- 优化部分组件样式以适配新主题
2025-08-19 00:08:26 +08:00

377 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, {useState} from 'react'
import {View, Text, Image} from '@tarojs/components'
import {Button, Input} from '@nutui/nutui-react-taro'
import {Scan, Search} from '@nutui/icons-react-taro'
import Taro from '@tarojs/taro'
import dayjs from 'dayjs'
import {getShopGiftByCode, updateShopGift, decryptQrData} from "@/api/shop/shopGift";
import {useUser} from "@/hooks/useUser";
import type {ShopGift} from "@/api/shop/shopGift/model";
import {isValidJSON} from "@/utils/jsonUtils";
const StoreVerification: React.FC = () => {
const {
isAdmin
} = useUser();
const [scanResult, setScanResult] = useState<string>('')
const [verificationCode, setVerificationCode] = useState<string>('')
const [giftInfo, setGiftInfo] = useState<ShopGift | null>(null)
const [loading, setLoading] = useState(false)
// 扫码功能
const handleScan = () => {
Taro.scanCode({
success: (res) => {
if (res.result) {
console.log('扫码结果:', res.result)
// 判断是否为JSON格式
if (isValidJSON(res.result)) {
setLoading(true)
const json = JSON.parse(res.result)
console.log(json, 'json')
if (json.businessType === 'gift') {
// 调用解密接口
handleDecryptAndVerify(json.token, json.data).then()
}
}
}
},
fail: (err) => {
console.error('扫码失败:', err)
Taro.showToast({
title: '扫码失败',
icon: 'error'
})
}
})
}
// 调用解密接口
const handleDecryptAndVerify = async (token: string, encryptedData: string) => {
decryptQrData({token, encryptedData}).then(res => {
const decryptedData = res;
console.log('解密结果:', decryptedData)
console.log('解密成功:', decryptedData)
setScanResult(`${decryptedData}`)
setVerificationCode(`${decryptedData}`)
handleVerification(`${decryptedData}`)
}).catch(() => {
console.error('解密失败:')
Taro.showToast({
title: `token失效请刷新二维码重试`,
icon: 'none'
})
}).finally(() => {
setLoading(false)
})
}
// 验证商品信息
const handleVerification = async (code?: string) => {
setGiftInfo(null)
setVerificationCode(`${code}`)
// 这里应该调用后端API验证核销码
const gift = await getShopGiftByCode(`${code}`)
if(gift){
// 设置礼品信息用于显示
setGiftInfo(gift)
}
}
// 手动输入核销码验证
const handleManualVerification = async (code?: string) => {
const codeToVerify = code || verificationCode.trim()
if (!codeToVerify) {
return false;
}
setLoading(true)
try {
// 这里应该调用后端API验证核销码
const gift = await getShopGiftByCode(codeToVerify)
// 设置礼品信息用于显示
setGiftInfo(gift)
if (!isAdmin()) {
setLoading(false)
return Taro.showToast({
title: '您没有核销权限',
icon: 'error'
})
}
if (gift.status === 1) {
setLoading(false)
return Taro.showToast({
title: '此礼品码已使用',
icon: 'error'
})
}
if (gift.status === 2) {
setLoading(false)
return Taro.showToast({
title: '此礼品码已失效',
icon: 'error'
})
}
if (gift.userId === 0) {
setLoading(false)
return Taro.showToast({
title: '此礼品码未认领',
icon: 'error'
})
}
// 验证成功,设置状态
await updateShopGift({
...gift,
status: 1,
operatorUserId: Number(Taro.getStorageSync('UserId')) || 0,
takeTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
verificationTime: dayjs().format('YYYY-MM-DD HH:mm:ss')
})
Taro.showToast({
title: '核销成功',
icon: 'success'
})
// 重置状态
setTimeout(() => {
resetForm()
}, 2000)
} catch (error) {
console.error('验证失败:', error)
Taro.showToast({
title: '验证失败',
icon: 'error'
})
} finally {
setLoading(false)
}
}
// 重置表单
const resetForm = () => {
setScanResult('')
setVerificationCode('')
setGiftInfo(null)
}
// 获取类型文本
const getTypeText = (type: number) => {
switch (type) {
case 10:
return '实物礼品卡'
case 20:
return '虚拟礼品卡'
case 30:
return '服务礼品卡'
default:
return '礼品卡'
}
}
// useEffect(() => {
// handleManualVerification().then()
// },[verificationCode])
return (
<View className="bg-gray-50 min-h-screen">
{/* 页面标题 */}
<View className="bg-white px-4 py-3 border-b border-gray-100">
<Text className="text-lg font-bold text-center">
</Text>
<Text className="text-sm text-gray-600 text-center mt-1 px-2">
</Text>
</View>
{/* 扫码区域 */}
<View className="bg-white mx-4 mt-4 p-4 rounded-lg">
<View className={'mb-3'}>
<Text className="font-bold"></Text>
</View>
<Button
size="large"
type="primary"
icon={<Scan/>}
onClick={handleScan}
block
>
</Button>
{/* 手动输入区域 */}
<View className="mt-8"></View>
<View className="font-bold mb-3"></View>
<View className="flex items-center justify-between">
<Input
placeholder="请输入6位核销码"
value={verificationCode}
onChange={setVerificationCode}
maxLength={6}
className="flex-1 mr-8"
style={{
backgroundColor: '#f3f3f3',
borderRadius: '8px'
}}
/>
<Button
type="primary"
icon={<Search/>}
loading={loading}
onClick={() => handleVerification(verificationCode)}
>
</Button>
</View>
</View>
{/*在扫码结果显示区域添加解密状态提示*/}
{scanResult && !giftInfo && (
<View className="mt-4 p-4 bg-gray-50 rounded-lg">
<Text className="text-sm text-gray-600">
{loading ? '正在解密验证...' : '扫码结果:'}
</Text>
<Text className="text-xs text-gray-500 break-all mt-1">
{scanResult}
</Text>
</View>
)}
{/* 商品信息展示 */}
{giftInfo && (
<View className="mt-4 mx-4 p-4 bg-white rounded-lg shadow-sm border border-gray-100">
<View className="flex items-center mb-3">
<Text className="text-lg font-semibold text-gray-800"></Text>
<View className={`ml-2 px-2 py-1 rounded text-xs ${
giftInfo.status === 0 ? 'bg-green-100 text-green-600' :
giftInfo.status === 1 ? 'bg-gray-100 text-gray-600' :
'bg-red-100 text-red-600'
}`}>
{giftInfo.status === 0 ? '未使用' :
giftInfo.status === 1 ? '已使用' : '已过期'}
</View>
</View>
<View className="mt-3 pt-3 border-t border-gray-100 flex justify-between">
{/* 商品图片 */}
{giftInfo.goodsImage && (
<View className="w-20 h-20 mr-3 flex-shrink-0">
<Image
src={giftInfo.goodsImage}
className="w-full h-full rounded-lg object-cover border border-gray-200"
mode="aspectFill"
/>
</View>
)}
{/* 商品详情 */}
<View className="flex-1">
<View className="text-base font-medium text-gray-900 mb-1">
{giftInfo.goodsName || giftInfo.name}
</View>
<View className="text-sm text-gray-400">
使{giftInfo.useLocation || '123123'}
</View>
{giftInfo.description && (
<>
<View className="text-sm text-gray-600 mb-2" style={{
overflow: 'hidden'
// 注意:小程序不支持 WebKit 文本截断属性
}}>
{giftInfo.description}
</View>
</>
)}
<View className="flex items-center justify-between">
<Text className="text-lg font-bold text-red-500">
¥{giftInfo.faceValue}
</Text>
<Text className="text-xs text-gray-500">
{giftInfo.type === 10 ? '实物礼品' :
giftInfo.type === 20 ? '虚拟礼品' : '服务礼品'}
</Text>
</View>
</View>
</View>
{/* 附加信息 */}
<View className="mt-3 pt-3 border-t border-gray-100">
{giftInfo.expireTime && (
<View className="flex items-center mb-2">
<Text className="text-sm text-gray-600">:</Text>
<Text className="text-sm text-gray-800">
{dayjs(giftInfo.expireTime).format('YYYY-MM-DD HH:mm')}
</Text>
</View>
)}
{giftInfo.contactInfo && (
<View className="flex items-center">
<Text className="text-sm text-gray-600">:</Text>
<Text className="text-sm text-blue-600">{giftInfo.contactInfo}</Text>
</View>
)}
<View className="flex justify-between mb-3">
<Text className="text-gray-600"></Text>
<Text>{getTypeText(giftInfo.type as number)}</Text>
</View>
<View className="flex justify-between mb-3">
<Text className="text-gray-600"></Text>
<Text className="font-mono text-sm">{giftInfo.code}</Text>
</View>
{giftInfo.operatorUserName && (
<View className="flex justify-between mb-3">
<Text className="text-gray-600"></Text>
<Text className="font-mono text-sm">{giftInfo.operatorUserName}</Text>
</View>
)}
{giftInfo.takeTime && (
<View className="flex justify-between mb-3">
<Text className="text-gray-600"></Text>
<Text className="font-mono text-sm">{giftInfo.takeTime}</Text>
</View>
)}
{giftInfo && giftInfo.status === 0 && (
<Button
size="large"
type="info"
loading={loading}
onClick={() => handleManualVerification()}
block
>
</Button>
)}
</View>
</View>
)}
{/* 使用说明 */}
<View className="bg-blue-50 mx-4 my-4 p-4 rounded-lg">
<Text className="font-bold mb-2 text-gray-500"></Text>
<View>
<Text className="text-sm text-gray-500 mb-1">1. </Text>
<Text className="text-sm text-gray-500 mb-1">2. "扫描二维码"</Text>
<Text className="text-sm text-gray-500 mb-1">3. </Text>
<Text className="text-sm text-gray-500">4. "确认核销"</Text>
</View>
</View>
<View className={'h-10'}></View>
</View>
)
}
export default StoreVerification