feat(withdraw): 添加实名认证验证功能
- 在提现页面集成实名认证状态检查 - 添加 fetchVerifyStatus 函数用于获取认证状态 - 实现认证状态包括未知、已认证、未认证、审核中、已驳回 - 在提交提现前验证用户是否已完成实名认证 - 添加去认证按钮跳转到认证页面 - 优化订单详情和订单列表中的取消订单逻辑 - 修复用户认证页面的表单验证逻辑 - 添加真实姓名和身份证号输入字段到企业认证表单
This commit is contained in:
@@ -16,6 +16,8 @@ import {Wallet} from '@nutui/icons-react-taro'
|
|||||||
import {businessGradients} from '@/styles/gradients'
|
import {businessGradients} from '@/styles/gradients'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import {useDealerUser} from '@/hooks/useDealerUser'
|
import {useDealerUser} from '@/hooks/useDealerUser'
|
||||||
|
import {myUserVerify} from '@/api/system/userVerify'
|
||||||
|
import {goTo} from '@/utils/navigation'
|
||||||
import {
|
import {
|
||||||
pageShopDealerWithdraw,
|
pageShopDealerWithdraw,
|
||||||
addShopDealerWithdraw,
|
addShopDealerWithdraw,
|
||||||
@@ -106,6 +108,8 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
const formRef = useRef<any>(null)
|
const formRef = useRef<any>(null)
|
||||||
|
|
||||||
const {dealerUser} = useDealerUser()
|
const {dealerUser} = useDealerUser()
|
||||||
|
const [verifyStatus, setVerifyStatus] = useState<'unknown' | 'verified' | 'unverified' | 'pending' | 'rejected'>('unknown')
|
||||||
|
const [verifyStatusText, setVerifyStatusText] = useState<string>('')
|
||||||
|
|
||||||
// Tab 切换处理函数
|
// Tab 切换处理函数
|
||||||
const handleTabChange = (value: string | number) => {
|
const handleTabChange = (value: string | number) => {
|
||||||
@@ -185,6 +189,57 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}, [fetchBalance, fetchWithdrawRecords])
|
}, [fetchBalance, fetchWithdrawRecords])
|
||||||
|
|
||||||
|
// 判断实名认证状态:提现前必须完成实名认证(已通过)
|
||||||
|
const fetchVerifyStatus = useCallback(async () => {
|
||||||
|
// Fast path: some pages store this flag after login.
|
||||||
|
if (String(Taro.getStorageSync('Certification')) === '1') {
|
||||||
|
setVerifyStatus('verified')
|
||||||
|
setVerifyStatusText('已实名认证')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const r = await myUserVerify({})
|
||||||
|
if (!r) {
|
||||||
|
setVerifyStatus('unverified')
|
||||||
|
setVerifyStatusText('未实名认证')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const s = Number((r as any).status)
|
||||||
|
const st = String((r as any).statusText || '')
|
||||||
|
|
||||||
|
// Common convention in this project: 0审核中/待审核, 1已通过, 2已驳回
|
||||||
|
if (s === 1) {
|
||||||
|
setVerifyStatus('verified')
|
||||||
|
setVerifyStatusText(st || '已实名认证')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (s === 0) {
|
||||||
|
setVerifyStatus('pending')
|
||||||
|
setVerifyStatusText(st || '审核中')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (s === 2) {
|
||||||
|
setVerifyStatus('rejected')
|
||||||
|
setVerifyStatusText(st || '已驳回')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setVerifyStatus('unverified')
|
||||||
|
setVerifyStatusText(st || '未实名认证')
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('获取实名认证状态失败,将按未认证处理:', e)
|
||||||
|
setVerifyStatus('unverified')
|
||||||
|
setVerifyStatusText('未实名认证')
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!dealerUser?.userId) return
|
||||||
|
fetchVerifyStatus().then()
|
||||||
|
}, [dealerUser?.userId, fetchVerifyStatus])
|
||||||
|
|
||||||
const getStatusText = (status?: number) => {
|
const getStatusText = (status?: number) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 40:
|
case 40:
|
||||||
@@ -224,6 +279,14 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (verifyStatus !== 'verified') {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '请先完成实名认证',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 验证提现金额
|
// 验证提现金额
|
||||||
const amount = parseFloat(String(values.amount))
|
const amount = parseFloat(String(values.amount))
|
||||||
const available = parseFloat(normalizeMoneyString(availableAmount).replace(/,/g, ''))
|
const available = parseFloat(normalizeMoneyString(availableAmount).replace(/,/g, ''))
|
||||||
@@ -366,8 +429,28 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
return Number.isFinite(n) ? n.toFixed(2) : '0.00'
|
return Number.isFinite(n) ? n.toFixed(2) : '0.00'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const goVerify = () => {
|
||||||
|
goTo('/user/userVerify/index')
|
||||||
|
}
|
||||||
|
|
||||||
const renderWithdrawForm = () => (
|
const renderWithdrawForm = () => (
|
||||||
<View>
|
<View>
|
||||||
|
{(verifyStatus === 'unverified' || verifyStatus === 'pending' || verifyStatus === 'rejected') && (
|
||||||
|
<View className="rounded-lg bg-white px-4 py-3 mb-4 mx-4">
|
||||||
|
<View className="flex items-center justify-between">
|
||||||
|
<View className="flex flex-col">
|
||||||
|
<Text className="text-sm text-red-500">提现前请先完成实名认证</Text>
|
||||||
|
{verifyStatusText ? (
|
||||||
|
<Text className="text-xs text-gray-500 mt-1">当前状态:{verifyStatusText}</Text>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
<Text className="text-sm text-blue-600" onClick={goVerify}>
|
||||||
|
去认证
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 余额卡片 */}
|
{/* 余额卡片 */}
|
||||||
<View className="rounded-xl p-6 mb-6 text-white relative overflow-hidden" style={{
|
<View className="rounded-xl p-6 mb-6 text-white relative overflow-hidden" style={{
|
||||||
background: businessGradients.dealer.header
|
background: businessGradients.dealer.header
|
||||||
@@ -452,7 +535,7 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
type="primary"
|
type="primary"
|
||||||
nativeType="submit"
|
nativeType="submit"
|
||||||
loading={submitting}
|
loading={submitting}
|
||||||
disabled={submitting}
|
disabled={submitting || verifyStatus !== 'verified'}
|
||||||
>
|
>
|
||||||
{submitting ? '提交中...' : '申请提现'}
|
{submitting ? '提交中...' : '申请提现'}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -20,11 +20,13 @@ const OrderDetail = () => {
|
|||||||
// 处理支付超时
|
// 处理支付超时
|
||||||
const handlePaymentExpired = async () => {
|
const handlePaymentExpired = async () => {
|
||||||
if (!order) return;
|
if (!order) return;
|
||||||
|
if (!order.orderId) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 自动取消过期订单
|
// 自动取消过期订单
|
||||||
await updateShopOrder({
|
await updateShopOrder({
|
||||||
...order,
|
// 只传最小字段,避免误取消/误走售后流程
|
||||||
|
orderId: order.orderId,
|
||||||
orderStatus: 2 // 已取消
|
orderStatus: 2 // 已取消
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -362,13 +362,23 @@ function OrderList(props: OrderListProps) {
|
|||||||
// 确认取消订单
|
// 确认取消订单
|
||||||
const handleConfirmCancel = async () => {
|
const handleConfirmCancel = async () => {
|
||||||
if (!orderToCancel) return;
|
if (!orderToCancel) return;
|
||||||
|
if (!orderToCancel.orderId) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '订单信息错误',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
setOrderToCancel(null);
|
||||||
|
setCancelDialogVisible(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setCancelDialogVisible(false);
|
setCancelDialogVisible(false);
|
||||||
|
|
||||||
// 更新订单状态为已取消,而不是删除订单
|
// 更新订单状态为已取消,而不是删除订单
|
||||||
await updateShopOrder({
|
await updateShopOrder({
|
||||||
...orderToCancel,
|
// 只传最小字段,避免误取消/误走售后流程
|
||||||
|
orderId: orderToCancel.orderId,
|
||||||
orderStatus: 2 // 已取消
|
orderStatus: 2 // 已取消
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -53,17 +53,17 @@ function Index() {
|
|||||||
const submitSucceed = (values: any) => {
|
const submitSucceed = (values: any) => {
|
||||||
console.log('提交表单', values);
|
console.log('提交表单', values);
|
||||||
if (FormData.status != 2 && FormData.status != undefined) return false;
|
if (FormData.status != 2 && FormData.status != undefined) return false;
|
||||||
if (FormData.type == 0) {
|
if (FormData.type == 0 || FormData.type == 1) {
|
||||||
if (!FormData.sfz1 || !FormData.sfz2) {
|
if (!FormData.realName || !FormData.idCard) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '请上传身份证正反面',
|
title: '请填写真实姓名和身份证号码',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!FormData.realName || !FormData.idCard) {
|
if (!FormData.sfz1 || !FormData.sfz2) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '请填写真实姓名和身份证号码',
|
title: '请上传身份证正反面',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
@@ -85,13 +85,6 @@ function Index() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!FormData.realName){
|
|
||||||
Taro.showToast({
|
|
||||||
title: '请填写真实姓名',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const saveOrUpdate = isUpdate ? updateUserVerify : addUserVerify;
|
const saveOrUpdate = isUpdate ? updateUserVerify : addUserVerify;
|
||||||
saveOrUpdate({...FormData, status: 0}).then(() => {
|
saveOrUpdate({...FormData, status: 0}).then(() => {
|
||||||
Taro.showToast({title: `提交成功`, icon: 'success'})
|
Taro.showToast({title: `提交成功`, icon: 'success'})
|
||||||
@@ -249,6 +242,54 @@ function Index() {
|
|||||||
// 企业类型
|
// 企业类型
|
||||||
FormData.type == 1 && (
|
FormData.type == 1 && (
|
||||||
<>
|
<>
|
||||||
|
<Form.Item
|
||||||
|
label={'真实姓名'}
|
||||||
|
name="realName"
|
||||||
|
required
|
||||||
|
initialValue={FormData.realName}
|
||||||
|
rules={[{message: '请输入真实姓名'}]}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
placeholder={'请输入真实姓名'}
|
||||||
|
type="text"
|
||||||
|
disabled={FormData.status != 2 && FormData.status != undefined}
|
||||||
|
value={FormData?.realName}
|
||||||
|
onChange={(value) => setFormData({...FormData, realName: value})}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={'身份证号码'}
|
||||||
|
name="idCard"
|
||||||
|
required
|
||||||
|
initialValue={FormData.idCard}
|
||||||
|
rules={[{message: '请输入身份证号码'}]}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
placeholder="请输入身份证号码"
|
||||||
|
type="text"
|
||||||
|
value={FormData?.idCard}
|
||||||
|
disabled={FormData.status != 2 && FormData.status != undefined}
|
||||||
|
maxLength={18}
|
||||||
|
onChange={(value) => setFormData({...FormData, idCard: value})}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={'上传证件'}
|
||||||
|
name="image"
|
||||||
|
required
|
||||||
|
rules={[{message: '请上传身份证正反面'}]}
|
||||||
|
>
|
||||||
|
<div className={'flex gap-2'}>
|
||||||
|
<div onClick={uploadSfz1}>
|
||||||
|
<Image src={FormData.sfz1} lazyLoad={false}
|
||||||
|
radius="10%" width="80" height="80"/>
|
||||||
|
</div>
|
||||||
|
<div onClick={uploadSfz2}>
|
||||||
|
<Image src={FormData.sfz2} mode={'scaleToFill'} lazyLoad={false}
|
||||||
|
radius="10%" width="80" height="80"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={'主体名称'}
|
label={'主体名称'}
|
||||||
name="name"
|
name="name"
|
||||||
@@ -260,6 +301,7 @@ function Index() {
|
|||||||
placeholder={'请输入主体名称'}
|
placeholder={'请输入主体名称'}
|
||||||
type="text"
|
type="text"
|
||||||
value={FormData?.name}
|
value={FormData?.name}
|
||||||
|
disabled={FormData.status != 2 && FormData.status != undefined}
|
||||||
onChange={(value) => setFormData({...FormData, name: value})}
|
onChange={(value) => setFormData({...FormData, name: value})}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -274,6 +316,7 @@ function Index() {
|
|||||||
placeholder="请输入营业执照号码"
|
placeholder="请输入营业执照号码"
|
||||||
type="text"
|
type="text"
|
||||||
value={FormData?.zzCode}
|
value={FormData?.zzCode}
|
||||||
|
disabled={FormData.status != 2 && FormData.status != undefined}
|
||||||
maxLength={18}
|
maxLength={18}
|
||||||
onChange={(value) => setFormData({...FormData, zzCode: value})}
|
onChange={(value) => setFormData({...FormData, zzCode: value})}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user