优化打手提现审核流程

This commit is contained in:
yml2213
2026-08-17 11:18:14 +08:00
parent 9ed2e0d4fe
commit ea5c2a4105
13 changed files with 623 additions and 138 deletions
@@ -40,6 +40,7 @@ import ImageUpload from '@/components/files/ImageUpload'
import {
changeWorkerPassword,
createWorkerRechargeRequest,
createWorkerWithdrawalAccount,
createWorkerWithdrawRequest,
fetchWorkerFinanceRequests,
fetchWorkerProfile,
@@ -66,10 +67,15 @@ type RechargeFormValues = {
type WithdrawFormValues = {
amount?: number
accountChannel?: string
note?: string
}
type WithdrawalAccountFormValues = {
accountChannel?: string
accountName?: string
accountNo?: string
note?: string
wechatQrCodeImages?: UploadedFile[]
}
type PasswordFormValues = {
@@ -94,14 +100,19 @@ export default function WorkerProfilePage() {
const [requestStatus, setRequestStatus] = useState('')
const [rechargeOpen, setRechargeOpen] = useState(false)
const [withdrawOpen, setWithdrawOpen] = useState(false)
const [withdrawalAccountOpen, setWithdrawalAccountOpen] = useState(false)
const [passwordOpen, setPasswordOpen] = useState(false)
const [contactOpen, setContactOpen] = useState(false)
const [submittingRecharge, setSubmittingRecharge] = useState(false)
const [submittingWithdraw, setSubmittingWithdraw] = useState(false)
const [submittingWithdrawalAccount, setSubmittingWithdrawalAccount] = useState(false)
const [submittingPassword, setSubmittingPassword] = useState(false)
const [rechargeForm] = Form.useForm<RechargeFormValues>()
const [withdrawForm] = Form.useForm<WithdrawFormValues>()
const [withdrawalAccountForm] = Form.useForm<WithdrawalAccountFormValues>()
const [passwordForm] = Form.useForm<PasswordFormValues>()
const selectedWithdrawChannel = Form.useWatch('accountChannel', withdrawForm)
const selectedWithdrawalAccountChannel = Form.useWatch('accountChannel', withdrawalAccountForm)
const profileQuery = useQuery({
queryKey: ['worker-profile'],
@@ -113,6 +124,10 @@ export default function WorkerProfilePage() {
const summary = profileQuery.data?.data.summary
const levelProgress = profileQuery.data?.data.levelProgress
const financeConfig = profileQuery.data?.data.financeConfig
const withdrawalAccounts = profileQuery.data?.data.withdrawalAccounts || []
const selectedWithdrawalAccount = withdrawalAccounts.find(
(account) => account.accountChannel === selectedWithdrawChannel,
)
const canUseFinanceActions = worker?.status === 'active'
async function copyInviteCode() {
@@ -191,9 +206,26 @@ export default function WorkerProfilePage() {
message.warning('当前提现入口暂未开放,请联系管理员')
return
}
if (withdrawalAccounts.length === 0) {
openWithdrawalAccountModal()
return
}
withdrawForm.setFieldValue('accountChannel', withdrawalAccounts[0].accountChannel)
setWithdrawOpen(true)
}
function openWithdrawalAccountModal() {
const nextChannel = ['alipay', 'wechat'].find(
(channel) => !withdrawalAccounts.some((account) => account.accountChannel === channel),
)
if (!nextChannel) {
message.info('支付宝和微信提现信息均已添加,修改请联系客服处理')
return
}
withdrawalAccountForm.setFieldValue('accountChannel', nextChannel)
setWithdrawalAccountOpen(true)
}
async function submitRecharge(values: RechargeFormValues) {
setSubmittingRecharge(true)
try {
@@ -222,9 +254,7 @@ export default function WorkerProfilePage() {
try {
await createWorkerWithdrawRequest({
amount: values.amount,
accountChannel: values.accountChannel,
accountName: String(values.accountName || '').trim(),
accountNo: String(values.accountNo || '').trim(),
accountChannel: String(values.accountChannel || ''),
note: String(values.note || '').trim(),
})
message.success('提现申请已提交,请等待管理员审核')
@@ -241,6 +271,28 @@ export default function WorkerProfilePage() {
}
}
async function submitWithdrawalAccount(values: WithdrawalAccountFormValues) {
setSubmittingWithdrawalAccount(true)
try {
await createWorkerWithdrawalAccount({
accountChannel: String(values.accountChannel || ''),
accountName: String(values.accountName || '').trim(),
accountNo: String(values.accountNo || '').trim(),
wechatQrCodeImages: values.wechatQrCodeImages || [],
})
message.success('提现信息已添加,该方式后续修改请联系客服处理')
setWithdrawalAccountOpen(false)
withdrawalAccountForm.resetFields()
await refreshAll()
withdrawForm.setFieldValue('accountChannel', String(values.accountChannel || ''))
setWithdrawOpen(true)
} catch (error) {
message.error(error instanceof Error ? error.message : '添加提现信息失败')
} finally {
setSubmittingWithdrawalAccount(false)
}
}
async function submitPasswordChange(values: PasswordFormValues) {
setSubmittingPassword(true)
try {
@@ -1055,6 +1107,15 @@ export default function WorkerProfilePage() {
>
</Button>
{withdrawalAccounts.length < 2 ? (
<Button
icon={<BankOutlined />}
disabled={!canUseFinanceActions}
onClick={openWithdrawalAccountModal}
>
</Button>
) : null}
<Button icon={<LockOutlined />} onClick={() => setPasswordOpen(true)}>
</Button>
@@ -1122,6 +1183,37 @@ export default function WorkerProfilePage() {
<Descriptions.Item label="提现中金额">
{formatMoney(summary?.pendingWithdrawAmount)}
</Descriptions.Item>
<Descriptions.Item label="提现信息" span={2}>
{withdrawalAccounts.length > 0 ? (
<Space direction="vertical" size={2}>
{withdrawalAccounts.map((account) => (
<Space key={account.accountChannel} wrap size={4}>
<Typography.Text>
{formatWithdrawChannel(account.accountChannel)}
</Typography.Text>
<Typography.Text>{account.accountName}</Typography.Text>
<Typography.Text type="secondary">
{account.accountChannel === 'wechat'
? '已上传收款二维码'
: account.accountNoMasked}
</Typography.Text>
<Typography.Text type="secondary">
</Typography.Text>
</Space>
))}
</Space>
) : (
<Button
type="link"
size="small"
disabled={!canUseFinanceActions}
onClick={openWithdrawalAccountModal}
>
</Button>
)}
</Descriptions.Item>
<Descriptions.Item label="免押额度">
{formatMoney(worker.level?.permissions.depositFreeAmount || 0)}
</Descriptions.Item>
@@ -1387,6 +1479,69 @@ export default function WorkerProfilePage() {
</Form>
</Modal>
<Modal
title="添加提现信息"
open={withdrawalAccountOpen}
width={isMobile ? '92%' : 500}
destroyOnHidden
confirmLoading={submittingWithdrawalAccount}
onCancel={() => {
setWithdrawalAccountOpen(false)
withdrawalAccountForm.resetFields()
}}
onOk={() => withdrawalAccountForm.submit()}
>
<Alert
showIcon
type="warning"
message="支付宝和微信可分别添加一次;每种方式添加后均无法自行修改,如需修改请联系客服。"
style={{ marginBottom: 16 }}
/>
<Form form={withdrawalAccountForm} layout="vertical" onFinish={submitWithdrawalAccount}>
<Form.Item
label="提现方式"
name="accountChannel"
rules={[{ required: true, message: '请选择提现方式' }]}
>
<Select
options={[
...(!withdrawalAccounts.some((account) => account.accountChannel === 'alipay')
? [{ value: 'alipay', label: '支付宝' }]
: []),
...(!withdrawalAccounts.some((account) => account.accountChannel === 'wechat')
? [{ value: 'wechat', label: '微信收款(仅支持 100 元及以下提现)' }]
: []),
]}
/>
</Form.Item>
<Form.Item
label="收款人姓名"
name="accountName"
rules={[{ required: true, message: '请输入收款人姓名' }]}
>
<Input placeholder="请输入真实姓名" />
</Form.Item>
{selectedWithdrawalAccountChannel === 'wechat' ? (
<Form.Item
label="微信收款二维码"
name="wechatQrCodeImages"
rules={[{ required: true, message: '请上传微信收款二维码' }]}
extra="微信提现仅使用收款二维码,不需要填写微信账号。"
>
<ImageUpload scene="worker-withdrawal-wechat-qr" scope="worker" maxCount={1} />
</Form.Item>
) : (
<Form.Item
label="支付宝账号"
name="accountNo"
rules={[{ required: true, message: '请输入支付宝账号' }]}
>
<Input placeholder="请输入支付宝账号" />
</Form.Item>
)}
</Form>
</Modal>
<Modal
title="提现申请"
open={withdrawOpen}
@@ -1399,12 +1554,7 @@ export default function WorkerProfilePage() {
}}
onOk={() => withdrawForm.submit()}
>
<Form
form={withdrawForm}
layout="vertical"
initialValues={{ accountChannel: 'alipay' }}
onFinish={submitWithdraw}
>
<Form form={withdrawForm} layout="vertical" onFinish={submitWithdraw}>
{financeConfig?.withdraw.instructions ? (
<Alert
showIcon
@@ -1416,49 +1566,58 @@ export default function WorkerProfilePage() {
<Alert
showIcon
type="warning"
message={`每天提现 1 次;有押金的工单验收通过后,押金需 ${Number(
message={`每天最多提现 3 次(支付宝、微信共享次数);100 元及以下支持微信和支付宝,超过 100 元仅支持支付宝;有押金的工单验收通过后,押金需 ${Number(
financeConfig?.depositUnfreezeDays ?? 3,
)} 天解冻到账后方可提现。`}
style={{ marginBottom: 16 }}
/>
<Descriptions column={1} bordered size="small" style={{ marginBottom: 16 }}>
<Descriptions.Item label="提现账户">
{formatWithdrawChannel(selectedWithdrawalAccount?.accountChannel || '')} ·{' '}
{selectedWithdrawalAccount?.accountName || '-'} ·{' '}
{selectedWithdrawalAccount?.accountChannel === 'wechat'
? '收款二维码'
: selectedWithdrawalAccount?.accountNoMasked || '-'}
</Descriptions.Item>
</Descriptions>
<Form.Item label="可提现余额">
<Typography.Text strong>
{formatMoney(resolveAvailableForWithdraw(worker, summary))}
</Typography.Text>
</Form.Item>
<Form.Item
label="提现金额"
name="amount"
rules={[{ required: true, message: '请输入提现金额' }]}
>
<InputNumber min={0.01} step={1} addonAfter="元" className="full-width" />
</Form.Item>
<Form.Item
label="提现方式"
name="accountChannel"
rules={[{ required: true, message: '请选择提现方式' }]}
>
<Select
options={[
{ value: 'alipay', label: '支付宝' },
{ value: 'wechat', label: '微信收款' },
{ value: 'bank', label: '银行卡' },
]}
options={withdrawalAccounts.map((account) => ({
value: account.accountChannel,
label:
account.accountChannel === 'wechat'
? '微信收款 · 收款二维码'
: `${formatWithdrawChannel(account.accountChannel)} · ${account.accountNoMasked}`,
}))}
/>
</Form.Item>
<Form.Item
label="收款人姓名"
name="accountName"
rules={[{ required: true, message: '请输入收款人姓名' }]}
label="提现金额"
name="amount"
rules={[
{ required: true, message: '请输入提现金额' },
{
validator(_, value) {
if (Number(value || 0) > 100 && selectedWithdrawChannel !== 'alipay') {
return Promise.reject(
new Error('超过 100 元仅支持支付宝提现,请联系客服修改提现信息'),
)
}
return Promise.resolve()
},
},
]}
>
<Input placeholder="请输入真实姓名" />
</Form.Item>
<Form.Item
label="收款账号"
name="accountNo"
rules={[{ required: true, message: '请输入收款账号' }]}
>
<Input placeholder="请输入支付宝账号、微信号或银行卡号" />
<InputNumber min={0.01} step={1} addonAfter="元" className="full-width" />
</Form.Item>
<Form.Item label="备注说明" name="note">
<Input.TextArea rows={4} placeholder="可补充到账要求、手机号、开户行等说明。" />