修改ImageViewer显示错误的bug,添加到底了提示,暂时去除下拉刷新功能

This commit is contained in:
LeonspaceX
2025-11-28 19:55:13 +08:00
parent e239416871
commit 91334d4712
2 changed files with 34 additions and 60 deletions

View File

@@ -1,7 +1,7 @@
// 你好感谢你愿意看源代码但是悄悄告诉你代码其实是AI写的所以质量很差喵。抱歉呜呜呜😭。 // 你好感谢你愿意看源代码但是悄悄告诉你代码其实是AI写的所以质量很差喵。抱歉呜呜呜😭。
import React, { useState, useEffect, useRef, useCallback } from 'react'; 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 { BrowserRouter, Routes, Route } from 'react-router-dom';
import PostCard from './components/PostCard'; import PostCard from './components/PostCard';
import MainLayout from './layouts/MainLayout'; import MainLayout from './layouts/MainLayout';
@@ -16,6 +16,7 @@ import ReportState from './components/ReportState';
import AdminPage from './components/AdminPage'; import AdminPage from './components/AdminPage';
import InitPage from './pages/InitPage'; import InitPage from './pages/InitPage';
import NotFound from './pages/NotFound'; import NotFound from './pages/NotFound';
import ImageViewer from './components/ImageViewer';
function App() { function App() {
const [isDarkMode, setIsDarkMode] = React.useState(false); const [isDarkMode, setIsDarkMode] = React.useState(false);
@@ -32,15 +33,15 @@ function App() {
const [refreshing, setRefreshing] = useState(false); const [refreshing, setRefreshing] = useState(false);
const observer = useRef<IntersectionObserver>(null); const observer = useRef<IntersectionObserver>(null);
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const touchStartYRef = useRef<number | null>(null);
const pullDeltaRef = useRef<number>(0);
const lastRefreshAtRef = useRef<number>(0); const lastRefreshAtRef = useRef<number>(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 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) => { const lastArticleRef = useCallback((node: HTMLDivElement) => {
if (loading) return; if (loading) return;
@@ -66,40 +67,9 @@ function App() {
if (containerRef.current) containerRef.current.scrollTop = 0; if (containerRef.current) containerRef.current.scrollTop = 0;
}; };
const onTouchStart: React.TouchEventHandler<HTMLDivElement> = (e) => { // 移除触摸下拉刷新逻辑
touchStartYRef.current = e.touches[0]?.clientY ?? null;
pullDeltaRef.current = 0;
setOffsetAnimated(false);
};
const onTouchMove: React.TouchEventHandler<HTMLDivElement> = (e) => { // 撤销 Pointer 事件回退,恢复为纯 Touch 逻辑
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<HTMLDivElement> = () => {
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);
};
const onWheel: React.WheelEventHandler<HTMLDivElement> = (e) => { const onWheel: React.WheelEventHandler<HTMLDivElement> = (e) => {
const atTop = (containerRef.current?.scrollTop ?? 0) <= 0; const atTop = (containerRef.current?.scrollTop ?? 0) <= 0;
@@ -152,9 +122,6 @@ function App() {
<div <div
style={{ width: '100%', height: 'calc(100vh - 64px)', overflowY: 'auto', padding: '20px' }} style={{ width: '100%', height: 'calc(100vh - 64px)', overflowY: 'auto', padding: '20px' }}
ref={containerRef} ref={containerRef}
onTouchStart={onTouchStart}
onTouchMove={onTouchMove}
onTouchEnd={onTouchEnd}
onWheel={onWheel} onWheel={onWheel}
> >
<div style={{ <div style={{
@@ -162,8 +129,7 @@ function App() {
flexDirection: 'column', flexDirection: 'column',
alignItems: 'center', alignItems: 'center',
minHeight: '100%', minHeight: '100%',
transform: `translateY(${pullOffset}px)`, // 移除下拉位移动画
transition: offsetAnimated ? 'transform 200ms ease' : 'none'
}}> }}>
{/* 刷新提示改为 toast不显示顶部灰字 */} {/* 刷新提示改为 toast不显示顶部灰字 */}
{articles.map((article, index) => { {articles.map((article, index) => {
@@ -175,6 +141,7 @@ function App() {
content={article.content} content={article.content}
upvotes={article.upvotes} upvotes={article.upvotes}
downvotes={article.downvotes} downvotes={article.downvotes}
onPreviewImage={openImageViewer}
/> />
</div> </div>
); );
@@ -186,11 +153,21 @@ function App() {
content={article.content} content={article.content}
upvotes={article.upvotes} upvotes={article.upvotes}
downvotes={article.downvotes} downvotes={article.downvotes}
onPreviewImage={openImageViewer}
/> />
); );
} }
})} })}
{loading && <div>...</div>} {loading && <div>...</div>}
{!loading && !hasMore && (
<div style={{ width: '100%', display: 'flex', alignItems: 'center', margin: '16px 0' }}>
<div style={{ flex: 1, height: 1, backgroundColor: tokens.colorNeutralStroke2 }} />
<div style={{ padding: '0 12px', color: tokens.colorNeutralForeground3, textAlign: 'center', whiteSpace: 'nowrap' }}>
~
</div>
<div style={{ flex: 1, height: 1, backgroundColor: tokens.colorNeutralStroke2 }} />
</div>
)}
</div> </div>
</div> </div>
} }
@@ -206,6 +183,9 @@ function App() {
</Routes> </Routes>
</BrowserRouter> </BrowserRouter>
<ToastContainer /> <ToastContainer />
{imageViewer.open && imageViewer.src && (
<ImageViewer src={imageViewer.src!} alt={imageViewer.alt} onClose={closeImageViewer} />
)}
</FluentProvider> </FluentProvider>
); );
} }

