Merge commit '56dfffd12647e1128ac4360aa9a4044361f0f7ab' into dev_jacy
CI / verify (pull_request) Has been cancelled
CI / verify (pull_request) Has been cancelled
This commit is contained in:
@@ -0,0 +1,50 @@
|
|||||||
|
import { Button, Text } from '@tarojs/components';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
export type ContactCsSessionContext = {
|
||||||
|
orderId?: string;
|
||||||
|
orderNo?: string;
|
||||||
|
from?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ContactCsButtonProps = {
|
||||||
|
className?: string;
|
||||||
|
children?: ReactNode;
|
||||||
|
/** 客服会话来源上下文,便于客服后台识别 */
|
||||||
|
session?: ContactCsSessionContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isWeapp = process.env.TARO_ENV === 'weapp';
|
||||||
|
|
||||||
|
/** 组装 session-from(微信限制约 1000 字符) */
|
||||||
|
export function buildCsSessionFrom(session?: ContactCsSessionContext): string {
|
||||||
|
if (!session) return 'dukang|from=mini-user';
|
||||||
|
const parts = ['dukang'];
|
||||||
|
if (session.from) parts.push(`from=${session.from}`);
|
||||||
|
if (session.orderNo) parts.push(`orderNo=${session.orderNo}`);
|
||||||
|
if (session.orderId) parts.push(`orderId=${session.orderId}`);
|
||||||
|
return parts.join('|');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信小程序客服入口(open-type=contact)。
|
||||||
|
* 非 weapp 环境不渲染,由调用方走电话等兜底。
|
||||||
|
*/
|
||||||
|
export default function ContactCsButton({
|
||||||
|
className = '',
|
||||||
|
children = '联系在线客服',
|
||||||
|
session,
|
||||||
|
}: ContactCsButtonProps) {
|
||||||
|
if (!isWeapp) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
className={className}
|
||||||
|
openType="contact"
|
||||||
|
sessionFrom={buildCsSessionFrom(session)}
|
||||||
|
hoverClass="none"
|
||||||
|
>
|
||||||
|
{typeof children === 'string' ? <Text>{children}</Text> : children}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,28 +3,42 @@ import Taro from '@tarojs/taro';
|
|||||||
import { CUSTOMER_SERVICE_PHONE } from '@dukang/shared-types';
|
import { CUSTOMER_SERVICE_PHONE } from '@dukang/shared-types';
|
||||||
import PageShell from '../../components/PageShell';
|
import PageShell from '../../components/PageShell';
|
||||||
import SubPageHeader from '../../components/SubPageHeader';
|
import SubPageHeader from '../../components/SubPageHeader';
|
||||||
|
import ContactCsButton from '../../components/ContactCsButton';
|
||||||
import { toast } from '../../lib/api';
|
import { toast } from '../../lib/api';
|
||||||
|
|
||||||
|
const isWeapp = process.env.TARO_ENV === 'weapp';
|
||||||
const DIAL_PHONE = CUSTOMER_SERVICE_PHONE.replace(/-/g, '');
|
const DIAL_PHONE = CUSTOMER_SERVICE_PHONE.replace(/-/g, '');
|
||||||
|
|
||||||
|
function dialPhone() {
|
||||||
|
Taro.makePhoneCall({ phoneNumber: DIAL_PHONE }).catch(() => toast('无法拨打电话'));
|
||||||
|
}
|
||||||
|
|
||||||
export default function CustomerServicePage() {
|
export default function CustomerServicePage() {
|
||||||
return (
|
return (
|
||||||
<PageShell variant="sub" className="cs-page">
|
<PageShell variant="sub" className="cs-page">
|
||||||
<SubPageHeader title="联系客服" />
|
<SubPageHeader title="联系客服" />
|
||||||
<View className="sub-page-body inset-page cs-body">
|
<View className="sub-page-body inset-page cs-body">
|
||||||
<Text className="cs-title">客服热线</Text>
|
<Text className="cs-brand">杜康好客客服</Text>
|
||||||
<Text className="cs-phone">{CUSTOMER_SERVICE_PHONE}</Text>
|
<Text className="cs-hint">
|
||||||
<Text className="cs-hint">工作时间:9:00 - 21:00</Text>
|
{isWeapp
|
||||||
<View
|
? '点击下方按钮,进入小程序在线客服会话'
|
||||||
className="cs-call-btn"
|
: '请在微信小程序内打开以使用在线客服,或拨打客服电话'}
|
||||||
onClick={() => {
|
</Text>
|
||||||
Taro.makePhoneCall({ phoneNumber: DIAL_PHONE }).catch(() =>
|
<Text className="cs-hours">工作时间:9:00 - 21:00</Text>
|
||||||
toast('无法拨打电话'),
|
|
||||||
);
|
{isWeapp ? (
|
||||||
}}
|
<ContactCsButton className="cs-online-btn" session={{ from: 'customer-service' }} />
|
||||||
>
|
) : null}
|
||||||
<Text>拨打客服电话</Text>
|
|
||||||
|
<View className={isWeapp ? 'cs-phone-link' : 'cs-call-btn'} onClick={dialPhone}>
|
||||||
|
<Text>
|
||||||
|
{isWeapp ? `或拨打客服电话 ${CUSTOMER_SERVICE_PHONE}` : '拨打客服电话'}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{!isWeapp ? (
|
||||||
|
<Text className="cs-phone-display">{CUSTOMER_SERVICE_PHONE}</Text>
|
||||||
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
</PageShell>
|
</PageShell>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import PageShell from '../../components/PageShell';
|
|||||||
import SubPageHeader from '../../components/SubPageHeader';
|
import SubPageHeader from '../../components/SubPageHeader';
|
||||||
import ShareNavButton from '../../components/ShareNavButton';
|
import ShareNavButton from '../../components/ShareNavButton';
|
||||||
import WechatShareReady from '../../components/WechatShareReady';
|
import WechatShareReady from '../../components/WechatShareReady';
|
||||||
|
import ContactCsButton from '../../components/ContactCsButton';
|
||||||
import { request, toast } from '../../lib/api';
|
import { request, toast } from '../../lib/api';
|
||||||
import { buildPayUrl } from '../../lib/checkout-nav';
|
import { buildPayUrl } from '../../lib/checkout-nav';
|
||||||
import { maskPhone } from '../../lib/phone';
|
import { maskPhone } from '../../lib/phone';
|
||||||
@@ -14,6 +15,8 @@ import {
|
|||||||
toWeappShareMessage,
|
toWeappShareMessage,
|
||||||
} from '../../lib/wechat-share';
|
} from '../../lib/wechat-share';
|
||||||
|
|
||||||
|
const isWeapp = process.env.TARO_ENV === 'weapp';
|
||||||
|
|
||||||
type OrderItem = {
|
type OrderItem = {
|
||||||
productName?: string;
|
productName?: string;
|
||||||
productSpec?: string;
|
productSpec?: string;
|
||||||
@@ -104,8 +107,20 @@ export default function OrderDetailPage() {
|
|||||||
Taro.navigateTo({ url: buildPayUrl({ orderId: order.id }) });
|
Taro.navigateTo({ url: buildPayUrl({ orderId: order.id }) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function goCustomerService() {
|
||||||
|
Taro.navigateTo({ url: '/pages/customer-service/index' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageClass = [
|
||||||
|
'order-detail-page',
|
||||||
|
order ? 'order-detail-page--with-actions' : '',
|
||||||
|
canPay ? 'order-detail-page--with-pay' : '',
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageShell variant="sub" className={`order-detail-page${canPay ? ' order-detail-page--with-pay' : ''}`}>
|
<PageShell variant="sub" className={pageClass}>
|
||||||
<WechatShareReady payload={sharePayload} />
|
<WechatShareReady payload={sharePayload} />
|
||||||
<SubPageHeader
|
<SubPageHeader
|
||||||
title="订单详情"
|
title="订单详情"
|
||||||
@@ -173,8 +188,26 @@ export default function OrderDetailPage() {
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{canPay && order && (
|
{order ? (
|
||||||
<View className="pay-bar order-detail-pay-bar">
|
<View className={`order-detail-actionbar${canPay ? ' 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">
|
<View className="order-confirm-total">
|
||||||
<Text className="order-confirm-total-label">待支付</Text>
|
<Text className="order-confirm-total-label">待支付</Text>
|
||||||
<Text className="order-confirm-total-value">
|
<Text className="order-confirm-total-value">
|
||||||
@@ -184,8 +217,10 @@ export default function OrderDetailPage() {
|
|||||||
<View className="order-confirm-submit" onClick={goPay}>
|
<View className="order-confirm-submit" onClick={goPay}>
|
||||||
去付款
|
去付款
|
||||||
</View>
|
</View>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
)}
|
) : null}
|
||||||
</PageShell>
|
</PageShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-detail-page--with-pay .sub-page-body {
|
.order-detail-page--with-pay .sub-page-body,
|
||||||
|
.order-detail-page--with-actions .sub-page-body {
|
||||||
padding-bottom: calc(88px + env(safe-area-inset-bottom, 0px));
|
padding-bottom: calc(88px + env(safe-area-inset-bottom, 0px));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,6 +17,57 @@
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.order-detail-actionbar {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 50;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px var(--space-page) calc(10px + env(safe-area-inset-bottom, 0px));
|
||||||
|
background: var(--color-card, #fff);
|
||||||
|
box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.06);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-detail-actionbar--with-pay {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-detail-cs-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
min-width: 96px;
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 14px;
|
||||||
|
margin: 0;
|
||||||
|
border: 1px solid var(--color-outline, #c8c4be);
|
||||||
|
border-radius: 999px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-on-surface);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.2;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-detail-cs-btn::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-detail-actionbar .order-confirm-total {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-detail-actionbar .order-confirm-submit {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.order-card {
|
.order-card {
|
||||||
background: var(--color-card);
|
background: var(--color-card);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
|
|||||||
@@ -499,27 +499,70 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cs-brand {
|
||||||
|
display: block;
|
||||||
|
font-family: var(--font-headline);
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--color-on-surface);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.cs-title {
|
.cs-title {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: var(--color-subtle-gray);
|
color: var(--color-subtle-gray);
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cs-phone {
|
.cs-phone,
|
||||||
|
.cs-phone-display {
|
||||||
|
display: block;
|
||||||
font-family: var(--font-headline);
|
font-family: var(--font-headline);
|
||||||
font-size: 32px;
|
font-size: 28px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--color-heritage-red);
|
color: var(--color-heritage-red);
|
||||||
margin-bottom: 8px;
|
margin-top: 20px;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cs-hint {
|
.cs-hint {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-subtle-gray);
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
max-width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-hours {
|
||||||
|
display: block;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--color-subtle-gray);
|
color: var(--color-subtle-gray);
|
||||||
margin-bottom: 32px;
|
margin-bottom: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cs-online-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 280px;
|
||||||
|
padding: 14px 24px;
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--color-heritage-red);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.4;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cs-online-btn::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
.cs-call-btn {
|
.cs-call-btn {
|
||||||
padding: 12px 32px;
|
padding: 12px 32px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
@@ -528,3 +571,12 @@
|
|||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cs-phone-link {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-subtle-gray);
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 3px;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user