feat: 初始化孕育无忧全栈项目基础代码结构
完成项目初始搭建,包含后端FastAPI+MongoDB服务、前端React+Vite项目的完整基础结构,添加各类配置文件与依赖清单,涵盖核心业务初始框架。
This commit is contained in:
574
frontend/src/app/pages/SharePage.tsx
Normal file
574
frontend/src/app/pages/SharePage.tsx
Normal file
@@ -0,0 +1,574 @@
|
||||
import { useState, useEffect, useRef, useCallback } from "react";
|
||||
import { useNavigate, useParams } from "react-router";
|
||||
import { motion, AnimatePresence } from "motion/react";
|
||||
import {
|
||||
ChevronLeft,
|
||||
Share2,
|
||||
CheckCircle2,
|
||||
AlertCircle,
|
||||
Info,
|
||||
Loader2,
|
||||
Heart,
|
||||
Baby,
|
||||
TrendingUp,
|
||||
Download,
|
||||
Check,
|
||||
Image as ImageIcon,
|
||||
} from "lucide-react";
|
||||
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";
|
||||
|
||||
type SaveStatus = "idle" | "generating" | "success" | "error";
|
||||
|
||||
export function SharePage() {
|
||||
const navigate = useNavigate();
|
||||
const { reportId } = useParams();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [shareData, setShareData] = useState<any>(null);
|
||||
const [saveStatus, setSaveStatus] = useState<SaveStatus>("idle");
|
||||
const [imageUrl, setImageUrl] = useState<string | null>(null);
|
||||
const cardRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!reportId) return;
|
||||
generateShareData(reportId)
|
||||
.then((data) => {
|
||||
setShareData(data.share_data);
|
||||
})
|
||||
.catch(() => {/* keep null */})
|
||||
.finally(() => setLoading(false));
|
||||
}, [reportId]);
|
||||
|
||||
// 生成长图
|
||||
const generateImage = useCallback(async (): Promise<string | null> => {
|
||||
if (!cardRef.current) return null;
|
||||
|
||||
try {
|
||||
// html-to-image: 将 DOM 节点转为 PNG data URL
|
||||
const dataUrl = await toPng(cardRef.current, {
|
||||
cacheBust: true,
|
||||
pixelRatio: 2, // 2x 高清
|
||||
backgroundColor: "#ffffff",
|
||||
style: {
|
||||
// 确保截图时移除圆角阴影的裁切问题
|
||||
borderRadius: "0",
|
||||
},
|
||||
});
|
||||
return dataUrl;
|
||||
} catch (err) {
|
||||
console.error("生成图片失败:", err);
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 一键保存到相册 / 下载
|
||||
const handleSaveImage = useCallback(async () => {
|
||||
setSaveStatus("generating");
|
||||
|
||||
try {
|
||||
const dataUrl = imageUrl || (await generateImage());
|
||||
if (!dataUrl) {
|
||||
setSaveStatus("error");
|
||||
setTimeout(() => setSaveStatus("idle"), 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!imageUrl) setImageUrl(dataUrl);
|
||||
|
||||
// 尝试使用 Web Share API(移动端可直接分享/保存到相册)
|
||||
if (navigator.share && navigator.canShare) {
|
||||
try {
|
||||
const response = await fetch(dataUrl);
|
||||
const blob = await response.blob();
|
||||
const file = new File([blob], `孕育无忧-报告解读-${reportId}.png`, {
|
||||
type: "image/png",
|
||||
});
|
||||
|
||||
if (navigator.canShare({ files: [file] })) {
|
||||
await navigator.share({
|
||||
files: [file],
|
||||
title: "孕育无忧 · AI 报告解读",
|
||||
});
|
||||
setSaveStatus("success");
|
||||
setTimeout(() => setSaveStatus("idle"), 3000);
|
||||
return;
|
||||
}
|
||||
} catch (shareErr) {
|
||||
// 用户取消分享或不支持,降级到下载
|
||||
if ((shareErr as Error).name === "AbortError") {
|
||||
setSaveStatus("idle");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 降级方案:触发下载(iOS Safari / Android 浏览器)
|
||||
const link = document.createElement("a");
|
||||
link.download = `孕育无忧-报告解读-${reportId}.png`;
|
||||
link.href = dataUrl;
|
||||
link.style.display = "none";
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
setSaveStatus("success");
|
||||
setTimeout(() => setSaveStatus("idle"), 3000);
|
||||
} catch (err) {
|
||||
console.error("保存失败:", err);
|
||||
setSaveStatus("error");
|
||||
setTimeout(() => setSaveStatus("idle"), 2000);
|
||||
}
|
||||
}, [imageUrl, generateImage, reportId]);
|
||||
|
||||
// 预览:在页面加载数据后自动生成图片预览
|
||||
useEffect(() => {
|
||||
if (shareData && cardRef.current && !imageUrl) {
|
||||
// 延迟一帧确保 DOM 渲染完毕
|
||||
const timer = setTimeout(async () => {
|
||||
const url = await generateImage();
|
||||
if (url) setImageUrl(url);
|
||||
}, 500);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [shareData, generateImage, imageUrl]);
|
||||
|
||||
if (loading || !shareData) {
|
||||
return (
|
||||
<div className="min-h-dvh bg-morandi-50 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<Loader2 className="w-8 h-8 text-morandi-400 animate-spin mx-auto mb-3" />
|
||||
<p className="text-sm text-gray-500">正在准备分享内容...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case "normal":
|
||||
return {
|
||||
bg: "bg-morandi-sage-100",
|
||||
text: "text-morandi-sage-600",
|
||||
icon: "text-morandi-sage-400",
|
||||
};
|
||||
case "attention":
|
||||
return {
|
||||
bg: "bg-morandi-rose-100",
|
||||
text: "text-morandi-600",
|
||||
icon: "text-morandi-rose-400",
|
||||
};
|
||||
default:
|
||||
return {
|
||||
bg: "bg-gray-100",
|
||||
text: "text-gray-600",
|
||||
icon: "text-gray-400",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const statusColors = getStatusColor(shareData.overall_status);
|
||||
|
||||
const getSaveButtonContent = () => {
|
||||
switch (saveStatus) {
|
||||
case "generating":
|
||||
return (
|
||||
<>
|
||||
<Loader2 className="w-5 h-5 animate-spin" />
|
||||
<span>正在生成图片...</span>
|
||||
</>
|
||||
);
|
||||
case "success":
|
||||
return (
|
||||
<>
|
||||
<Check className="w-5 h-5" />
|
||||
<span>已保存到相册</span>
|
||||
</>
|
||||
);
|
||||
case "error":
|
||||
return (
|
||||
<>
|
||||
<AlertCircle className="w-5 h-5" />
|
||||
<span>保存失败,请重试</span>
|
||||
</>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<>
|
||||
<Download className="w-5 h-5" />
|
||||
<span>保存图片到相册</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-dvh bg-morandi-50 pb-28">
|
||||
{/* 顶部导航 */}
|
||||
<div className="bg-white border-b border-gray-100 sticky top-0 z-10">
|
||||
<div className="mx-auto px-4 py-4 flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={() => navigate(-1)}
|
||||
className="w-10 h-10 rounded-full hover:bg-gray-100 flex items-center justify-center"
|
||||
>
|
||||
<ChevronLeft className="w-6 h-6 text-gray-600" />
|
||||
</button>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-10 h-10 bg-morandi-400 rounded-full flex items-center justify-center">
|
||||
<Share2 className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="font-semibold text-gray-800">分享长图</h1>
|
||||
<p className="text-xs text-gray-500">生成精美报告长图</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mx-auto p-4 space-y-5">
|
||||
{/* 图片预览区域 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
>
|
||||
{/* 如果已经生成了图片,直接展示图片(可长按保存) */}
|
||||
{imageUrl ? (
|
||||
<div className="relative">
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt="分享长图"
|
||||
className="w-full rounded-3xl shadow-lg"
|
||||
style={{ userSelect: "none" }}
|
||||
/>
|
||||
{/* 长按提示覆盖层 */}
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/30 to-transparent rounded-b-3xl px-4 py-3">
|
||||
<p className="text-white text-xs text-center opacity-80">
|
||||
💡 长按图片也可直接保存
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-center py-16 bg-white rounded-3xl shadow-lg">
|
||||
<div className="text-center">
|
||||
<Loader2 className="w-8 h-8 text-morandi-400 animate-spin mx-auto mb-3" />
|
||||
<p className="text-sm text-gray-500">正在生成长图预览...</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
|
||||
{/* 隐藏的 HTML 卡片(用于截图,不展示给用户) */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: "-9999px",
|
||||
top: 0,
|
||||
width: "375px", // 固定 iPhone 宽度,保证截图一致
|
||||
}}
|
||||
>
|
||||
<div ref={cardRef} style={{ background: "#ffffff", padding: "0" }}>
|
||||
<ShareCardContent shareData={shareData} getStatusColor={getStatusColor} statusColors={statusColors} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 操作提示 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.1 }}
|
||||
>
|
||||
<Card className="bg-morandi-50 border-morandi-200 rounded-2xl p-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<Info className="w-5 h-5 text-morandi-400 flex-shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="font-semibold text-gray-800 text-sm mb-2">💡 分享说明</h4>
|
||||
<div className="space-y-1.5">
|
||||
<p className="text-xs text-gray-600 leading-relaxed">
|
||||
• 点击下方按钮,一键保存图片到手机相册
|
||||
</p>
|
||||
<p className="text-xs text-gray-600 leading-relaxed">
|
||||
• 也可以长按上方图片直接保存
|
||||
</p>
|
||||
<p className="text-xs text-gray-600 leading-relaxed">
|
||||
• 个人信息已自动脱敏处理,保护您的隐私
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* 底部固定操作栏 */}
|
||||
<div className="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 p-4 pb-safe z-10">
|
||||
<div className="max-w-md mx-auto">
|
||||
<Button
|
||||
onClick={handleSaveImage}
|
||||
disabled={saveStatus === "generating"}
|
||||
className={`w-full h-14 rounded-2xl text-base font-semibold flex items-center justify-center gap-2 transition-all ${
|
||||
saveStatus === "success"
|
||||
? "bg-morandi-sage-400 hover:bg-morandi-sage-500 text-white"
|
||||
: saveStatus === "error"
|
||||
? "bg-red-400 hover:bg-red-500 text-white"
|
||||
: "bg-gradient-to-r from-morandi-400 to-morandi-500 hover:from-morandi-500 hover:to-morandi-600 text-white shadow-lg shadow-morandi-300/30"
|
||||
}`}
|
||||
>
|
||||
{getSaveButtonContent()}
|
||||
</Button>
|
||||
<p className="text-center text-xs text-gray-400 mt-2">
|
||||
图片将保存到您的手机相册中
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分享卡片内容组件(纯 HTML/CSS,不使用 motion 动画,用于截图)
|
||||
* 独立提取,确保截图时样式完整、无动画干扰
|
||||
*/
|
||||
function ShareCardContent({
|
||||
shareData,
|
||||
getStatusColor,
|
||||
statusColors,
|
||||
}: {
|
||||
shareData: any;
|
||||
getStatusColor: (status: string) => { bg: string; text: string; icon: string };
|
||||
statusColors: { bg: string; text: string; icon: string };
|
||||
}) {
|
||||
return (
|
||||
<div style={{ background: "#ffffff", fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" }}>
|
||||
<div style={{ padding: "24px", display: "flex", flexDirection: "column", gap: "20px" }}>
|
||||
{/* 标题区 */}
|
||||
<div style={{ textAlign: "center", paddingBottom: "20px", borderBottom: "2px solid #f3f4f6" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
padding: "8px 16px",
|
||||
background: "linear-gradient(135deg, #b8a9c9, #9d8ab5)",
|
||||
color: "white",
|
||||
borderRadius: "9999px",
|
||||
fontSize: "14px",
|
||||
fontWeight: 600,
|
||||
marginBottom: "16px",
|
||||
}}
|
||||
>
|
||||
<Heart style={{ width: 16, height: 16, fill: "white", color: "white" }} />
|
||||
<span>孕育无忧 · AI 智能解读</span>
|
||||
</div>
|
||||
<h2
|
||||
style={{
|
||||
fontSize: "24px",
|
||||
fontWeight: 700,
|
||||
color: "#1f2937",
|
||||
marginBottom: "8px",
|
||||
marginTop: "0",
|
||||
}}
|
||||
>
|
||||
{shareData.report_type}
|
||||
</h2>
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: "12px", fontSize: "14px", color: "#6b7280" }}>
|
||||
{shareData.pregnancy_week && <span>孕 {shareData.pregnancy_week} 周</span>}
|
||||
{shareData.user_name && (
|
||||
<>
|
||||
<span>·</span>
|
||||
<span>{shareData.user_name}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 整体评估 */}
|
||||
<div className={`${statusColors.bg} rounded-2xl p-5`}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "12px", marginBottom: "12px" }}>
|
||||
<div
|
||||
style={{
|
||||
width: 48,
|
||||
height: 48,
|
||||
background: "rgba(255,255,255,0.5)",
|
||||
borderRadius: "12px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Baby className={`w-6 h-6 ${statusColors.icon}`} />
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<h3 style={{ fontWeight: 700, color: "#1f2937", fontSize: "18px", margin: 0 }}>
|
||||
整体评估
|
||||
</h3>
|
||||
<Badge className={`${statusColors.bg} ${statusColors.text} mt-1`}>
|
||||
{shareData.overall_status === "normal" ? "一切正常" : "需要关注"}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
<p className={`text-sm ${statusColors.text} leading-relaxed`} style={{ margin: 0 }}>
|
||||
{shareData.emotional_summary || shareData.overall_summary}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 详细指标 */}
|
||||
{shareData.categories && shareData.categories.length > 0 && (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|
||||
<h3 style={{ fontWeight: 700, color: "#1f2937", fontSize: "18px", display: "flex", alignItems: "center", gap: "8px", margin: 0 }}>
|
||||
<TrendingUp className="w-5 h-5 text-morandi-400" />
|
||||
详细指标
|
||||
</h3>
|
||||
{shareData.categories.map((category: any, idx: number) => {
|
||||
const catColors = getStatusColor(category.status);
|
||||
return (
|
||||
<div key={idx} className={`${catColors.bg} rounded-2xl p-4`}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px", marginBottom: "12px" }}>
|
||||
<h4 style={{ fontWeight: 600, color: "#1f2937", margin: 0 }}>{category.category}</h4>
|
||||
<Badge className={`${catColors.bg} ${catColors.text} text-xs`}>
|
||||
{category.status === "normal" ? "正常" : "需关注"}
|
||||
</Badge>
|
||||
</div>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
|
||||
{category.indicators.map((ind: any, indIdx: number) => (
|
||||
<div key={indIdx} style={{ background: "rgba(255,255,255,0.7)", borderRadius: "12px", padding: "12px" }}>
|
||||
<div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", marginBottom: "8px" }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ fontWeight: 600, color: "#1f2937", fontSize: "14px", marginBottom: "4px" }}>
|
||||
{ind.name}
|
||||
{ind.nickname && (
|
||||
<span style={{ fontSize: "12px", color: "#9ca3af", marginLeft: "8px" }}>({ind.nickname})</span>
|
||||
)}
|
||||
</div>
|
||||
{ind.value && (
|
||||
<div style={{ fontSize: "18px", fontWeight: 700, color: "#7c6995" }}>
|
||||
{ind.value} {ind.unit}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Badge className={`text-xs ${
|
||||
ind.status === "normal"
|
||||
? "bg-morandi-sage-100 text-morandi-sage-600"
|
||||
: "bg-morandi-rose-100 text-morandi-rose-600"
|
||||
}`}>
|
||||
{ind.status === "normal" ? "正常" : ind.status === "high" ? "偏高" : "偏低"}
|
||||
</Badge>
|
||||
</div>
|
||||
{ind.what_is_it && (
|
||||
<p style={{ fontSize: "12px", color: "#6b7280", marginBottom: "8px", margin: "0 0 8px 0" }}>
|
||||
{ind.what_is_it}
|
||||
</p>
|
||||
)}
|
||||
{ind.suggestion && (
|
||||
<div style={{ background: "#faf7fc", borderRadius: "8px", padding: "8px", marginTop: "8px" }}>
|
||||
<p style={{ fontSize: "12px", color: "#4b5563", margin: 0 }}>💡 {ind.suggestion}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 行动建议 */}
|
||||
{shareData.action_items && shareData.action_items.length > 0 && (
|
||||
<div>
|
||||
<h3 style={{ fontWeight: 700, color: "#1f2937", fontSize: "18px", marginBottom: "12px", marginTop: 0 }}>
|
||||
接下来该做什么?
|
||||
</h3>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
|
||||
{shareData.action_items.map((item: any, idx: number) => (
|
||||
<div
|
||||
key={idx}
|
||||
style={{
|
||||
borderRadius: "12px",
|
||||
padding: "12px",
|
||||
background: item.priority === "high" ? "#fce4ec" : "#faf7fc",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", alignItems: "flex-start", gap: "8px" }}>
|
||||
<span
|
||||
style={{
|
||||
flexShrink: 0,
|
||||
width: 20,
|
||||
height: 20,
|
||||
background: "#b8a9c9",
|
||||
color: "white",
|
||||
borderRadius: "50%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
fontSize: "12px",
|
||||
fontWeight: 700,
|
||||
marginTop: "2px",
|
||||
}}
|
||||
>
|
||||
{idx + 1}
|
||||
</span>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ fontWeight: 600, color: "#1f2937", fontSize: "14px", marginBottom: "4px" }}>
|
||||
{item.title || item.content}
|
||||
</div>
|
||||
{item.description && (
|
||||
<p style={{ fontSize: "12px", color: "#6b7280", margin: 0 }}>{item.description}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 温馨提示 */}
|
||||
{shareData.tips && shareData.tips.length > 0 && (
|
||||
<div className="bg-morandi-50 rounded-2xl p-4">
|
||||
<h4 style={{ fontWeight: 600, color: "#1f2937", fontSize: "14px", marginBottom: "8px", display: "flex", alignItems: "center", gap: "8px", marginTop: 0 }}>
|
||||
<Heart className="w-4 h-4 text-morandi-400" />
|
||||
温馨提示
|
||||
</h4>
|
||||
<ul style={{ listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: "4px" }}>
|
||||
{shareData.tips.map((tip: string, idx: number) => (
|
||||
<li key={idx} style={{ fontSize: "12px", color: "#6b7280", display: "flex", alignItems: "flex-start", gap: "8px" }}>
|
||||
<span style={{ color: "#b8a9c9" }}>•</span>
|
||||
<span>{tip}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 底部声明 */}
|
||||
<div style={{ paddingTop: "20px", borderTop: "2px solid #f3f4f6", textAlign: "center" }}>
|
||||
<p style={{ fontSize: "12px", color: "#9ca3af", marginBottom: "4px", margin: "0 0 4px 0" }}>
|
||||
本报告由 AI 生成,仅供参考,不能替代医生诊断
|
||||
</p>
|
||||
<p style={{ fontSize: "12px", color: "#9ca3af", marginBottom: "8px", margin: "0 0 8px 0" }}>
|
||||
如有疑问请及时咨询专业医生
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: "4px",
|
||||
fontSize: "11px",
|
||||
color: "#b8a9c9",
|
||||
padding: "4px 12px",
|
||||
background: "#faf7fc",
|
||||
borderRadius: "9999px",
|
||||
}}
|
||||
>
|
||||
<Heart style={{ width: 12, height: 12, fill: "#b8a9c9", color: "#b8a9c9" }} />
|
||||
<span>孕育无忧 · yun.tun88.com</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user