From e2488e161c8d832c6f0ddbfa92e7f3f23af582c3 Mon Sep 17 00:00:00 2001
From: vincentlu <t1341870251@gmail.com>
Date: 星期二, 17 三月 2026 15:52:55 +0800
Subject: [PATCH] #

---
 zy-acs-flow/src/map/NewsLogDialog.jsx |  141 +++++++++++++++++++++++++++++++++++------------
 1 files changed, 105 insertions(+), 36 deletions(-)

diff --git a/zy-acs-flow/src/map/NewsLogDialog.jsx b/zy-acs-flow/src/map/NewsLogDialog.jsx
index 695c9bf..d344ee7 100644
--- a/zy-acs-flow/src/map/NewsLogDialog.jsx
+++ b/zy-acs-flow/src/map/NewsLogDialog.jsx
@@ -23,9 +23,9 @@
     3: { label: 'ERROR', color: 'error' },
 };
 
-const POLLING_INTERVAL = 2000;
-const PANEL_WIDTH = 480;
-const PANEL_HEIGHT = 360;
+const POLLING_INTERVAL = 1000;
+const PANEL_WIDTH = 600;
+const PANEL_HEIGHT = 420;
 const MIN_WIDTH = 320;
 const MIN_HEIGHT = 240;
 const EDGE_MARGIN = 16;
@@ -44,6 +44,9 @@
     const panelRef = useRef(null);
     const pollingRef = useRef(null);
     const dragRef = useRef({ active: false, offsetX: 0, offsetY: 0 });
