feat(worker-platform): 接单平台工单详情抽屉支持卖家与买家资料一键复制与单项复制

1. 寄售赠送进度卡片右上角新增【一键复制卖家资料】按钮,一键聚合复制卖家角色昵称、游戏ID、区服、系统、联系方式(微信/QQ/手机)、提报/仓库单号及备注;
2. 资料信息卡片右上角新增【一键复制买家资料】按钮,一键复制买家提交的全部表单字段;
3. 卖家资料与买家资料的各项字段值(角色名、数字ID、区服、系统、联系方式、单号等)右侧均补充内联一键复制小图标,支持快捷单项提取。
This commit is contained in:
yml2213
2026-09-19 14:39:43 +08:00
parent 2c120bd3e5
commit f08a221e15
2 changed files with 219 additions and 62 deletions
@@ -1,5 +1,6 @@
import {
CheckOutlined,
CopyOutlined,
DeleteOutlined,
EditOutlined,
FormOutlined,
@@ -1295,6 +1296,63 @@ export default function WorkOrdersPanel() {
})
}
function copyBuyerMaterialInfo(order: WorkOrder) {
const items = getMaterialDetailItems(order).filter((item) => String(item.value || '').trim())
if (items.length === 0) {
message.warning('当前工单暂无买家资料')
return
}
const lines = items.map((item) => `${item.label}: ${item.value}`)
navigator.clipboard
.writeText(lines.join('\n'))
.then(() => message.success('买家资料已复制到剪贴板'))
.catch(() => message.error('复制失败,请手动复制'))
}
function copyConsignmentSellerInfo(order: WorkOrder) {
const consignmentInfo = getConsignmentFulfillment(order)
const sellerFields = order.gift?.boosterMaterial?.fields || {}
const gameNickname = String(
sellerFields.gameNickname || consignmentInfo?.buyerName || '',
).trim()
const gameId = String(sellerFields.gameId || consignmentInfo?.sellerGameId || '').trim()
const serverZone = String(sellerFields.serverZone || consignmentInfo?.serverZone || '').trim()
const system = String(sellerFields.system || consignmentInfo?.system || '').trim()
const sellerContact = String(
sellerFields.sellerContact || consignmentInfo?.sellerContact || '',
).trim()
const sellerPhone = String(
sellerFields.sellerPhone || consignmentInfo?.sellerPhone || '',
).trim()
const sourceSubmissionNo = String(
sellerFields.sourceSubmissionNo || consignmentInfo?.sourceSubmissionNo || '',
).trim()
const inventoryNo = String(
sellerFields.inventoryNo || consignmentInfo?.inventoryNo || '',
).trim()
const note = String(order.gift?.boosterMaterial?.note || consignmentInfo?.remark || '').trim()
const lines: string[] = []
if (gameNickname) lines.push(`卖家角色昵称: ${gameNickname}`)
if (gameId) lines.push(`卖家游戏ID: ${gameId}`)
if (serverZone) lines.push(`区服: ${serverZone}`)
if (system) lines.push(`系统: ${system}`)
if (sellerContact) lines.push(`卖家微信/QQ: ${sellerContact}`)
if (sellerPhone) lines.push(`卖家手机: ${sellerPhone}`)
if (sourceSubmissionNo) lines.push(`提报单号: ${sourceSubmissionNo}`)
if (inventoryNo) lines.push(`仓库单号: ${inventoryNo}`)
if (note) lines.push(`卖家备注: ${note}`)
if (lines.length === 0) {
message.warning('当前工单暂无可复制的卖家资料')
return
}
navigator.clipboard
.writeText(lines.join('\n'))
.then(() => message.success('卖家资料已复制到剪贴板'))
.catch(() => message.error('复制失败,请手动复制'))
}
const columns: TableColumnsType<WorkOrder> = [
{
title: '订单',
@@ -2093,7 +2151,19 @@ export default function WorkOrdersPanel() {
</Descriptions>
</Card>
<Card title="资料信息" size="small">
<Card
title="资料信息"
size="small"
extra={
<Button
size="small"
icon={<CopyOutlined />}
onClick={() => copyBuyerMaterialInfo(detailOrder)}
>
一键复制买家资料
</Button>
}
>
{renderDetailFieldValues(getMaterialDetailItems(detailOrder), '当前还没有填写资料')}
<div style={{ marginTop: 12 }}>
<Typography.Text type="secondary">买家主页截图</Typography.Text>
@@ -2109,15 +2179,30 @@ export default function WorkOrdersPanel() {
isInternalConsignmentOrder(detailOrder) || isConsignmentOrder(detailOrder)
const consignmentInfo = getConsignmentFulfillment(detailOrder)
const sellerFields = detailOrder.gift.boosterMaterial?.fields || {}
const sellerContact =
sellerFields.sellerContact || String(consignmentInfo?.sellerContact || '')
const sellerPhone =
sellerFields.sellerPhone || String(consignmentInfo?.sellerPhone || '')
const inventoryNo =
sellerFields.inventoryNo || String(consignmentInfo?.inventoryNo || '')
const sourceSubmissionNo =
sellerFields.sourceSubmissionNo ||
String(consignmentInfo?.sourceSubmissionNo || '')
const sellerNickname = String(
sellerFields.gameNickname || consignmentInfo?.buyerName || '',
).trim()
const sellerGameId = String(
sellerFields.gameId || consignmentInfo?.sellerGameId || '',
).trim()
const sellerServerZone = String(
sellerFields.serverZone || consignmentInfo?.serverZone || '',
).trim()
const sellerSystem = String(
sellerFields.system || consignmentInfo?.system || '',
).trim()
const sellerContact = String(
sellerFields.sellerContact || consignmentInfo?.sellerContact || '',
).trim()
const sellerPhone = String(
sellerFields.sellerPhone || consignmentInfo?.sellerPhone || '',
).trim()
const inventoryNo = String(
sellerFields.inventoryNo || consignmentInfo?.inventoryNo || '',
).trim()
const sourceSubmissionNo = String(
sellerFields.sourceSubmissionNo || consignmentInfo?.sourceSubmissionNo || '',
).trim()
const effectiveNote = String(
detailOrder.gift?.boosterMaterial?.note || consignmentInfo?.remark || '',
).trim()
@@ -2127,7 +2212,7 @@ export default function WorkOrdersPanel() {
title={isConsignment ? '寄售赠送进度' : '好友赠送进度'}
size="small"
extra={
<Space>
<Space wrap>
{isInternalConsignmentOrder(detailOrder) ? (
<Tag color="cyan">内部寄售匹配</Tag>
) : isConsignment ? (
@@ -2140,6 +2225,15 @@ export default function WorkOrdersPanel() {
<Tag color={resolveGiftPhaseColor(detailOrder.gift.phase)}>
{formatGiftPhase(detailOrder.gift.phase, isConsignment)}
</Tag>
{isConsignment ? (
<Button
size="small"
icon={<CopyOutlined />}
onClick={() => copyConsignmentSellerInfo(detailOrder)}
>
一键复制卖家资料
</Button>
) : null}
{detailOrder.capabilities?.canConfirmFriend && canManageProblem ? (
<Button
type="primary"
@@ -2194,55 +2288,110 @@ export default function WorkOrdersPanel() {
</Descriptions>
<Descriptions column={{ xs: 1, md: 2 }} bordered size="small">
<Descriptions.Item label={isConsignment ? '卖家角色昵称' : '游戏昵称'}>
<span className="worker-order-detail-value">
{String(
sellerFields.gameNickname || consignmentInfo?.buyerName || '',
).trim() || '-'}
</span>
{sellerNickname ? (
<Space size={6} align="center">
<span className="worker-order-detail-value">{sellerNickname}</span>
<Typography.Text
copyable={{
text: sellerNickname,
tooltips: ['复制昵称', '已复制'],
}}
/>
</Space>
) : (
<span className="worker-order-detail-value">-</span>
)}
</Descriptions.Item>
<Descriptions.Item label={isConsignment ? '卖家游戏ID' : '游戏ID'}>
<span className="worker-order-detail-value">
{String(
sellerFields.gameId || consignmentInfo?.sellerGameId || '',
).trim() || '-'}
</span>
{sellerGameId ? (
<Space size={6} align="center">
<span className="worker-order-detail-value">{sellerGameId}</span>
<Typography.Text
copyable={{
text: sellerGameId,
tooltips: ['复制游戏ID', '已复制'],
}}
/>
</Space>
) : (
<span className="worker-order-detail-value">-</span>
)}
</Descriptions.Item>
<Descriptions.Item label="区服">
<span className="worker-order-detail-value">
{String(
sellerFields.serverZone || consignmentInfo?.serverZone || '',
).trim() || '-'}
</span>
{sellerServerZone ? (
<Space size={6} align="center">
<span className="worker-order-detail-value">
{sellerServerZone}
</span>
<Typography.Text
copyable={{
text: sellerServerZone,
tooltips: ['复制区服', '已复制'],
}}
/>
</Space>
) : (
<span className="worker-order-detail-value">-</span>
)}
</Descriptions.Item>
<Descriptions.Item label="系统">
<span className="worker-order-detail-value">
{String(
sellerFields.system || consignmentInfo?.system || '',
).trim() || '-'}
</span>
{sellerSystem ? (
<Space size={6} align="center">
<span className="worker-order-detail-value">{sellerSystem}</span>
<Typography.Text
copyable={{
text: sellerSystem,
tooltips: ['复制系统', '已复制'],
}}
/>
</Space>
) : (
<span className="worker-order-detail-value">-</span>
)}
</Descriptions.Item>
{isConsignment && (sellerContact || sellerPhone) ? (
<Descriptions.Item label="卖家联系方式" span={2}>
<span className="worker-order-detail-value">
{[
sellerContact ? `微信/QQ: ${sellerContact}` : '',
sellerPhone ? `手机: ${sellerPhone}` : '',
]
.filter(Boolean)
.join(' / ')}
</span>
<Space size={6} align="center" wrap>
<span className="worker-order-detail-value">
{[
sellerContact ? `微信/QQ: ${sellerContact}` : '',
sellerPhone ? `手机: ${sellerPhone}` : '',
]
.filter(Boolean)
.join(' / ')}
</span>
<Typography.Text
copyable={{
text: [
sellerContact ? `微信/QQ: ${sellerContact}` : '',
sellerPhone ? `手机: ${sellerPhone}` : '',
]
.filter(Boolean)
.join(' '),
tooltips: ['复制联系方式', '已复制'],
}}
/>
</Space>
</Descriptions.Item>
) : null}
{isConsignment && (inventoryNo || sourceSubmissionNo) ? (
<Descriptions.Item label="寄售单号" span={2}>
<span className="worker-order-detail-value">
{[
sourceSubmissionNo ? `提报单: ${sourceSubmissionNo}` : '',
inventoryNo ? `仓库单: ${inventoryNo}` : '',
]
.filter(Boolean)
.join(' / ')}
</span>
<Space size={6} align="center" wrap>
<span className="worker-order-detail-value">
{[
sourceSubmissionNo ? `提报单: ${sourceSubmissionNo}` : '',
inventoryNo ? `仓库单: ${inventoryNo}` : '',
]
.filter(Boolean)
.join(' / ')}
</span>
<Typography.Text
copyable={{
text: sourceSubmissionNo || inventoryNo,
tooltips: ['复制单号', '已复制'],
}}
/>
</Space>
</Descriptions.Item>
) : null}
<Descriptions.Item
+23 -15
View File
@@ -84,21 +84,29 @@ export function renderDetailFieldValues(items: DetailFieldItem[], emptyText: str
return (
<Descriptions column={1} bordered size="small">
{items.map((item) => (
<Descriptions.Item
key={item.key}
label={
<Space size={6} wrap>
<span>{item.label}</span>
{item.required ? <Tag color="orange">必填</Tag> : null}
</Space>
}
>
<span className="worker-order-detail-value">
{String(item.value || '').trim() || '-'}
</span>
</Descriptions.Item>
))}
{items.map((item) => {
const val = String(item.value || '').trim()
return (
<Descriptions.Item
key={item.key}
label={
<Space size={6} wrap>
<span>{item.label}</span>
{item.required ? <Tag color="orange">必填</Tag> : null}
</Space>
}
>
{val ? (
<Space size={6} align="center">
<span className="worker-order-detail-value">{val}</span>
<Typography.Text copyable={{ text: val, tooltips: ['复制', '已复制'] }} />
</Space>
) : (
<span className="worker-order-detail-value">-</span>
)}
</Descriptions.Item>
)
})}
</Descriptions>
)
}