feat(trade): 仓配可调配履约与三分支推单
CI / verify (pull_request) Has been cancelled

同城有仓按仓库绑定承运商自动推单或自管填单,无仓/跨城走总部快递;新增仓配注册表、FulfillmentService 及三端运单追踪。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-16 17:51:20 +08:00
parent f1dc23c54c
commit f6d97b4ee8
30 changed files with 1615 additions and 57 deletions
+64 -5
View File
@@ -41,6 +41,8 @@ export default function OrderListPage({ tabRoot = false }: OrderListPageProps) {
const [dateFilter, setDateFilter] = useState<DateFilter>('today');
const [statusFilter, setStatusFilter] = useState<StatusFilter>('ALL');
const [shippingId, setShippingId] = useState<string | null>(null);
const [shipForm, setShipForm] = useState({ logisticsCompany: '', trackingNo: '', manualQueryUrl: '' });
const [shipModalId, setShipModalId] = useState<string | null>(null);
useEffect(() => {
document.title = '订单管理';
@@ -64,14 +66,27 @@ export default function OrderListPage({ tabRoot = false }: OrderListPageProps) {
async function handleShip(orderId: string, e: React.MouseEvent) {
e.preventDefault();
e.stopPropagation();
setShippingId(orderId);
setShipModalId(orderId);
setShipForm({ logisticsCompany: '', trackingNo: '', manualQueryUrl: '' });
}
async function submitManualShip() {
if (!shipModalId) return;
if (!shipForm.logisticsCompany.trim() || !shipForm.trackingNo.trim()) {
window.alert('请填写快递公司和运单号');
return;
}
setShippingId(shipModalId);
try {
await request('PARTNER_H5', `/partner/orders/${orderId}/mock-advance-delivery`, {
await request('PARTNER_H5', `/partner/orders/${shipModalId}/manual-ship`, {
method: 'POST',
body: JSON.stringify({ targetStatus: 'OUT_WAREHOUSE' }),
body: JSON.stringify(shipForm),
});
setShipModalId(null);
const next = await request<{ list: Array<Record<string, unknown>> }>('PARTNER_H5', '/partner/orders');
setData(next);
} catch (err) {
window.alert(err instanceof Error ? err.message : '发货失败');
} finally {
setShippingId(null);
}
@@ -137,14 +152,14 @@ export default function OrderListPage({ tabRoot = false }: OrderListPageProps) {
<p className="line-2-clamp">{String(o.receiverAddress || '收货地址')}</p>
</div>
</Link>
{canShip(String(o.status)) && (
{canShip(String(o.status)) && !(o.delivery as { trackingNo?: string } | undefined)?.trackingNo && (
<button
type="button"
className="partner-order-ship-btn"
disabled={shippingId === orderId}
onClick={(e) => void handleShip(orderId, e)}
>
{shippingId === orderId ? '发货中…' : '确认发货'}
</button>
)}
</div>
@@ -162,6 +177,50 @@ export default function OrderListPage({ tabRoot = false }: OrderListPageProps) {
<p className="text-muted body-md text-center"> preV1 </p>
</div>
)}
{shipModalId && (
<div className="partner-ship-modal-backdrop" role="presentation" onClick={() => setShipModalId(null)}>
<div className="partner-ship-modal" role="dialog" onClick={(e) => e.stopPropagation()}>
<h3 className="headline-md"></h3>
<label className="partner-ship-field">
<span></span>
<input
value={shipForm.logisticsCompany}
onChange={(e) => setShipForm((f) => ({ ...f, logisticsCompany: e.target.value }))}
placeholder="如 顺丰速运"
/>
</label>
<label className="partner-ship-field">
<span></span>
<input
value={shipForm.trackingNo}
onChange={(e) => setShipForm((f) => ({ ...f, trackingNo: e.target.value }))}
/>
</label>
<label className="partner-ship-field">
<span></span>
<input
value={shipForm.manualQueryUrl}
onChange={(e) => setShipForm((f) => ({ ...f, manualQueryUrl: e.target.value }))}
placeholder="https://..."
/>
</label>
<div className="partner-ship-actions">
<button type="button" className="partner-btn-secondary" onClick={() => setShipModalId(null)}>
</button>
<button
type="button"
className="partner-btn-primary"
disabled={shippingId === shipModalId}
onClick={() => void submitManualShip()}
>
{shippingId === shipModalId ? '提交中…' : '确认发货'}
</button>
</div>
</div>
</div>
)}
</div>
);
}
+44
View File
@@ -3129,6 +3129,50 @@ body {
justify-content: center;
}
.partner-ship-modal-backdrop {
position: fixed;
inset: 0;
z-index: 200;
background: rgba(0, 0, 0, 0.45);
display: flex;
align-items: flex-end;
justify-content: center;
padding: 16px;
}
.partner-ship-modal {
width: 100%;
max-width: 420px;
background: #fff;
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
padding: 20px;
}
.partner-ship-field {
display: flex;
flex-direction: column;
gap: 6px;
margin-top: 12px;
font-size: 13px;
}
.partner-ship-field input {
padding: 10px 12px;
border: 1px solid var(--color-outline-variant);
border-radius: var(--radius-sm);
}
.partner-ship-actions {
display: flex;
gap: 12px;
margin-top: 20px;
}
.partner-ship-actions .partner-btn-secondary,
.partner-ship-actions .partner-btn-primary {
flex: 1;
}
/* 微信 H5 系统标题已展示:隐藏页内重复标题,保留返回键与操作区 */
.app-page-title {
display: none !important;