优化打手提现审核流程

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
@@ -68,6 +68,7 @@ export default function FinancePanel() {
const [page, setPage] = useState(1)
const [pageSize, setPageSize] = useState(ADMIN_DEFAULT_PAGE_SIZE)
const [savingConfig, setSavingConfig] = useState(false)
const [configExpanded, setConfigExpanded] = useState(false)
const [reviewing, setReviewing] = useState(false)
const [reviewState, setReviewState] = useState<{
action: FinanceReviewAction
@@ -207,14 +208,16 @@ export default function FinancePanel() {
render: (_, row) => renderFinanceRequestAccount(row),
},
{
title: '付款凭证',
title: '凭证/收款码',
minWidth: 140,
render: (_, row) =>
row.requestType === 'recharge' ? (
<ImagePreviewList files={getRechargeProofs(row)} size={48} />
) : (
'-'
),
render: (_, row) => (
<ImagePreviewList
files={
row.requestType === 'recharge' ? getRechargeProofs(row) : getWechatWithdrawQrCode(row)
}
size={48}
/>
),
},
{
title: '备注',
@@ -276,87 +279,102 @@ export default function FinancePanel() {
bordered={false}
loading={financeConfigQuery.isLoading}
extra={
<Button
icon={<ReloadOutlined />}
loading={financeConfigQuery.isFetching}
onClick={() => financeConfigQuery.refetch()}
>
</Button>
<Space>
<Button type="link" onClick={() => setConfigExpanded((expanded) => !expanded)}>
{configExpanded ? '收起配置' : '展开配置'}
</Button>
<Button
icon={<ReloadOutlined />}
loading={financeConfigQuery.isFetching}
onClick={() => financeConfigQuery.refetch()}
>
</Button>
</Space>
}
>
{financeConfigQuery.error ? (
{configExpanded && financeConfigQuery.error ? (
<Typography.Text type="danger">
{financeConfigQuery.error instanceof Error
? financeConfigQuery.error.message
: '读取资金配置失败'}
</Typography.Text>
) : null}
<Form
form={configForm}
layout="vertical"
onFinish={submitFinanceConfig}
initialValues={mapFinanceConfigToFormValues()}
>
<Card title="充值入口" size="small" style={{ marginBottom: 16 }}>
<Form.Item label="开启充值申请" name={['recharge', 'enabled']} valuePropName="checked">
<Switch />
</Form.Item>
<Form.Item label="收款通道名称" name={['recharge', 'channelName']}>
<Input placeholder="如:支付宝扫码、微信收款" />
</Form.Item>
<Form.Item label="收款人" name={['recharge', 'accountName']}>
<Input placeholder="请输入收款人姓名" />
</Form.Item>
<Form.Item label="收款账号" name={['recharge', 'accountNo']}>
<Input placeholder="请输入支付宝账号、微信号或银行卡号" />
</Form.Item>
<Form.Item label="收款二维码" name={['recharge', 'qrCodeImageList']}>
<ImageUpload scene="worker-finance" scope="admin" maxCount={1} />
</Form.Item>
<Form.Item label="充值说明" name={['recharge', 'instructions']}>
<Input.TextArea
rows={4}
placeholder="例如:转账后请填写付款时间、付款尾号、截图说明。"
/>
</Form.Item>
</Card>
{configExpanded ? (
<Form
form={configForm}
layout="vertical"
onFinish={submitFinanceConfig}
initialValues={mapFinanceConfigToFormValues()}
>
<Card title="充值入口" size="small" style={{ marginBottom: 16 }}>
<Form.Item
label="开启充值申请"
name={['recharge', 'enabled']}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item label="收款通道名称" name={['recharge', 'channelName']}>
<Input placeholder="如:支付宝扫码、微信收款" />
</Form.Item>
<Form.Item label="收款人" name={['recharge', 'accountName']}>
<Input placeholder="请输入收款人姓名" />
</Form.Item>
<Form.Item label="收款账号" name={['recharge', 'accountNo']}>
<Input placeholder="请输入支付宝账号、微信号或银行卡号" />
</Form.Item>
<Form.Item label="收款二维码" name={['recharge', 'qrCodeImageList']}>
<ImageUpload scene="worker-finance" scope="admin" maxCount={1} />
</Form.Item>
<Form.Item label="充值说明" name={['recharge', 'instructions']}>
<Input.TextArea
rows={4}
placeholder="例如:转账后请填写付款时间、付款尾号、截图说明。"
/>
</Form.Item>
</Card>
<Card title="管理员联系" size="small" style={{ marginBottom: 16 }}>
<Form.Item label="管理员微信二维码" name={['adminContact', 'wechatQrCodeImageList']}>
<ImageUpload scene="worker-admin-contact" scope="admin" maxCount={1} />
</Form.Item>
</Card>
<Card title="管理员联系" size="small" style={{ marginBottom: 16 }}>
<Form.Item label="管理员微信二维码" name={['adminContact', 'wechatQrCodeImageList']}>
<ImageUpload scene="worker-admin-contact" scope="admin" maxCount={1} />
</Form.Item>
</Card>
<Card title="押金与提现规则" size="small" style={{ marginBottom: 16 }}>
<Form.Item
label="押金解冻天数"
name="depositUnfreezeDays"
tooltip="有押金的工单验收通过后,押金进入待解冻,N 天后自动转入可用余额;0 表示验收通过立即释放"
>
<InputNumber min={0} max={30} step={1} addonAfter="天" />
</Form.Item>
<Typography.Text type="secondary">
"接单工单"
</Typography.Text>
</Card>
<Card title="押金与提现规则" size="small" style={{ marginBottom: 16 }}>
<Form.Item
label="押金解冻天数"
name="depositUnfreezeDays"
tooltip="有押金的工单验收通过后,押金进入待解冻,N 天后自动转入可用余额;0 表示验收通过立即释放"
>
<InputNumber min={0} max={30} step={1} addonAfter="天" />
</Form.Item>
<Typography.Text type="secondary">
"接单工单"
</Typography.Text>
</Card>
<Card title="提现入口" size="small" style={{ marginBottom: 16 }}>
<Form.Item label="开启提现申请" name={['withdraw', 'enabled']} valuePropName="checked">
<Switch />
</Form.Item>
<Form.Item label="提现说明" name={['withdraw', 'instructions']}>
<Input.TextArea
rows={4}
placeholder="例如:每天 18:00 前审核,当日打款;银行卡请备注开户行。"
/>
</Form.Item>
</Card>
<Card title="提现入口" size="small" style={{ marginBottom: 16 }}>
<Form.Item
label="开启提现申请"
name={['withdraw', 'enabled']}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item label="提现说明" name={['withdraw', 'instructions']}>
<Input.TextArea
rows={4}
placeholder="例如:每天 18:00 前审核,当日打款;银行卡请备注开户行。"
/>
</Form.Item>
</Card>
<Button type="primary" htmlType="submit" loading={savingConfig}>
</Button>
</Form>
<Button type="primary" htmlType="submit" loading={savingConfig}>
</Button>
</Form>
) : null}
</Card>
<Card
@@ -461,6 +479,16 @@ export default function FinancePanel() {
<Descriptions.Item label="收款信息">
{renderFinanceRequestAccountText(reviewState.request)}
</Descriptions.Item>
{reviewState.request.requestType === 'withdraw' &&
reviewState.request.accountChannel === 'alipay' ? (
<Descriptions.Item label="支付宝账号">
{reviewState.request.accountNo ? (
<Typography.Text copyable>{reviewState.request.accountNo}</Typography.Text>
) : (
'-'
)}
</Descriptions.Item>
) : null}
{reviewState.request.requestType === 'recharge' ? (
<>
<Descriptions.Item label="付款时间">
@@ -473,6 +501,15 @@ export default function FinancePanel() {
</Descriptions.Item>
</>
) : null}
{reviewState.request.requestType === 'withdraw' &&
reviewState.request.accountChannel === 'wechat' ? (
<Descriptions.Item label="微信收款二维码">
<ImagePreviewList
files={getWechatWithdrawQrCode(reviewState.request)}
size={88}
/>
</Descriptions.Item>
) : null}
<Descriptions.Item label="申请备注">
{reviewState.request.note || '-'}
</Descriptions.Item>
@@ -551,7 +588,7 @@ function renderFinanceRequestAccount(request: WorkerFinanceRequest) {
<div className="cell-stack">
<Typography.Text>{renderFinanceRequestAccountText(request)}</Typography.Text>
{request.requestType === 'withdraw' && request.accountNo ? (
<Typography.Text type="secondary">{maskFinanceAccount(request.accountNo)}</Typography.Text>
<Typography.Text copyable>{request.accountNo}</Typography.Text>
) : null}
</div>
)
@@ -606,6 +643,25 @@ function getRechargeProofs(request: WorkerFinanceRequest): UploadedFile[] {
.filter((item): item is UploadedFile => Boolean(item))
}
function getWechatWithdrawQrCode(request: WorkerFinanceRequest): UploadedFile[] {
if (request.requestType !== 'withdraw' || request.accountChannel !== 'wechat') return []
const payload = asRecord(request.payload)
const file = asRecord(payload.wechatQrCodeImage)
const url = String(file.url || '').trim()
if (!url) return []
return [
{
url,
mediumUrl: String(file.mediumUrl || '').trim(),
thumbnailUrl: String(file.thumbnailUrl || '').trim(),
objectKey: String(file.objectKey || '').trim(),
filename: String(file.filename || '').trim(),
contentType: String(file.contentType || '').trim(),
size: Number(file.size || 0),
},
]
}
function getRechargePaidAt(request: WorkerFinanceRequest): string {
const payload = asRecord(request.payload)
return String(payload.paidAt || '').trim()