完善初始化更新
This commit is contained in:
127
sproutgate-frontend/src/components/PublicUserPage.jsx
Normal file
127
sproutgate-frontend/src/components/PublicUserPage.jsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { API_BASE, marked, formatWebsiteLabel, formatUserRegisteredAt } from "../config";
|
||||
import icons from "../icons";
|
||||
import { InfoRow, StatItem } from "./common";
|
||||
|
||||
export default function PublicUserPage({ account, onReady, onPreviewImage }) {
|
||||
const [user, setUser] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
const loadPublicUser = async () => {
|
||||
if (!account) {
|
||||
setError("缺少账户名");
|
||||
setLoading(false);
|
||||
if (!cancelled && onReady) onReady();
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
setError("");
|
||||
setUser(null);
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/api/public/users/${encodeURIComponent(account)}`);
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || "加载公开主页失败");
|
||||
setUser(data.user || null);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
if (!cancelled && onReady) onReady();
|
||||
}
|
||||
};
|
||||
|
||||
loadPublicUser();
|
||||
return () => { cancelled = true; };
|
||||
}, [account, onReady]);
|
||||
|
||||
const avatarUrl = user?.avatarUrl || "https://dummyimage.com/160x160/ddd/fff&text=Avatar";
|
||||
const displayName = user?.username || user?.account || "未命名用户";
|
||||
|
||||
return (
|
||||
<section className="panel">
|
||||
{loading && <div className="unified-page-loading">加载公开主页中…</div>}
|
||||
{!loading && error && (
|
||||
<div className="unified-page-miss">
|
||||
<h2>未找到用户</h2>
|
||||
<div className="error">{error}</div>
|
||||
<div className="actions">
|
||||
<a className="primary" href="/">返回首页</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!loading && user && (
|
||||
<div className="card profile">
|
||||
<div className="profile-header">
|
||||
<img
|
||||
src={avatarUrl}
|
||||
alt={displayName}
|
||||
className="previewable-image"
|
||||
onClick={() => onPreviewImage?.(avatarUrl, displayName)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
/>
|
||||
<div>
|
||||
<h2>{displayName}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="profile-section-title profile-section-title--lead">基本信息</div>
|
||||
<div className="profile-info-rows">
|
||||
<InfoRow icon={icons.account} label="账户" value={user.account} />
|
||||
<InfoRow icon={icons.username} label="用户名" value={user.username || "未填写"} />
|
||||
<InfoRow icon={icons.calendar} label="注册时间" value={formatUserRegisteredAt(user.createdAt)} />
|
||||
{user.websiteUrl ? (
|
||||
<InfoRow icon={icons.link} label="个人主页">
|
||||
<a
|
||||
href={user.websiteUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="profile-external-link"
|
||||
>
|
||||
{formatWebsiteLabel(user.websiteUrl)}
|
||||
</a>
|
||||
</InfoRow>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="profile-section-title">统计信息</div>
|
||||
<div className="profile-stats-flow">
|
||||
<StatItem icon={icons.level} label="等级" value={`${user.level ?? 0}`} />
|
||||
<StatItem icon={icons.coins} label="萌芽币" value={user.sproutCoins ?? 0} />
|
||||
<StatItem icon={icons.calendar} label="签到天数" value={`${user.checkInDays ?? 0}`} />
|
||||
<StatItem icon={icons.statLightning} label="连续签到" value={`${user.checkInStreak ?? 0}`} />
|
||||
<StatItem icon={icons.statClock} label="访问天数" value={`${user.visitDays ?? 0}`} />
|
||||
<StatItem icon={icons.statRepeat} label="连续访问" value={`${user.visitStreak ?? 0}`} />
|
||||
</div>
|
||||
|
||||
<div className="profile-section-title">活动记录</div>
|
||||
<div className="profile-activity-row">
|
||||
<span>最后签到 <strong>{user.lastCheckInAt || user.lastCheckInDate || "未签到"}</strong></span>
|
||||
<span>最后访问 <strong>{user.lastVisitAt || user.lastVisitDate || "未访问"}</strong></span>
|
||||
</div>
|
||||
<div className="profile-activity-row profile-visit-meta">
|
||||
<span>
|
||||
最后访问 IP{" "}
|
||||
<strong className="mono">{user.lastVisitIp || "暂无"}</strong>
|
||||
</span>
|
||||
<span>
|
||||
最后位置 <strong>{user.lastVisitDisplayLocation || "暂无"}</strong>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="profile-section-title">个人简介</div>
|
||||
<div className="markdown profile-markdown">
|
||||
<div
|
||||
className="markdown-body"
|
||||
dangerouslySetInnerHTML={{ __html: marked.parse(user.bio || "暂无简介") }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user