847a3f0dc3
Enable online/cross-city/on-site purchase switches with trade gates, HQ and proxy UI, and server-generated DK SKUs. Co-authored-by: Cursor <cursoragent@cursor.com>
122 lines
4.9 KiB
TypeScript
122 lines
4.9 KiB
TypeScript
import { useEffect, useMemo, useState } from 'react';
|
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
|
import PageHeader from '@dukang/shared-ui/PageHeader';
|
|
import { request } from '../lib/api';
|
|
import { toastError } from '../lib/toast';
|
|
import type { PartnerProxyOrderOptions, PartnerProxyOrderProductOption } from '@dukang/shared-types';
|
|
|
|
function fmtMoney(n: number) {
|
|
return n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
}
|
|
|
|
type FulfillmentFilter = 'ALL' | 'ONLINE' | 'CROSS_CITY' | 'ON_SITE';
|
|
|
|
const FILTERS: Array<{ key: FulfillmentFilter; label: string }> = [
|
|
{ key: 'ALL', label: '全部' },
|
|
{ key: 'ONLINE', label: '线上' },
|
|
{ key: 'CROSS_CITY', label: '跨城' },
|
|
{ key: 'ON_SITE', label: '现场' },
|
|
];
|
|
|
|
function matchFilter(p: PartnerProxyOrderProductOption, filter: FulfillmentFilter): boolean {
|
|
if (filter === 'ALL') return true;
|
|
if (filter === 'ONLINE') return p.allowOnlinePurchase !== false;
|
|
if (filter === 'CROSS_CITY') return p.allowCrossCityDelivery !== false;
|
|
return !!p.allowOnSitePickup;
|
|
}
|
|
|
|
export default function ProxyOrderProductsPage() {
|
|
const navigate = useNavigate();
|
|
const [searchParams] = useSearchParams();
|
|
const selectedId = searchParams.get('selected') || '';
|
|
const [products, setProducts] = useState<PartnerProxyOrderProductOption[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [keyword, setKeyword] = useState('');
|
|
const [filter, setFilter] = useState<FulfillmentFilter>('ALL');
|
|
|
|
useEffect(() => {
|
|
request<PartnerProxyOrderOptions>('PARTNER_H5', '/partner/proxy-orders/options')
|
|
.then((data) => setProducts(Array.isArray(data.products) ? data.products : []))
|
|
.catch((e) => toastError(e instanceof Error ? e.message : '加载失败'))
|
|
.finally(() => setLoading(false));
|
|
}, []);
|
|
|
|
const filtered = useMemo(() => {
|
|
const q = keyword.trim().toLowerCase();
|
|
return products.filter((p) => {
|
|
if (!matchFilter(p, filter)) return false;
|
|
if (!q) return true;
|
|
return `${p.name} ${p.spec}`.toLowerCase().includes(q);
|
|
});
|
|
}, [products, keyword, filter]);
|
|
|
|
function pick(product: PartnerProxyOrderProductOption) {
|
|
navigate(`/proxy-order?productId=${encodeURIComponent(product.id)}`, { replace: true });
|
|
}
|
|
|
|
return (
|
|
<div className="page partner-proxy-order-page">
|
|
<PageHeader title="选择酒品" onBack={() => navigate(-1)} />
|
|
<div className="partner-proxy-product-toolbar">
|
|
<input
|
|
className="partner-input"
|
|
placeholder="搜索酒品名称 / 规格"
|
|
value={keyword}
|
|
onChange={(e) => setKeyword(e.target.value)}
|
|
/>
|
|
<div className="partner-proxy-product-filters">
|
|
{FILTERS.map((f) => (
|
|
<button
|
|
key={f.key}
|
|
type="button"
|
|
className={`partner-proxy-product-filter${filter === f.key ? ' is-active' : ''}`}
|
|
onClick={() => setFilter(f.key)}
|
|
>
|
|
{f.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<main className="partner-proxy-product-list">
|
|
{loading ? <p className="label-md text-muted">加载中…</p> : null}
|
|
{!loading && filtered.length === 0 ? (
|
|
<p className="label-md text-muted">暂无符合条件的商品</p>
|
|
) : null}
|
|
{filtered.map((p) => {
|
|
const benefit = p.benefitAmount != null ? Number(p.benefitAmount) : Number(p.price);
|
|
const active = p.id === selectedId;
|
|
return (
|
|
<button
|
|
key={p.id}
|
|
type="button"
|
|
className={`partner-proxy-product-card${active ? ' partner-proxy-product-card--active' : ''}`}
|
|
onClick={() => pick(p)}
|
|
>
|
|
<div className="partner-proxy-product-thumb">
|
|
{p.coverUrl ? (
|
|
<img src={p.coverUrl} alt="" />
|
|
) : (
|
|
<span className="partner-proxy-product-thumb-empty" />
|
|
)}
|
|
</div>
|
|
<div className="partner-proxy-product-main">
|
|
<div className="partner-proxy-product-row">
|
|
<span className="partner-proxy-product-name">{p.name}</span>
|
|
<span className="partner-proxy-product-price">¥{fmtMoney(p.price)}</span>
|
|
</div>
|
|
<p className="partner-proxy-product-spec">{p.spec}</p>
|
|
<p className="partner-proxy-product-benefit">好客权益 ¥{fmtMoney(benefit)}</p>
|
|
<div className="partner-proxy-product-tags">
|
|
{p.allowOnlinePurchase !== false ? <span className="partner-proxy-tag">线上</span> : null}
|
|
{p.allowCrossCityDelivery !== false ? <span className="partner-proxy-tag">跨城</span> : null}
|
|
{p.allowOnSitePickup ? <span className="partner-proxy-tag">现场</span> : null}
|
|
</div>
|
|
</div>
|
|
</button>
|
|
);
|
|
})}
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|