fix(admin): 日志页改为保持倒序,打开自动定位到顶部(最新)
按用户预期调整方案:列表保持最新在最上面(后端倒序),打开页面/ 自动刷新时 scrollTop=0 定位顶部;向下滚动回看历史时暂停定位, 拉回顶部即恢复。替代上一版的时间正序+贴底方案。
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
* - 缓冲上限 2000 条,进程重启后清零
|
* - 缓冲上限 2000 条,进程重启后清零
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useEffect, useState, useCallback, useRef, useMemo } from 'react'
|
import { useEffect, useState, useCallback, useRef } from 'react'
|
||||||
import { fetchLogs } from '../dal'
|
import { fetchLogs } from '../dal'
|
||||||
import type { LogEntry } from '../types'
|
import type { LogEntry } from '../types'
|
||||||
import { Card, CardBody, CardHeader, Badge, SectionHeader, Alert, Spinner, SkeletonBlock } from '../components'
|
import { Card, CardBody, CardHeader, Badge, SectionHeader, Alert, Spinner, SkeletonBlock } from '../components'
|
||||||
@@ -53,24 +53,21 @@ export default function LogsPage() {
|
|||||||
}, [load])
|
}, [load])
|
||||||
|
|
||||||
const scrollRef = useRef<HTMLDivElement>(null)
|
const scrollRef = useRef<HTMLDivElement>(null)
|
||||||
const userScrolledUp = useRef(false)
|
const userScrolledDown = useRef(false)
|
||||||
|
|
||||||
// 检测用户是否向上滚动过
|
// 检测用户是否向下滚动回看历史(旧日志在下方)
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
const el = scrollRef.current
|
const el = scrollRef.current
|
||||||
if (!el) return
|
if (!el) return
|
||||||
const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 50
|
const atTop = el.scrollTop < 50
|
||||||
userScrolledUp.current = !atBottom
|
userScrolledDown.current = !atTop
|
||||||
}
|
}
|
||||||
|
|
||||||
// 后端返回最新在前;渲染转为时间正序(旧→新),配合「自动贴底」滚动:
|
// 列表最新在最上面(后端倒序返回);打开页面/自动刷新时定位到顶部=最新。
|
||||||
// 打开页面/自动刷新默认定位到最新日志,用户上滚回看时暂停跟随
|
// 用户向下滚动回看历史时暂停定位,拉回顶部即恢复。
|
||||||
const ordered = useMemo(() => [...entries].reverse(), [entries])
|
|
||||||
|
|
||||||
// 加载后自动滚动到底部(仅当用户未向上滚动时)
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (autoRefresh && !userScrolledUp.current && scrollRef.current) {
|
if (autoRefresh && !userScrolledDown.current && scrollRef.current) {
|
||||||
scrollRef.current.scrollTop = scrollRef.current.scrollHeight
|
scrollRef.current.scrollTop = 0
|
||||||
}
|
}
|
||||||
}, [entries, autoRefresh])
|
}, [entries, autoRefresh])
|
||||||
|
|
||||||
@@ -146,7 +143,7 @@ export default function LogsPage() {
|
|||||||
<p className="py-10 text-center text-xs text-ink-400">暂无匹配的日志</p>
|
<p className="py-10 text-center text-xs text-ink-400">暂无匹配的日志</p>
|
||||||
) : (
|
) : (
|
||||||
<div ref={scrollRef} onScroll={handleScroll} className="max-h-[60vh] overflow-y-auto">
|
<div ref={scrollRef} onScroll={handleScroll} className="max-h-[60vh] overflow-y-auto">
|
||||||
{ordered.map((e, i) => {
|
{entries.map((e, i) => {
|
||||||
const badge = LEVEL_BADGE[e.level] ?? { status: 'info' as const, text: e.level }
|
const badge = LEVEL_BADGE[e.level] ?? { status: 'info' as const, text: e.level }
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user