feat(consignment): 内部现货占库进入赠送冷静期并闭环发布押金
打手发布冻结履约押金,驳回/作废退还,验收释放;内部匹配履约立刻占用库存并跳过打手资料进入冷静期,撤销回库时补冻押金。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
import { CheckCircleOutlined, CloseCircleOutlined, EyeOutlined, ReloadOutlined } from '@ant-design/icons'
|
||||
import {
|
||||
CheckCircleOutlined,
|
||||
CloseCircleOutlined,
|
||||
EyeOutlined,
|
||||
ReloadOutlined,
|
||||
} from '@ant-design/icons'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import {
|
||||
Badge,
|
||||
|
||||
@@ -43,6 +43,7 @@ import { asRecord, formatMoney } from './shared'
|
||||
type FinanceConfigFormValues = {
|
||||
depositUnfreezeDays?: number
|
||||
rewardUnfreezeEnabled?: boolean
|
||||
consignmentPublishDepositYuan?: number
|
||||
recharge?: {
|
||||
enabled?: boolean
|
||||
channelName?: string
|
||||
@@ -169,6 +170,9 @@ export default function FinancePanel() {
|
||||
await saveAdminWorkerFinanceConfig({
|
||||
depositUnfreezeDays: Number(values.depositUnfreezeDays ?? 3),
|
||||
rewardUnfreezeEnabled: values.rewardUnfreezeEnabled !== false,
|
||||
consignmentPublishDepositFen: Math.round(
|
||||
Number(values.consignmentPublishDepositYuan ?? 20) * 100,
|
||||
),
|
||||
recharge: {
|
||||
enabled: values.recharge?.enabled !== false,
|
||||
channelName: String(values.recharge?.channelName || '').trim(),
|
||||
@@ -416,6 +420,13 @@ export default function FinancePanel() {
|
||||
>
|
||||
<InputNumber min={0} max={30} step={1} addonAfter="天" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="苏尔南寄卖发布押金"
|
||||
name="consignmentPublishDepositYuan"
|
||||
tooltip="打手发布寄卖时从可用余额冻结的履约押金;驳回或履约完成后释放"
|
||||
>
|
||||
<InputNumber min={0} max={10000} step={1} addonBefore="¥" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="报酬待解冻"
|
||||
name="rewardUnfreezeEnabled"
|
||||
@@ -676,6 +687,7 @@ function mapFinanceConfigToFormValues(config?: WorkerFinanceConfig): FinanceConf
|
||||
return {
|
||||
depositUnfreezeDays: Number(config?.depositUnfreezeDays ?? 3),
|
||||
rewardUnfreezeEnabled: config?.rewardUnfreezeEnabled !== false,
|
||||
consignmentPublishDepositYuan: Number(config?.consignmentPublishDepositFen ?? 2000) / 100,
|
||||
recharge: {
|
||||
enabled: config?.recharge.enabled !== false,
|
||||
channelName: config?.recharge.channelName || '',
|
||||
|
||||
@@ -161,6 +161,12 @@ type CancelSharingFormValues = {
|
||||
reason?: string
|
||||
}
|
||||
|
||||
function isInternalConsignmentOrder(row: WorkOrder) {
|
||||
const consignment = asRecord((row.material as Record<string, unknown> | undefined)?.consignment)
|
||||
if (consignment.fulfillmentType === 'worker_deposit' || consignment.workerId) return false
|
||||
return Boolean(consignment.inventoryId) && !row.worker
|
||||
}
|
||||
|
||||
export default function WorkOrdersPanel() {
|
||||
const { message, modal } = App.useApp()
|
||||
const navigate = useNavigate()
|
||||
@@ -715,7 +721,7 @@ export default function WorkOrdersPanel() {
|
||||
workOrderId: consignmentInterceptModal.order.workOrderId,
|
||||
inventoryId,
|
||||
})
|
||||
message.success('已使用内部寄售库存直接履约!工单已自动完成,无需打手接单佣金')
|
||||
message.success('已占用内部库存,跳过打手资料,进入赠送冷静期')
|
||||
setConsignmentInterceptModal({ open: false, order: null, matchData: null, submitting: false })
|
||||
await refreshAll()
|
||||
} catch (err: any) {
|
||||
@@ -1034,34 +1040,50 @@ export default function WorkOrdersPanel() {
|
||||
const cooldownHours = Number(row.gift?.cooldownHours || 72)
|
||||
const cooldownMinutes = Number(row.gift?.cooldownMinutes || 0)
|
||||
const cooldownLabel = cooldownMinutes > 0 ? `${cooldownMinutes} 分钟` : `${cooldownHours} 小时`
|
||||
const internal = isInternalConsignmentOrder(row)
|
||||
modal.confirm({
|
||||
title: '确认已添加打手为游戏好友?',
|
||||
title: internal ? '跳过打手资料,开始赠送冷却?' : '确认已添加打手为游戏好友?',
|
||||
content: (
|
||||
<div>
|
||||
<Typography.Paragraph>
|
||||
请先核对打手提交的昵称 / ID / 区服与主页截图,确认已在游戏内互为好友后再点击确认。
|
||||
</Typography.Paragraph>
|
||||
<Typography.Paragraph>
|
||||
确认后开始 {cooldownLabel}
|
||||
赠送冷却,冷却结束前打手不能提交赠送凭证;任务时限将顺延到冷却结束后重新起算。
|
||||
</Typography.Paragraph>
|
||||
{internal ? (
|
||||
<>
|
||||
<Typography.Paragraph>
|
||||
内部寄售履约没有打手交资料。确认后直接开始 {cooldownLabel}
|
||||
赠送冷却,冷却结束后再用库存账号给买家赠送并验收。
|
||||
</Typography.Paragraph>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Typography.Paragraph>
|
||||
请先核对打手提交的昵称 / ID / 区服与主页截图,确认已在游戏内互为好友后再点击确认。
|
||||
</Typography.Paragraph>
|
||||
<Typography.Paragraph>
|
||||
确认后开始 {cooldownLabel}
|
||||
赠送冷却,冷却结束前打手不能提交赠送凭证;任务时限将顺延到冷却结束后重新起算。
|
||||
</Typography.Paragraph>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
okText: '已加好友,开始冷却',
|
||||
okText: internal ? '开始冷却' : '已加好友,开始冷却',
|
||||
cancelText: '取消',
|
||||
onOk: () =>
|
||||
runAction(
|
||||
() => confirmAdminWorkOrderFriend(row.workOrderId),
|
||||
`已确认加好友,${cooldownLabel} 冷却开始`,
|
||||
internal
|
||||
? `已跳过打手资料,${cooldownLabel} 冷却开始`
|
||||
: `已确认加好友,${cooldownLabel} 冷却开始`,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
function completeGiftCooldown(row: WorkOrder) {
|
||||
const internal = isInternalConsignmentOrder(row)
|
||||
modal.confirm({
|
||||
title: '手动结束赠送冷却?',
|
||||
content:
|
||||
'确认后将立即允许打手提交赠送凭证与验收资料;订单任务时限将从当前时刻重新计算。此操作会记录在订单流转记录中。',
|
||||
content: internal
|
||||
? '确认后将立即进入可赠送,可用库存账号给买家赠送,然后直接验收。此操作会记录在订单流转记录中。'
|
||||
: '确认后将立即允许打手提交赠送凭证与验收资料;订单任务时限将从当前时刻重新计算。此操作会记录在订单流转记录中。',
|
||||
okText: '确认结束冷却',
|
||||
okButtonProps: { type: 'primary' },
|
||||
cancelText: '取消',
|
||||
@@ -1237,7 +1259,8 @@ export default function WorkOrdersPanel() {
|
||||
{row.acceptanceMode === 'friend_gift' ? <Tag color="purple">好友赠送</Tag> : null}
|
||||
{row.acceptanceMode === 'friend_gift' &&
|
||||
row.gift &&
|
||||
!['pending_material', 'unassigned', 'open'].includes(row.rawStatus || row.status) ? (
|
||||
(!['pending_material', 'unassigned', 'open'].includes(row.rawStatus || row.status) ||
|
||||
isInternalConsignmentOrder(row)) ? (
|
||||
<Tag color={resolveGiftPhaseColor(row.gift.phase)}>
|
||||
{formatGiftPhase(row.gift.phase)}
|
||||
</Tag>
|
||||
@@ -1459,7 +1482,9 @@ export default function WorkOrdersPanel() {
|
||||
icon={<UserAddOutlined />}
|
||||
onClick={() => confirmFriendAdded(row)}
|
||||
>
|
||||
确认加好友
|
||||
{isInternalConsignmentOrder(row) && row.gift?.phase === 'material_required'
|
||||
? '开始赠送冷却'
|
||||
: '确认加好友'}
|
||||
</Button>
|
||||
) : null}
|
||||
{row.capabilities?.canCompleteGiftCooldown && canManageProblem ? (
|
||||
@@ -1873,7 +1898,10 @@ export default function WorkOrdersPanel() {
|
||||
size="small"
|
||||
onClick={() => confirmFriendAdded(detailOrder)}
|
||||
>
|
||||
确认加好友
|
||||
{isInternalConsignmentOrder(detailOrder) &&
|
||||
detailOrder.gift?.phase === 'material_required'
|
||||
? '开始赠送冷却'
|
||||
: '确认加好友'}
|
||||
</Button>
|
||||
) : null}
|
||||
{detailOrder.capabilities?.canCompleteGiftCooldown && canManageProblem ? (
|
||||
@@ -2821,7 +2849,8 @@ export default function WorkOrdersPanel() {
|
||||
</div>
|
||||
<div style={{ color: '#389e0d', marginTop: 4 }}>
|
||||
若使用内部寄售库存履约,<strong>无需向外部打手支付佣金(打手支出 ¥0.00)</strong>
|
||||
,系统将自动核销工单并归档!
|
||||
。系统会立刻占用库存,并跳过「待打手提交资料」,直接进入默认 3
|
||||
天赠送冷静期;冷静期结束后再用库存账号赠送并验收。
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -2914,7 +2943,7 @@ export default function WorkOrdersPanel() {
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 4, fontSize: 11, color: '#8c8c8c' }}>
|
||||
匹配分析: {m.matchReasons.join(' · ')}
|
||||
匹配分析: {(m.matchReasons || []).join(' · ') || m.reason || '规格匹配'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2935,7 +2964,7 @@ export default function WorkOrdersPanel() {
|
||||
loading={consignmentInterceptModal.submitting}
|
||||
onClick={() => handleFulfillWithConsignment(inv.id)}
|
||||
>
|
||||
内部现货直接核销
|
||||
内部现货履约
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
@@ -22,8 +22,8 @@ import {
|
||||
Image,
|
||||
Input,
|
||||
InputNumber,
|
||||
Radio,
|
||||
Row,
|
||||
Select,
|
||||
Space,
|
||||
Tabs,
|
||||
Tag,
|
||||
@@ -59,14 +59,15 @@ const BRILLIANCE_OPTIONS = [
|
||||
|
||||
type RecordFilter = 'all' | 'pending' | 'in_stock' | 'done'
|
||||
|
||||
const DEPOSIT_TIP_KEY = 'worker-consignment-deposit-tip-seen'
|
||||
|
||||
export default function WorkerConsignmentPage() {
|
||||
const isMobile = useIsMobile()
|
||||
const { message } = App.useApp()
|
||||
const { message, modal } = App.useApp()
|
||||
const [activeTab, setActiveTab] = useState<'publish' | 'records'>('publish')
|
||||
const [recordFilter, setRecordFilter] = useState<RecordFilter>('all')
|
||||
const [screenshots, setScreenshots] = useState<UploadedFile[]>([])
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const [depositTouched, setDepositTouched] = useState(false)
|
||||
const [form] = Form.useForm()
|
||||
|
||||
const profileQuery = useQuery({
|
||||
@@ -75,6 +76,7 @@ export default function WorkerConsignmentPage() {
|
||||
staleTime: 60_000,
|
||||
})
|
||||
const worker = profileQuery.data?.data.worker
|
||||
const financeConfig = profileQuery.data?.data.financeConfig
|
||||
|
||||
const myConsignmentsQuery = useQuery({
|
||||
queryKey: ['worker-my-consignments'],
|
||||
@@ -83,6 +85,14 @@ export default function WorkerConsignmentPage() {
|
||||
|
||||
const submissions = myConsignmentsQuery.data?.data.submissions || []
|
||||
const inventories = myConsignmentsQuery.data?.data.inventories || []
|
||||
const publishDepositFen = Number(
|
||||
myConsignmentsQuery.data?.data.publishDepositFen ??
|
||||
financeConfig?.consignmentPublishDepositFen ??
|
||||
2000,
|
||||
)
|
||||
const publishDepositYuan = publishDepositFen / 100
|
||||
const availableAmount = Number(worker?.wallet?.availableAmount || 0)
|
||||
const canPublish = availableAmount >= publishDepositFen
|
||||
|
||||
const pendingCount = submissions.filter((s) => s.status === 'pending_review').length
|
||||
const inStockCount = inventories.filter((i) => i.status === 'in_stock').length
|
||||
@@ -95,39 +105,53 @@ export default function WorkerConsignmentPage() {
|
||||
}
|
||||
}, [worker, form])
|
||||
|
||||
function handleFormValuesChange(changedValues: Record<string, unknown>) {
|
||||
useEffect(() => {
|
||||
if (profileQuery.isLoading) return
|
||||
if (typeof window === 'undefined') return
|
||||
if (window.localStorage.getItem(DEPOSIT_TIP_KEY)) return
|
||||
window.localStorage.setItem(DEPOSIT_TIP_KEY, '1')
|
||||
modal.info({
|
||||
title: '发布寄售需先充值履约押金',
|
||||
content: (
|
||||
<div>
|
||||
<p>
|
||||
首次发布苏尔南寄售前,请先联系客服充值至少{' '}
|
||||
<Typography.Text strong type="danger">
|
||||
¥{publishDepositYuan.toFixed(0)}
|
||||
</Typography.Text>{' '}
|
||||
履约押金到接单钱包。
|
||||
</p>
|
||||
<p style={{ marginBottom: 0 }}>
|
||||
发布成功后系统将暂扣该押金;审核驳回或履约完成后自动释放回可用余额。
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
okText: '我知道了',
|
||||
centered: true,
|
||||
})
|
||||
}, [profileQuery.isLoading, modal, publishDepositYuan])
|
||||
|
||||
function handleFormValuesChange() {
|
||||
const sys = form.getFieldValue('system')
|
||||
const zone = form.getFieldValue('serverZone')
|
||||
const ser = form.getFieldValue('series')
|
||||
const dia = form.getFieldValue('diamondLevel')
|
||||
const bri = form.getFieldValue('brillianceCount')
|
||||
const next: Record<string, unknown> = {}
|
||||
|
||||
if (sys && zone && ser && dia != null && bri != null) {
|
||||
const briNum = Number(bri)
|
||||
const briStr = briNum > 0 ? `${briNum}璀璨` : ''
|
||||
next.itemName = `${sys}${zone}${ser}${dia}钻${briStr} 苏尔南`
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(changedValues, 'depositAmount')) {
|
||||
setDepositTouched(true)
|
||||
}
|
||||
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(changedValues, 'askingPrice') &&
|
||||
!depositTouched &&
|
||||
!Object.prototype.hasOwnProperty.call(changedValues, 'depositAmount')
|
||||
) {
|
||||
const price = Number(changedValues.askingPrice || 0)
|
||||
if (price > 0) next.depositAmount = price
|
||||
}
|
||||
|
||||
if (Object.keys(next).length > 0) {
|
||||
form.setFieldsValue(next)
|
||||
form.setFieldsValue({
|
||||
itemName: `${sys}${zone}${ser}${dia}钻${briStr} 苏尔南`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function handlePublishSubmit(values: Record<string, any>) {
|
||||
if (!canPublish) {
|
||||
message.warning(`可用余额不足 ¥${publishDepositYuan.toFixed(0)},请先联系客服充值履约押金`)
|
||||
return
|
||||
}
|
||||
|
||||
const urls = screenshots.map((f) => f.url).filter(Boolean)
|
||||
if (urls.length === 0) {
|
||||
message.error('请上传游戏主页截图以供核验')
|
||||
@@ -140,18 +164,16 @@ export default function WorkerConsignmentPage() {
|
||||
return
|
||||
}
|
||||
|
||||
const depositAmountFen = Math.round(
|
||||
Number(values.depositAmount || values.askingPrice || 100) * 100,
|
||||
)
|
||||
if (depositAmountFen < 100) {
|
||||
message.error('履约押金不能少于1元')
|
||||
return
|
||||
}
|
||||
const briNum = Number(values.brillianceCount || 0)
|
||||
const briStr = briNum > 0 ? `${briNum}璀璨` : ''
|
||||
const itemName =
|
||||
String(values.itemName || '').trim() ||
|
||||
`${values.system}${values.serverZone}${values.series}${values.diamondLevel}钻${briStr} 苏尔南`
|
||||
|
||||
setSubmitting(true)
|
||||
try {
|
||||
await publishWorkerConsignment({
|
||||
itemName: values.itemName,
|
||||
itemName,
|
||||
system: values.system,
|
||||
serverZone: values.serverZone,
|
||||
series: values.series,
|
||||
@@ -164,14 +186,13 @@ export default function WorkerConsignmentPage() {
|
||||
characterId: values.characterId,
|
||||
screenshotsJson: urls,
|
||||
sellerNote: values.sellerNote,
|
||||
depositAmountFen,
|
||||
depositAmountFen: publishDepositFen,
|
||||
})
|
||||
|
||||
message.success('寄卖提报成功,已进入审核')
|
||||
message.success(`寄卖提报成功,已暂扣履约押金 ¥${publishDepositYuan.toFixed(0)}`)
|
||||
form.resetFields()
|
||||
setDepositTouched(false)
|
||||
setScreenshots([])
|
||||
await myConsignmentsQuery.refetch()
|
||||
await Promise.all([myConsignmentsQuery.refetch(), profileQuery.refetch()])
|
||||
setRecordFilter('pending')
|
||||
setActiveTab('records')
|
||||
} catch (err: any) {
|
||||
@@ -189,7 +210,10 @@ export default function WorkerConsignmentPage() {
|
||||
const done = inventories.filter((i) => i.status === 'consumed' || i.status === 'archived')
|
||||
|
||||
if (recordFilter === 'pending') {
|
||||
return { submissions: [...pendingSubs, ...rejectedSubs], inventories: [] as WorkerInventoryItem[] }
|
||||
return {
|
||||
submissions: [...pendingSubs, ...rejectedSubs],
|
||||
inventories: [] as WorkerInventoryItem[],
|
||||
}
|
||||
}
|
||||
if (recordFilter === 'in_stock') {
|
||||
return { submissions: [] as WorkerConsignmentItem[], inventories: inStock }
|
||||
@@ -203,8 +227,7 @@ export default function WorkerConsignmentPage() {
|
||||
}
|
||||
}, [submissions, inventories, recordFilter])
|
||||
|
||||
const recordTotal =
|
||||
filteredRecords.submissions.length + filteredRecords.inventories.length
|
||||
const recordTotal = filteredRecords.submissions.length + filteredRecords.inventories.length
|
||||
|
||||
return (
|
||||
<section className="page-stack worker-consignment-page">
|
||||
@@ -347,92 +370,112 @@ export default function WorkerConsignmentPage() {
|
||||
onValuesChange={handleFormValuesChange}
|
||||
>
|
||||
<Alert
|
||||
type="info"
|
||||
type={canPublish ? 'info' : 'warning'}
|
||||
showIcon
|
||||
icon={<SafetyCertificateOutlined />}
|
||||
className="worker-consignment-tip"
|
||||
message="走接单平台押金履约:审核入库后匹配买家工单,系统按登记押金冻结钱包;局内交枪验货通过后解冻并结算货款。"
|
||||
message={
|
||||
canPublish
|
||||
? `履约押金 ¥${publishDepositYuan.toFixed(0)}:发布成功后暂扣,驳回或履约完成后释放。当前可用余额 ¥${(availableAmount / 100).toFixed(2)}`
|
||||
: `可用余额不足 ¥${publishDepositYuan.toFixed(0)}。请先联系客服充值履约押金后,才能发布寄售。当前可用 ¥${(availableAmount / 100).toFixed(2)}`
|
||||
}
|
||||
/>
|
||||
|
||||
<Divider orientation="left" plain className="worker-consignment-divider">
|
||||
<Divider plain className="worker-consignment-divider">
|
||||
规格与价格
|
||||
</Divider>
|
||||
|
||||
<Row gutter={[12, 0]}>
|
||||
<Col xs={12} sm={8} md={6}>
|
||||
<div className="worker-consignment-specs">
|
||||
<div className="worker-consignment-spec-grid">
|
||||
<Form.Item
|
||||
name="system"
|
||||
label="操作系统"
|
||||
rules={[{ required: true, message: '请选择系统' }]}
|
||||
className="worker-consignment-spec-card"
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
<Radio.Group
|
||||
className="worker-consignment-chips"
|
||||
optionType="button"
|
||||
buttonStyle="solid"
|
||||
options={[
|
||||
{ label: '苹果 (iOS)', value: '苹果' },
|
||||
{ label: '安卓 (Android)', value: '安卓' },
|
||||
{ label: '通用/双端', value: '通用' },
|
||||
{ label: '苹果', value: '苹果' },
|
||||
{ label: '安卓', value: '安卓' },
|
||||
{ label: '通用', value: '通用' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={12} sm={8} md={6}>
|
||||
<Form.Item
|
||||
name="serverZone"
|
||||
label="游戏区服"
|
||||
rules={[{ required: true, message: '请选择区服' }]}
|
||||
className="worker-consignment-spec-card"
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
<Radio.Group
|
||||
className="worker-consignment-chips"
|
||||
optionType="button"
|
||||
buttonStyle="solid"
|
||||
options={[
|
||||
{ label: '微信区', value: '微信区' },
|
||||
{ label: 'QQ区', value: 'QQ区' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={12} sm={8} md={6}>
|
||||
<Form.Item
|
||||
name="series"
|
||||
label="系列/枪系"
|
||||
label="系列 / 枪系"
|
||||
rules={[{ required: true, message: '请选择枪系' }]}
|
||||
className="worker-consignment-spec-card"
|
||||
>
|
||||
<Select placeholder="请选择" options={SERIES_OPTIONS} />
|
||||
<Radio.Group
|
||||
className="worker-consignment-chips"
|
||||
optionType="button"
|
||||
buttonStyle="solid"
|
||||
options={SERIES_OPTIONS}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={12} sm={8} md={6}>
|
||||
<Form.Item
|
||||
name="diamondLevel"
|
||||
label="钻石等级"
|
||||
rules={[{ required: true, message: '请选择钻级' }]}
|
||||
className="worker-consignment-spec-card is-count"
|
||||
>
|
||||
<Select placeholder="请选择" options={DIAMOND_OPTIONS} />
|
||||
<Radio.Group
|
||||
className="worker-consignment-chips is-count"
|
||||
optionType="button"
|
||||
buttonStyle="solid"
|
||||
options={DIAMOND_OPTIONS.map((o) => ({
|
||||
label: `${o.value}钻`,
|
||||
value: o.value,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={12} sm={8} md={6}>
|
||||
<Form.Item
|
||||
name="brillianceCount"
|
||||
label="璀璨数"
|
||||
rules={[{ required: true, message: '请选择璀璨数' }]}
|
||||
className="worker-consignment-spec-card is-count"
|
||||
>
|
||||
<Select placeholder="请选择" options={BRILLIANCE_OPTIONS} />
|
||||
<Radio.Group
|
||||
className="worker-consignment-chips is-count"
|
||||
optionType="button"
|
||||
buttonStyle="solid"
|
||||
options={BRILLIANCE_OPTIONS.map((o) => ({
|
||||
label: o.value === 0 ? '无' : `${o.value}璀`,
|
||||
value: o.value,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} sm={16} md={12}>
|
||||
<Form.Item
|
||||
name="itemName"
|
||||
label="物品名称"
|
||||
rules={[{ required: true, message: '请输入名称' }]}
|
||||
>
|
||||
<Input placeholder="选择规格后自动生成,也可手动修改" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={12} sm={8} md={6}>
|
||||
</div>
|
||||
|
||||
<div className="worker-consignment-price-grid">
|
||||
<Form.Item
|
||||
name="askingPrice"
|
||||
label="寄售价格 (元)"
|
||||
label="寄售价格"
|
||||
rules={[{ required: true, message: '请填写寄售价格' }]}
|
||||
className="worker-consignment-spec-card is-price"
|
||||
>
|
||||
<InputNumber
|
||||
size="large"
|
||||
min={1}
|
||||
step={1}
|
||||
style={{ width: '100%' }}
|
||||
@@ -440,26 +483,26 @@ export default function WorkerConsignmentPage() {
|
||||
addonBefore="¥"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={12} sm={8} md={6}>
|
||||
<Form.Item
|
||||
name="depositAmount"
|
||||
label="履约押金 (元)"
|
||||
rules={[{ required: true, message: '请填写履约押金' }]}
|
||||
tooltip="派单交枪时冻结,验货后解冻。填写寄售价后可自动带出,也可手动改。"
|
||||
label="履约押金"
|
||||
tooltip="平台统一配置。发布成功后暂扣,驳回或履约完成后释放。"
|
||||
className="worker-consignment-spec-card is-price"
|
||||
>
|
||||
<InputNumber
|
||||
min={1}
|
||||
step={10}
|
||||
size="large"
|
||||
disabled
|
||||
value={publishDepositYuan}
|
||||
style={{ width: '100%' }}
|
||||
placeholder="请输入"
|
||||
addonBefore="¥"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item name="itemName" hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider orientation="left" plain className="worker-consignment-divider">
|
||||
<Divider plain className="worker-consignment-divider">
|
||||
联系方式与游戏凭据
|
||||
</Divider>
|
||||
|
||||
@@ -532,12 +575,15 @@ export default function WorkerConsignmentPage() {
|
||||
htmlType="submit"
|
||||
size="large"
|
||||
loading={submitting}
|
||||
disabled={!canPublish}
|
||||
icon={<CloudUploadOutlined />}
|
||||
>
|
||||
提交寄卖提报
|
||||
{canPublish
|
||||
? `提交寄卖(暂扣押金 ¥${publishDepositYuan.toFixed(0)})`
|
||||
: '余额不足,请先充值押金'}
|
||||
</Button>
|
||||
<Typography.Text type="secondary">
|
||||
提交后进入客服审核,通过后入库匹配
|
||||
提交后进入客服审核;押金在驳回或履约完成后释放
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
@@ -495,6 +495,7 @@ export function fetchAdminWorkerFinanceConfig() {
|
||||
export function saveAdminWorkerFinanceConfig(payload: {
|
||||
depositUnfreezeDays?: number
|
||||
rewardUnfreezeEnabled?: boolean
|
||||
consignmentPublishDepositFen?: number
|
||||
recharge?: {
|
||||
enabled?: boolean
|
||||
channelName?: string
|
||||
|
||||
@@ -412,7 +412,9 @@ export function publishWorkerConsignment(payload: WorkerConsignmentPublishPayloa
|
||||
}
|
||||
|
||||
export function fetchWorkerConsignments() {
|
||||
return apiGet<{ submissions: WorkerConsignmentItem[]; inventories: WorkerInventoryItem[] }>(
|
||||
'/api/v1/worker/consignment/my-items',
|
||||
)
|
||||
return apiGet<{
|
||||
publishDepositFen: number
|
||||
submissions: WorkerConsignmentItem[]
|
||||
inventories: WorkerInventoryItem[]
|
||||
}>('/api/v1/worker/consignment/my-items')
|
||||
}
|
||||
|
||||
@@ -4223,8 +4223,12 @@ body {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.worker-consignment-tip.ant-alert {
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.worker-consignment-divider.ant-divider {
|
||||
margin: 8px 0 16px;
|
||||
margin: 4px 0 10px;
|
||||
border-color: #e2e8f0;
|
||||
color: #64748b;
|
||||
font-size: 13px;
|
||||
@@ -4232,7 +4236,102 @@ body {
|
||||
}
|
||||
|
||||
.worker-consignment-form .ant-form-item {
|
||||
margin-bottom: 14px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.worker-consignment-specs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.worker-consignment-spec-grid {
|
||||
display: grid;
|
||||
grid-template-columns:
|
||||
minmax(168px, 1.05fr) minmax(148px, 0.9fr) minmax(176px, 1.05fr) minmax(280px, 1.7fr)
|
||||
minmax(280px, 1.7fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.worker-consignment-price-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 240px 240px;
|
||||
gap: 12px;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.worker-consignment-spec-card {
|
||||
margin-bottom: 0 !important;
|
||||
padding: 12px 14px 14px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.worker-consignment-spec-card .ant-form-item-label {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.worker-consignment-spec-card .ant-form-item-label > label {
|
||||
height: auto;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.worker-consignment-spec-card .ant-form-item-control-input,
|
||||
.worker-consignment-spec-card .ant-form-item-control-input-content {
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.worker-consignment-chips {
|
||||
display: flex !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.worker-consignment-chips .ant-radio-button-wrapper {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
height: 40px;
|
||||
padding: 0 8px;
|
||||
line-height: 38px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
border-color: #e2e8f0;
|
||||
}
|
||||
|
||||
.worker-consignment-chips .ant-radio-button-wrapper:first-child {
|
||||
border-radius: 8px 0 0 8px;
|
||||
}
|
||||
|
||||
.worker-consignment-chips .ant-radio-button-wrapper:last-child {
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
.worker-consignment-chips .ant-radio-button-wrapper-checked {
|
||||
background: #ff6a00;
|
||||
border-color: #ff6a00;
|
||||
color: #fff;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.worker-consignment-chips.is-count .ant-radio-button-wrapper {
|
||||
padding: 0 4px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.worker-consignment-spec-card.is-name .ant-input,
|
||||
.worker-consignment-spec-card.is-price .ant-input-number,
|
||||
.worker-consignment-spec-card.is-price .ant-input-number-input {
|
||||
height: 40px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.worker-consignment-spec-card.is-price .ant-input-number-group-addon {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.worker-consignment-actions {
|
||||
@@ -4245,6 +4344,27 @@ body {
|
||||
border-top: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
.worker-consignment-spec-grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.worker-consignment-spec-card.is-count {
|
||||
grid-column: span 3;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.worker-consignment-spec-grid,
|
||||
.worker-consignment-price-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.worker-consignment-spec-card.is-count {
|
||||
grid-column: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.worker-consignment-filter-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -4327,4 +4447,3 @@ body {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ export type ConsignmentMatchResult = {
|
||||
matchType: 'exact' | 'partial'
|
||||
matchScore: number
|
||||
matchReasons: string[]
|
||||
reason?: string
|
||||
}
|
||||
|
||||
export type WorkOrderInventoryMatchCheck = {
|
||||
|
||||
@@ -259,6 +259,8 @@ export type WorkerAfterSalesCaseEvent = {
|
||||
export type WorkerFinanceConfig = {
|
||||
depositUnfreezeDays: number
|
||||
rewardUnfreezeEnabled: boolean
|
||||
/** 苏尔南寄卖发布履约押金(分),默认 2000 */
|
||||
consignmentPublishDepositFen?: number
|
||||
recharge: {
|
||||
enabled: boolean
|
||||
channelName: string
|
||||
|
||||
Reference in New Issue
Block a user