Files
dukang/apps/mini-user/src/pages/addresses/index.tsx
T
jacy c6b01def4c
CI / verify (push) Waiting to run
feat(trade): v4.0.13 收货禁全市与小飞侠拒单可感知
拦截伪区县写入与下单;推单失败挂 fulfillmentHold,HQ 可见超区/推单失败。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 17:20:40 +08:00

162 lines
5.3 KiB
TypeScript

import { useCallback, useState } from 'react';
import { View, Text } from '@tarojs/components';
import '../../styles/address.css';
import Taro, { useDidShow, usePullDownRefresh, useRouter } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
import {
buildAddressEditUrl,
buildOrderConfirmUrl,
readCheckoutContext,
} from '../../lib/checkout-nav';
import { isDirtyShippingAddress } from '../../lib/shipping-address';
import { request, toast } from '../../lib/api';
type Address = {
id: string;
receiverName: string;
phone: string;
province: string;
city: string;
district: string;
detail: string;
isDefault?: number | boolean;
};
function formatAddress(a: Address) {
return `${a.province}${a.city}${a.district}${a.detail}`;
}
export default function AddressesPage() {
const router = useRouter();
const checkoutCtx = readCheckoutContext(router.params);
const selectMode = checkoutCtx.select === true;
const [list, setList] = useState<Address[]>([]);
const [loading, setLoading] = useState(true);
const loadList = useCallback(() => {
setLoading(true);
request<Address[]>('/user/addresses')
.then((data) => setList(Array.isArray(data) ? data : []))
.catch(() => setList([]))
.finally(() => setLoading(false));
}, []);
useDidShow(() => {
loadList();
});
usePullDownRefresh(() => {
void Promise.resolve(loadList()).finally(() => Taro.stopPullDownRefresh());
});
function selectAddress(addr: Address) {
if (!selectMode) return;
if (isDirtyShippingAddress(addr)) {
toast('该地址缺少具体区县,请先完善');
Taro.navigateTo({ url: buildAddressEditUrl(addr.id, checkoutCtx) }).catch(() => {});
return;
}
Taro.redirectTo({
url: buildOrderConfirmUrl({
productId: checkoutCtx.productId,
qty: checkoutCtx.qty,
addressId: addr.id,
cross: checkoutCtx.cross,
}),
});
}
async function removeAddress(id: string) {
const res = await Taro.showModal({
title: '删除地址',
content: '确定删除该收货地址吗?',
});
if (!res.confirm) return;
try {
await request(`/user/addresses/${id}`, { method: 'DELETE' });
toast('已删除', 'success');
loadList();
} catch (e) {
toast(e instanceof Error ? e.message : '删除失败');
}
}
const sorted = [...list].sort((a, b) => {
const da = isDirtyShippingAddress(a) ? 0 : 1;
const db = isDirtyShippingAddress(b) ? 0 : 1;
return da - db;
});
return (
<PageShell variant="sub" className="address-page" hasFixedFooter>
<SubPageHeader title={selectMode ? '选择收货地址' : '地址管理'} />
<View className="sub-page-body" style={{ paddingBottom: 80 }}>
{loading ? <View className="u-empty">加载中…</View> : null}
{!loading && list.length === 0 ? (
<View className="u-empty">暂无收货地址</View>
) : null}
{sorted.map((a) => {
const dirty = isDirtyShippingAddress(a);
return (
<View
key={a.id}
className={`address-item${dirty ? ' address-item--dirty' : ''}`}
onClick={() => selectAddress(a)}
>
<View className="address-item-head">
<Text className="address-item-name">{a.receiverName}</Text>
<Text className="address-item-phone">{a.phone}</Text>
{dirty ? <Text className="address-need-fix-tag">需完善</Text> : null}
{a.isDefault === 1 || a.isDefault === true ? (
<Text className="address-default-tag">默认</Text>
) : null}
</View>
<Text className="address-item-detail">{formatAddress(a)}</Text>
{dirty ? (
<Text className="address-item-warn">缺少具体区县,同城配送可能失败,请编辑完善</Text>
) : null}
{!selectMode || dirty ? (
<View className="address-item-actions">
<Text
className="address-action"
onClick={(e) => {
e.stopPropagation();
Taro.navigateTo({ url: buildAddressEditUrl(a.id, checkoutCtx) }).catch((err) => {
toast(err instanceof Error ? err.message : '无法打开编辑页');
});
}}
>
{dirty ? '去完善' : '编辑'}
</Text>
{!selectMode ? (
<Text
className="address-action"
onClick={(e) => {
e.stopPropagation();
void removeAddress(a.id);
}}
>
删除
</Text>
) : null}
</View>
) : null}
</View>
);
})}
</View>
<View
className="address-fab"
onClick={() => {
Taro.navigateTo({ url: buildAddressEditUrl(undefined, checkoutCtx) }).catch((e) => {
toast(e instanceof Error ? e.message : '无法打开新增地址页');
});
}}
>
<Text>新增地址</Text>
</View>
</PageShell>
);
}