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