|
|
@ -1,22 +1,12 @@ |
|
|
|
import React, { useState } from 'react' |
|
|
|
import { View, Text } from '@tarojs/components' |
|
|
|
import { Button, Input, Tag, Divider } from '@nutui/nutui-react-taro' |
|
|
|
import { Scan, Search } from '@nutui/icons-react-taro' |
|
|
|
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"; |
|
|
|
|
|
|
|
interface GiftCardInfo { |
|
|
|
id: number |
|
|
|
name: string |
|
|
|
goodsName?: string |
|
|
|
faceValue: string |
|
|
|
type: number |
|
|
|
status: number |
|
|
|
expireTime?: string |
|
|
|
code: string |
|
|
|
} |
|
|
|
import type { ShopGift } from "@/api/shop/shopGift/model"; |
|
|
|
|
|
|
|
const StoreVerification: React.FC = () => { |
|
|
|
const { |
|
|
@ -24,7 +14,7 @@ const StoreVerification: React.FC = () => { |
|
|
|
} = useUser(); |
|
|
|
const [scanResult, setScanResult] = useState<string>('') |
|
|
|
const [verificationCode, setVerificationCode] = useState<string>('') |
|
|
|
const [giftInfo, setGiftInfo] = useState<GiftCardInfo | null>(null) |
|
|
|
const [giftInfo, setGiftInfo] = useState<ShopGift | null>(null) |
|
|
|
const [verificationResult, setVerificationResult] = useState<'success' | 'failed' | null>(null) |
|
|
|
const [loading, setLoading] = useState(false) |
|
|
|
|
|
|
@ -32,9 +22,11 @@ const StoreVerification: React.FC = () => { |
|
|
|
const handleScan = () => { |
|
|
|
Taro.scanCode({ |
|
|
|
success: (res) => { |
|
|
|
setScanResult(res.result) |
|
|
|
setVerificationCode(res.result) |
|
|
|
handleManualVerification().then() |
|
|
|
if (res.result) { |
|
|
|
setScanResult(res.result) |
|
|
|
setVerificationCode(res.result) |
|
|
|
handleManualVerification(res.result) |
|
|
|
} |
|
|
|
}, |
|
|
|
fail: (err) => { |
|
|
|
console.error('扫码失败:', err) |
|
|
@ -45,83 +37,78 @@ const StoreVerification: React.FC = () => { |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 手动输入核销码验证
|
|
|
|
const handleManualVerification = async () => { |
|
|
|
const handleManualVerification = async (code?: string) => { |
|
|
|
const codeToVerify = code || verificationCode.trim() |
|
|
|
if (!codeToVerify) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
setLoading(true) |
|
|
|
try { |
|
|
|
// 这里应该调用后端API验证核销码
|
|
|
|
const gift = await getShopGiftByCode(verificationCode.trim()) |
|
|
|
if(!isAdmin()){ |
|
|
|
const gift = await getShopGiftByCode(codeToVerify) |
|
|
|
|
|
|
|
// 设置礼品信息用于显示
|
|
|
|
setGiftInfo(gift) |
|
|
|
|
|
|
|
if (!isAdmin()) { |
|
|
|
setVerificationResult('failed') |
|
|
|
setLoading(false) |
|
|
|
return Taro.showToast({ |
|
|
|
title: '您没有核销权限', |
|
|
|
icon: 'error' |
|
|
|
}) |
|
|
|
} |
|
|
|
if(gift.status == 1){ |
|
|
|
if (gift.status === 1) { |
|
|
|
setVerificationResult('failed') |
|
|
|
setLoading(false) |
|
|
|
return Taro.showToast({ |
|
|
|
title: '此礼品码已使用', |
|
|
|
icon: 'error' |
|
|
|
}) |
|
|
|
} |
|
|
|
if(gift.status == 2){ |
|
|
|
if (gift.status === 2) { |
|
|
|
setVerificationResult('failed') |
|
|
|
setLoading(false) |
|
|
|
return Taro.showToast({ |
|
|
|
title: '此礼品码已失效', |
|
|
|
icon: 'error' |
|
|
|
}) |
|
|
|
} |
|
|
|
if(gift.userId == 0){ |
|
|
|
if (gift.userId === 0) { |
|
|
|
setVerificationResult('failed') |
|
|
|
setLoading(false) |
|
|
|
return Taro.showToast({ |
|
|
|
title: '此礼品码未认领', |
|
|
|
icon: 'error' |
|
|
|
}) |
|
|
|
} |
|
|
|
if (!verificationCode.trim()) { |
|
|
|
Taro.showToast({ |
|
|
|
title: '请输入核销码', |
|
|
|
icon: 'none' |
|
|
|
}) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// 验证成功,设置状态
|
|
|
|
setVerificationResult('success') |
|
|
|
await updateShopGift({ |
|
|
|
...gift, |
|
|
|
status: 1, |
|
|
|
operatorUserId: Taro.getStorageSync('UserId'), |
|
|
|
takeTime: dayjs.unix(Date.now() / 1000).format('YYYY-MM-DD HH:mm:ss'), |
|
|
|
verificationTime: dayjs.unix(Date.now() / 1000).format('YYYY-MM-DD HH:mm:ss') |
|
|
|
}) |
|
|
|
Taro.showToast({ |
|
|
|
title: '核销成功', |
|
|
|
icon: 'success' |
|
|
|
operatorUserId: Number(Taro.getStorageSync('UserId')) || 0, |
|
|
|
takeTime: dayjs().format('YYYY-MM-DD HH:mm:ss'), |
|
|
|
verificationTime: dayjs().format('YYYY-MM-DD HH:mm:ss') |
|
|
|
}) |
|
|
|
} catch (error) { |
|
|
|
console.error('验证失败:', error) |
|
|
|
} finally { |
|
|
|
setLoading(false) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 确认核销
|
|
|
|
const handleConfirmVerification = async () => { |
|
|
|
if (!giftInfo) return |
|
|
|
|
|
|
|
setLoading(true) |
|
|
|
try { |
|
|
|
// 这里应该调用后端API完成核销
|
|
|
|
await mockCompleteVerification(giftInfo.id) |
|
|
|
|
|
|
|
Taro.showToast({ |
|
|
|
title: '核销成功', |
|
|
|
icon: 'success' |
|
|
|
}) |
|
|
|
|
|
|
|
// 重置状态
|
|
|
|
setTimeout(() => { |
|
|
|
resetForm() |
|
|
|
}, 2000) |
|
|
|
} catch (error) { |
|
|
|
console.error('核销失败:', error) |
|
|
|
console.error('验证失败:', error) |
|
|
|
setVerificationResult('failed') |
|
|
|
Taro.showToast({ |
|
|
|
title: '核销失败', |
|
|
|
title: '验证失败', |
|
|
|
icon: 'error' |
|
|
|
}) |
|
|
|
} finally { |
|
|
@ -129,12 +116,6 @@ const StoreVerification: React.FC = () => { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 模拟完成核销
|
|
|
|
const mockCompleteVerification = async (giftId: number) => { |
|
|
|
await new Promise(resolve => setTimeout(resolve, 1000)) |
|
|
|
console.log('核销礼品卡:', giftId) |
|
|
|
} |
|
|
|
|
|
|
|
// 重置表单
|
|
|
|
const resetForm = () => { |
|
|
|
setScanResult('') |
|
|
@ -146,13 +127,21 @@ const StoreVerification: React.FC = () => { |
|
|
|
// 获取类型文本
|
|
|
|
const getTypeText = (type: number) => { |
|
|
|
switch (type) { |
|
|
|
case 10: return '实物礼品卡' |
|
|
|
case 20: return '虚拟礼品卡' |
|
|
|
case 30: return '服务礼品卡' |
|
|
|
default: return '礼品卡' |
|
|
|
case 10: |
|
|
|
return '实物礼品卡' |
|
|
|
case 20: |
|
|
|
return '虚拟礼品卡' |
|
|
|
case 30: |
|
|
|
return '服务礼品卡' |
|
|
|
default: |
|
|
|
return '礼品卡' |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// useEffect(() => {
|
|
|
|
// handleManualVerification().then()
|
|
|
|
// },[verificationCode])
|
|
|
|
|
|
|
|
return ( |
|
|
|
<View className="bg-gray-50 min-h-screen"> |
|
|
|
{/* 页面标题 */} |
|
|
@ -173,14 +162,15 @@ const StoreVerification: React.FC = () => { |
|
|
|
<Button |
|
|
|
size="large" |
|
|
|
type="primary" |
|
|
|
icon={<Scan />} |
|
|
|
icon={<Scan/>} |
|
|
|
onClick={handleScan} |
|
|
|
block |
|
|
|
> |
|
|
|
扫描二维码 |
|
|
|
</Button> |
|
|
|
|
|
|
|
{scanResult && ( |
|
|
|
{/* 扫码结果显示 */} |
|
|
|
{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"> |
|
|
@ -188,29 +178,118 @@ const StoreVerification: React.FC = () => { |
|
|
|
</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> |
|
|
|
|
|
|
|
{/* 手动输入区域 */} |
|
|
|
<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} |
|
|
|
> |
|
|
|
验证 |
|
|
|
</Button> |
|
|
|
</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 && ( |
|
|
@ -246,7 +325,7 @@ const StoreVerification: React.FC = () => { |
|
|
|
|
|
|
|
<View className="flex justify-between mb-3"> |
|
|
|
<Text className="text-gray-600">类型</Text> |
|
|
|
<Text>{getTypeText(giftInfo.type)}</Text> |
|
|
|
<Text>{getTypeText(giftInfo.type as number)}</Text> |
|
|
|
</View> |
|
|
|
|
|
|
|
<View className="flex justify-between mb-3"> |
|
|
@ -264,14 +343,14 @@ const StoreVerification: React.FC = () => { |
|
|
|
)} |
|
|
|
</View> |
|
|
|
|
|
|
|
<Divider /> |
|
|
|
<Divider/> |
|
|
|
|
|
|
|
{verificationResult === 'success' && ( |
|
|
|
{giftInfo && ( |
|
|
|
<Button |
|
|
|
size="large" |
|
|
|
type="primary" |
|
|
|
loading={loading} |
|
|
|
onClick={handleConfirmVerification} |
|
|
|
onClick={() => handleManualVerification()} |
|
|
|
block |
|
|
|
> |
|
|
|
确认核销 |
|
|
|