From 772700cd858167c278a88fb19e4e0b4bff55b2c1 Mon Sep 17 00:00:00 2001 From: Karlcarlo Date: Wed, 8 Jul 2026 15:28:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(share):=20SharePage=20=E9=9B=86=E6=88=90?= =?UTF-8?q?=20box=5Fapp=20=E5=A4=AA=E9=98=B3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 调 box_app /api/wxacode 生成小程序太阳码 目标路径: pages/annex/web_view/index(CRMEB web-view 容器) scene 留空——扫码直接进 app_03 H5 首页 - ShareCardContent 末尾新增太阳码区 200×200 PNG + 引导文字「微信扫码下载/长按保存」 太阳码在长图截图中可见 - 截图前等待太阳码就绪(最多 1.5s),避免截图缺失 - 卸载时清理 blob URL 防内存泄漏 - crossOrigin="anonymous" 防止 html-to-image 截取跨域图失败 - 太阳码生成失败时降级:长图照常生成,仅缺二维码区 设计决策: - 不引入匿名 token 机制:收件人通过 CRMEB 微信小程序 web-view 进入 共享 CRMEB 主体 openid 即可正常使用 app_03 - SharePage 仍走 ProtectedRoute(受保护) 收件人访问 /share/:reportId 由 CRMEB 注入 openid 完成鉴权 --- frontend/src/app/pages/SharePage.tsx | 84 +++++++++++++++++++++++++++- frontend/src/app/services/api.ts | 24 ++++++++ 2 files changed, 105 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/pages/SharePage.tsx b/frontend/src/app/pages/SharePage.tsx index 1256c35..6b2fb7d 100644 --- a/frontend/src/app/pages/SharePage.tsx +++ b/frontend/src/app/pages/SharePage.tsx @@ -20,7 +20,7 @@ import { toPng } from "html-to-image"; import { Card } from "../components/ui/card"; import { Badge } from "../components/ui/badge"; import { Button } from "../components/ui/button"; -import { generateShareData } from "../services/api" +import { generateShareData, generateWxacode } from "../services/api" import bridge from "../services/bridge"; type SaveStatus = "idle" | "generating" | "success" | "error"; @@ -42,6 +42,7 @@ export function SharePage() { const [imageUrl, setImageUrl] = useState(null); const [linkCopied, setLinkCopied] = useState(false); const [canShareToWechat, setCanShareToWechat] = useState(false); + const [wxacodeUrl, setWxacodeUrl] = useState(null); const cardRef = useRef(null); useEffect(() => { @@ -85,10 +86,51 @@ export function SharePage() { .finally(() => setLoading(false)); }, [reportId]); + // 太阳码:shareData 就绪后异步生成,blob URL 存到 wxacodeUrl + useEffect(() => { + if (!shareData) return; + let cancelled = false; + generateWxacode({ width: 430 }) + .then((url) => { + if (!cancelled) setWxacodeUrl(url); + }) + .catch((err) => { + console.warn("[SharePage] 太阳码生成失败:", err); + }); + return () => { + cancelled = true; + }; + }, [shareData]); + + // 卸载时清理 blob URL + useEffect(() => { + return () => { + if (wxacodeUrl?.startsWith("blob:")) URL.revokeObjectURL(wxacodeUrl); + }; + }, [wxacodeUrl]); + // 生成长图 const generateImage = useCallback(async (): Promise => { if (!cardRef.current) return null; + // 等待太阳码渲染完成(blob URL 需浏览器解码) + if (shareData && !wxacodeUrl) { + await new Promise((resolve) => { + const start = Date.now(); + const tick = () => { + if (wxacodeUrl || Date.now() - start > 1500) { + resolve(); + return; + } + setTimeout(tick, 100); + }; + tick(); + }); + if (!wxacodeUrl) { + console.warn("[SharePage] 太阳码未就绪,截图中可能缺失二维码"); + } + } + try { // html-to-image: 将 DOM 节点转为 PNG data URL const dataUrl = await toPng(cardRef.current, { @@ -105,7 +147,7 @@ export function SharePage() { console.error("生成图片失败:", err); return null; } - }, []); + }, [shareData, wxacodeUrl]); // 一键保存到相册 / 下载 const handleSaveImage = useCallback(async () => { @@ -622,6 +664,42 @@ function ShareCardContent({ )} + {/* 太阳码区(截图包含) */} + {wxacodeUrl && ( +
+ 扫码下载孕育无忧 +

+ 微信扫码下载「孕育无忧」APP +
+ 长按图片也可保存到相册 +

+
+ )} + {/* 底部声明 */}

@@ -643,7 +721,7 @@ function ShareCardContent({ }} > - 孕育无忧 · yun.tun88.com + 孕育无忧 · 兜爸心选+

diff --git a/frontend/src/app/services/api.ts b/frontend/src/app/services/api.ts index 641421e..a1dea65 100644 --- a/frontend/src/app/services/api.ts +++ b/frontend/src/app/services/api.ts @@ -533,3 +533,27 @@ export async function generateShareData( body: JSON.stringify({ privacy_mode: true, include_details: false, ...options }), }); } + +// ─── WXacode(外部服务,box_app 太阳码生成)─── +// +// 调用 box_app /api/wxacode,生成可被微信扫码直接进入 CRMEB 小程序的太阳码。 +// scene 留空——目标页是 CRMEB 通用 web-view 容器 pages/annex/web_view/index, +// 扫码后收件人直接进入 app_03 H5 首页(与「分享 → 扫码进首页」流程一致)。 +const WXACODE_API_HOST = "https://mp.yizhezhe.com"; +const WXACODE_PATH = "pages/annex/web_view/index"; + +/** 调用 box_app 生成小程序太阳码,返回 blob URL(用于 展示) */ +export async function generateWxacode( + options: { width?: number } = {} +): Promise { + const params = new URLSearchParams({ + path: WXACODE_PATH, + width: String(options.width ?? 430), + }); + const res = await fetch(`${WXACODE_API_HOST}/api/wxacode?${params}`); + if (!res.ok) { + throw new Error(`太阳码生成失败 (${res.status})`); + } + const blob = await res.blob(); + return URL.createObjectURL(blob); +}