View File

@@ -20,7 +20,7 @@ import {
} from '@fluentui/react-icons'; } from '@fluentui/react-icons';
import ReportPost from './ReportPost'; import ReportPost from './ReportPost';
import CommentSection from './CommentSection'; import CommentSection from './CommentSection';
import ImageViewer from './ImageViewer'; // 由上层统一挂载 ImageViewerPostCard 只负责触发
const useStyles = makeStyles({ const useStyles = makeStyles({
card: { card: {
@@ -136,13 +136,15 @@ interface PostCardProps {
content: string; content: string;
upvotes: number; upvotes: number;
downvotes: number; downvotes: number;
onPreviewImage?: (src: string, alt?: string) => void;
} }
const PostCard = ({ const PostCard = ({
id, id,
content, content,
upvotes, upvotes,
downvotes downvotes,
onPreviewImage,
}: PostCardProps) => { }: PostCardProps) => {
const styles = useStyles(); const styles = useStyles();
const markdownContent = content; const markdownContent = content;
@@ -154,13 +156,7 @@ const PostCard = ({
const [hasVoted, setHasVoted] = React.useState(false); const [hasVoted, setHasVoted] = React.useState(false);
const [showReportModal, setShowReportModal] = React.useState(false); const [showReportModal, setShowReportModal] = React.useState(false);
const [showComments, setShowComments] = 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 ( return (
<Card className={styles.card}> <Card className={styles.card}>
@@ -173,7 +169,7 @@ const PostCard = ({
<img <img
{...props} {...props}
style={{ cursor: 'zoom-in', maxWidth: '100%', height: 'auto', display: 'block' }} style={{ cursor: 'zoom-in', maxWidth: '100%', height: 'auto', display: 'block' }}
onClick={() => 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 = ({
</div> </div>
)} )}
{imageViewer.open && imageViewer.src && ( {/* 预览层由上层组件统一挂载,这里不再渲染 ImageViewer */}
<ImageViewer src={imageViewer.src!} alt={imageViewer.alt} onClose={closeImageViewer} />
)}
</Card> </Card>
); );
}; };
export default PostCard; export default PostCard;