微信分享
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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('分享配置失败,请刷新页面后重试'));
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user