From 70930071a49190f414c8d8bc9c9e9795a4096739 Mon Sep 17 00:00:00 2001
From: zhang <zc857179121@qq.com>
Date: 星期一, 23 三月 2026 16:08:27 +0800
Subject: [PATCH] Merge branch 'refs/heads/rcs_master' into jdxaj

---
 zy-acs-flow/src/map/NewsLogDialog.jsx |   80 ++++++++++++++++++++++++++++++++++++----
 1 files changed, 72 insertions(+), 8 deletions(-)

diff --git a/zy-acs-flow/src/map/NewsLogDialog.jsx b/zy-acs-flow/src/map/NewsLogDialog.jsx
index d344ee7..a636920 100644
--- a/zy-acs-flow/src/map/NewsLogDialog.jsx
+++ b/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,

--
Gitblit v1.9.1