feat: 站点媒体库与移动端底部导航,服务测试补强

- 新增媒体库:media_library/media_thumbs 服务(WebP 缩略图)、管理端 media 页面
- 移动端底部导航 MobileTabBar 替换 MobilePostBar
- 旧数据导入增强与测试、路由与登录会话/板块侧边栏测试补强
- site-doc 组件精简(移除 Breadcrumb),文档新增迁移公告说明
This commit is contained in:
2026-09-27 02:35:38 +08:00
parent 734afbc39f
commit 1d862a0dd3
61 changed files with 4757 additions and 1129 deletions

View File

@@ -114,12 +114,14 @@ export default function ChatShell({
const longPressTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
/** 本人刚发出的消息 ID:拉列表时若水位尚未跟上,避免把这条算成未读 */
const lastSentIdByRoom = useRef<Map<number, number>>(new Map());
/** 搜索重开私聊:router.push 完成前 activeRoomId 仍为 null,列表刷新需据此抑制目标会话未读 */
const enteringRoomIdRef = useRef<number | null>(null);
/** 拉列表/SSR 回填时,当前打开的会话未读一律置 0(已读请求可能尚未落库) */
const applyRooms = useCallback(
(list: ChatRoomView[]) => {
const next = (list || []).map((r) => {
if (activeRoomId && r.id === activeRoomId) {
if ((activeRoomId && r.id === activeRoomId) || r.id === enteringRoomIdRef.current) {
return { ...r, unread_count: 0 };
}
const sentId = lastSentIdByRoom.current.get(r.id);
@@ -302,6 +304,9 @@ export default function ChatShell({
// 进房即清角标;MarkRead 成功后再清一次(防 refresh 抢在已读落库前写回旧未读)
useEffect(() => {
if (!activeRoomId) return;
if (enteringRoomIdRef.current === activeRoomId) {
enteringRoomIdRef.current = null;
}
const clearActiveUnread = () => {
setRooms((prev) =>
prev.map((r) => (r.id === activeRoomId ? { ...r, unread_count: 0 } : r))
@@ -346,6 +351,7 @@ export default function ChatShell({
const openDM = async (uid: number) => {
try {
const { room } = await apiOpenDirectChat(uid);
enteringRoomIdRef.current = room.id;
setQuery("");
setUserHits([]);
router.push(`/chat/${room.id}`);
@@ -468,7 +474,7 @@ export default function ChatShell({
)}
</div>
<div className="flex-1 overflow-y-auto min-h-0">
<div className="j13-chat-scroll flex-1 overflow-y-auto min-h-0">
{query.trim() && (
<div className="px-2 pb-2">
<p