import Taro from '@tarojs/taro'; import type { WechatShareData } from '@dukang/weixin-sdk'; import { getWechatShareLink, isWechatBrowser, isMiniProgram } from '@dukang/weixin-sdk'; import { applyShareTitleTemplate, DEFAULT_SHARE_DESC, DEFAULT_SHARE_HINT, DEFAULT_SHARE_TITLE, resolveMiniShareRuntime, type ClientRuntimeConfig, type MiniShareRuntime, type MiniShareSceneConfig, } from '@dukang/shared-types'; import { toast } from './api'; import { getBrandAssetsSync, loadBrandAssets } from './brand-assets'; import { fetchClientConfig } from './pay-wechat'; import { isWechatEnv, weixinSdk } from './weixin'; export type ShareScene = | 'home' | 'stores' | 'storeDetail' | 'benefit' | 'mine' | 'productDetail' | 'orderDetail'; export { DEFAULT_SHARE_TITLE, DEFAULT_SHARE_DESC }; export const WECHAT_SHARE_HINT = DEFAULT_SHARE_HINT; const FALLBACK_SHARE: MiniShareRuntime = resolveMiniShareRuntime({}); let shareCached: MiniShareRuntime | null = null; export function getShareRuntimeSync(): MiniShareRuntime { return shareCached ?? FALLBACK_SHARE; } export function applyShareFromClientConfig(config?: ClientRuntimeConfig | null) { if (config?.share) { shareCached = config.share; return shareCached; } return getShareRuntimeSync(); } export async function loadShareConfig(force = false): Promise { if (!force && shareCached) return shareCached; try { const cfg = await fetchClientConfig(); if (cfg?.share) { shareCached = cfg.share; return shareCached; } } catch { /* ignore */ } shareCached = FALLBACK_SHARE; return shareCached; } export function getDefaultShareImageUrl(): string { const share = getShareRuntimeSync(); return share.default.imageUrl || getBrandAssetsSync().brandLogoUrl; } export function getShareHint(): string { return getShareRuntimeSync().hint || DEFAULT_SHARE_HINT; } /** 预热分享配置与品牌图 */ export function prefetchShareBrandAssets() { void loadBrandAssets(); void loadShareConfig(); } function sceneConfig(scene?: ShareScene): MiniShareSceneConfig { const runtime = getShareRuntimeSync(); if (!scene) return runtime.default; return runtime[scene] ?? runtime.default; } /** * 组装页面分享:场景配置优先;空字段用 dynamic → 默认分享。 * orderDetail 标题支持 {productName}。 */ export function buildSceneSharePayload( scene: ShareScene, options?: { path?: string; /** 业务动态标题(场景配置为空时使用) */ dynamicTitle?: string | null; dynamicDesc?: string | null; dynamicImageUrl?: string | null; titleVars?: Record; }, ): PageSharePayload { const def = getShareRuntimeSync().default; const sc = sceneConfig(scene); let title = (sc.title || '').trim(); if (title && options?.titleVars) { const vars = options.titleVars; const missingRequired = Object.entries(vars).some( ([key, value]) => title.includes(`{${key}}`) && !(value != null && String(value).trim()), ); title = missingRequired ? '' : applyShareTitleTemplate(title, vars); } if (!title) { title = (options?.dynamicTitle || '').trim() || def.title; } const desc = (sc.desc || '').trim() || (options?.dynamicDesc || '').trim() || def.desc; const imgUrl = (sc.imageUrl || '').trim() || (options?.dynamicImageUrl || '').trim() || def.imageUrl || getDefaultShareImageUrl(); return { title, desc, path: options?.path, imgUrl, }; } export function buildDefaultShareData( overrides?: Partial, ): WechatShareData { let link = overrides?.link; if (!link) { if (process.env.TARO_ENV === 'h5' && typeof window !== 'undefined') { link = getWechatShareLink(); } else { link = ''; } } const def = getShareRuntimeSync().default; return { title: overrides?.title ?? def.title, desc: overrides?.desc ?? def.desc, link, imgUrl: overrides?.imgUrl ?? getDefaultShareImageUrl(), }; } /** 配置 H5 微信内右上角分享卡片 */ export async function applyWechatShare( overrides?: Partial, ): Promise { if (process.env.TARO_ENV !== 'h5') return; if (!isWechatBrowser()) return; await weixinSdk.setShare(buildDefaultShareData(overrides)); } export type PageSharePayload = { title?: string; desc?: string; /** 小程序分享 path,如 /pages/product-detail/index?id=1 */ path?: string; imgUrl?: string; /** H5 自定义分享 link,默认当前页 */ link?: string; }; /** * 点击「分享」按钮。 * H5:走微信 JSSDK 配置分享卡片,并尝试 invoke 调起面板;否则返回需展示引导蒙层。 */ export async function handleShareButtonClick( payload?: PageSharePayload, ): Promise<{ showGuide: boolean }> { if (process.env.TARO_ENV === 'weapp' || isMiniProgram()) { try { await Taro.showShareMenu({ withShareTicket: true, showShareItems: ['shareAppMessage', 'shareTimeline'] }); } catch { try { await Taro.showShareMenu({ withShareTicket: true }); } catch { /* ignore */ } } toast(getShareHint()); return { showGuide: false }; } if (!isWechatEnv()) { toast('请在微信内打开后分享'); return { showGuide: false }; } const data = buildDefaultShareData({ title: payload?.title, desc: payload?.desc, imgUrl: payload?.imgUrl, link: payload?.link, }); try { const result = await weixinSdk.share(data); if (result.invoked) { return { showGuide: false }; } toast(getShareHint()); return { showGuide: true }; } catch (e) { toast(e instanceof Error ? e.message : '分享配置失败,请刷新后重试'); return { showGuide: true }; } } /** 供 useShareAppMessage 使用的标题/路径/图 */ export function toWeappShareMessage(payload?: PageSharePayload) { const def = getShareRuntimeSync().default; return { title: payload?.title || def.title, path: payload?.path || '/pages/home/index', imageUrl: payload?.imgUrl || getDefaultShareImageUrl(), }; } /** 供 useShareTimeline */ export function toWeappShareTimeline(payload?: PageSharePayload, query = '') { const def = getShareRuntimeSync().default; return { title: payload?.title || def.title, query, imageUrl: payload?.imgUrl || getDefaultShareImageUrl(), }; }