From 56dfffd12647e1128ac4360aa9a4044361f0f7ab Mon Sep 17 00:00:00 2001 From: ljy Date: Tue, 21 Jul 2026 22:24:53 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E5=A2=9E=E5=8A=A0=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E5=AE=A2=E6=9C=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/ContactCsButton.tsx | 50 +++++++++++++++ .../src/pages/customer-service/index.tsx | 38 ++++++++---- .../src/pages/order-detail/index.tsx | 61 +++++++++++++++---- apps/mini-user/src/styles/order.css | 54 +++++++++++++++- apps/mini-user/src/styles/redeem.css | 58 +++++++++++++++++- 5 files changed, 232 insertions(+), 29 deletions(-) create mode 100644 apps/mini-user/src/components/ContactCsButton.tsx diff --git a/apps/mini-user/src/components/ContactCsButton.tsx b/apps/mini-user/src/components/ContactCsButton.tsx new file mode 100644 index 0000000..f1dfab7 --- /dev/null +++ b/apps/mini-user/src/components/ContactCsButton.tsx @@ -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 ( + + ); +} diff --git a/apps/mini-user/src/pages/customer-service/index.tsx b/apps/mini-user/src/pages/customer-service/index.tsx index 39208be..fe448fd 100644 --- a/apps/mini-user/src/pages/customer-service/index.tsx +++ b/apps/mini-user/src/pages/customer-service/index.tsx @@ -3,28 +3,42 @@ import Taro from '@tarojs/taro'; import { CUSTOMER_SERVICE_PHONE } from '@dukang/shared-types'; import PageShell from '../../components/PageShell'; import SubPageHeader from '../../components/SubPageHeader'; +import ContactCsButton from '../../components/ContactCsButton'; import { toast } from '../../lib/api'; +const isWeapp = process.env.TARO_ENV === 'weapp'; const DIAL_PHONE = CUSTOMER_SERVICE_PHONE.replace(/-/g, ''); +function dialPhone() { + Taro.makePhoneCall({ phoneNumber: DIAL_PHONE }).catch(() => toast('无法拨打电话')); +} + export default function CustomerServicePage() { return ( - 客服热线 - {CUSTOMER_SERVICE_PHONE} - 工作时间:9:00 - 21:00 - { - Taro.makePhoneCall({ phoneNumber: DIAL_PHONE }).catch(() => - toast('无法拨打电话'), - ); - }} - > - 拨打客服电话 + 杜康好客客服 + + {isWeapp + ? '点击下方按钮,进入小程序在线客服会话' + : '请在微信小程序内打开以使用在线客服,或拨打客服电话'} + + 工作时间:9:00 - 21:00 + + {isWeapp ? ( + + ) : null} + + + + {isWeapp ? `或拨打客服电话 ${CUSTOMER_SERVICE_PHONE}` : '拨打客服电话'} + + + {!isWeapp ? ( + {CUSTOMER_SERVICE_PHONE} + ) : null} ); diff --git a/apps/mini-user/src/pages/order-detail/index.tsx b/apps/mini-user/src/pages/order-detail/index.tsx index 808fa63..47393a8 100644 --- a/apps/mini-user/src/pages/order-detail/index.tsx +++ b/apps/mini-user/src/pages/order-detail/index.tsx @@ -5,6 +5,7 @@ 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'; @@ -14,6 +15,8 @@ import { toWeappShareMessage, } from '../../lib/wechat-share'; +const isWeapp = process.env.TARO_ENV === 'weapp'; + type OrderItem = { productName?: string; productSpec?: string; @@ -104,8 +107,20 @@ export default function OrderDetailPage() { 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 ( - + - {canPay && order && ( - - - 待支付 - - ¥{Number(order.payAmount ?? 0).toFixed(2)} - - - - 去付款 - + {order ? ( + + {isWeapp ? ( + + 联系客服 + + ) : ( + + 联系客服 + + )} + {canPay ? ( + <> + + 待支付 + + ¥{Number(order.payAmount ?? 0).toFixed(2)} + + + + 去付款 + + + ) : null} - )} + ) : null} ); } diff --git a/apps/mini-user/src/styles/order.css b/apps/mini-user/src/styles/order.css index 4f64095..6d4432d 100644 --- a/apps/mini-user/src/styles/order.css +++ b/apps/mini-user/src/styles/order.css @@ -7,7 +7,8 @@ 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)); } @@ -16,6 +17,57 @@ 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 { background: var(--color-card); border-radius: var(--radius-lg); diff --git a/apps/mini-user/src/styles/redeem.css b/apps/mini-user/src/styles/redeem.css index bd2fe36..63a62a5 100644 --- a/apps/mini-user/src/styles/redeem.css +++ b/apps/mini-user/src/styles/redeem.css @@ -499,27 +499,70 @@ 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 { font-size: 14px; color: var(--color-subtle-gray); margin-bottom: 12px; } -.cs-phone { +.cs-phone, +.cs-phone-display { + display: block; font-family: var(--font-headline); - font-size: 32px; + font-size: 28px; font-weight: 700; color: var(--color-heritage-red); - margin-bottom: 8px; + margin-top: 20px; letter-spacing: 1px; } .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; color: var(--color-subtle-gray); 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 { padding: 12px 32px; border-radius: 999px; @@ -528,3 +571,12 @@ font-size: 15px; 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; +}