微信分享

This commit is contained in:
2026-07-09 17:14:00 +08:00
parent 3d3588babb
commit 3f60c37265
9 changed files with 180 additions and 3 deletions
+2
View File
@@ -21,6 +21,7 @@ import PayPage from './pages/PayPage';
import CustomerServicePage from './pages/CustomerServicePage';
import { UserSessionProvider } from './contexts/UserSessionContext';
import { capturePromoFromUrl, touchPromoIfNeeded } from './lib/promo';
import WechatShareBootstrap from './components/WechatShareBootstrap';
function PromoBootstrap() {
useEffect(() => {
@@ -34,6 +35,7 @@ export default function App() {
return (
<UserSessionProvider>
<PromoBootstrap />
<WechatShareBootstrap />
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route element={<TabLayout />}>
@@ -0,0 +1,14 @@
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { applyDefaultWechatShare } from '../lib/wechat-share';
/** 路由变化时刷新微信右上角分享卡片 */
export default function WechatShareBootstrap() {
const location = useLocation();
useEffect(() => {
void applyDefaultWechatShare().catch(() => {});
}, [location.pathname, location.search]);
return null;
}
+47
View File
@@ -0,0 +1,47 @@
import type { WechatShareData } from '@dukang/weixin-sdk';
import { getWechatShareLink, toAppPath } from '@dukang/weixin-sdk';
import { isWechatEnv, weixinSdk } from './weixin';
export const DEFAULT_SHARE_TITLE = '你吃饭,杜康买单';
export const DEFAULT_SHARE_DESC = '杜康好客 · 买酒享权益,全城门店可用';
export const WECHAT_SHARE_HINT = '请点击右上角 ··· 分享给好友';
export function getDefaultShareImageUrl(): string {
if (typeof window === 'undefined') return toAppPath('/logo.png');
return new URL(toAppPath('/logo.png'), window.location.origin).href;
}
export function buildDefaultShareData(
overrides?: Partial<WechatShareData>,
): WechatShareData {
return {
title: overrides?.title ?? DEFAULT_SHARE_TITLE,
desc: overrides?.desc ?? DEFAULT_SHARE_DESC,
link: overrides?.link ?? getWechatShareLink(),
imgUrl: overrides?.imgUrl ?? getDefaultShareImageUrl(),
};
}
export async function applyDefaultWechatShare(
overrides?: Partial<WechatShareData>,
): Promise<void> {
if (!isWechatEnv()) return;
await weixinSdk.setShare(buildDefaultShareData(overrides));
}
export function handleShareButtonClick(onHint: (message: string) => void): void {
const showHint = (message: string) => {
onHint(message);
if (message) {
window.setTimeout(() => onHint(''), 2500);
}
};
if (!isWechatEnv()) {
showHint('请在微信内打开后分享');
return;
}
void applyDefaultWechatShare()
.then(() => showHint(WECHAT_SHARE_HINT))
.catch(() => showHint('分享配置失败,请刷新页面后重试'));
}
+6 -1
View File
@@ -1,9 +1,11 @@
import { useEffect, useMemo, useState } from 'react';
import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom';
import AppImage from '@dukang/shared-ui/AppImage';
import AppToast from '../components/AppToast';
import { request } from '../lib/api';
import { buildOrderAddressSelectUrl } from '../lib/navigation';
import { STITCH_ORDER_PRODUCT_IMAGE } from '../lib/order-images';
import { handleShareButtonClick } from '../lib/wechat-share';
import ContactCustomerSheet from '../components/ContactCustomerSheet';
type OrderItem = {
@@ -121,6 +123,7 @@ export default function OrderDetailPage() {
const [order, setOrder] = useState<Order | null>(null);
const [showCs, setShowCs] = useState(false);
const [copyHint, setCopyHint] = useState('');
const [shareToast, setShareToast] = useState('');
const [confirming, setConfirming] = useState(false);
const isReship = order?.orderType === 'RESHIPMENT' || params.get('type') === 'reship';
@@ -206,7 +209,7 @@ export default function OrderDetailPage() {
</button>
<h1 className="order-detail-topbar-title"></h1>
<div className="order-detail-topbar-actions">
<button type="button" className="order-detail-topbar-btn" aria-label="分享" onClick={() => {}}>
<button type="button" className="order-detail-topbar-btn" aria-label="分享" onClick={() => handleShareButtonClick(setShareToast)}>
<span className="material-symbols-outlined">share</span>
</button>
<button type="button" className="order-detail-topbar-btn" aria-label="更多" onClick={() => {}}>
@@ -215,6 +218,8 @@ export default function OrderDetailPage() {
</div>
</header>
<AppToast message={shareToast} />
<main className="order-detail-main order-detail-main--stitch">
<section className="order-detail-status-card">
<div className="order-detail-status-deco" aria-hidden>
+6 -1
View File
@@ -3,8 +3,10 @@ import { Link, useNavigate, useParams } from 'react-router-dom';
import { useEffect, useState } from 'react';
import AppImage from '@dukang/shared-ui/AppImage';
import ProductCarousel from '../components/ProductCarousel';
import AppToast from '../components/AppToast';
import { request } from '../lib/api';
import { track } from '../lib/analytics';
import { handleShareButtonClick } from '../lib/wechat-share';
import { getProductCarouselImages, getProductDetailImages } from '../lib/product-images';
import type { ProductImageSource } from '../lib/product-images';
@@ -21,6 +23,7 @@ export default function ProductDetailPage() {
const navigate = useNavigate();
const [product, setProduct] = useState<Product | null>(null);
const [headerSolid, setHeaderSolid] = useState(false);
const [toast, setToast] = useState('');
useEffect(() => {
if (id) request<Product>('USER_H5', `/catalog/products/${id}`).then(setProduct);
@@ -66,12 +69,14 @@ export default function ProductDetailPage() {
type="button"
className="product-detail-header-btn"
aria-label="分享"
onClick={() => {}}
onClick={() => handleShareButtonClick(setToast)}
>
<span className="material-symbols-outlined">share</span>
</button>
</header>
<AppToast message={toast} />
<main className="product-detail-main">
<section className="product-detail-hero">
<ProductCarousel images={carouselImages} alt={product.name} variant="detail" />
+6 -1
View File
@@ -2,8 +2,10 @@ import { useEffect, useMemo, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import AppImage from '@dukang/shared-ui/AppImage';
import ProductCarousel from '../components/ProductCarousel';
import AppToast from '../components/AppToast';
import { request } from '../lib/api';
import { track } from '../lib/analytics';
import { handleShareButtonClick } from '../lib/wechat-share';
import { STITCH_STORE_MAP, getStoreGalleryImages } from '../lib/store-images';
type StoreMedia = { url: string; mediaType?: string; sortOrder?: number };
@@ -54,6 +56,7 @@ export default function StoreDetailPage() {
const [store, setStore] = useState<StoreDetail | null>(null);
const [benefitBalance, setBenefitBalance] = useState(0);
const [headerSolid, setHeaderSolid] = useState(false);
const [toast, setToast] = useState('');
useEffect(() => {
if (id) {
@@ -114,11 +117,13 @@ export default function StoreDetailPage() {
<span className="material-symbols-outlined">arrow_back</span>
</button>
<h1 className={`app-page-title store-detail-header-title${headerSolid ? ' visible' : ''}`}></h1>
<button type="button" className="store-detail-header-btn" aria-label="分享" onClick={() => {}}>
<button type="button" className="store-detail-header-btn" aria-label="分享" onClick={() => handleShareButtonClick(setToast)}>
<span className="material-symbols-outlined">share</span>
</button>
</header>
<AppToast message={toast} />
<main className="store-detail-main">
<section className="store-detail-hero">
<ProductCarousel images={galleryImages} alt={store.name} variant="store" />