This commit is contained in:
2026-06-30 10:33:56 +08:00
commit 6e047dc0a5
607 changed files with 65966 additions and 0 deletions
@@ -0,0 +1,179 @@
import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import PageHeader from '@dukang/shared-ui/PageHeader';
import { request } from '../lib/api';
const TIMELINE = [
{ key: 'confirm', label: '待确认' },
{ key: 'accepted', label: '已接单' },
{ key: 'delivering', label: '配送中' },
{ key: 'shipping', label: '运输中', desc: '包裹正在送往目的地' },
{ key: 'done', label: '已送达' },
];
function statusIndex(status: string) {
const s = String(status).toUpperCase();
if (s === 'COMPLETED') return 4;
if (s.includes('SHIP') || s === 'OUT_WAREHOUSE') return 3;
if (s === 'PENDING_RECEIVE') return 3;
if (s.includes('DELIVER')) return 2;
return 1;
}
function statusBanner(status: string) {
const s = String(status).toUpperCase();
if (s.includes('SHIP') || s === 'OUT_WAREHOUSE') return { title: '运输中', desc: '预计今日 18:00 前送达', icon: 'local_shipping' };
if (s === 'COMPLETED') return { title: '已完成', desc: '订单已送达', icon: 'check_circle' };
if (s.includes('PENDING')) return { title: '待发货', desc: '商家正在备货', icon: 'inventory_2' };
return { title: status, desc: '', icon: 'receipt_long' };
}
export default function OrderDetailPage() {
const { id } = useParams();
const navigate = useNavigate();
const [order, setOrder] = useState<Record<string, unknown> | null>(null);
useEffect(() => {
if (id) request('PARTNER_H5', `/partner/orders/${id}`).then(setOrder);
}, [id]);
async function advance(status: string) {
await request('PARTNER_H5', `/partner/orders/${id}/mock-advance-delivery`, {
method: 'POST',
body: JSON.stringify({ targetStatus: status }),
});
if (id) request('PARTNER_H5', `/partner/orders/${id}`).then(setOrder);
}
if (!order) return <div className="empty">...</div>;
const banner = statusBanner(String(order.status));
const currentIdx = statusIndex(String(order.status));
const payAmount = Number(order.payAmount || 0);
return (
<div className="partner-order-detail">
<PageHeader title="订单详情" onBack={() => navigate('/orders')} />
<section className="partner-status-banner">
<div style={{ position: 'relative', zIndex: 1 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
<span className="material-symbols-outlined" style={{ fontVariationSettings: "'FILL' 1" }}>{banner.icon}</span>
<h2 className="headline-md" style={{ color: '#fff' }}>{banner.title}</h2>
</div>
{banner.desc && <p className="body-md" style={{ color: 'rgba(255,255,255,0.9)' }}>{banner.desc}</p>}
</div>
</section>
<section className="partner-timeline">
<div className="partner-timeline-line" />
{TIMELINE.map((item, i) => {
const done = i < currentIdx;
const current = i === currentIdx;
const pending = i > currentIdx;
return (
<div key={item.key} className="partner-timeline-item">
<div className={`partner-timeline-dot${done ? ' partner-timeline-dot--done' : ''}${current ? ' partner-timeline-dot--current' : ''}${pending ? ' partner-timeline-dot--pending' : ''}`}>
{done && <span className="material-symbols-outlined">check</span>}
</div>
<div>
<p className={current ? 'headline-md text-primary' : 'label-md'} style={{ fontWeight: current ? 700 : 500 }}>{item.label}</p>
{item.desc && current && <p className="text-muted body-md" style={{ marginTop: 4 }}>{item.desc}</p>}
</div>
</div>
);
})}
</section>
<section className="partner-detail-section partner-detail-section--accent">
<div style={{ display: 'flex', gap: 16 }}>
<div className="partner-order-product-img" style={{ width: 96, height: 96, position: 'relative' }}>
<div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--color-surface-container)' }}>
<span className="material-symbols-outlined text-muted" style={{ fontSize: 32 }}>liquor</span>
</div>
<div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, background: 'rgba(166,29,36,0.8)', textAlign: 'center', padding: '2px 0' }}>
<span className="label-md" style={{ color: '#fff', fontSize: 10 }}></span>
</div>
</div>
<div style={{ flex: 1 }}>
<h3 className="headline-md">{String(order.productName || '杜康好酒')}</h3>
<p className="text-muted body-md" style={{ marginTop: 4 }}>x{Number(order.quantity || 1)}</p>
<div style={{ marginTop: 8, display: 'flex', alignItems: 'baseline', gap: 2 }}>
<span className="label-md text-primary">¥</span>
<span className="amount-lg">{payAmount.toFixed(2)}</span>
</div>
</div>
</div>
</section>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, margin: '0 20px' }}>
<div className="partner-detail-section" style={{ margin: 0, borderTop: '2px solid var(--color-heritage-red)' }}>
<p className="label-md text-muted" style={{ marginBottom: 8 }}></p>
<div className="partner-info-row">
<span className="label-md text-muted"></span>
<span className="text-primary" style={{ fontWeight: 700, fontSize: 12 }}>¥{(payAmount * 0.04).toFixed(2)}</span>
</div>
<div className="partner-info-row">
<span className="label-md text-muted"></span>
<span className="text-primary" style={{ fontWeight: 700, fontSize: 12 }}>¥{(payAmount * 0.04).toFixed(2)}</span>
</div>
</div>
<div className="partner-detail-section" style={{ margin: 0, borderTop: '2px solid var(--color-aged-amber)' }}>
<p className="label-md text-muted" style={{ marginBottom: 4 }}></p>
<span className="amount-lg" style={{ color: 'var(--color-aged-amber)' }}>¥{Math.round(payAmount * 0.2)}</span>
</div>
</div>
<section className="partner-detail-section">
<h4 className="headline-md" style={{ marginBottom: 16, display: 'flex', alignItems: 'center', gap: 8 }}>
<span style={{ width: 4, height: 16, background: 'var(--color-heritage-red)', borderRadius: 2 }} />
</h4>
<div className="partner-info-row">
<span className="text-muted body-md"></span>
<span className="body-md">{String(order.orderNo)}</span>
</div>
<div className="partner-info-row">
<span className="text-muted body-md"></span>
<span className="body-md">{String(order.status)}</span>
</div>
</section>
<section className="partner-detail-section">
<h4 className="headline-md" style={{ marginBottom: 16, display: 'flex', alignItems: 'center', gap: 8 }}>
<span style={{ width: 4, height: 16, background: 'var(--color-heritage-red)', borderRadius: 2 }} />
</h4>
<div style={{ display: 'flex', gap: 12 }}>
<span className="material-symbols-outlined text-muted">location_on</span>
<div>
<p className="headline-md" style={{ fontSize: 16 }}>
{String(order.receiverName)} <span className="body-md text-muted" style={{ fontWeight: 400 }}>{String(order.receiverPhone)}</span>
</p>
<p className="text-muted body-md" style={{ marginTop: 4, lineHeight: 1.5 }}>{String(order.receiverAddress)}</p>
</div>
</div>
</section>
<footer className="partner-order-footer">
<div>
<span className="label-md text-muted"></span>
<p className="headline-md text-primary" style={{ fontWeight: 700 }}>¥{(payAmount * 0.08).toFixed(2)}</p>
</div>
<button type="button" className="btn btn-outline" style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 24px' }}>
<span className="material-symbols-outlined" style={{ fontSize: 18 }}>support_agent</span>
</button>
</footer>
{import.meta.env.DEV && (
<details className="partner-dev-tools">
<summary>Dev: Mock </summary>
{['OUT_WAREHOUSE', 'SHIPPING', 'PENDING_RECEIVE', 'COMPLETED'].map((s) => (
<button key={s} type="button" onClick={() => advance(s)}>{s}</button>
))}
</details>
)}
</div>
);
}