zhang
7 小时以前 70930071a49190f414c8d8bc9c9e9795a4096739
zy-acs-flow/src/map/NewsLogDialog.jsx
@@ -30,6 +30,9 @@
const MIN_HEIGHT = 240;
const EDGE_MARGIN = 16;
const MAX_VISIBLE_LOGS = 200;
const MAX_BUFFER_LOGS = 600;
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
const NewsLogDialog = ({ open, onClose }) => {
@@ -47,6 +50,9 @@
    const programmaticScrollRef = useRef(false);
    const initialScrollDoneRef = useRef(false);
    const userScrollRef = useRef(false);
    const autoScrollRef = useRef(true);
    const nearBottomRef = useRef(true);
    const pendingLogsRef = useRef(null);
    const resizeRef = useRef({
        active: false,
        startX: 0,
@@ -73,14 +79,52 @@
        };
    }, [getSizeLimits]);
    const applyLogs = useCallback((payload, { programmatic = false } = {}) => {
        if (!payload) {
            return;
        }
        programmaticScrollRef.current = programmatic;
        pendingLogsRef.current = null;
        setLogs(payload.logs);
        setLastUpdated(payload.timestamp);
    }, []);
    const flushPendingLogs = useCallback((options = {}) => {
        if (!pendingLogsRef.current) {
            return false;
        }
        applyLogs(pendingLogsRef.current, options);
        return true;
    }, [applyLogs]);
    const computeNearBottom = useCallback(() => {
        const container = scrollRef.current;
        if (!container) {
            return true;
        }
        const { scrollTop, scrollHeight, clientHeight } = container;
        return scrollHeight - (scrollTop + clientHeight) < 32;
    }, []);
    const fetchLogs = useCallback(async () => {
        const data = await Http.fetchNewsLogs();
        if (Array.isArray(data)) {
            programmaticScrollRef.current = true;
            setLogs(data);
            setLastUpdated(new Date());
            let nextLogs = data;
            if (data.length > MAX_VISIBLE_LOGS) {
                const shouldStickToBottom = autoScrollRef.current && nearBottomRef.current;
                const limit = shouldStickToBottom
                    ? MAX_VISIBLE_LOGS
                    : Math.min(MAX_BUFFER_LOGS, data.length);
                nextLogs = data.slice(data.length - limit);
            }
            const payload = { logs: nextLogs, timestamp: new Date() };
            if (autoScrollRef.current && nearBottomRef.current) {
                applyLogs(payload, { programmatic: true });
            } else {
                pendingLogsRef.current = payload;
            }
        }
    }, []);
    }, [applyLogs]);
    useEffect(() => {
        if (!open) {
@@ -91,6 +135,8 @@
            setPosition(null);
            setSize(null);
            initialScrollDoneRef.current = false;
            nearBottomRef.current = true;
            pendingLogsRef.current = null;
            return;
        }
        setSize(buildInitialSize());
@@ -98,6 +144,8 @@
        userScrollRef.current = false;
        programmaticScrollRef.current = true;
        initialScrollDoneRef.current = false;
        nearBottomRef.current = true;
        pendingLogsRef.current = null;
        fetchLogs();
        pollingRef.current = setInterval(fetchLogs, POLLING_INTERVAL);
        return () => {
@@ -111,6 +159,7 @@
            return;
        }
        programmaticScrollRef.current = true;
        nearBottomRef.current = true;
        const step = (remaining) => {
            if (!scrollRef.current) {
                return;
@@ -139,19 +188,25 @@
        initialScrollDoneRef.current = true;
    }, [logs, autoScroll, scrollToBottom]);
    useEffect(() => {
        autoScrollRef.current = autoScroll;
    }, [autoScroll]);
    const handleScroll = useCallback(() => {
        if (!scrollRef.current) {
            return;
        }
        if (programmaticScrollRef.current) {
            programmaticScrollRef.current = false;
            nearBottomRef.current = computeNearBottom();
            return;
        }
        const isNearBottom = computeNearBottom();
        nearBottomRef.current = isNearBottom;
        if (!autoScroll) {
            userScrollRef.current = !isNearBottom;
            return;
        }
        const { scrollTop, scrollHeight, clientHeight } = scrollRef.current;
        const isNearBottom = scrollHeight - (scrollTop + clientHeight) < 32;
        if (isNearBottom) {
            if (userScrollRef.current) {
                userScrollRef.current = false;
@@ -159,9 +214,10 @@
        } else {
            userScrollRef.current = true;
        }
    }, [autoScroll]);
    }, [autoScroll, computeNearBottom]);
    const handleJumpLatest = () => {
        flushPendingLogs({ programmatic: true });
        userScrollRef.current = false;
        setAutoScroll(true);
        scrollToBottom('smooth', 2);
@@ -176,8 +232,10 @@
        const { checked } = event.target;
        setAutoScroll(checked);
        if (checked) {
            flushPendingLogs({ programmatic: true });
            userScrollRef.current = false;
            initialScrollDoneRef.current = false;
            nearBottomRef.current = true;
            scrollToBottom('auto', 5);
        } else {
            userScrollRef.current = true;
@@ -382,6 +440,7 @@
                        flex: 1,
                        minHeight: 0,
                        overflowY: 'auto',
                        overflowX: 'auto',
                        px: 0,
                        py: 0,
                        backgroundColor: theme.palette.background.default,
@@ -392,7 +451,11 @@
                            {translate('page.map.monitor.log.empty', { _: 'No Logs' })}
                        </Typography>
                    ) : (
                        <Stack spacing={0} divider={<Divider sx={{ borderColor: theme.palette.divider, opacity: 0.8 }} />}>
                        <Stack
                            spacing={0}
                            divider={<Divider sx={{ borderColor: theme.palette.divider, opacity: 0.8 }} />}
                            sx={{ minWidth: '100%', width: 'fit-content' }}
                        >
                            {logs.map((item, index) => {
                                const level = item?.l;
                                const levelMeta = LOG_LEVEL_META[level] || LOG_LEVEL_META[1];
@@ -402,6 +465,7 @@
                                        sx={{
                                            px: 2,
                                            py: 1.25,
                                            width: '100%',
                                            backgroundColor: index % 2 === 0
                                                ? theme.palette.background.paper
                                                : theme.palette.background.default,