feat: 技术支持工单/企微权限/开发版本管理/消息推送等迭代
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationStyle: 'custom',
|
||||
navigationBarTitleText: '订单详情',
|
||||
enableShareAppMessage: true,
|
||||
enableShareTimeline: true,
|
||||
});
|
||||
@@ -1,313 +0,0 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { View, Text } from '@tarojs/components';
|
||||
import Taro, { useDidShow, useRouter, useShareAppMessage, useShareTimeline } from '@tarojs/taro';
|
||||
import PageShell from '../../components/PageShell';
|
||||
import SubPageHeader from '../../components/SubPageHeader';
|
||||
import ShareNavButton from '../../components/ShareNavButton';
|
||||
import WechatShareReady from '../../components/WechatShareReady';
|
||||
import ContactCsButton from '../../components/ContactCsButton';
|
||||
import { request, toast } from '../../lib/api';
|
||||
import { buildPayUrl } from '../../lib/checkout-nav';
|
||||
import { maskPhone } from '../../lib/phone';
|
||||
import { confirmOrderReceive, type WechatConfirmPayload } from '../../lib/wechat-order-confirm';
|
||||
import {
|
||||
DEFAULT_SHARE_DESC,
|
||||
DEFAULT_SHARE_TITLE,
|
||||
toWeappShareMessage,
|
||||
} from '../../lib/wechat-share';
|
||||
import { usePageView } from '../../lib/usePageView';
|
||||
|
||||
const isWeapp = process.env.TARO_ENV === 'weapp';
|
||||
|
||||
type OrderItem = {
|
||||
productName?: string;
|
||||
productSpec?: string;
|
||||
quantity?: number;
|
||||
};
|
||||
|
||||
type OrderDetail = {
|
||||
id: string;
|
||||
orderNo?: string;
|
||||
status?: string;
|
||||
payAmount?: number;
|
||||
productName?: string;
|
||||
quantity?: number;
|
||||
qty?: number;
|
||||
receiverName?: string;
|
||||
receiverPhone?: string;
|
||||
receiverProvince?: string;
|
||||
receiverCity?: string;
|
||||
receiverDistrict?: string;
|
||||
receiverAddress?: string;
|
||||
createdAt?: string;
|
||||
originOrderId?: string | null;
|
||||
orderType?: string;
|
||||
isProxyOrder?: boolean;
|
||||
proxyPartnerName?: string | null;
|
||||
items?: OrderItem[];
|
||||
wechatConfirm?: WechatConfirmPayload | null;
|
||||
};
|
||||
|
||||
const STATUS_LABELS: Record<string, string> = {
|
||||
PENDING_PAY: '待付款',
|
||||
PENDING_SHIP: '待发货',
|
||||
OUT_WAREHOUSE: '出库中',
|
||||
SHIPPING: '配送中',
|
||||
SHIPPED: '配送中',
|
||||
PENDING_RECEIVE: '待签收',
|
||||
DELIVERED: '待签收',
|
||||
COMPLETED: '已完成',
|
||||
CANCELLED: '已取消',
|
||||
REFUNDING: '退款中',
|
||||
REFUNDED: '已退款',
|
||||
};
|
||||
|
||||
function fullReceiverAddress(order: OrderDetail) {
|
||||
const detail = (order.receiverAddress || '').trim();
|
||||
const region = [order.receiverProvince, order.receiverCity, order.receiverDistrict]
|
||||
.filter(Boolean)
|
||||
.join('');
|
||||
if (!region && !detail) return '';
|
||||
if (region && detail.startsWith(region)) return detail;
|
||||
return `${region}${detail}`;
|
||||
}
|
||||
|
||||
export default function OrderDetailPage() {
|
||||
const router = useRouter();
|
||||
const orderId = router.params.id ?? '';
|
||||
usePageView('order_detail_view', orderId ? { orderId } : undefined);
|
||||
const [order, setOrder] = useState<OrderDetail | null>(null);
|
||||
const [confirming, setConfirming] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!orderId) return;
|
||||
request<OrderDetail>(`/trade/orders/${orderId}`)
|
||||
.then(setOrder)
|
||||
.catch((e) => toast(e instanceof Error ? e.message : '加载失败'));
|
||||
}, [orderId]);
|
||||
|
||||
useDidShow(() => {
|
||||
if (!orderId) return;
|
||||
// 从微信确认收货组件返回后刷新
|
||||
request<OrderDetail>(`/trade/orders/${orderId}`)
|
||||
.then(setOrder)
|
||||
.catch(() => {});
|
||||
});
|
||||
|
||||
const isReship = !!order?.originOrderId;
|
||||
const isProxy = !!order && (order.isProxyOrder || order.orderType === 'PROXY');
|
||||
const canPay = !!order && order.status === 'PENDING_PAY' && !isReship;
|
||||
const canConfirmReceive =
|
||||
!!order && !isReship && !isProxy && ['PENDING_RECEIVE', 'DELIVERED'].includes(order.status || '');
|
||||
|
||||
const item = order?.items?.[0];
|
||||
const productName = item?.productName || order?.productName || '杜康商品';
|
||||
const quantity = item?.quantity ?? order?.quantity ?? order?.qty ?? 1;
|
||||
const addressText = order ? fullReceiverAddress(order) : '';
|
||||
const receiverLine = order
|
||||
? [order.receiverName, order.receiverPhone ? maskPhone(String(order.receiverPhone)) : '']
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
: '';
|
||||
|
||||
const sharePayload = useMemo(
|
||||
() => ({
|
||||
title: productName !== '杜康商品' ? `我买了${productName} · 杜康好客` : DEFAULT_SHARE_TITLE,
|
||||
desc: DEFAULT_SHARE_DESC,
|
||||
path: orderId ? `/pages/order-detail/index?id=${orderId}` : '/pages/home/index',
|
||||
}),
|
||||
[productName, orderId],
|
||||
);
|
||||
|
||||
useShareAppMessage(() => toWeappShareMessage(sharePayload));
|
||||
useShareTimeline(() => ({
|
||||
title: sharePayload.title || DEFAULT_SHARE_TITLE,
|
||||
query: orderId ? `id=${orderId}` : '',
|
||||
}));
|
||||
|
||||
function goPay() {
|
||||
if (!order) return;
|
||||
Taro.navigateTo({ url: buildPayUrl({ orderId: order.id }) });
|
||||
}
|
||||
|
||||
function goCustomerService() {
|
||||
Taro.navigateTo({ url: '/pages/customer-service/index' });
|
||||
}
|
||||
|
||||
async function confirmReceive() {
|
||||
if (!order || !canConfirmReceive || confirming) return;
|
||||
|
||||
const { confirm } = await Taro.showModal({
|
||||
title: '确认收货?',
|
||||
content: isWeapp
|
||||
? '将打开微信确认收货,完成后订单即完结,无需再点服务通知。'
|
||||
: '请确认已收到商品。确认后订单将完成,好客权益可正常使用。',
|
||||
confirmText: '确认收货',
|
||||
cancelText: '再想想',
|
||||
});
|
||||
if (!confirm) return;
|
||||
|
||||
setConfirming(true);
|
||||
try {
|
||||
const mode = await confirmOrderReceive({
|
||||
orderId: order.id,
|
||||
wechatConfirm: order.wechatConfirm,
|
||||
onLocalSuccess: async () => {
|
||||
const updated = await request<OrderDetail>(`/trade/orders/${order.id}`);
|
||||
setOrder(updated);
|
||||
toast('已确认收货');
|
||||
},
|
||||
});
|
||||
if (mode === 'wechat') {
|
||||
// 回跳后由 App.onShow / 本页 useDidShow 处理
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
toast(e instanceof Error ? e.message : '确认收货失败');
|
||||
} finally {
|
||||
setConfirming(false);
|
||||
}
|
||||
}
|
||||
|
||||
const pageClass = [
|
||||
'order-detail-page',
|
||||
order ? 'order-detail-page--with-actions' : '',
|
||||
canPay || canConfirmReceive ? 'order-detail-page--with-pay' : '',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
|
||||
return (
|
||||
<PageShell variant="sub" className={pageClass}>
|
||||
<WechatShareReady payload={sharePayload} />
|
||||
<SubPageHeader
|
||||
title="订单详情"
|
||||
onBack={() => {
|
||||
// 支付完成后 reLaunch 进详情:栈仅一页时 navigateBack 会退出小程序,统一回首页
|
||||
const fromPay = String(router.params.from || '') === 'pay';
|
||||
if (fromPay || Taro.getCurrentPages().length <= 1) {
|
||||
Taro.switchTab({ url: '/pages/home/index' }).catch(() => {
|
||||
Taro.reLaunch({ url: '/pages/home/index' });
|
||||
});
|
||||
return;
|
||||
}
|
||||
Taro.navigateBack();
|
||||
}}
|
||||
right={<ShareNavButton payload={sharePayload} />}
|
||||
/>
|
||||
<View className="sub-page-body">
|
||||
{!order ? (
|
||||
<View className="u-empty">加载中…</View>
|
||||
) : (
|
||||
<>
|
||||
<View className="order-card">
|
||||
<Text className="order-card-title">订单状态</Text>
|
||||
<View className="order-status-row">
|
||||
<Text className="order-list-status">
|
||||
{STATUS_LABELS[order.status || ''] || order.status || '处理中'}
|
||||
</Text>
|
||||
{isProxy ? <Text className="order-proxy-badge">代下单</Text> : null}
|
||||
</View>
|
||||
{isProxy && order.proxyPartnerName ? (
|
||||
<Text className="order-proxy-hint">由合伙人 {order.proxyPartnerName} 代下</Text>
|
||||
) : null}
|
||||
</View>
|
||||
<View className="order-card">
|
||||
<Text className="order-card-title">商品信息</Text>
|
||||
<View className="order-row">
|
||||
<Text className="order-row-label">{productName}</Text>
|
||||
<Text className="order-row-value">x{quantity}</Text>
|
||||
</View>
|
||||
<View className="order-row">
|
||||
<Text className="order-row-label">实付金额</Text>
|
||||
<Text className="order-row-value--price">
|
||||
¥{Number(order.payAmount ?? 0).toFixed(2)}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View className="order-card">
|
||||
<Text className="order-card-title">收货信息</Text>
|
||||
{receiverLine || addressText ? (
|
||||
<>
|
||||
{receiverLine ? (
|
||||
<View className="order-row">
|
||||
<Text className="order-row-label">收货人</Text>
|
||||
<Text className="order-row-value">{receiverLine}</Text>
|
||||
</View>
|
||||
) : null}
|
||||
{addressText ? (
|
||||
<View className="order-row order-row--address">
|
||||
<Text className="order-row-label">收货地址</Text>
|
||||
<Text className="order-row-value order-row-value--wrap">{addressText}</Text>
|
||||
</View>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<Text className="u-muted">地址信息待完善</Text>
|
||||
)}
|
||||
</View>
|
||||
<View className="order-card">
|
||||
<Text className="order-card-title">订单信息</Text>
|
||||
<View className="order-row">
|
||||
<Text className="order-row-label">订单编号</Text>
|
||||
<Text className="order-row-value">{order.orderNo || order.id}</Text>
|
||||
</View>
|
||||
<View className="order-row">
|
||||
<Text className="order-row-label">下单时间</Text>
|
||||
<Text className="order-row-value">
|
||||
{order.createdAt ? String(order.createdAt).slice(0, 19).replace('T', ' ') : '-'}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{order ? (
|
||||
<View
|
||||
className={`order-detail-actionbar${
|
||||
canPay || canConfirmReceive ? ' order-detail-actionbar--with-pay' : ''
|
||||
}`}
|
||||
>
|
||||
{isWeapp ? (
|
||||
<ContactCsButton
|
||||
className="order-detail-cs-btn"
|
||||
session={{
|
||||
from: 'order-detail',
|
||||
orderId: order.id,
|
||||
orderNo: order.orderNo,
|
||||
}}
|
||||
>
|
||||
联系客服
|
||||
</ContactCsButton>
|
||||
) : (
|
||||
<View className="order-detail-cs-btn" onClick={goCustomerService}>
|
||||
<Text>联系客服</Text>
|
||||
</View>
|
||||
)}
|
||||
{canPay ? (
|
||||
<>
|
||||
<View className="order-confirm-total">
|
||||
<Text className="order-confirm-total-label">待支付</Text>
|
||||
<Text className="order-confirm-total-value">
|
||||
¥{Number(order.payAmount ?? 0).toFixed(2)}
|
||||
</Text>
|
||||
</View>
|
||||
<View className="order-confirm-submit" onClick={goPay}>
|
||||
去付款
|
||||
</View>
|
||||
</>
|
||||
) : null}
|
||||
{canConfirmReceive ? (
|
||||
<View
|
||||
className={`order-confirm-submit${confirming ? ' order-confirm-submit--disabled' : ''}`}
|
||||
onClick={confirming ? undefined : () => void confirmReceive()}
|
||||
>
|
||||
{confirming ? '提交中…' : '确认收货'}
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
) : null}
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user