权限门控(P1-8 剩余): - 新增 resolveAdminPermission(sessionPermissions, code):会话接口的最新权限列表优先, 接口异常才回退登录时的本地快照;空数组也判无权限,避免"刚被收回权限仍能点"。 - 寄售仓库面板:consignment.manage 控制手动入库/编辑/作废/退单回库/匹配开关(无权限时开关禁用)。 - 寄卖提报面板:consignment.review 控制审核入库/驳回。 - 接单平台页:无 consignment.view 不渲染两个寄卖 tab,并把 URL/localStorage 的旧选择拉回工单页。 数据约束(P2-19 剩余): - 迁移 080:allocation_status 历史非法值归位「待安排」(备注留痕)后加 CHECK(待安排/已安排), 并顺手修正 077 遗留的"收集链接/玩家提交"权限描述文案。 - 编辑接口拒绝直接改 allocationStatus(400 consignment_allocation_status_update_forbidden)。 运营体验: - 两个面板补 isError 错误态与重试;审核通过/驳回加 confirmLoading 与提交中守卫(防双提交)。 - 待审徽标改用后端真实总数(原来只统计当前页);编辑弹窗押金不再兜底 ¥100(会误写押金)。 - 履约拦截弹窗冷静期文案不再硬编码"3 天",改为按商品规则设定。 - 核实并撤回"倒计时时区偏移"误报:deadlineAt 为带 Z 的 ISO,前端无偏移问题。 验证:后端 412 例(409 通过)、前端 21 例(新增 5 例权限解析)全绿, typecheck / lint / format / check:sql / build 通过;迁移 001→080 在全新空库跑通并在 dev 库应用。
695 lines
24 KiB
TypeScript
695 lines
24 KiB
TypeScript
import {
|
||
CheckCircleOutlined,
|
||
CloseCircleOutlined,
|
||
EyeOutlined,
|
||
ReloadOutlined,
|
||
} from '@ant-design/icons'
|
||
import { useQuery } from '@tanstack/react-query'
|
||
import {
|
||
Alert,
|
||
Badge,
|
||
Button,
|
||
Card,
|
||
Col,
|
||
Descriptions,
|
||
Form,
|
||
Image,
|
||
Input,
|
||
InputNumber,
|
||
Modal,
|
||
Radio,
|
||
Row,
|
||
Select,
|
||
Space,
|
||
Table,
|
||
Tag,
|
||
Typography,
|
||
App,
|
||
} from 'antd'
|
||
import type { TableColumnsType } from 'antd'
|
||
import { useState } from 'react'
|
||
|
||
import {
|
||
approveAdminConsignmentSubmission,
|
||
fetchAdminConsignmentSubmissions,
|
||
rejectAdminConsignmentSubmission,
|
||
} from '@/services/admin/consignment'
|
||
import { fetchAdminSession } from '@/services/admin/auth'
|
||
import { fetchAdminWorkerUsers } from '@/services/admin/worker-platform'
|
||
import type { ConsignmentSubmission } from '@/types/consignment'
|
||
import { resolveAdminPermission } from '@/utils/admin-auth'
|
||
|
||
const { Text } = Typography
|
||
|
||
export default function ConsignmentSubmissionsPanel() {
|
||
const { message } = App.useApp()
|
||
|
||
const [submissionStatus, setSubmissionStatus] = useState<string>('pending_review')
|
||
const [submissionKeyword, setSubmissionKeyword] = useState<string>('')
|
||
const [submissionPage, setSubmissionPage] = useState<number>(1)
|
||
const [submissionPageSize, setSubmissionPageSize] = useState<number>(20)
|
||
|
||
const [approveModalItem, setApproveModalItem] = useState<ConsignmentSubmission | null>(null)
|
||
const [rejectModalItem, setRejectModalItem] = useState<ConsignmentSubmission | null>(null)
|
||
const [viewingSubmission, setViewingSubmission] = useState<ConsignmentSubmission | null>(null)
|
||
const [rejectNote, setRejectNote] = useState('')
|
||
const [approveForm] = Form.useForm()
|
||
const [approving, setApproving] = useState(false)
|
||
const [rejecting, setRejecting] = useState(false)
|
||
|
||
// 通过/驳回要 consignment.review:后端接口已挂权限,前端不隐藏会让人点了必 403
|
||
const adminSessionQuery = useQuery({
|
||
queryKey: ['admin-session'],
|
||
queryFn: fetchAdminSession,
|
||
retry: false,
|
||
})
|
||
const sessionPermissions = adminSessionQuery.data?.data.user.permissions
|
||
const canReviewConsignment = resolveAdminPermission(sessionPermissions, 'consignment.review')
|
||
|
||
const workersQuery = useQuery({
|
||
queryKey: ['admin-worker-users-consignment-options'],
|
||
queryFn: () => fetchAdminWorkerUsers({ page: 1, pageSize: 200, status: 'active' }),
|
||
})
|
||
const workerOptions = (workersQuery.data?.data.items || []).map((w) => ({
|
||
label: `${w.displayName || w.username} (${w.phone || '无手机号'}) [ID:${w.workerId}]`,
|
||
value: w.workerId,
|
||
phone: w.phone,
|
||
name: w.displayName || w.username,
|
||
}))
|
||
|
||
const submissionsQuery = useQuery({
|
||
queryKey: [
|
||
'admin-consignment-submissions',
|
||
submissionStatus,
|
||
submissionKeyword,
|
||
submissionPage,
|
||
submissionPageSize,
|
||
],
|
||
queryFn: () =>
|
||
fetchAdminConsignmentSubmissions({
|
||
status: submissionStatus === 'all' ? undefined : submissionStatus,
|
||
keyword: submissionKeyword || undefined,
|
||
page: submissionPage,
|
||
pageSize: submissionPageSize,
|
||
}),
|
||
})
|
||
|
||
const submissions = submissionsQuery.data?.data.items || []
|
||
const submissionTotal = submissionsQuery.data?.data.total || 0
|
||
|
||
// 待审徽标必须用后端总数:原来只统计当前页(pageSize=20 时最多显示 20,且翻页就变)。
|
||
const pendingTotalQuery = useQuery({
|
||
queryKey: ['admin-consignment-submissions-pending-total'],
|
||
queryFn: () =>
|
||
fetchAdminConsignmentSubmissions({
|
||
status: 'pending_review',
|
||
page: 1,
|
||
pageSize: 1,
|
||
}),
|
||
refetchInterval: 60_000,
|
||
})
|
||
const pendingCount = pendingTotalQuery.data?.data.total || 0
|
||
|
||
async function handleApproveSubmit() {
|
||
if (!approveModalItem || approving) return
|
||
setApproving(true)
|
||
try {
|
||
const values = await approveForm.validateFields()
|
||
const costPriceFen = Math.round(Number(values.costPrice || 0) * 100)
|
||
const depositAmountFen = values.depositAmount
|
||
? Math.round(Number(values.depositAmount) * 100)
|
||
: undefined
|
||
const selectedWorker = workerOptions.find((w) => w.value === values.workerId)
|
||
|
||
await approveAdminConsignmentSubmission(approveModalItem.id, {
|
||
costPriceFen,
|
||
buyerName: values.buyerName,
|
||
shopName: values.shopName,
|
||
inboundBatch: values.inboundBatch,
|
||
itemName: values.itemName,
|
||
system: values.system,
|
||
serverZone: values.serverZone,
|
||
series: values.series,
|
||
diamondLevel: Number(values.diamondLevel || 1),
|
||
brillianceCount: Number(values.brillianceCount || 0),
|
||
sellerContact: values.sellerContact,
|
||
sellerGameId: values.sellerGameId,
|
||
sellerPaymentInfo: values.sellerPaymentInfo,
|
||
sellerPhone: values.sellerPhone,
|
||
workerId: values.workerId || undefined,
|
||
workerName: selectedWorker?.name || approveModalItem.workerName || undefined,
|
||
workerPhone: selectedWorker?.phone || approveModalItem.workerPhone || undefined,
|
||
depositAmountFen,
|
||
reviewNote: values.reviewNote,
|
||
})
|
||
|
||
message.success('审核通过!已正式存入寄售库存待匹配!')
|
||
setApproveModalItem(null)
|
||
await Promise.all([submissionsQuery.refetch(), pendingTotalQuery.refetch()])
|
||
} catch (err: any) {
|
||
if (err.errorFields) return
|
||
message.error(err.message || '审核入库失败')
|
||
} finally {
|
||
setApproving(false)
|
||
}
|
||
}
|
||
|
||
async function handleRejectConfirm() {
|
||
if (!rejectModalItem || rejecting) return
|
||
setRejecting(true)
|
||
try {
|
||
await rejectAdminConsignmentSubmission(rejectModalItem.id, { reviewNote: rejectNote })
|
||
message.success('已驳回该提报申请')
|
||
setRejectModalItem(null)
|
||
setRejectNote('')
|
||
await Promise.all([submissionsQuery.refetch(), pendingTotalQuery.refetch()])
|
||
} catch (err: any) {
|
||
message.error(err.message || '驳回失败')
|
||
} finally {
|
||
setRejecting(false)
|
||
}
|
||
}
|
||
|
||
function resolveSubmissionSource(record: ConsignmentSubmission) {
|
||
const channel = (record.contentJson as { channel?: string } | undefined)?.channel
|
||
if (channel === 'worker_portal' || record.workerId) return '打手端提报'
|
||
return '其他渠道'
|
||
}
|
||
|
||
const submissionColumns: TableColumnsType<ConsignmentSubmission> = [
|
||
{
|
||
title: '提报单号',
|
||
key: 'submissionNo',
|
||
width: 180,
|
||
render: (_, record) => (
|
||
<div>
|
||
<Text strong>{record.submissionNo}</Text>
|
||
<div style={{ fontSize: 11, color: '#8c8c8c' }}>{resolveSubmissionSource(record)}</div>
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
title: '提报物品规格',
|
||
key: 'specs',
|
||
width: 210,
|
||
render: (_, record) => (
|
||
<Space direction="vertical" size={2}>
|
||
<div style={{ fontWeight: 600 }}>{record.itemName}</div>
|
||
<Space size={4} wrap>
|
||
<Tag color={record.system === '苹果' ? 'geekblue' : 'green'}>{record.system}</Tag>
|
||
<Tag color="cyan">{record.serverZone}</Tag>
|
||
<Tag color="orange">{record.series}</Tag>
|
||
<Tag color="gold">{record.diamondLevel} 钻</Tag>
|
||
{record.brillianceCount > 0 ? (
|
||
<Tag color="magenta">{record.brillianceCount} 璀璨</Tag>
|
||
) : null}
|
||
</Space>
|
||
</Space>
|
||
),
|
||
},
|
||
{
|
||
title: '卖家报价',
|
||
key: 'price',
|
||
width: 110,
|
||
render: (_, record) => (
|
||
<Text strong style={{ color: '#cf1322', fontSize: 15 }}>
|
||
¥{(record.askingPriceFen / 100).toFixed(2)}
|
||
</Text>
|
||
),
|
||
},
|
||
{
|
||
title: '卖家与关联打手',
|
||
key: 'contact',
|
||
width: 200,
|
||
render: (_, record) => (
|
||
<div style={{ fontSize: 12 }}>
|
||
<div>联系: {record.contactInfo}</div>
|
||
{record.sellerGameId ? <div>游戏ID: {record.sellerGameId}</div> : null}
|
||
{record.sellerPhone ? <div>电话: {record.sellerPhone}</div> : null}
|
||
{record.workerId ? (
|
||
<div style={{ marginTop: 4 }}>
|
||
<Tag color="blue">
|
||
打手: {record.workerName || `ID:${record.workerId}`}
|
||
{record.depositAmountFen
|
||
? ` (押¥${(record.depositAmountFen / 100).toFixed(0)})`
|
||
: ''}
|
||
</Tag>
|
||
</div>
|
||
) : null}
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
title: '凭据截图',
|
||
key: 'screens',
|
||
width: 110,
|
||
render: (_, record) => {
|
||
const screens = Array.isArray(record.screenshotsJson) ? record.screenshotsJson : []
|
||
if (screens.length === 0) return <Text type="secondary">无截图</Text>
|
||
return (
|
||
<Image.PreviewGroup>
|
||
<Image
|
||
src={screens[0]}
|
||
width={48}
|
||
height={48}
|
||
style={{ objectFit: 'cover', borderRadius: 4 }}
|
||
/>
|
||
</Image.PreviewGroup>
|
||
)
|
||
},
|
||
},
|
||
{
|
||
title: '状态',
|
||
key: 'status',
|
||
width: 100,
|
||
render: (_, record) => {
|
||
if (record.status === 'pending_review') return <Tag color="gold">待客服审核</Tag>
|
||
if (record.status === 'approved') return <Tag color="green">已入库</Tag>
|
||
return <Tag color="red">已驳回</Tag>
|
||
},
|
||
},
|
||
{
|
||
title: '提报时间',
|
||
dataIndex: 'createdAt',
|
||
width: 150,
|
||
render: (v) => <Text style={{ fontSize: 12 }}>{v}</Text>,
|
||
},
|
||
{
|
||
title: '操作',
|
||
key: 'actions',
|
||
width: 200,
|
||
render: (_, record) => (
|
||
<Space size={8}>
|
||
<Button
|
||
size="small"
|
||
type="text"
|
||
icon={<EyeOutlined />}
|
||
onClick={() => setViewingSubmission(record)}
|
||
>
|
||
详情
|
||
</Button>
|
||
{record.status === 'pending_review' && canReviewConsignment ? (
|
||
<>
|
||
<Button
|
||
size="small"
|
||
type="primary"
|
||
icon={<CheckCircleOutlined />}
|
||
onClick={() => {
|
||
setApproveModalItem(record)
|
||
approveForm.setFieldsValue({
|
||
...record,
|
||
costPrice: record.askingPriceFen / 100,
|
||
sellerContact: record.contactInfo,
|
||
buyerName: '客服',
|
||
inboundBatch: new Date().toISOString().slice(0, 7),
|
||
workerId: record.workerId || undefined,
|
||
depositAmount: record.depositAmountFen
|
||
? record.depositAmountFen / 100
|
||
: record.askingPriceFen / 100,
|
||
})
|
||
}}
|
||
>
|
||
审核入库
|
||
</Button>
|
||
<Button
|
||
size="small"
|
||
danger
|
||
type="text"
|
||
icon={<CloseCircleOutlined />}
|
||
onClick={() => {
|
||
setRejectModalItem(record)
|
||
setRejectNote('')
|
||
}}
|
||
>
|
||
驳回
|
||
</Button>
|
||
</>
|
||
) : null}
|
||
</Space>
|
||
),
|
||
},
|
||
]
|
||
|
||
return (
|
||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||
<Card size="small">
|
||
<Space wrap size={12}>
|
||
<Radio.Group
|
||
value={submissionStatus}
|
||
onChange={(e) => {
|
||
setSubmissionStatus(e.target.value)
|
||
setSubmissionPage(1)
|
||
}}
|
||
buttonStyle="solid"
|
||
>
|
||
<Radio.Button value="pending_review">
|
||
待客服审核
|
||
{pendingCount > 0 ? (
|
||
<Badge count={pendingCount} size="small" offset={[6, -2]} />
|
||
) : null}
|
||
</Radio.Button>
|
||
<Radio.Button value="approved">已审核入库</Radio.Button>
|
||
<Radio.Button value="rejected">已驳回</Radio.Button>
|
||
<Radio.Button value="all">全部</Radio.Button>
|
||
</Radio.Group>
|
||
|
||
<Input.Search
|
||
placeholder="搜索提报单号/微信号/游戏ID/打手"
|
||
allowClear
|
||
style={{ width: 260 }}
|
||
onSearch={(v) => {
|
||
setSubmissionKeyword(v)
|
||
setSubmissionPage(1)
|
||
}}
|
||
/>
|
||
|
||
<Button icon={<ReloadOutlined />} onClick={() => submissionsQuery.refetch()}>
|
||
刷新
|
||
</Button>
|
||
</Space>
|
||
</Card>
|
||
|
||
<Card size="small">
|
||
{submissionsQuery.isError ? (
|
||
<Alert
|
||
type="error"
|
||
showIcon
|
||
style={{ marginBottom: 12 }}
|
||
message="读取寄卖提报单失败"
|
||
description={
|
||
(submissionsQuery.error as any)?.message ||
|
||
'请检查网络或稍后重试;也可能是当前账号缺少 consignment.view 权限。'
|
||
}
|
||
action={
|
||
<Button size="small" onClick={() => void submissionsQuery.refetch()}>
|
||
重试
|
||
</Button>
|
||
}
|
||
/>
|
||
) : null}
|
||
<Table
|
||
rowKey="id"
|
||
columns={submissionColumns}
|
||
dataSource={submissions}
|
||
loading={submissionsQuery.isLoading}
|
||
pagination={{
|
||
current: submissionPage,
|
||
pageSize: submissionPageSize,
|
||
total: submissionTotal,
|
||
showSizeChanger: true,
|
||
showTotal: (t) => `共 ${t} 条提报记录`,
|
||
onChange: (p, ps) => {
|
||
setSubmissionPage(p)
|
||
setSubmissionPageSize(ps)
|
||
},
|
||
}}
|
||
/>
|
||
</Card>
|
||
|
||
<Modal
|
||
title="审核寄卖申请并正式入库"
|
||
open={Boolean(approveModalItem)}
|
||
width={680}
|
||
onCancel={() => setApproveModalItem(null)}
|
||
onOk={handleApproveSubmit}
|
||
confirmLoading={approving}
|
||
okText="审核通过并正式入库"
|
||
okType="primary"
|
||
destroyOnClose
|
||
>
|
||
<div style={{ marginBottom: 16 }}>
|
||
<Text type="secondary">
|
||
提报人联系方式:<strong>{approveModalItem?.contactInfo}</strong> | 游戏ID:
|
||
<strong>{approveModalItem?.sellerGameId || '-'}</strong> | 卖家要价:
|
||
<strong style={{ color: '#cf1322' }}>
|
||
¥{((approveModalItem?.askingPriceFen || 0) / 100).toFixed(2)}
|
||
</strong>
|
||
</Text>
|
||
</div>
|
||
|
||
<Form form={approveForm} layout="vertical">
|
||
<Row gutter={16}>
|
||
<Col span={14}>
|
||
<Form.Item
|
||
name="itemName"
|
||
label="正式入库物品名"
|
||
rules={[{ required: true, message: '请输入入库物品名' }]}
|
||
>
|
||
<Input />
|
||
</Form.Item>
|
||
</Col>
|
||
<Col span={10}>
|
||
<Form.Item
|
||
name="costPrice"
|
||
label="实际收购成本 (元)"
|
||
rules={[{ required: true, message: '请输入实际收购成本' }]}
|
||
>
|
||
<InputNumber min={0} step={1} style={{ width: '100%' }} />
|
||
</Form.Item>
|
||
</Col>
|
||
</Row>
|
||
|
||
<Row gutter={16}>
|
||
<Col span={5}>
|
||
<Form.Item name="system" label="系统">
|
||
<Input />
|
||
</Form.Item>
|
||
</Col>
|
||
<Col span={5}>
|
||
<Form.Item name="serverZone" label="区服">
|
||
<Input />
|
||
</Form.Item>
|
||
</Col>
|
||
<Col span={5}>
|
||
<Form.Item name="series" label="系列">
|
||
<Input />
|
||
</Form.Item>
|
||
</Col>
|
||
<Col span={5}>
|
||
<Form.Item name="diamondLevel" label="钻石等级 (1-7)">
|
||
<InputNumber min={1} max={7} style={{ width: '100%' }} />
|
||
</Form.Item>
|
||
</Col>
|
||
<Col span={4}>
|
||
<Form.Item name="brillianceCount" label="璀璨数 (0-7)">
|
||
<InputNumber min={0} max={7} style={{ width: '100%' }} />
|
||
</Form.Item>
|
||
</Col>
|
||
</Row>
|
||
|
||
<Row gutter={16}>
|
||
<Col span={14}>
|
||
<Form.Item
|
||
name="workerId"
|
||
label="走打手接单平台 (关联履约打手)"
|
||
tooltip="指派该打手接单丢枪交货时,接单平台将自动冻结其押金并担保交付"
|
||
>
|
||
<Select
|
||
allowClear
|
||
showSearch
|
||
filterOption={(input, option) =>
|
||
String(option?.label ?? '')
|
||
.toLowerCase()
|
||
.includes(input.toLowerCase())
|
||
}
|
||
placeholder="可选择关联的接单打手 (选填)"
|
||
options={workerOptions}
|
||
/>
|
||
</Form.Item>
|
||
</Col>
|
||
<Col span={10}>
|
||
<Form.Item
|
||
name="depositAmount"
|
||
label="履约押金金额 (元)"
|
||
tooltip="指派该打手交枪履约时,系统冻结的押金金额"
|
||
>
|
||
<InputNumber
|
||
min={0}
|
||
step={10}
|
||
addonBefore="¥"
|
||
style={{ width: '100%' }}
|
||
placeholder="如 100"
|
||
/>
|
||
</Form.Item>
|
||
</Col>
|
||
</Row>
|
||
|
||
<Row gutter={16}>
|
||
<Col span={8}>
|
||
<Form.Item
|
||
name="buyerName"
|
||
label="收购人 (客服)"
|
||
rules={[{ required: true, message: '请填写收购人' }]}
|
||
>
|
||
<Input />
|
||
</Form.Item>
|
||
</Col>
|
||
<Col span={8}>
|
||
<Form.Item name="shopName" label="关联店铺">
|
||
<Input placeholder="店铺名" />
|
||
</Form.Item>
|
||
</Col>
|
||
<Col span={8}>
|
||
<Form.Item name="inboundBatch" label="收购批次">
|
||
<Input placeholder="批次" />
|
||
</Form.Item>
|
||
</Col>
|
||
</Row>
|
||
|
||
<Row gutter={16}>
|
||
<Col span={8}>
|
||
<Form.Item name="sellerContact" label="卖家微信号">
|
||
<Input />
|
||
</Form.Item>
|
||
</Col>
|
||
<Col span={8}>
|
||
<Form.Item name="sellerPhone" label="卖家手机">
|
||
<Input />
|
||
</Form.Item>
|
||
</Col>
|
||
<Col span={8}>
|
||
<Form.Item name="sellerGameId" label="卖家游戏ID">
|
||
<Input />
|
||
</Form.Item>
|
||
</Col>
|
||
</Row>
|
||
|
||
<Form.Item name="sellerPaymentInfo" label="卖家收款信息">
|
||
<Input />
|
||
</Form.Item>
|
||
|
||
<Form.Item name="reviewNote" label="审核备注">
|
||
<Input.TextArea rows={2} placeholder="审核核准说明" />
|
||
</Form.Item>
|
||
</Form>
|
||
</Modal>
|
||
|
||
<Modal
|
||
title="驳回寄卖提报"
|
||
open={Boolean(rejectModalItem)}
|
||
onCancel={() => setRejectModalItem(null)}
|
||
onOk={handleRejectConfirm}
|
||
confirmLoading={rejecting}
|
||
okText="确认驳回"
|
||
okType="danger"
|
||
>
|
||
<div style={{ marginBottom: 12 }}>
|
||
<Text>
|
||
确认驳回 <strong>[{rejectModalItem?.submissionNo}]</strong> 的寄卖申请吗?
|
||
</Text>
|
||
</div>
|
||
<Form layout="vertical">
|
||
<Form.Item label="驳回原因说明">
|
||
<Input.TextArea
|
||
rows={3}
|
||
value={rejectNote}
|
||
onChange={(e) => setRejectNote(e.target.value)}
|
||
placeholder="如:要价过高、物品规格不符、账号存在实名绑定限制等"
|
||
/>
|
||
</Form.Item>
|
||
</Form>
|
||
</Modal>
|
||
|
||
<Modal
|
||
title={`提报详情 [${viewingSubmission?.submissionNo}]`}
|
||
open={Boolean(viewingSubmission)}
|
||
width={680}
|
||
footer={[
|
||
<Button key="close" onClick={() => setViewingSubmission(null)}>
|
||
关闭
|
||
</Button>,
|
||
]}
|
||
onCancel={() => setViewingSubmission(null)}
|
||
>
|
||
{viewingSubmission ? (
|
||
<Descriptions bordered size="small" column={2}>
|
||
<Descriptions.Item label="提报单号" span={2}>
|
||
<Text copyable>{viewingSubmission.submissionNo}</Text>
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="来源" span={2}>
|
||
{resolveSubmissionSource(viewingSubmission)}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="物品名称" span={2}>
|
||
{viewingSubmission.itemName}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="规格">
|
||
{viewingSubmission.system} / {viewingSubmission.serverZone} /{' '}
|
||
{viewingSubmission.series} / {viewingSubmission.diamondLevel}钻 (
|
||
{viewingSubmission.brillianceCount}璀璨)
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="卖家报价">
|
||
<Text strong style={{ color: '#cf1322' }}>
|
||
¥{(viewingSubmission.askingPriceFen / 100).toFixed(2)}
|
||
</Text>
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="卖家微信号">
|
||
<Text copyable>{viewingSubmission.contactInfo}</Text>
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="卖家电话">
|
||
<Text copyable>{viewingSubmission.sellerPhone || '-'}</Text>
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="卖家游戏ID" span={2}>
|
||
<Text copyable>{viewingSubmission.sellerGameId || '-'}</Text>
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="关联接单打手">
|
||
{viewingSubmission.workerId ? (
|
||
<Tag color="blue">
|
||
{viewingSubmission.workerName || `ID:${viewingSubmission.workerId}`} (
|
||
{viewingSubmission.workerPhone || '无手机号'})
|
||
</Tag>
|
||
) : (
|
||
<Text type="secondary">未关联打手账号</Text>
|
||
)}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="履约押金要求">
|
||
{viewingSubmission.depositAmountFen ? (
|
||
<Tag color="gold">¥{(viewingSubmission.depositAmountFen / 100).toFixed(0)}</Tag>
|
||
) : (
|
||
<Text type="secondary">按平台默认规则</Text>
|
||
)}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="卖家收款方式" span={2}>
|
||
<Text copyable>{viewingSubmission.sellerPaymentInfo || '-'}</Text>
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="凭据截图" span={2}>
|
||
{Array.isArray(viewingSubmission.screenshotsJson) &&
|
||
viewingSubmission.screenshotsJson.length > 0 ? (
|
||
<Image.PreviewGroup>
|
||
<Space wrap>
|
||
{viewingSubmission.screenshotsJson.map((src, i) => (
|
||
<Image
|
||
key={i}
|
||
src={src}
|
||
width={80}
|
||
height={80}
|
||
style={{ objectFit: 'cover', borderRadius: 4 }}
|
||
/>
|
||
))}
|
||
</Space>
|
||
</Image.PreviewGroup>
|
||
) : (
|
||
<Text type="secondary">无截图</Text>
|
||
)}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="审核状态">
|
||
{viewingSubmission.status === 'pending_review' ? (
|
||
<Tag color="gold">待审核</Tag>
|
||
) : viewingSubmission.status === 'approved' ? (
|
||
<Tag color="green">已入库</Tag>
|
||
) : (
|
||
<Tag color="red">已驳回</Tag>
|
||
)}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="审核人">
|
||
{viewingSubmission.reviewerName || '-'}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="审核备注" span={2}>
|
||
{viewingSubmission.reviewNote || '-'}
|
||
</Descriptions.Item>
|
||
</Descriptions>
|
||
) : null}
|
||
</Modal>
|
||
</div>
|
||
)
|
||
}
|