Files
glt-taro/src/user/store/verification.tsx
赵忠林 1f6ecebc6a feat(user): 优化礼品卡核销功能
- 修改 API基础 URL 为正式环境地址
- 移除礼品卡信息中的 useTime 字段
-优化礼品卡核销页面布局和功能
- 完善礼品卡信息展示,包括商品图片、描述等
- 优化手动输入核销码流程,支持直接验证- 调整礼品卡状态展示方式,增加过期状态- 优化代码结构,提高可读性和可维护性
2025-08-18 02:10:42 +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, Tag, Divider} 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} from "@/api/shop/shopGift";
import {useUser} from "@/hooks/useUser";
import type { ShopGift } from "@/api/shop/shopGift/model";
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 [verificationResult, setVerificationResult] = useState<'success' | 'failed' | null>(null)
const [loading, setLoading] = useState(false)
// 扫码功能
const handleScan = () => {
Taro.scanCode({
success: (res) => {
if (res.result) {
setScanResult(res.result)
setVerificationCode(res.result)
handleManualVerification(res.result)
}
},
fail: (err) => {
console.error('扫码失败:', err)
Taro.showToast({
title: '扫码失败',
icon: 'error'
})
}
})
}
// 手动输入核销码验证
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()) {
setVerificationResult('failed')
setLoading(false)
return Taro.showToast({
title: '您没有核销权限',
icon: 'error'
})
}
if (gift.status === 1) {
setVerificationResult('failed')
setLoading(false)
return Taro.showToast({
title: '此礼品码已使用',
icon: 'error'
})
}
if (gift.status === 2) {
setVerificationResult('failed')
setLoading(false)
return Taro.showToast({
title: '此礼品码已失效',
icon: 'error'
})
}
if (gift.userId === 0) {
setVerificationResult('failed')
setLoading(false)
return Taro.showToast({
title: '此礼品码未认领',
icon: 'error'
})
}
// 验证成功,设置状态
setVerificationResult('success')
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)
setVerificationResult('failed')
Taro.showToast({
title: '验证失败',
icon: 'error'
})
} finally {
setLoading(false)
}
}
// 重置表单
const resetForm = () => {
setScanResult('')
setVerificationCode('')
setGiftInfo(null)
setVerificationResult(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>
{/* 扫码结果显示 */}
{scanResult && !giftInfo && (
<View className="mt-3 p-3 bg-gray-50 rounded">
<Text className="text-sm text-gray-600">:</Text>
<Text className="text-xs text-gray-500 break-all mt-1">
{scanResult}
</Text>
</View>
)}
{/* 商品信息展示 */}
{giftInfo && (
<View className="mt-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="flex">
{/* 商品图片 */}
{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">
<Text className="text-base font-medium text-gray-900 mb-1">
{giftInfo.goodsName || giftInfo.name}
</Text>
{giftInfo.description && (
<Text className="text-sm text-gray-600 mb-2" style={{
display: '-webkit-box',
WebkitLineClamp: 2,
WebkitBoxOrient: 'vertical',
overflow: 'hidden'
}}>
{giftInfo.description}
</Text>
)}
<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.useLocation && (
<View className="flex items-center mb-2">
<Text className="text-sm text-gray-600 w-16">使:</Text>
<Text className="text-sm text-gray-800 flex-1">{giftInfo.useLocation}</Text>
</View>
)}
{giftInfo.expireTime && (
<View className="flex items-center mb-2">
<Text className="text-sm text-gray-600 w-16">:</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 w-16">:</Text>
<Text className="text-sm text-blue-600">{giftInfo.contactInfo}</Text>
</View>
)}
</View>
</View>
)}
</View>
{/* 手动输入区域 */}
{
!giftInfo && (
<View className="bg-white mx-4 mt-4 p-4 rounded-lg">
<Text className="font-bold mb-3"></Text>
<View className="flex">
<Input
placeholder="请输入6位核销码"
value={verificationCode}
onChange={setVerificationCode}
maxLength={6}
className="flex-1 mr-2"
/>
<Button
type="primary"
icon={<Search/>}
loading={loading}
onClick={() => handleManualVerification(verificationCode)}
>
</Button>
</View>
</View>
)
}
{/* 礼品卡信息 */}
{giftInfo && (
<View className="bg-white mx-4 mt-4 p-4 rounded-lg">
<View className="flex justify-between items-center mb-3">
<Text className="font-bold"></Text>
{verificationResult === 'success' && (
<Tag type="success">
</Tag>
)}
{verificationResult === 'failed' && (
<Tag type="danger">
</Tag>
)}
</View>
<View>
<View className="flex justify-between mb-3">
<Text className="text-gray-600"></Text>
<Text className="font-medium">
{giftInfo.goodsName || giftInfo.name}
</Text>
</View>
<View className="flex justify-between mb-3">
<Text className="text-gray-600"></Text>
<Text className="text-lg font-bold text-red-500">
¥{giftInfo.faceValue}
</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.expireTime && (
<View className="flex justify-between">
<Text className="text-gray-600"></Text>
<Text className="text-orange-600">
{dayjs(giftInfo.expireTime).format('YYYY-MM-DD')}
</Text>
</View>
)}
</View>
<Divider/>
{giftInfo && (
<Button
size="large"
type="primary"
loading={loading}
onClick={() => handleManualVerification()}
block
>
</Button>
)}
</View>
)}
{/* 使用说明 */}
<View className="bg-blue-50 mx-4 mb-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>
)
}
export default StoreVerification