feat: 客服可修改工单,商品名双击编辑,订单列显示来源店铺
- 工单编辑接口放开 support 角色,客服可双击改发单金额/押金及商品名 - 订单列商品名支持双击原地修改(单击看详情不变) - 订单列新增来源/店铺展示;Mock 工单补充店铺信息并回填存量数据
This commit is contained in:
@@ -325,7 +325,7 @@ router.post(
|
|||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
'/worker-platform/orders/:workOrderId',
|
'/worker-platform/orders/:workOrderId',
|
||||||
requireAdminRoles(['admin', 'operator']),
|
requireAdminRoles(['admin', 'operator', 'support']),
|
||||||
createJsonHandler(
|
createJsonHandler(
|
||||||
(req) => updateAdminWorkOrder(String(req.params.workOrderId || ''), req.body || {}),
|
(req) => updateAdminWorkOrder(String(req.params.workOrderId || ''), req.body || {}),
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -834,6 +834,9 @@ export async function createAdminMockWorkOrder(payload: JsonObject = {}) {
|
|||||||
orderAmountFen: paymentFen,
|
orderAmountFen: paymentFen,
|
||||||
syncSource: 'mock',
|
syncSource: 'mock',
|
||||||
mock: true,
|
mock: true,
|
||||||
|
provider: 'mock',
|
||||||
|
platform: 'mock',
|
||||||
|
shopName: '模拟店铺',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
const workOrder = await createWorkOrder({
|
const workOrder = await createWorkOrder({
|
||||||
|
|||||||
@@ -483,24 +483,38 @@ export default function WorkOrdersPanel() {
|
|||||||
{
|
{
|
||||||
title: '订单',
|
title: '订单',
|
||||||
minWidth: 300,
|
minWidth: 300,
|
||||||
render: (_, row) => (
|
render: (_, row) => {
|
||||||
<div className="cell-stack">
|
const source = asRecord(row.material?.source)
|
||||||
<Typography.Link onClick={() => openDetailDrawer(row)}>
|
const shopName = String(source.shopName || '').trim()
|
||||||
{row.productName || row.workOrderNo}
|
const provider = String(source.provider || '').trim()
|
||||||
</Typography.Link>
|
return (
|
||||||
<Space size={[6, 6]} wrap>
|
<div className="cell-stack">
|
||||||
{row.categoryName ? <Tag>{row.categoryName}</Tag> : null}
|
<ProductNameEditableCell
|
||||||
{row.orderId ? (
|
order={row}
|
||||||
<Typography.Text type="secondary">
|
onOpenDetail={() => openDetailDrawer(row)}
|
||||||
源订单 #{row.orderId}
|
onSaved={refreshAll}
|
||||||
|
/>
|
||||||
|
<Space size={[6, 6]} wrap>
|
||||||
|
{row.categoryName ? <Tag>{row.categoryName}</Tag> : null}
|
||||||
|
{row.orderId ? (
|
||||||
|
<Typography.Text type="secondary">
|
||||||
|
源订单 #{row.orderId}
|
||||||
|
</Typography.Text>
|
||||||
|
) : null}
|
||||||
|
</Space>
|
||||||
|
{shopName || provider ? (
|
||||||
|
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||||
|
{provider ? `来源:${provider}` : ''}
|
||||||
|
{provider && shopName ? ' · ' : ''}
|
||||||
|
{shopName ? `店铺:${shopName}` : ''}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
) : null}
|
) : null}
|
||||||
</Space>
|
<Typography.Text type="secondary">
|
||||||
<Typography.Text type="secondary">
|
{row.platformOrderId} · {row.workOrderNo}
|
||||||
{row.platformOrderId} · {row.workOrderNo}
|
</Typography.Text>
|
||||||
</Typography.Text>
|
</div>
|
||||||
</div>
|
)
|
||||||
),
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '用户付款金额',
|
title: '用户付款金额',
|
||||||
@@ -1438,6 +1452,92 @@ function getUserPaymentFen(order: WorkOrder): number | null {
|
|||||||
return Number.isFinite(paymentFen) ? paymentFen : null
|
return Number.isFinite(paymentFen) ? paymentFen : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 商品名:单击看详情,双击原地修改(仅未接走工单) */
|
||||||
|
function ProductNameEditableCell({
|
||||||
|
order,
|
||||||
|
onOpenDetail,
|
||||||
|
onSaved,
|
||||||
|
}: {
|
||||||
|
order: WorkOrder
|
||||||
|
onOpenDetail: () => void
|
||||||
|
onSaved: () => Promise<void> | void
|
||||||
|
}) {
|
||||||
|
const { message } = App.useApp()
|
||||||
|
const [editing, setEditing] = useState(false)
|
||||||
|
const [draft, setDraft] = useState('')
|
||||||
|
const savingRef = useRef(false)
|
||||||
|
const clickTimerRef = useRef<number | null>(null)
|
||||||
|
|
||||||
|
const editable =
|
||||||
|
['pending_material', 'unassigned', 'open'].includes(order.status) && !order.worker
|
||||||
|
|
||||||
|
function handleClick() {
|
||||||
|
if (clickTimerRef.current) window.clearTimeout(clickTimerRef.current)
|
||||||
|
clickTimerRef.current = window.setTimeout(() => onOpenDetail(), 260)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDoubleClick() {
|
||||||
|
if (clickTimerRef.current) {
|
||||||
|
window.clearTimeout(clickTimerRef.current)
|
||||||
|
clickTimerRef.current = null
|
||||||
|
}
|
||||||
|
if (!editable) return
|
||||||
|
setDraft(order.productName || order.workOrderNo)
|
||||||
|
setEditing(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
if (savingRef.current) return
|
||||||
|
const next = draft.trim()
|
||||||
|
if (!next) {
|
||||||
|
message.error('商品名不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (next === order.productName) {
|
||||||
|
setEditing(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
savingRef.current = true
|
||||||
|
try {
|
||||||
|
await updateAdminWorkOrder(order.workOrderId, { productName: next })
|
||||||
|
message.success('商品名已更新')
|
||||||
|
setEditing(false)
|
||||||
|
await onSaved()
|
||||||
|
} catch (error) {
|
||||||
|
message.error(error instanceof Error ? error.message : '保存失败')
|
||||||
|
} finally {
|
||||||
|
savingRef.current = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!editing) {
|
||||||
|
return (
|
||||||
|
<Typography.Link
|
||||||
|
title={editable ? '双击修改商品名' : undefined}
|
||||||
|
onClick={handleClick}
|
||||||
|
onDoubleClick={handleDoubleClick}
|
||||||
|
>
|
||||||
|
{order.productName || order.workOrderNo}
|
||||||
|
</Typography.Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
autoFocus
|
||||||
|
size="small"
|
||||||
|
style={{ width: 200 }}
|
||||||
|
value={draft}
|
||||||
|
onChange={(event) => setDraft(event.target.value)}
|
||||||
|
onPressEnter={() => void save()}
|
||||||
|
onBlur={() => void save()}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === 'Escape') setEditing(false)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function AmountEditableCell({
|
function AmountEditableCell({
|
||||||
order,
|
order,
|
||||||
field,
|
field,
|
||||||
|
|||||||
Reference in New Issue
Block a user