feat(mini-user): configurable WeChat mini share scenes in system settings
Add HQ group for default and per-page share title/desc/image; expose via client-config and wire all mini-user share entry points. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,21 +1,126 @@
|
||||
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 const DEFAULT_SHARE_TITLE = '你吃饭,我买单';
|
||||
export const DEFAULT_SHARE_DESC = '杜康好客 · 买美酒,享好礼,全城门店可用';
|
||||
export const WECHAT_SHARE_HINT = '请点击右上角 ··· 分享给好友';
|
||||
export type ShareScene =
|
||||
| 'home'
|
||||
| 'stores'
|
||||
| 'storeDetail'
|
||||
| 'benefit'
|
||||
| 'mine'
|
||||
| 'productDetail'
|
||||
| 'orderDetail';
|
||||
|
||||
export function getDefaultShareImageUrl(): string {
|
||||
return getBrandAssetsSync().brandLogoUrl;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装页面分享:场景配置优先;空字段用 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);
|
||||
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(
|
||||
@@ -29,9 +134,10 @@ export function buildDefaultShareData(
|
||||
link = '';
|
||||
}
|
||||
}
|
||||
const def = getShareRuntimeSync().default;
|
||||
return {
|
||||
title: overrides?.title ?? DEFAULT_SHARE_TITLE,
|
||||
desc: overrides?.desc ?? DEFAULT_SHARE_DESC,
|
||||
title: overrides?.title ?? def.title,
|
||||
desc: overrides?.desc ?? def.desc,
|
||||
link,
|
||||
imgUrl: overrides?.imgUrl ?? getDefaultShareImageUrl(),
|
||||
};
|
||||
@@ -73,7 +179,7 @@ export async function handleShareButtonClick(
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
toast(WECHAT_SHARE_HINT);
|
||||
toast(getShareHint());
|
||||
return { showGuide: false };
|
||||
}
|
||||
|
||||
@@ -94,7 +200,7 @@ export async function handleShareButtonClick(
|
||||
if (result.invoked) {
|
||||
return { showGuide: false };
|
||||
}
|
||||
toast(WECHAT_SHARE_HINT);
|
||||
toast(getShareHint());
|
||||
return { showGuide: true };
|
||||
} catch (e) {
|
||||
toast(e instanceof Error ? e.message : '分享配置失败,请刷新后重试');
|
||||
@@ -104,9 +210,20 @@ export async function handleShareButtonClick(
|
||||
|
||||
/** 供 useShareAppMessage 使用的标题/路径/图 */
|
||||
export function toWeappShareMessage(payload?: PageSharePayload) {
|
||||
const def = getShareRuntimeSync().default;
|
||||
return {
|
||||
title: payload?.title || DEFAULT_SHARE_TITLE,
|
||||
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(),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user