- 修改 API基础 URL 为正式环境地址 - 移除礼品卡信息中的 useTime 字段 -优化礼品卡核销页面布局和功能 - 完善礼品卡信息展示,包括商品图片、描述等 - 优化手动输入核销码流程,支持直接验证- 调整礼品卡状态展示方式,增加过期状态- 优化代码结构,提高可读性和可维护性
377 lines
12 KiB
TypeScript
377 lines
12 KiB
TypeScript
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
|