From 91334d471281a49371e92245d7c1ab8994ffaa43 Mon Sep 17 00:00:00 2001 From: LeonspaceX Date: Fri, 28 Nov 2025 19:55:13 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9ImageViewer=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84bug=EF=BC=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=88=B0=E5=BA=95=E4=BA=86=E6=8F=90=E7=A4=BA=EF=BC=8C=E6=9A=82?= =?UTF-8?q?=E6=97=B6=E5=8E=BB=E9=99=A4=E4=B8=8B=E6=8B=89=E5=88=B7=E6=96=B0?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 72 ++++++++++++++----------------------- src/components/PostCard.tsx | 22 +++++------- 2 files changed, 34 insertions(+), 60 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 3b9b392..6057462 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ // 你好,感谢你愿意看源代码,但是悄悄告诉你,代码其实是AI写的所以质量很差喵。抱歉呜呜呜😭。 import React, { useState, useEffect, useRef, useCallback } from 'react'; -import { FluentProvider, webLightTheme, webDarkTheme } from '@fluentui/react-components'; +import { FluentProvider, webLightTheme, webDarkTheme, tokens } from '@fluentui/react-components'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; import PostCard from './components/PostCard'; import MainLayout from './layouts/MainLayout'; @@ -16,6 +16,7 @@ import ReportState from './components/ReportState'; import AdminPage from './components/AdminPage'; import InitPage from './pages/InitPage'; import NotFound from './pages/NotFound'; +import ImageViewer from './components/ImageViewer'; function App() { const [isDarkMode, setIsDarkMode] = React.useState(false); @@ -32,15 +33,15 @@ function App() { const [refreshing, setRefreshing] = useState(false); const observer = useRef(null); const containerRef = useRef(null); - const touchStartYRef = useRef(null); - const pullDeltaRef = useRef(0); const lastRefreshAtRef = useRef(0); - const [pullOffset, setPullOffset] = useState(0); - const [offsetAnimated, setOffsetAnimated] = useState(false); - const MAX_PULL = 80; // 最大下拉位移 - const TRIGGER_PULL = 60; // 触发刷新的阈值 - const DAMPING = 0.5; // 阻尼系数,减少位移幅度 const REFRESH_COOLDOWN_MS = 5000; // 刷新冷却时间 + const [imageViewer, setImageViewer] = useState<{ open: boolean; src?: string; alt?: string }>({ open: false }); + + const openImageViewer = (src?: string, alt?: string) => { + if (!src) return; + setImageViewer({ open: true, src, alt }); + }; + const closeImageViewer = () => setImageViewer({ open: false }); const lastArticleRef = useCallback((node: HTMLDivElement) => { if (loading) return; @@ -66,40 +67,9 @@ function App() { if (containerRef.current) containerRef.current.scrollTop = 0; }; - const onTouchStart: React.TouchEventHandler = (e) => { - touchStartYRef.current = e.touches[0]?.clientY ?? null; - pullDeltaRef.current = 0; - setOffsetAnimated(false); - }; + // 移除触摸下拉刷新逻辑 - const onTouchMove: React.TouchEventHandler = (e) => { - const startY = touchStartYRef.current; - if (startY == null) return; - const currentY = e.touches[0]?.clientY ?? startY; - const rawDelta = currentY - startY; - pullDeltaRef.current = rawDelta; - const atTop = (containerRef.current?.scrollTop ?? 0) <= 0; - if (atTop && rawDelta > 0 && !loading && !refreshing) { - const offset = Math.min(MAX_PULL, rawDelta * DAMPING); - setPullOffset(offset); - } else { - setPullOffset(0); - } - }; - - const onTouchEnd: React.TouchEventHandler = () => { - const atTop = (containerRef.current?.scrollTop ?? 0) <= 0; - const shouldRefresh = atTop && pullOffset >= TRIGGER_PULL; - setOffsetAnimated(true); - setPullOffset(0); - if (shouldRefresh) { - doRefresh(); - } - touchStartYRef.current = null; - pullDeltaRef.current = 0; - // 结束后移除动画标记,下一次拖动为无动画的跟随效果 - setTimeout(() => setOffsetAnimated(false), 220); - }; + // 撤销 Pointer 事件回退,恢复为纯 Touch 逻辑 const onWheel: React.WheelEventHandler = (e) => { const atTop = (containerRef.current?.scrollTop ?? 0) <= 0; @@ -152,9 +122,6 @@ function App() {
{/* 刷新提示改为 toast,不显示顶部灰字 */} {articles.map((article, index) => { @@ -175,6 +141,7 @@ function App() { content={article.content} upvotes={article.upvotes} downvotes={article.downvotes} + onPreviewImage={openImageViewer} />
); @@ -186,11 +153,21 @@ function App() { content={article.content} upvotes={article.upvotes} downvotes={article.downvotes} + onPreviewImage={openImageViewer} /> ); } })} {loading &&
加载中...
} + {!loading && !hasMore && ( +
+
+
+ 已经到底了喵~ +
+
+
+ )}
} @@ -206,6 +183,9 @@ function App() { + {imageViewer.open && imageViewer.src && ( + + )} ); } diff --git a/src/components/PostCard.tsx b/src/components/PostCard.tsx index 0b83755..ec72cbe 100644 --- a/src/components/PostCard.tsx +++ b/src/components/PostCard.tsx @@ -20,7 +20,7 @@ import { } from '@fluentui/react-icons'; import ReportPost from './ReportPost'; import CommentSection from './CommentSection'; -import ImageViewer from './ImageViewer'; +// 由上层统一挂载 ImageViewer,PostCard 只负责触发 const useStyles = makeStyles({ card: { @@ -136,13 +136,15 @@ interface PostCardProps { content: string; upvotes: number; downvotes: number; + onPreviewImage?: (src: string, alt?: string) => void; } const PostCard = ({ id, content, upvotes, - downvotes + downvotes, + onPreviewImage, }: PostCardProps) => { const styles = useStyles(); const markdownContent = content; @@ -154,13 +156,7 @@ const PostCard = ({ const [hasVoted, setHasVoted] = React.useState(false); const [showReportModal, setShowReportModal] = React.useState(false); const [showComments, setShowComments] = React.useState(false); - const [imageViewer, setImageViewer] = React.useState<{ open: boolean; src?: string; alt?: string }>({ open: false }); - - const openImageViewer = (src?: string, alt?: string) => { - if (!src) return; - setImageViewer({ open: true, src, alt }); - }; - const closeImageViewer = () => setImageViewer({ open: false }); + // 图片预览交由上层统一处理,此处仅触发回调 return ( @@ -173,7 +169,7 @@ const PostCard = ({ openImageViewer(props.src as string, props.alt as string)} + onClick={() => onPreviewImage?.(props.src as string, props.alt as string)} /> ), }} @@ -249,11 +245,9 @@ const PostCard = ({
)} - {imageViewer.open && imageViewer.src && ( - - )} + {/* 预览层由上层组件统一挂载,这里不再渲染 ImageViewer */} ); }; -export default PostCard; \ No newline at end of file +export default PostCard;