feat(worker-platform): 优化打手我的寄卖卡片资料展示,前置展示打手自身上架账号资料
1. 后端 consignment-worker-service 在我的寄卖列表中解析并返回打手上架时填写的 sellerInfo(角色名、数字ID、区服系统、微信号/QQ、寄卖留言等); 2. 前端新增 ConsignmentSellerInfoBlock 卖家资料区块,提供角色名/ID高亮与一键复制功能; 3. 寄卖卡片布局升级为前置展示【我的寄卖账号资料】、后置展示【买家加好友资料】,多号上架时号主与买家清晰对应,彻底杜绝登错号加错人。
This commit is contained in:
@@ -70,6 +70,71 @@ function extractConsignmentGiftEvidenceFromWorkOrder(boundWo?: WorkOrderRow | nu
|
||||
}
|
||||
}
|
||||
|
||||
export type ConsignmentSellerInfo = {
|
||||
characterName: string
|
||||
characterId: string
|
||||
sellerContact?: string
|
||||
sellerPhone?: string
|
||||
sellerNote?: string
|
||||
system?: string
|
||||
serverZone?: string
|
||||
}
|
||||
|
||||
function extractConsignmentSellerInfoFromSubmission(
|
||||
sub: Awaited<ReturnType<typeof listConsignmentSubmissions>>['items'][number],
|
||||
): ConsignmentSellerInfo {
|
||||
const content = toRecord(sub.content_json) || {}
|
||||
let characterName = String(content.characterName || '').trim()
|
||||
let characterId = String(content.characterId || '').trim()
|
||||
|
||||
if (!characterName && sub.seller_game_id) {
|
||||
const raw = String(sub.seller_game_id).trim()
|
||||
const match = raw.match(/^(.*?)\s*\((.*?)\)$/)
|
||||
if (match) {
|
||||
characterName = match[1]?.trim() || raw
|
||||
if (!characterId) characterId = match[2]?.trim() || ''
|
||||
} else {
|
||||
characterName = raw
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
characterName,
|
||||
characterId,
|
||||
sellerContact: String(sub.contact_info || '').trim(),
|
||||
sellerPhone: String(sub.seller_phone || '').trim(),
|
||||
sellerNote: String(content.sellerNote || '').trim(),
|
||||
system: sub.system,
|
||||
serverZone: sub.server_zone,
|
||||
}
|
||||
}
|
||||
|
||||
function extractConsignmentSellerInfoFromInventory(
|
||||
inv: Awaited<ReturnType<typeof listConsignmentInventories>>['items'][number],
|
||||
): ConsignmentSellerInfo {
|
||||
let characterName = ''
|
||||
let characterId = ''
|
||||
if (inv.seller_game_id) {
|
||||
const raw = String(inv.seller_game_id).trim()
|
||||
const match = raw.match(/^(.*?)\s*\((.*?)\)$/)
|
||||
if (match) {
|
||||
characterName = match[1]?.trim() || raw
|
||||
characterId = match[2]?.trim() || ''
|
||||
} else {
|
||||
characterName = raw
|
||||
}
|
||||
}
|
||||
return {
|
||||
characterName,
|
||||
characterId,
|
||||
sellerContact: String(inv.seller_contact || '').trim(),
|
||||
sellerPhone: String(inv.seller_phone || '').trim(),
|
||||
sellerNote: String(inv.remark || '').trim(),
|
||||
system: inv.system,
|
||||
serverZone: inv.server_zone,
|
||||
}
|
||||
}
|
||||
|
||||
export function getConsignmentPublishDepositFen() {
|
||||
return getConfiguredConsignmentPublishDepositFen()
|
||||
}
|
||||
@@ -358,6 +423,7 @@ export async function listWorkerConsignments(workerId: number) {
|
||||
friendAddedAt: inv?.bound_work_order_id
|
||||
? (workOrderById.get(Number(inv.bound_work_order_id))?.friend_added_at ?? null)
|
||||
: null,
|
||||
sellerInfo: extractConsignmentSellerInfoFromSubmission(sub),
|
||||
buyerInfo: extractConsignmentBuyerInfoFromWorkOrder(boundWo),
|
||||
}
|
||||
}),
|
||||
@@ -397,6 +463,7 @@ export async function listWorkerConsignments(workerId: number) {
|
||||
friendAddedAt: inv.bound_work_order_id
|
||||
? (workOrderById.get(Number(inv.bound_work_order_id))?.friend_added_at ?? null)
|
||||
: null,
|
||||
sellerInfo: extractConsignmentSellerInfoFromInventory(inv),
|
||||
buyerInfo: extractConsignmentBuyerInfoFromWorkOrder(boundWo),
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -929,6 +929,119 @@ function SpecTags({
|
||||
)
|
||||
}
|
||||
|
||||
function ConsignmentSellerInfoBlock({
|
||||
sellerInfo,
|
||||
record,
|
||||
}: {
|
||||
sellerInfo?: WorkerConsignmentRecord['sellerInfo']
|
||||
record: WorkerConsignmentRecord
|
||||
}) {
|
||||
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 characterName = sellerInfo?.characterName || ''
|
||||
const characterId = sellerInfo?.characterId || ''
|
||||
const serverZone = sellerInfo?.serverZone || record.serverZone || ''
|
||||
const system = sellerInfo?.system || record.system || ''
|
||||
const sellerContact = sellerInfo?.sellerContact || ''
|
||||
const sellerNote = sellerInfo?.sellerNote || ''
|
||||
|
||||
const copyAll = () => {
|
||||
const parts: string[] = []
|
||||
if (characterName) parts.push(`卖家角色: ${characterName}`)
|
||||
if (characterId) parts.push(`角色ID: ${characterId}`)
|
||||
if (serverZone) parts.push(`区服: ${serverZone}`)
|
||||
if (system) parts.push(`系统: ${system}`)
|
||||
if (sellerContact) parts.push(`联系方式: ${sellerContact}`)
|
||||
copyText(parts.join(' | '), '我的寄卖账号资料')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="worker-consignment-seller-box">
|
||||
<div className="worker-consignment-seller-header">
|
||||
<Space size={6}>
|
||||
<UserOutlined style={{ color: '#d97706' }} />
|
||||
<Typography.Text strong style={{ color: '#b45309', fontSize: 13 }}>
|
||||
我的寄卖账号 / 角色资料
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
icon={<CopyOutlined />}
|
||||
style={{ padding: 0, height: 'auto', fontSize: 12, color: '#d97706' }}
|
||||
onClick={copyAll}
|
||||
>
|
||||
一键复制资料
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="worker-consignment-seller-grid">
|
||||
{characterName ? (
|
||||
<div className="seller-field-cell">
|
||||
<span className="seller-field-label">我的游戏角色名</span>
|
||||
<Typography.Text
|
||||
strong
|
||||
copyable={{ text: characterName }}
|
||||
className="seller-field-highlight"
|
||||
>
|
||||
{characterName}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{characterId ? (
|
||||
<div className="seller-field-cell">
|
||||
<span className="seller-field-label">角色数字ID</span>
|
||||
<Typography.Text
|
||||
strong
|
||||
copyable={{ text: characterId }}
|
||||
className="seller-field-highlight"
|
||||
>
|
||||
{characterId}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{serverZone || system ? (
|
||||
<div className="seller-field-cell">
|
||||
<span className="seller-field-label">上架区服与系统</span>
|
||||
<Space size={4} style={{ marginTop: 2 }}>
|
||||
{system ? <Tag color="gold">{system}</Tag> : null}
|
||||
{serverZone ? <Tag color="orange">{serverZone}</Tag> : null}
|
||||
</Space>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{sellerContact ? (
|
||||
<div className="seller-field-cell">
|
||||
<span className="seller-field-label">微信号/QQ</span>
|
||||
<Typography.Text
|
||||
copyable={{ text: sellerContact }}
|
||||
style={{ fontSize: 13, color: '#78350f' }}
|
||||
>
|
||||
{sellerContact}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{sellerNote ? (
|
||||
<div className="worker-consignment-seller-note-wrap">
|
||||
<span>寄卖备注:{sellerNote}</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ConsignmentBuyerInfoBlock({
|
||||
buyerInfo,
|
||||
}: {
|
||||
@@ -946,8 +1059,8 @@ function ConsignmentBuyerInfoBlock({
|
||||
|
||||
const copyAll = () => {
|
||||
const parts: string[] = []
|
||||
if (buyerInfo.gameNickname) parts.push(`角色名: ${buyerInfo.gameNickname}`)
|
||||
if (buyerInfo.gameId) parts.push(`游戏ID: ${buyerInfo.gameId}`)
|
||||
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(' | '), '买家加好友资料')
|
||||
@@ -959,7 +1072,7 @@ function ConsignmentBuyerInfoBlock({
|
||||
<Space size={6}>
|
||||
<UserOutlined style={{ color: '#1677ff' }} />
|
||||
<Typography.Text strong style={{ color: '#1677ff', fontSize: 13 }}>
|
||||
买家加好友资料
|
||||
买家加好友资料(请在游戏内添加此人)
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<Button
|
||||
@@ -1097,7 +1210,11 @@ function ConsignmentRecordCard({
|
||||
{record.reviewNote ? (
|
||||
<Typography.Text type="danger">审核说明:{record.reviewNote}</Typography.Text>
|
||||
) : null}
|
||||
{record.buyerInfo ? <ConsignmentBuyerInfoBlock buyerInfo={record.buyerInfo} /> : null}
|
||||
|
||||
<div className={`worker-consignment-info-deck ${record.buyerInfo ? 'is-dual' : ''}`}>
|
||||
<ConsignmentSellerInfoBlock sellerInfo={record.sellerInfo} record={record} />
|
||||
{record.buyerInfo ? <ConsignmentBuyerInfoBlock buyerInfo={record.buyerInfo} /> : null}
|
||||
</div>
|
||||
{record.hint ? (
|
||||
<Typography.Text type="secondary" className="worker-consignment-record-hint">
|
||||
{record.hint}
|
||||
|
||||
@@ -23,7 +23,7 @@ import type {
|
||||
WorkerUser,
|
||||
WorkerWalletLedger,
|
||||
} from '@/types/worker-platform'
|
||||
import type { ConsignmentBuyerInfo } from '@/types/consignment'
|
||||
import type { ConsignmentBuyerInfo, ConsignmentSellerInfo } from '@/types/consignment'
|
||||
|
||||
import { getWorkerDeviceId } from '@/utils/worker-auth'
|
||||
|
||||
@@ -450,11 +450,13 @@ export type WorkerConsignmentRecord = {
|
||||
/** 赠送冷却截止时间,前端拿它做倒计时 */
|
||||
giftAvailableAt: string | null
|
||||
friendAddedAt: string | null
|
||||
/** 卖家自己寄卖时填写的账号/角色资料 */
|
||||
sellerInfo?: ConsignmentSellerInfo | null
|
||||
/** 匹配工单的买家资料(用于加好友) */
|
||||
buyerInfo?: ConsignmentBuyerInfo | null
|
||||
}
|
||||
|
||||
export type { ConsignmentBuyerInfo } from '@/types/consignment'
|
||||
export type { ConsignmentBuyerInfo, ConsignmentSellerInfo } from '@/types/consignment'
|
||||
|
||||
export type WorkerConsignmentListResponse = {
|
||||
publishDepositFen: number
|
||||
|
||||
@@ -4467,13 +4467,81 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.worker-consignment-info-deck {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.worker-consignment-info-deck.is-dual {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
|
||||
.worker-consignment-seller-box {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
background: #fffbeb;
|
||||
border: 1px solid #fde68a;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.worker-consignment-seller-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.worker-consignment-seller-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px 20px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.seller-field-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.seller-field-label {
|
||||
font-size: 11px;
|
||||
color: #92400e;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.seller-field-highlight {
|
||||
font-size: 14px;
|
||||
color: #78350f;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.worker-consignment-seller-note-wrap {
|
||||
margin-top: 6px;
|
||||
padding-top: 6px;
|
||||
border-top: 1px dashed #fcd34d;
|
||||
font-size: 12px;
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
.worker-consignment-buyer-box {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
background: #eff6ff;
|
||||
border: 1px solid #bfdbfe;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.worker-consignment-buyer-header {
|
||||
@@ -4498,16 +4566,18 @@ body {
|
||||
|
||||
.buyer-field-label {
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
color: #1e40af;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.buyer-field-highlight {
|
||||
font-size: 14px;
|
||||
color: #1e293b;
|
||||
color: #1e3a8a;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.worker-consignment-buyer-shots-wrap {
|
||||
margin-top: 6px;
|
||||
padding-top: 6px;
|
||||
border-top: 1px dashed #cbd5e1;
|
||||
border-top: 1px dashed #bfdbfe;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,16 @@ export type ConsignmentInventory = ConsignmentEvidenceFields & {
|
||||
buyerInfo?: ConsignmentBuyerInfo | null
|
||||
}
|
||||
|
||||
export type ConsignmentSellerInfo = {
|
||||
characterName: string
|
||||
characterId: string
|
||||
sellerContact?: string
|
||||
sellerPhone?: string
|
||||
sellerNote?: string
|
||||
system?: string
|
||||
serverZone?: string
|
||||
}
|
||||
|
||||
export type ConsignmentBuyerInfo = {
|
||||
gameNickname: string
|
||||
gameId: string
|
||||
|
||||
Reference in New Issue
Block a user