Files
dukang/apps/mini-user/src/lib/wechat-share.ts
T
jacy fc2e5b65de
CI / verify (pull_request) Has been cancelled
v3.5.3版本更新1
2026-08-20 18:54:15 +08:00

242 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<MiniShareRuntime> {
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;
}
/**
* 组装页面分享。
* - productDetail / storeDetailtitle 与 imageUrl 固定用业务字段(不被 HQ 场景配置覆盖)
* - 其它场景:场景配置优先;空字段用 dynamic → 默认分享
* - orderDetail 标题支持 {productName}
*/
export function buildSceneSharePayload(
scene: ShareScene,
options?: {
path?: string;
/** 业务动态标题(场景配置为空时使用) */
dynamicTitle?: string | null;
dynamicDesc?: string | null;
dynamicImageUrl?: string | null;
titleVars?: Record<string, string | undefined | null>;
},
): PageSharePayload {
const def = getShareRuntimeSync().default;
const sc = sceneConfig(scene);
const preferEntity = scene === 'productDetail' || scene === 'storeDetail';
let title: string;
if (preferEntity) {
title = (options?.dynamicTitle || '').trim() || def.title;
} else {
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 = preferEntity
? (options?.dynamicImageUrl || '').trim() || def.imageUrl || getDefaultShareImageUrl()
: (sc.imageUrl || '').trim() ||
(options?.dynamicImageUrl || '').trim() ||
def.imageUrl ||
getDefaultShareImageUrl();
return {
title,
desc,
path: options?.path,
imgUrl,
};
}
export function buildDefaultShareData(
overrides?: Partial<WechatShareData>,
): 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<WechatShareData>,
): Promise<void> {
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(),
};
}