+    const programmaticScrollRef = useRef(false);
+    const initialScrollDoneRef = useRef(false);
+    const userScrollRef = useRef(false);
     const resizeRef = useRef({
         active: false,
         startX: 0,
@@ -73,6 +76,7 @@
     const fetchLogs = useCallback(async () => {
         const data = await Http.fetchNewsLogs();
         if (Array.isArray(data)) {
+            programmaticScrollRef.current = true;
             setLogs(data);
             setLastUpdated(new Date());
         }
@@ -86,10 +90,14 @@
             setLastUpdated(null);
             setPosition(null);
             setSize(null);
+            initialScrollDoneRef.current = false;
             return;
         }
         setSize(buildInitialSize());
         setAutoScroll(true);
+        userScrollRef.current = false;
+        programmaticScrollRef.current = true;
+        initialScrollDoneRef.current = false;
         fetchLogs();
         pollingRef.current = setInterval(fetchLogs, POLLING_INTERVAL);
         return () => {
@@ -98,38 +106,82 @@
         };
     }, [open, fetchLogs, buildInitialSize]);
 
-    useEffect(() => {
-        if (!autoScroll || !scrollRef.current) {
+    const scrollToBottom = useCallback((behavior = 'smooth', attempts = 3) => {
+        if (!scrollRef.current) {
             return;
         }
-        scrollRef.current.scrollTo({
-            top: scrollRef.current.scrollHeight,
-            behavior: 'smooth',
-        });
-    }, [logs, autoScroll]);
+        programmaticScrollRef.current = true;
+        const step = (remaining) => {
+            if (!scrollRef.current) {
+                return;
+            }
+            scrollRef.current.scrollTo({
+                top: scrollRef.current.scrollHeight,
+                behavior,
+            });
+            if (remaining > 0) {
+                requestAnimationFrame(() => step(remaining - 1));
+            }
+        };
+        requestAnimationFrame(() => step(attempts));
+    }, []);
+
+    useEffect(() => {
+        if (!autoScroll || userScrollRef.current) {
+            return;
+        }
+        if (!logs.length) {
+            return;
+        }
+        const behavior = initialScrollDoneRef.current ? 'smooth' : 'auto';
+        const attempts = initialScrollDoneRef.current ? 1 : 5;
+        scrollToBottom(behavior, attempts);
+        initialScrollDoneRef.current = true;
+    }, [logs, autoScroll, scrollToBottom]);
 
     const handleScroll = useCallback(() => {
-        if (!scrollRef.current || !autoScroll) {
+        if (!scrollRef.current) {
+            return;
+        }
+        if (programmaticScrollRef.current) {
+            programmaticScrollRef.current = false;
+            return;
+        }
+        if (!autoScroll) {
             return;
         }
         const { scrollTop, scrollHeight, clientHeight } = scrollRef.current;
-        if (scrollHeight - (scrollTop + clientHeight) > 32) {
-            setAutoScroll(false);
+        const isNearBottom = scrollHeight - (scrollTop + clientHeight) < 32;
+        if (isNearBottom) {
+            if (userScrollRef.current) {
+                userScrollRef.current = false;
+            }
+        } else {
+            userScrollRef.current = true;
         }
     }, [autoScroll]);
 
     const handleJumpLatest = () => {
+        userScrollRef.current = false;
         setAutoScroll(true);
-        if (scrollRef.current) {
-            scrollRef.current.scrollTo({
-                top: scrollRef.current.scrollHeight,
-                behavior: 'smooth',
-            });
-        }
+        scrollToBottom('smooth', 2);
+        initialScrollDoneRef.current = true;
     };
 
     const handleClose = () => {
         onClose?.();
+    };
+
+    const handleAutoScrollChange = (event) => {
+        const { checked } = event.target;
+        setAutoScroll(checked);
+        if (checked) {
+            userScrollRef.current = false;
+            initialScrollDoneRef.current = false;
+            scrollToBottom('auto', 5);
+        } else {
+            userScrollRef.current = true;
+        }
     };
 
     const handleDragging = useCallback((event) => {
@@ -307,7 +359,7 @@
                             <Switch
                                 size="small"
                                 checked={autoScroll}
-                                onChange={(event) => setAutoScroll(event.target.checked)}
+                                onChange={handleAutoScrollChange}
                             />
                         )}
                         label={translate('page.map.monitor.log.autoScroll', { _: 'Auto Scroll' })}
@@ -330,8 +382,8 @@
                         flex: 1,
                         minHeight: 0,
                         overflowY: 'auto',
-                        px: 3,
-                        py: 2,
+                        px: 0,
+                        py: 0,
                         backgroundColor: theme.palette.background.default,
                     }}
                 >
@@ -340,7 +392,7 @@
                             {translate('page.map.monitor.log.empty', { _: 'No Logs' })}
                         </Typography>
                     ) : (
-                        <Stack spacing={1.25}>
+                        <Stack spacing={0} divider={<Divider sx={{ borderColor: theme.palette.divider, opacity: 0.8 }} />}>
                             {logs.map((item, index) => {
                                 const level = item?.l;
                                 const levelMeta = LOG_LEVEL_META[level] || LOG_LEVEL_META[1];
@@ -348,26 +400,43 @@
                                     <Box
                                         key={`${item?.t}-${index}`}
                                         sx={{
-                                            borderRadius: 1.5,
-                                            border: `1px solid ${theme.palette.divider}`,
-                                            backgroundColor: theme.palette.background.paper,
                                             px: 2,
                                             py: 1.25,
+                                            backgroundColor: index % 2 === 0
+                                                ? theme.palette.background.paper
+                                                : theme.palette.background.default,
                                         }}
                                     >
-                                        <Stack direction="row" spacing={1} alignItems="center">
-                                            <Chip
-                                                label={levelMeta.label}
-                                                size="small"
-                                                color={levelMeta.color}
-                                                variant="outlined"
-                                            />
-                                            <Divider orientation="vertical" flexItem />
-                                            <Typography variant="caption" color="text.secondary">
+                                        <Stack direction="row" spacing={1.5} alignItems="center">
+                                            <Typography
+                                                variant="caption"
+                                                sx={{
+                                                    fontWeight: 700,
+                                                    letterSpacing: 0.6,
+                                                    textTransform: 'uppercase',
+                                                    color: theme.palette[levelMeta.color]?.main || theme.palette.text.secondary,
+                                                }}
+                                            >
+                                                {levelMeta.label}
+                                            </Typography>
+                                            <Typography
+                                                variant="caption"
+                                                color="text.secondary"
+                                                sx={{ fontFamily: 'SFMono-Regular, Menlo, monospace', letterSpacing: 0.3 }}
+                                            >
                                                 {item?.t || '--'}
                                             </Typography>
                                         </Stack>
-                                        <Typography variant="body2" sx={{ mt: 1, whiteSpace: 'pre-wrap' }}>
+                                        <Typography
+                                            variant="body2"
+                                            sx={{
+                                                mt: 0.5,
+                                                whiteSpace: 'pre-wrap',
+                                                fontSize: 13,
+                                                lineHeight: 1.5,
+                                                color: theme.palette.text.primary,
+                                            }}
+                                        >
                                             {item?.v || ''}
                                         </Typography>
                                     </Box>

--
Gitblit v1.9.1