feat(consignment): 寄售匹配成功后向卖家端与客服后台透出买家加好友资料

This commit is contained in:
yml2213
2026-09-17 20:46:45 +08:00
parent eff45bb9a8
commit c5d1e2c375
9 changed files with 327 additions and 2 deletions
@@ -1107,6 +1107,52 @@ export default function ConsignmentInventoryPanel() {
<Text type="secondary">未匹配工单</Text>
)}
</Descriptions.Item>
{viewingItem.buyerInfo ? (
<Descriptions.Item label="匹配买家资料" span={2}>
<Space direction="vertical" size={8} style={{ width: '100%' }}>
<Space size={16} wrap>
{viewingItem.buyerInfo.gameNickname ? (
<span>
买家角色名:
<Text strong copyable>
{viewingItem.buyerInfo.gameNickname}
</Text>
</span>
) : null}
{viewingItem.buyerInfo.gameId ? (
<span>
角色数字ID:
<Text strong copyable>
{viewingItem.buyerInfo.gameId}
</Text>
</span>
) : null}
{viewingItem.buyerInfo.serverZone ? (
<Tag color="blue">{viewingItem.buyerInfo.serverZone}</Tag>
) : null}
{viewingItem.buyerInfo.system ? (
<Tag color="cyan">{viewingItem.buyerInfo.system}</Tag>
) : null}
</Space>
{viewingItem.buyerInfo.screenshots &&
viewingItem.buyerInfo.screenshots.length > 0 ? (
<div>
<Text type="secondary" style={{ fontSize: 12 }}>
买家主页截图(加好友核对用):
</Text>
<div style={{ marginTop: 4 }}>
<ConsignmentEvidenceGallery
images={viewingItem.buyerInfo.screenshots}
emptyText="无买家截图"
max={6}
size={70}
/>
</div>
</div>
) : null}
</Space>
</Descriptions.Item>
) : null}
<Descriptions.Item label="登录方式">
{(viewingItem.credentialsJson as any)?.loginMethod || '-'}
</Descriptions.Item>
@@ -2,12 +2,15 @@ import {
CheckCircleOutlined,
ClockCircleOutlined,
CloudUploadOutlined,
CopyOutlined,
DollarCircleOutlined,
HistoryOutlined,
InboxOutlined,
PictureOutlined,
PlusOutlined,
ReloadOutlined,
SafetyCertificateOutlined,
UserOutlined,
} from '@ant-design/icons'
import { useQuery } from '@tanstack/react-query'
import {
@@ -912,6 +915,115 @@ function SpecTags({
)
}
function ConsignmentBuyerInfoBlock({
buyerInfo,
}: {
buyerInfo: NonNullable<WorkerConsignmentRecord['buyerInfo']>
}) {
const { message } = App.useApp()
const copyText = (text: string, label: string) => {
if (!text) return
navigator.clipboard
.writeText(text)
.then(() => message.success(`已复制${label}:${text}`))
.catch(() => message.error('复制失败,请手动长按复制'))
}
const copyAll = () => {
const parts: string[] = []
if (buyerInfo.gameNickname) parts.push(`角色名: ${buyerInfo.gameNickname}`)
if (buyerInfo.gameId) parts.push(`游戏ID: ${buyerInfo.gameId}`)
if (buyerInfo.serverZone) parts.push(`区服: ${buyerInfo.serverZone}`)
if (buyerInfo.system) parts.push(`系统: ${buyerInfo.system}`)
copyText(parts.join(' | '), '买家加好友资料')
}
return (
<div className="worker-consignment-buyer-box">
<div className="worker-consignment-buyer-header">
<Space size={6}>
<UserOutlined style={{ color: '#1677ff' }} />
<Typography.Text strong style={{ color: '#1677ff', fontSize: 13 }}>
买家加好友资料
</Typography.Text>
</Space>
<Button
type="link"
size="small"
icon={<CopyOutlined />}
style={{ padding: 0, height: 'auto', fontSize: 12 }}
onClick={copyAll}
>
一键复制资料
</Button>
</div>
<div className="worker-consignment-buyer-grid">
{buyerInfo.gameNickname ? (
<div className="buyer-field-cell">
<span className="buyer-field-label">买家角色名</span>
<Typography.Text
strong
copyable={{ text: buyerInfo.gameNickname }}
className="buyer-field-highlight"
>
{buyerInfo.gameNickname}
</Typography.Text>
</div>
) : null}
{buyerInfo.gameId ? (
<div className="buyer-field-cell">
<span className="buyer-field-label">角色数字ID</span>
<Typography.Text
strong
copyable={{ text: buyerInfo.gameId }}
className="buyer-field-highlight"
>
{buyerInfo.gameId}
</Typography.Text>
</div>
) : null}
{buyerInfo.serverZone || buyerInfo.system ? (
<div className="buyer-field-cell">
<span className="buyer-field-label">区服与系统</span>
<Space size={4} style={{ marginTop: 2 }}>
{buyerInfo.system ? <Tag color="blue">{buyerInfo.system}</Tag> : null}
{buyerInfo.serverZone ? <Tag color="cyan">{buyerInfo.serverZone}</Tag> : null}
</Space>
</div>
) : null}
</div>
{buyerInfo.screenshots && buyerInfo.screenshots.length > 0 ? (
<div className="worker-consignment-buyer-shots-wrap">
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
<PictureOutlined style={{ marginRight: 4 }} />
买家主页截图(在游戏内添加好友时核对头像/段位/等级,防加错人):
</Typography.Text>
<div style={{ marginTop: 6 }}>
<Image.PreviewGroup>
<Space size={8} wrap>
{buyerInfo.screenshots.map((src: string, i: number) => (
<Image
key={`buyer-shot-${i}`}
src={src}
width={48}
height={48}
style={{ objectFit: 'cover', borderRadius: 6, border: '1px solid #bfdbfe' }}
/>
))}
</Space>
</Image.PreviewGroup>
</div>
</div>
) : null}
</div>
)
}
/**
* 「我的寄卖」记录卡片:一件货一张卡。
*
@@ -968,6 +1080,7 @@ function ConsignmentRecordCard({
{record.reviewNote ? (
<Typography.Text type="danger">审核说明:{record.reviewNote}</Typography.Text>
) : null}
{record.buyerInfo ? <ConsignmentBuyerInfoBlock buyerInfo={record.buyerInfo} /> : null}
{record.hint ? (
<Typography.Text type="secondary" className="worker-consignment-record-hint">
{record.hint}
+6
View File
@@ -23,6 +23,8 @@ import type {
WorkerUser,
WorkerWalletLedger,
} from '@/types/worker-platform'
import type { ConsignmentBuyerInfo } from '@/types/consignment'
import { getWorkerDeviceId } from '@/utils/worker-auth'
export function sendWorkerSmsCode(payload: { phone: string }) {
@@ -445,8 +447,12 @@ export type WorkerConsignmentRecord = {
/** 赠送冷却截止时间,前端拿它做倒计时 */
giftAvailableAt: string | null
friendAddedAt: string | null
/** 匹配工单的买家资料(用于加好友) */
buyerInfo?: ConsignmentBuyerInfo | null
}
export type { ConsignmentBuyerInfo } from '@/types/consignment'
export type WorkerConsignmentListResponse = {
publishDepositFen: number
items: WorkerConsignmentRecord[]
+46
View File
@@ -4466,3 +4466,49 @@ body {
width: 100%;
}
}
.worker-consignment-buyer-box {
margin-top: 4px;
margin-bottom: 4px;
padding: 10px 12px;
border-radius: 8px;
background: #eff6ff;
border: 1px solid #bfdbfe;
}
.worker-consignment-buyer-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.worker-consignment-buyer-grid {
display: flex;
flex-wrap: wrap;
gap: 12px 20px;
margin-bottom: 6px;
}
.buyer-field-cell {
display: flex;
flex-direction: column;
gap: 2px;
}
.buyer-field-label {
font-size: 11px;
color: #64748b;
}
.buyer-field-highlight {
font-size: 14px;
color: #1e293b;
}
.worker-consignment-buyer-shots-wrap {
margin-top: 6px;
padding-top: 6px;
border-top: 1px dashed #cbd5e1;
}
+12
View File
@@ -72,8 +72,20 @@ export type ConsignmentInventory = ConsignmentEvidenceFields & {
consumedAt: string | null
createdAt: string
updatedAt: string
/** 关联履约工单的买家资料(加好友及主页截图) */
buyerInfo?: ConsignmentBuyerInfo | null
}
export type ConsignmentBuyerInfo = {
gameNickname: string
gameId: string
serverZone?: string
system?: string
fieldItems: Array<{ key: string; label: string; value: string }>
screenshots: string[]
}
/**
* 工单 `material.consignment` 里的寄售履约信息(履约/指派时后端写入)。
* 历史老单没有 sourceSubmissionNo,展示时必须回退到 inventoryNo。