feat(share): SharePage 集成 box_app 太阳码
- 调 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 完成鉴权
This commit is contained in:
@@ -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<string | null>(null);
|
||||
const [linkCopied, setLinkCopied] = useState(false);
|
||||
const [canShareToWechat, setCanShareToWechat] = useState(false);
|
||||
const [wxacodeUrl, setWxacodeUrl] = useState<string | null>(null);
|
||||
const cardRef = useRef<HTMLDivElement>(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<string | null> => {
|
||||
if (!cardRef.current) return null;
|
||||
|
||||
// 等待太阳码渲染完成(blob URL 需浏览器解码)
|
||||
if (shareData && !wxacodeUrl) {
|
||||
await new Promise<void>((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({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 太阳码区(截图包含) */}
|
||||
{wxacodeUrl && (
|
||||
<div
|
||||
style={{
|
||||
textAlign: "center",
|
||||
paddingTop: "20px",
|
||||
borderTop: "1px dashed #e5e7eb",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={wxacodeUrl}
|
||||
alt="扫码下载孕育无忧"
|
||||
crossOrigin="anonymous"
|
||||
style={{
|
||||
width: 200,
|
||||
height: 200,
|
||||
borderRadius: 8,
|
||||
background: "#ffffff",
|
||||
}}
|
||||
/>
|
||||
<p
|
||||
style={{
|
||||
marginTop: 8,
|
||||
fontSize: 12,
|
||||
color: "#9ca3af",
|
||||
lineHeight: 1.5,
|
||||
margin: "8px 0 0 0",
|
||||
}}
|
||||
>
|
||||
微信扫码下载「孕育无忧」APP
|
||||
<br />
|
||||
长按图片也可保存到相册
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 底部声明 */}
|
||||
<div style={{ paddingTop: "20px", borderTop: "2px solid #f3f4f6", textAlign: "center" }}>
|
||||
<p style={{ fontSize: "12px", color: "#9ca3af", marginBottom: "4px", margin: "0 0 4px 0" }}>
|
||||
@@ -643,7 +721,7 @@ function ShareCardContent({
|
||||
}}
|
||||
>
|
||||
<Heart style={{ width: 12, height: 12, fill: "#b8a9c9", color: "#b8a9c9" }} />
|
||||
<span>孕育无忧 · yun.tun88.com</span>
|
||||
<span>孕育无忧 · 兜爸心选+</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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(用于 <img> 展示) */
|
||||
export async function generateWxacode(
|
||||
options: { width?: number } = {}
|
||||
): Promise<string> {
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user