feat(worker-platform): 接单工单表格「打手」列支持展示寄售供货打手与现货资料
1. 新增 resolveConsignmentWorkerDisplay 工具函数,自动解析工单寄售现货与资料字段(供货打手、游戏角色昵称、游戏ID及联系方式); 2. 优化 renderWorkOrderWorkers 渲染逻辑:当工单为内部寄售现货履约时,打手列带「供货打手」或「内部现货」专属角标,主标题展示打手名/角色名,副标题拼接游戏ID与联系方式,解决原先显示为破折号 - 的问题; 3. 新增 consignment-worker-display 单元测试,更新寄卖库存优化现状与进度追踪文档,前端 57 个测试全绿并通过类型检查。
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Typography } from 'antd'
|
||||
import { Space, Tag, Typography } from 'antd'
|
||||
|
||||
import type { CollectField, WorkOrder } from '@/types/worker-platform'
|
||||
import { resolveConsignmentWorkerDisplay } from '@/utils/consignment-display'
|
||||
|
||||
import { asRecord, formatMoney } from './shared'
|
||||
|
||||
@@ -27,25 +28,58 @@ export function formatUserPaymentAmount(order: WorkOrder): string {
|
||||
return paymentFen === null ? '-' : formatMoney(paymentFen)
|
||||
}
|
||||
|
||||
/** 拼单母单没有单一 assigned worker,改为展示当前参与打手及其承接份数。 */
|
||||
/** 渲染工单关联打手:普通打手 / 拼单参与打手 / 寄卖供货打手与现货资料 */
|
||||
export function renderWorkOrderWorkers(order: WorkOrder) {
|
||||
if (order.worker) return order.worker.displayName || order.worker.username || '-'
|
||||
if (!order.sharing?.enabled || !order.sharingWorkers?.length) return '-'
|
||||
const names = order.sharingWorkers
|
||||
.map((worker) => worker.displayName || worker.username || `打手#${worker.workerId}`)
|
||||
.join('、')
|
||||
const quantity = order.sharingWorkers.reduce(
|
||||
(sum, worker) => sum + Number(worker.quantity || 0),
|
||||
0,
|
||||
)
|
||||
return (
|
||||
<div className="cell-stack">
|
||||
<Typography.Text ellipsis={{ tooltip: names }}>{names}</Typography.Text>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||
{order.sharingWorkers.length} 人 · {quantity} 份
|
||||
</Typography.Text>
|
||||
</div>
|
||||
)
|
||||
|
||||
if (order.sharing?.enabled && order.sharingWorkers?.length) {
|
||||
const names = order.sharingWorkers
|
||||
.map((worker) => worker.displayName || worker.username || `打手#${worker.workerId}`)
|
||||
.join('、')
|
||||
const quantity = order.sharingWorkers.reduce(
|
||||
(sum, worker) => sum + Number(worker.quantity || 0),
|
||||
0,
|
||||
)
|
||||
return (
|
||||
<div className="cell-stack">
|
||||
<Typography.Text ellipsis={{ tooltip: names }}>{names}</Typography.Text>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||
{order.sharingWorkers.length} 人 · {quantity} 份
|
||||
</Typography.Text>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 寄卖库存 / 内部现货履约单:回显供货打手或卖家现货资料
|
||||
const consignmentDisplay = resolveConsignmentWorkerDisplay(order)
|
||||
if (consignmentDisplay) {
|
||||
return (
|
||||
<div className="cell-stack">
|
||||
<Space size={4} align="center" wrap>
|
||||
<Tag
|
||||
color="cyan"
|
||||
style={{ margin: 0, fontSize: 11, padding: '0 4px', lineHeight: '18px' }}
|
||||
>
|
||||
{consignmentDisplay.tagText}
|
||||
</Tag>
|
||||
<Typography.Text ellipsis={{ tooltip: consignmentDisplay.primaryName }}>
|
||||
{consignmentDisplay.primaryName}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
{consignmentDisplay.secondaryInfo ? (
|
||||
<Typography.Text
|
||||
type="secondary"
|
||||
style={{ fontSize: 12 }}
|
||||
ellipsis={{ tooltip: consignmentDisplay.secondaryInfo }}
|
||||
>
|
||||
{consignmentDisplay.secondaryInfo}
|
||||
</Typography.Text>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return '-'
|
||||
}
|
||||
|
||||
export function getDisplayOrderNo(
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import type { WorkOrder } from '@/types/worker-platform'
|
||||
|
||||
function asRecord(value: unknown): Record<string, unknown> {
|
||||
return value && typeof value === 'object' && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: {}
|
||||
}
|
||||
|
||||
export type ConsignmentWorkerDisplayInfo = {
|
||||
tagText: '供货打手' | '内部现货'
|
||||
primaryName: string
|
||||
secondaryInfo: string
|
||||
}
|
||||
|
||||
/** 解析寄卖库存/内部现货履约单的供货人与现货资料信息 */
|
||||
export function resolveConsignmentWorkerDisplay(
|
||||
order: WorkOrder,
|
||||
): ConsignmentWorkerDisplayInfo | null {
|
||||
const material = asRecord(order.material)
|
||||
const consignment = asRecord(material.consignment)
|
||||
const boosterFields = asRecord(order.gift?.boosterMaterial?.fields)
|
||||
const hasConsignment =
|
||||
Object.keys(consignment).length > 0 ||
|
||||
Boolean(boosterFields.sourceType === 'consignment' || boosterFields.inventoryNo)
|
||||
|
||||
if (!hasConsignment) return null
|
||||
|
||||
const workerName = String(consignment.workerName || consignment.worker_name || '').trim()
|
||||
const sellerNickname = String(
|
||||
boosterFields.gameNickname || consignment.buyerName || consignment.sellerGameNickname || '',
|
||||
).trim()
|
||||
const sellerGameId = String(
|
||||
boosterFields.gameId || consignment.sellerGameId || consignment.seller_game_id || '',
|
||||
).trim()
|
||||
const sellerContact = String(
|
||||
boosterFields.sellerPhone ||
|
||||
consignment.sellerPhone ||
|
||||
consignment.workerPhone ||
|
||||
boosterFields.sellerContact ||
|
||||
consignment.sellerContact ||
|
||||
'',
|
||||
).trim()
|
||||
|
||||
const primaryName = workerName || sellerNickname || sellerGameId || '内部现货'
|
||||
const secondaryInfo = [
|
||||
workerName && sellerNickname && sellerNickname !== workerName ? sellerNickname : '',
|
||||
sellerGameId ? `ID:${sellerGameId}` : '',
|
||||
!sellerGameId && sellerContact ? sellerContact : '',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' · ')
|
||||
|
||||
return {
|
||||
tagText: workerName ? '供货打手' : '内部现货',
|
||||
primaryName,
|
||||
secondaryInfo,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
|
||||
import { resolveConsignmentWorkerDisplay } from '../src/utils/consignment-display.ts'
|
||||
|
||||
test('普通工单无寄售信息时 resolveConsignmentWorkerDisplay 返回 null', () => {
|
||||
const order = {
|
||||
material: {},
|
||||
} as never
|
||||
assert.equal(resolveConsignmentWorkerDisplay(order), null)
|
||||
})
|
||||
|
||||
test('寄售工单有供货打手时优先展示供货打手与角色ID', () => {
|
||||
const order = {
|
||||
material: {
|
||||
consignment: {
|
||||
workerName: '01邀请的',
|
||||
buyerName: '客服',
|
||||
sellerGameId: '草莓酱板鸭 (1234551)',
|
||||
sellerPhone: '16677778888',
|
||||
},
|
||||
},
|
||||
gift: {
|
||||
boosterMaterial: {
|
||||
fields: {
|
||||
gameNickname: '客服',
|
||||
gameId: '草莓酱板鸭 (1234551)',
|
||||
},
|
||||
},
|
||||
},
|
||||
} as never
|
||||
|
||||
const result = resolveConsignmentWorkerDisplay(order)
|
||||
assert.ok(result)
|
||||
assert.equal(result.tagText, '供货打手')
|
||||
assert.equal(result.primaryName, '01邀请的')
|
||||
assert.equal(result.secondaryInfo, '客服 · ID:草莓酱板鸭 (1234551)')
|
||||
})
|
||||
|
||||
test('寄售工单无供货打手名时回退展示角色昵称与现货标签', () => {
|
||||
const order = {
|
||||
material: {
|
||||
consignment: {
|
||||
buyerName: '客服角色',
|
||||
sellerGameId: '1234551',
|
||||
},
|
||||
},
|
||||
} as never
|
||||
|
||||
const result = resolveConsignmentWorkerDisplay(order)
|
||||
assert.ok(result)
|
||||
assert.equal(result.tagText, '内部现货')
|
||||
assert.equal(result.primaryName, '客服角色')
|
||||
assert.equal(result.secondaryInfo, 'ID:1234551')
|
||||
})
|
||||
Reference in New Issue
Block a user