该商品不支持跨城配送,请更换为开城城市内的收货地址
This commit is contained in:
@@ -28,3 +28,18 @@ export function normalizeFulfillmentFlags<T extends FulfillmentFlags>(p: T): T {
|
||||
allowCrossCityDelivery: canCrossCity(p),
|
||||
};
|
||||
}
|
||||
|
||||
/** 与交易侧一致:收货市 ≠ 开城市且 ≠ 郑州 → 跨城 */
|
||||
export function isCrossCityAddress(
|
||||
addressCity: string | null | undefined,
|
||||
openCityName: string | null | undefined,
|
||||
): boolean {
|
||||
const addr = (addressCity || '').trim();
|
||||
const open = (openCityName || '').trim();
|
||||
if (!addr) return false;
|
||||
if (addr === '郑州市') return false;
|
||||
if (open && addr === open) return false;
|
||||
// 尚无开城信息时,非郑州地址先按可能跨城处理(由预览接口最终裁定)
|
||||
if (!open) return addr !== '郑州市';
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ import { tryGetClientGpsLocation } from '../../lib/client-location';
|
||||
import { maskPhone } from '../../lib/phone';
|
||||
import { ensurePayReady } from '../../lib/pay-ready';
|
||||
import { fetchUserProfile } from '../../lib/pay-wechat';
|
||||
import { request } from '../../lib/api';
|
||||
import { request, toast } from '../../lib/api';
|
||||
import { canCrossCity, isCrossCityAddress } from '../../lib/product-fulfillment';
|
||||
import { getProductMainImage } from '../../lib/product-images';
|
||||
|
||||
type Address = {
|
||||
@@ -31,6 +32,8 @@ type PreviewProduct = {
|
||||
price: number;
|
||||
mainImageUrl?: string | null;
|
||||
carouselUrls?: string[] | null;
|
||||
allowCrossCityDelivery?: boolean;
|
||||
allowOnlinePurchase?: boolean;
|
||||
};
|
||||
|
||||
type OrderPreview = {
|
||||
@@ -41,12 +44,18 @@ type OrderPreview = {
|
||||
freightPayType: 'COD' | null;
|
||||
payAmount: number;
|
||||
benefitAmount: number;
|
||||
city?: { localMinQty: number; crossMinQty: number };
|
||||
city?: { name?: string; localMinQty: number; crossMinQty: number };
|
||||
quantityOk?: boolean;
|
||||
quantityMessage?: string | null;
|
||||
addressOk?: boolean;
|
||||
addressMessage?: string | null;
|
||||
minQty?: number;
|
||||
allowCrossCityDelivery?: boolean;
|
||||
allowOnlinePurchase?: boolean;
|
||||
};
|
||||
|
||||
const CROSS_CITY_BLOCK_MSG = '该商品不支持跨城配送,请更换为开城城市内的收货地址';
|
||||
|
||||
function formatAddress(a: Address) {
|
||||
return `${a.province}${a.city}${a.district}${a.detail}`;
|
||||
}
|
||||
@@ -64,6 +73,7 @@ export default function OrderConfirmPage() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [msg, setMsg] = useState('');
|
||||
const phonePromptSkipped = useRef(false);
|
||||
const toastedAddressBlockRef = useRef('');
|
||||
|
||||
useEffect(() => {
|
||||
request<Address[]>('/user/addresses')
|
||||
@@ -94,7 +104,24 @@ export default function OrderConfirmPage() {
|
||||
.then((data) => {
|
||||
if (!cancelled) {
|
||||
setPreview(data);
|
||||
setMsg(data.quantityOk === false ? (data.quantityMessage || '') : '');
|
||||
const nextMsg =
|
||||
data.addressOk === false
|
||||
? data.addressMessage || CROSS_CITY_BLOCK_MSG
|
||||
: data.quantityOk === false
|
||||
? data.quantityMessage || ''
|
||||
: '';
|
||||
setMsg(nextMsg);
|
||||
if (
|
||||
data.addressOk === false &&
|
||||
addressId &&
|
||||
toastedAddressBlockRef.current !== addressId
|
||||
) {
|
||||
toastedAddressBlockRef.current = addressId;
|
||||
toast(data.addressMessage || CROSS_CITY_BLOCK_MSG);
|
||||
}
|
||||
if (data.addressOk !== false) {
|
||||
toastedAddressBlockRef.current = '';
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
@@ -117,12 +144,27 @@ export default function OrderConfirmPage() {
|
||||
[addresses, addressId],
|
||||
);
|
||||
|
||||
const isCross = forceCross || preview?.deliveryType === 'CROSS_CITY';
|
||||
const allowCross =
|
||||
preview?.allowCrossCityDelivery !== undefined
|
||||
? canCrossCity({ allowCrossCityDelivery: preview.allowCrossCityDelivery })
|
||||
: canCrossCity(preview?.product ?? {});
|
||||
const localCross =
|
||||
!!selectedAddress &&
|
||||
isCrossCityAddress(selectedAddress.city, preview?.city?.name);
|
||||
const isCross =
|
||||
forceCross || preview?.deliveryType === 'CROSS_CITY' || localCross;
|
||||
const crossBlocked = isCross && !allowCross;
|
||||
const addressOk = preview ? preview.addressOk !== false && !crossBlocked : !crossBlocked;
|
||||
const minQty =
|
||||
preview?.minQty ??
|
||||
(isCross ? (preview?.city?.crossMinQty ?? 6) : (preview?.city?.localMinQty ?? 2));
|
||||
const quantityOk = preview ? preview.quantityOk !== false && quantity >= minQty : false;
|
||||
const canSubmit = !!addressId && !!preview && quantityOk && !loading && !previewLoading;
|
||||
const canSubmit =
|
||||
!!addressId && !!preview && quantityOk && addressOk && !loading && !previewLoading;
|
||||
|
||||
const addressHint = !addressOk
|
||||
? preview?.addressMessage || CROSS_CITY_BLOCK_MSG
|
||||
: '';
|
||||
|
||||
function updateQuantity(next: number) {
|
||||
if (next < 1) return;
|
||||
@@ -162,6 +204,12 @@ export default function OrderConfirmPage() {
|
||||
setMsg('请选择收货地址');
|
||||
return;
|
||||
}
|
||||
if (!addressOk) {
|
||||
const tip = addressHint || CROSS_CITY_BLOCK_MSG;
|
||||
setMsg(tip);
|
||||
toast(tip);
|
||||
return;
|
||||
}
|
||||
if (!quantityOk) {
|
||||
setMsg(
|
||||
isCross
|
||||
@@ -219,9 +267,12 @@ export default function OrderConfirmPage() {
|
||||
? '提交中…'
|
||||
: !addressId
|
||||
? '请选择地址'
|
||||
: !quantityOk
|
||||
? `至少购买 ${minQty} 瓶`
|
||||
: '提交订单';
|
||||
: !addressOk
|
||||
? '请更换地址'
|
||||
: !quantityOk
|
||||
? `至少购买 ${minQty} 瓶`
|
||||
: '提交订单';
|
||||
const displayMsg = msg || addressHint;
|
||||
|
||||
return (
|
||||
<PageShell variant="sub" className="order-confirm-page" hasFixedFooter>
|
||||
@@ -254,7 +305,13 @@ export default function OrderConfirmPage() {
|
||||
)}
|
||||
</View>
|
||||
|
||||
{isCross ? (
|
||||
{!addressOk && addressId ? (
|
||||
<View className="order-card order-card--warn">
|
||||
<Text className="order-warn-text">{addressHint || CROSS_CITY_BLOCK_MSG}</Text>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{isCross && addressOk ? (
|
||||
<View className="order-card">
|
||||
<Text className="u-muted">
|
||||
该地址超出同城配送范围,将由总部物流发货,运费到付
|
||||
@@ -333,9 +390,9 @@ export default function OrderConfirmPage() {
|
||||
{!previewLoading && !preview && productId ? (
|
||||
<View className="u-empty">无法加载商品信息</View>
|
||||
) : null}
|
||||
{msg ? (
|
||||
<Text className="u-muted" style={{ display: 'block', marginTop: 8, textAlign: 'center' }}>
|
||||
{msg}
|
||||
{displayMsg ? (
|
||||
<Text className="order-warn-text" style={{ display: 'block', marginTop: 8, textAlign: 'center' }}>
|
||||
{displayMsg}
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
@@ -81,6 +81,19 @@
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
|
||||
.order-card--warn {
|
||||
background: rgba(166, 29, 36, 0.06);
|
||||
box-shadow: none;
|
||||
border: 1px solid rgba(166, 29, 36, 0.15);
|
||||
}
|
||||
|
||||
.order-warn-text {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
color: var(--color-heritage-red);
|
||||
}
|
||||
|
||||
.order-card-title {
|
||||
display: block;
|
||||
font-family: var(--font-headline);
|
||||
|
||||
Reference in New Issue
Block a user