forked from gxwebsoft/mp-10550
feat(user): 优化礼品卡核销功能
- 修改 API基础 URL 为正式环境地址 - 移除礼品卡信息中的 useTime 字段 -优化礼品卡核销页面布局和功能 - 完善礼品卡信息展示,包括商品图片、描述等 - 优化手动输入核销码流程,支持直接验证- 调整礼品卡状态展示方式,增加过期状态- 优化代码结构,提高可读性和可维护性
This commit is contained in:
@@ -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',
|
||||
},
|
||||
|
||||
@@ -24,8 +24,6 @@ export interface ShopGift {
|
||||
type?: number;
|
||||
// 领取时间
|
||||
takeTime?: string;
|
||||
// 使用时间
|
||||
useTime?: string;
|
||||
// 过期时间
|
||||
expireTime?: string;
|
||||
// 有效期天数
|
||||
|
||||
@@ -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) => {
|
||||
if (res.result) {
|
||||
setScanResult(res.result)
|
||||
setVerificationCode(res.result)
|
||||
handleManualVerification().then()
|
||||
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')
|
||||
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'
|
||||
})
|
||||
} 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,9 +178,95 @@ 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>
|
||||
|
||||
{/* 手动输入区域 */}
|
||||
{
|
||||
!giftInfo && (
|
||||
<View className="bg-white mx-4 mt-4 p-4 rounded-lg">
|
||||
<Text className="font-bold mb-3">手动输入核销码</Text>
|
||||
<View className="flex">
|
||||
@@ -203,14 +279,17 @@ const StoreVerification: React.FC = () => {
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<Search />}
|
||||
icon={<Search/>}
|
||||
loading={loading}
|
||||
onClick={handleManualVerification}
|
||||
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
|
||||
>
|
||||
确认核销
|
||||
|
||||
Reference in New Issue
Block a user