Browse Source

feat(user): 优化礼品卡核销功能

- 修改 API基础 URL 为正式环境地址
- 移除礼品卡信息中的 useTime 字段
-优化礼品卡核销页面布局和功能
- 完善礼品卡信息展示,包括商品图片、描述等
- 优化手动输入核销码流程,支持直接验证- 调整礼品卡状态展示方式,增加过期状态- 优化代码结构,提高可读性和可维护性
dev
科技小王子 1 week ago
parent
commit
1f6ecebc6a
  1. 2
      config/env.ts
  2. 2
      src/api/shop/shopGift/model/index.ts
  3. 265
      src/user/store/verification.tsx

2
config/env.ts

@ -2,7 +2,7 @@
export const ENV_CONFIG = {
// 开发环境
development: {
API_BASE_URL: 'http://127.0.0.1:9200/api',
API_BASE_URL: 'https://cms-api.websoft.top/api',
APP_NAME: '开发环境',
DEBUG: 'true',
},

2
src/api/shop/shopGift/model/index.ts

@ -24,8 +24,6 @@ export interface ShopGift {
type?: number;
// 领取时间
takeTime?: string;
// 使用时间
useTime?: string;
// 过期时间
expireTime?: string;
// 有效期天数

265
src/user/store/verification.tsx

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

Loading…
Cancel
Save