From 10492a922d3a8d295ada4ec99cc928031f3abd0e Mon Sep 17 00:00:00 2001
From: vincentlu <t1341870251@gmail.com>
Date: 星期二, 17 三月 2026 15:29:22 +0800
Subject: [PATCH] #
---
zy-acs-flow/src/map/NewsLogDialog.jsx | 420 +++++++++++++++++++++++++++++++++++
zy-acs-flow/src/map/http.js | 15 +
zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/TrafficService.java | 3
zy-acs-flow/src/i18n/en.js | 11
zy-acs-manager/src/main/java/com/zy/acs/manager/common/CodeBuilder.java | 3
zy-acs-flow/src/i18n/zh.js | 11
zy-acs-flow/src/map/MapPage.jsx | 12
zy-acs-manager/src/main/java/com/zy/acs/manager/core/controller/NewsController.java | 2
zy-acs-common/src/main/java/com/zy/acs/common/utils/News.java | 197 ++++++++--------
9 files changed, 570 insertions(+), 104 deletions(-)
diff --git a/zy-acs-common/src/main/java/com/zy/acs/common/utils/News.java b/zy-acs-common/src/main/java/com/zy/acs/common/utils/News.java
index 34f9abf..5729f35 100644
--- a/zy-acs-common/src/main/java/com/zy/acs/common/utils/News.java
+++ b/zy-acs-common/src/main/java/com/zy/acs/common/utils/News.java
@@ -1,10 +1,16 @@
package com.zy.acs.common.utils;
+import lombok.Data;
import lombok.extern.slf4j.Slf4j;
+import org.slf4j.helpers.FormattingTuple;
+import org.slf4j.helpers.MessageFormatter;
-import java.lang.reflect.Array;
-import java.text.SimpleDateFormat;
-import java.util.*;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
/**
* news stories for vincent
@@ -13,85 +19,15 @@
@Slf4j
public class News {
+ private static final int DEFAULT_CAPACITY = 1024;
+ private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ private static final NewsQueue<NewsDomain> NEWS_QUEUE = new NewsQueue<>(DEFAULT_CAPACITY);
+
public static void main(String[] args) {
News.info("info{}", 1);
News.warn("warn{}", 2);
News.error("error{}", 3);
System.out.println(News.print());
- }
-
- interface NewsSupport<T> { boolean execute(T t); }
-
- private static final NewsQueue<NewsDomain> NEWS_QUEUE = new NewsQueue<>(NewsDomain.class, 1024);
-
- @SuppressWarnings({"unchecked"})
- static class NewsQueue<T> {
-
- private final transient Class<T> cls;
- private final T[] arr;
- private final int capacity;
- private int head;
- private int tail;
-
- { this.head = 0; this.tail = 0; }
-
- public NewsQueue(Class<T> cls, int capacity) {
- this.cls = cls;
- this.arr = (T[]) Array.newInstance(cls, capacity);
- this.capacity = capacity;
- }
-
- public synchronized boolean offer(T t) {
- if (this.tail == this.capacity) {
- this.peek();
- }
- this.reform();
- this.arr[this.tail] = t;
- this.tail ++;
- return true;
- }
-
- public synchronized boolean put(T t) {
- if (this.tail == this.capacity) {
- return false;
- } else {
- this.reform();
- }
- this.arr[this.tail] = t;
- this.tail ++;
- return true;
- }
-
- public synchronized T peek() {
- if (this.head == this.tail) {
- return null;
- }
- T t = this.arr[this.head];
- this.head ++;
- this.reform();
- return t;
- }
-
- private void reform() {
- for (int i = this.head; i < this.tail; i++) {
- this.arr[i-this.head] = this.arr[i];
- }
- this.tail -= this.head;
- this.head = 0;
- }
-
- public synchronized int size() {
- return this.tail - this.head;
- }
-
- public synchronized List<T> data() {
- T[] ts = (T[]) Array.newInstance(this.cls, size());
- if (this.tail - this.head >= 0) {
- System.arraycopy(this.arr, this.head, ts, 0, this.tail - this.head);
- }
- return Arrays.asList(ts);
- }
-
}
public static void info(String format, Object... arguments) {
@@ -115,9 +51,9 @@
for (int i = 0; i < domains.size(); i++) {
NewsDomain domain = domains.get(i);
sb.append("{");
- sb.append("\"l\":").append(domain.level.idx).append(",");
- sb.append("\"v\":\"").append(domain.content).append("\"").append(",");
- sb.append("\"t\":\"").append(domain.date).append("\"");
+ sb.append("\"l\":").append(domain.getLevel().idx).append(",");
+ sb.append("\"v\":\"").append(escapeJson(domain.getContent())).append("\"").append(",");
+ sb.append("\"t\":\"").append(escapeJson(domain.getDate())).append("\"");
sb.append("}");
if (i < domains.size() - 1) {
sb.append(",");
@@ -131,42 +67,107 @@
List<Map<String, Object>> res = new ArrayList<>();
for (NewsDomain datum : NEWS_QUEUE.data()) {
Map<String, Object> map = new HashMap<>();
- map.put("l", datum.level.idx);
- map.put("v", datum.content);
- map.put("t", datum.date);
+ map.put("l", datum.getLevel().idx);
+ map.put("v", datum.getContent());
+ map.put("t", datum.getDate());
res.add(map);
}
return res;
}
private static boolean offer(NewsLevel level, String msg, Object[] args) {
- return NEWS_QUEUE.offer(new NewsDomain(level, replace(msg, args), (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date())));
+ String template = msg == null ? "" : msg;
+ FormattingTuple tuple = MessageFormatter.arrayFormat(template, args);
+ String formatted = tuple.getMessage();
+ return NEWS_QUEUE.offer(new NewsDomain(level, formatted,
+ LocalDateTime.now().format(DATE_FORMATTER)));
}
- private static String replace(String str, Object[] objs){
- if (null == objs || objs.length == 0 || null == str || "".equals(str.trim())) {
- return str;
- } else {
- StringBuilder sb = new StringBuilder(str);
- for (Object obj : objs) {
- int idx = sb.indexOf("{}");
- if (idx == -1) { break; }
- sb.replace(idx, idx + 2, String.valueOf(obj));
+ private static String escapeJson(String value) {
+ if (value == null) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder(value.length());
+ for (char c : value.toCharArray()) {
+ switch (c) {
+ case '"':
+ sb.append("\\\"");
+ break;
+ case '\\':
+ sb.append("\\\\");
+ break;
+ case '\n':
+ sb.append("\\n");
+ break;
+ case '\r':
+ sb.append("\\r");
+ break;
+ case '\t':
+ sb.append("\\t");
+ break;
+ default:
+ if (c < 0x20) {
+ sb.append(String.format("\\u%04x", (int) c));
+ } else {
+ sb.append(c);
+ }
}
- return sb.toString();
+ }
+ return sb.toString();
+ }
+
+ private static final class NewsQueue<T> {
+
+ private final Object[] arr;
+ private final int capacity;
+ private int head;
+ private int size;
+
+ private NewsQueue(int capacity) {
+ if (capacity <= 0) {
+ throw new IllegalArgumentException("capacity must be > 0");
+ }
+ this.arr = new Object[capacity];
+ this.capacity = capacity;
+ this.head = 0;
+ this.size = 0;
+ }
+
+ public synchronized boolean offer(T t) {
+ int writeIndex = (head + size) % capacity;
+ arr[writeIndex] = t;
+ if (size == capacity) {
+ head = (head + 1) % capacity;
+ } else {
+ size++;
+ }
+ return true;
+ }
+
+ public synchronized List<T> data() {
+ List<T> copy = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ int idx = (head + i) % capacity;
+ @SuppressWarnings("unchecked")
+ T element = (T) arr[idx];
+ copy.add(element);
+ }
+ return copy;
}
}
+ @Data
static class NewsDomain {
- public NewsLevel level;
- public String content;
- public String date;
+ private final NewsLevel level;
+ private final String content;
+ private final String date;
public NewsDomain(NewsLevel level, String content, String date) {
this.level = level;
this.content = content;
this.date = date;
}
+
}
enum NewsLevel {
@@ -174,7 +175,7 @@
WARN(2),
ERROR(3),
;
- public int idx;
+ public final int idx;
NewsLevel(int idx) {
this.idx = idx;
}
diff --git a/zy-acs-flow/src/i18n/en.js b/zy-acs-flow/src/i18n/en.js
index 89a69d0..12465ff 100644
--- a/zy-acs-flow/src/i18n/en.js
+++ b/zy-acs-flow/src/i18n/en.js
@@ -875,6 +875,17 @@
},
map: {
welcome: 'Welcome to the RCS System. Tip: Left-click to select objects, right-click to pan the view, and use the scroll wheel to zoom the view.',
+ monitor: {
+ log: {
+ title: 'Real-time Logs',
+ autoScroll: 'Auto Scroll',
+ empty: 'No Logs',
+ jumpLatest: 'Jump to latest',
+ lastUpdate: {
+ empty: 'No Updates',
+ },
+ },
+ },
devices: {
title: 'Icons',
shelf: 'SHELF',
diff --git a/zy-acs-flow/src/i18n/zh.js b/zy-acs-flow/src/i18n/zh.js
index f44c842..b912507 100644
--- a/zy-acs-flow/src/i18n/zh.js
+++ b/zy-acs-flow/src/i18n/zh.js
@@ -875,6 +875,17 @@
},
map: {
welcome: '娆㈣繋浣跨敤 RCS 绯荤粺銆傛彁绀猴細榧犳爣宸﹂敭閫変腑瀵硅薄锛屽彸閿钩绉昏鍥撅紝婊氳疆缂╂斁瑙嗗浘銆�',
+ monitor: {
+ log: {
+ title: '鏃ュ織鐩戞帶',
+ autoScroll: '鑷姩婊氬姩',
+ empty: '鏆傛棤鏃ュ織',
+ jumpLatest: '鍥炲埌鏈�鏂�',
+ lastUpdate: {
+ empty: '鏆傛棤鏇存柊',
+ },
+ },
+ },
devices: {
title: '鍥炬爣搴�',
shelf: '璐ф灦',
diff --git a/zy-acs-flow/src/map/MapPage.jsx b/zy-acs-flow/src/map/MapPage.jsx
index 8b229e2..44f8813 100644
--- a/zy-acs-flow/src/map/MapPage.jsx
+++ b/zy-acs-flow/src/map/MapPage.jsx
@@ -29,6 +29,7 @@
import RouteFab from "./header/RouteFab";
import AreaFab from "./header/AreaFab";
import MoreOperate from "./header/MoreOperate";
+import NewsLogDialog from "./NewsLogDialog";
let player;
let websocket;
@@ -66,6 +67,7 @@
const storedValue = localStorage.getItem('curZone');
return storedValue !== null ? JSON.parse(storedValue) : null;
});
+ const [logDialogOpen, setLogDialogOpen] = useState(false);
const handleResize = () => {
if (!contentRef.current || !player) {
@@ -315,7 +317,11 @@
>
{rcsStatus ? translate('page.map.action.shutdown') : translate('page.map.action.startup')}
</Button>
- <Button variant="contained" color="primary">
+ <Button
+ variant="contained"
+ color="primary"
+ onClick={() => setLogDialogOpen(true)}
+ >
{translate('page.map.action.monitor')}
</Button>
<MoreOperate />
@@ -592,6 +598,10 @@
width={378}
/>
+ <NewsLogDialog
+ open={logDialogOpen}
+ onClose={() => setLogDialogOpen(false)}
+ />
</Box>
);
}
diff --git a/zy-acs-flow/src/map/NewsLogDialog.jsx b/zy-acs-flow/src/map/NewsLogDialog.jsx
new file mode 100644
index 0000000..695c9bf
--- /dev/null
+++ b/zy-acs-flow/src/map/NewsLogDialog.jsx
@@ -0,0 +1,420 @@
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import {
+ Box,
+ Button,
+ Chip,
+ Divider,
+ FormControlLabel,
+ IconButton,
+ Paper,
+ Stack,
+ Switch,
+ Typography,
+ useTheme,
+} from '@mui/material';
+import CloseIcon from '@mui/icons-material/Close';
+import PushPinIcon from '@mui/icons-material/PushPin';
+import { useTranslate } from 'react-admin';
+import * as Http from './http';
+
+const LOG_LEVEL_META = {
+ 1: { label: 'INFO', color: 'info' },
+ 2: { label: 'WARN', color: 'warning' },
+ 3: { label: 'ERROR', color: 'error' },
+};
+
+const POLLING_INTERVAL = 2000;
+const PANEL_WIDTH = 480;
+const PANEL_HEIGHT = 360;
+const MIN_WIDTH = 320;
+const MIN_HEIGHT = 240;
+const EDGE_MARGIN = 16;
+
+const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
+
+const NewsLogDialog = ({ open, onClose }) => {
+ const translate = useTranslate();
+ const theme = useTheme();
+ const [logs, setLogs] = useState([]);
+ const [autoScroll, setAutoScroll] = useState(true);
+ const [lastUpdated, setLastUpdated] = useState(null);
+ const [position, setPosition] = useState(null);
+ const [size, setSize] = useState(null);
+ const scrollRef = useRef(null);
+ const panelRef = useRef(null);
+ const pollingRef = useRef(null);
+ const dragRef = useRef({ active: false, offsetX: 0, offsetY: 0 });
+ const resizeRef = useRef({
+ active: false,
+ startX: 0,
+ startY: 0,
+ initialWidth: PANEL_WIDTH,
+ initialHeight: PANEL_HEIGHT,
+ initialX: EDGE_MARGIN,
+ initialY: EDGE_MARGIN,
+ });
+ const getSizeLimits = useCallback(() => {
+ const viewportWidth = typeof window === 'undefined' ? PANEL_WIDTH + 64 : window.innerWidth;
+ const viewportHeight = typeof window === 'undefined' ? PANEL_HEIGHT + 160 : window.innerHeight;
+ return {
+ maxWidth: Math.max(MIN_WIDTH, viewportWidth - 64),
+ maxHeight: Math.max(MIN_HEIGHT, viewportHeight - 160),
+ };
+ }, []);
+
+ const buildInitialSize = useCallback(() => {
+ const { maxWidth, maxHeight } = getSizeLimits();
+ return {
+ width: clamp(PANEL_WIDTH, MIN_WIDTH, maxWidth),
+ height: clamp(PANEL_HEIGHT, MIN_HEIGHT, maxHeight),
+ };
+ }, [getSizeLimits]);
+
+ const fetchLogs = useCallback(async () => {
+ const data = await Http.fetchNewsLogs();
+ if (Array.isArray(data)) {
+ setLogs(data);
+ setLastUpdated(new Date());
+ }
+ }, []);
+
+ useEffect(() => {
+ if (!open) {
+ clearInterval(pollingRef.current);
+ pollingRef.current = null;
+ setLogs([]);
+ setLastUpdated(null);
+ setPosition(null);
+ setSize(null);
+ return;
+ }
+ setSize(buildInitialSize());
+ setAutoScroll(true);
+ fetchLogs();
+ pollingRef.current = setInterval(fetchLogs, POLLING_INTERVAL);
+ return () => {
+ clearInterval(pollingRef.current);
+ pollingRef.current = null;
+ };
+ }, [open, fetchLogs, buildInitialSize]);
+
+ useEffect(() => {
+ if (!autoScroll || !scrollRef.current) {
+ return;
+ }
+ scrollRef.current.scrollTo({
+ top: scrollRef.current.scrollHeight,
+ behavior: 'smooth',
+ });
+ }, [logs, autoScroll]);
+
+ const handleScroll = useCallback(() => {
+ if (!scrollRef.current || !autoScroll) {
+ return;
+ }
+ const { scrollTop, scrollHeight, clientHeight } = scrollRef.current;
+ if (scrollHeight - (scrollTop + clientHeight) > 32) {
+ setAutoScroll(false);
+ }
+ }, [autoScroll]);
+
+ const handleJumpLatest = () => {
+ setAutoScroll(true);
+ if (scrollRef.current) {
+ scrollRef.current.scrollTo({
+ top: scrollRef.current.scrollHeight,
+ behavior: 'smooth',
+ });
+ }
+ };
+
+ const handleClose = () => {
+ onClose?.();
+ };
+
+ const handleDragging = useCallback((event) => {
+ if (!dragRef.current.active) {
+ return;
+ }
+ event.preventDefault();
+ const width = panelRef.current?.offsetWidth || PANEL_WIDTH;
+ const height = panelRef.current?.offsetHeight || PANEL_HEIGHT;
+ const viewportWidth = typeof window === 'undefined' ? width + 2 * EDGE_MARGIN : window.innerWidth;
+ const viewportHeight = typeof window === 'undefined' ? height + 10 * EDGE_MARGIN : window.innerHeight;
+ const maxX = viewportWidth - width - EDGE_MARGIN;
+ const maxY = viewportHeight - height - EDGE_MARGIN;
+ const x = event.clientX - dragRef.current.offsetX;
+ const y = event.clientY - dragRef.current.offsetY;
+ setPosition({
+ x: clamp(x, EDGE_MARGIN, Math.max(maxX, EDGE_MARGIN)),
+ y: clamp(y, EDGE_MARGIN, Math.max(maxY, EDGE_MARGIN)),
+ });
+ }, []);
+
+ const handleDragEnd = useCallback(() => {
+ if (!dragRef.current.active) {
+ return;
+ }
+ dragRef.current.active = false;
+ document.removeEventListener('mousemove', handleDragging);
+ document.removeEventListener('mouseup', handleDragEnd);
+ }, [handleDragging]);
+
+ const handleDragStart = useCallback((event) => {
+ if (!panelRef.current) {
+ return;
+ }
+ const rect = panelRef.current.getBoundingClientRect();
+ dragRef.current = {
+ active: true,
+ offsetX: event.clientX - rect.left,
+ offsetY: event.clientY - rect.top,
+ };
+ document.addEventListener('mousemove', handleDragging);
+ document.addEventListener('mouseup', handleDragEnd);
+ }, [handleDragging, handleDragEnd]);
+
+ const handleResizing = useCallback((event) => {
+ if (!resizeRef.current.active) {
+ return;
+ }
+ event.preventDefault();
+ const deltaX = event.clientX - resizeRef.current.startX;
+ const deltaY = event.clientY - resizeRef.current.startY;
+ const { maxWidth, maxHeight } = getSizeLimits();
+ const initialWidth = resizeRef.current.initialWidth;
+ const initialHeight = resizeRef.current.initialHeight;
+ const initialX = resizeRef.current.initialX;
+ const initialY = resizeRef.current.initialY;
+ const initialRight = initialX + initialWidth;
+ const maxWidthAllowed = Math.min(maxWidth, initialRight - EDGE_MARGIN);
+ const widthRaw = initialWidth - deltaX;
+ const newWidth = clamp(widthRaw, MIN_WIDTH, Math.max(MIN_WIDTH, maxWidthAllowed));
+ const newX = clamp(initialRight - newWidth, EDGE_MARGIN, Math.max(initialRight - MIN_WIDTH, EDGE_MARGIN));
+ const newHeight = clamp(initialHeight + deltaY, MIN_HEIGHT, maxHeight);
+ setSize({
+ width: newWidth,
+ height: newHeight,
+ });
+ setPosition({
+ x: newX,
+ y: initialY,
+ });
+ }, [getSizeLimits]);
+
+ const handleResizeEnd = useCallback(() => {
+ if (!resizeRef.current.active) {
+ return;
+ }
+ resizeRef.current.active = false;
+ document.removeEventListener('mousemove', handleResizing);
+ document.removeEventListener('mouseup', handleResizeEnd);
+ }, [handleResizing]);
+
+ const handleResizeStart = useCallback((event) => {
+ event.preventDefault();
+ resizeRef.current.active = true;
+ resizeRef.current.startX = event.clientX;
+ resizeRef.current.startY = event.clientY;
+ const rect = panelRef.current?.getBoundingClientRect();
+ const resolvedX = position?.x ?? rect?.left ?? EDGE_MARGIN;
+ const resolvedY = position?.y ?? rect?.top ?? EDGE_MARGIN;
+ resizeRef.current.initialWidth = panelRef.current?.offsetWidth || size?.width || PANEL_WIDTH;
+ resizeRef.current.initialHeight = panelRef.current?.offsetHeight || size?.height || PANEL_HEIGHT;
+ resizeRef.current.initialX = resolvedX;
+ resizeRef.current.initialY = resolvedY;
+ setPosition({ x: resolvedX, y: resolvedY });
+ document.addEventListener('mousemove', handleResizing);
+ document.addEventListener('mouseup', handleResizeEnd);
+ }, [handleResizing, handleResizeEnd, size, position]);
+
+ useEffect(() => {
+ return () => {
+ document.removeEventListener('mousemove', handleDragging);
+ document.removeEventListener('mouseup', handleDragEnd);
+ document.removeEventListener('mousemove', handleResizing);
+ document.removeEventListener('mouseup', handleResizeEnd);
+ };
+ }, [handleDragging, handleDragEnd, handleResizing, handleResizeEnd]);
+
+ const timeLabel = useMemo(() => {
+ if (!lastUpdated) {
+ return translate('page.map.monitor.log.lastUpdate.empty', { _: 'No Updates' });
+ }
+ return lastUpdated.toLocaleTimeString();
+ }, [lastUpdated, translate]);
+
+ const panelPositionStyle = position
+ ? { top: position.y, left: position.x }
+ : { top: 140, right: 24 };
+
+ if (!open) {
+ return null;
+ }
+
+ const panelWidth = size?.width ?? PANEL_WIDTH;
+ const panelHeight = size?.height ?? PANEL_HEIGHT;
+
+ return (
+ <Box
+ sx={{
+ position: 'fixed',
+ zIndex: theme.zIndex.drawer + 10,
+ pointerEvents: 'none',
+ ...panelPositionStyle,
+ }}
+ >
+ <Paper
+ ref={panelRef}
+ elevation={16}
+ sx={{
+ width: panelWidth,
+ height: panelHeight,
+ minWidth: MIN_WIDTH,
+ minHeight: MIN_HEIGHT,
+ display: 'flex',
+ flexDirection: 'column',
+ borderRadius: 2,
+ overflow: 'hidden',
+ pointerEvents: 'auto',
+ backdropFilter: 'blur(4px)',
+ position: 'relative',
+ }}
+ >
+ <Box
+ sx={{
+ display: 'flex',
+ alignItems: 'center',
+ padding: '12px 16px',
+ borderBottom: `1px solid ${theme.palette.divider}`,
+ cursor: 'move',
+ background: theme.palette.mode === 'light'
+ ? theme.palette.grey[50]
+ : theme.palette.grey[900],
+ }}
+ onMouseDown={handleDragStart}
+ >
+ <Stack direction="row" spacing={1} alignItems="center">
+ <PushPinIcon fontSize="small" color="primary" />
+ <Typography variant="subtitle1" fontWeight={600}>
+ {translate('page.map.monitor.log.title', { _: 'Real-time Logs' })}
+ </Typography>
+ </Stack>
+ <Box sx={{ flexGrow: 1 }} />
+ <FormControlLabel
+ sx={{ mr: 2, userSelect: 'none' }}
+ control={(
+ <Switch
+ size="small"
+ checked={autoScroll}
+ onChange={(event) => setAutoScroll(event.target.checked)}
+ />
+ )}
+ label={translate('page.map.monitor.log.autoScroll', { _: 'Auto Scroll' })}
+ />
+ <Chip
+ label={timeLabel}
+ size="small"
+ color="primary"
+ variant="outlined"
+ sx={{ mr: 1, fontWeight: 500 }}
+ />
+ <IconButton size="small" onClick={handleClose}>
+ <CloseIcon fontSize="small" />
+ </IconButton>
+ </Box>
+ <Box
+ ref={scrollRef}
+ onScroll={handleScroll}
+ sx={{
+ flex: 1,
+ minHeight: 0,
+ overflowY: 'auto',
+ px: 3,
+ py: 2,
+ backgroundColor: theme.palette.background.default,
+ }}
+ >
+ {logs.length === 0 ? (
+ <Typography variant="body2" color="text.secondary" sx={{ textAlign: 'center', py: 6 }}>
+ {translate('page.map.monitor.log.empty', { _: 'No Logs' })}
+ </Typography>
+ ) : (
+ <Stack spacing={1.25}>
+ {logs.map((item, index) => {
+ const level = item?.l;
+ const levelMeta = LOG_LEVEL_META[level] || LOG_LEVEL_META[1];
+ return (
+ <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,
+ }}
+ >
+ <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">
+ {item?.t || '--'}
+ </Typography>
+ </Stack>
+ <Typography variant="body2" sx={{ mt: 1, whiteSpace: 'pre-wrap' }}>
+ {item?.v || ''}
+ </Typography>
+ </Box>
+ );
+ })}
+ </Stack>
+ )}
+ </Box>
+ <Divider />
+ <Box
+ sx={{
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ py: 1,
+ px: 2,
+ backgroundColor: theme.palette.background.paper,
+ }}
+ >
+ {!autoScroll && logs.length > 0 ? (
+ <Button onClick={handleJumpLatest} size="small">
+ {translate('page.map.monitor.log.jumpLatest', { _: 'Jump to latest' })}
+ </Button>
+ ) : (
+ <span />
+ )}
+ <Button onClick={handleClose} size="small">
+ {translate('ra.action.close', { _: 'ra.action.close' })}
+ </Button>
+ </Box>
+ <Box
+ onMouseDown={handleResizeStart}
+ sx={{
+ position: 'absolute',
+ width: 18,
+ height: 18,
+ left: 6,
+ bottom: 6,
+ cursor: 'nesw-resize',
+ borderLeft: `2px solid ${theme.palette.divider}`,
+ borderBottom: `2px solid ${theme.palette.divider}`,
+ borderBottomLeftRadius: 2,
+ }}
+ />
+ </Paper>
+ </Box>
+ );
+};
+
+export default NewsLogDialog;
diff --git a/zy-acs-flow/src/map/http.js b/zy-acs-flow/src/map/http.js
index f1be56e..bf59dcc 100644
--- a/zy-acs-flow/src/map/http.js
+++ b/zy-acs-flow/src/map/http.js
@@ -497,3 +497,18 @@
}
return [];
}
+
+export const fetchNewsLogs = async () => {
+ try {
+ const res = await request.get('/news/print');
+ const { code, msg, data } = res.data;
+ if (code === 200) {
+ return Array.isArray(data) ? data : [];
+ }
+ notify?.error(msg);
+ } catch (error) {
+ notify?.error(error.message);
+ console.error(error.message);
+ }
+ return null;
+}
\ No newline at end of file
diff --git a/zy-acs-manager/src/main/java/com/zy/acs/manager/common/CodeBuilder.java b/zy-acs-manager/src/main/java/com/zy/acs/manager/common/CodeBuilder.java
index c45fd63..634781e 100644
--- a/zy-acs-manager/src/main/java/com/zy/acs/manager/common/CodeBuilder.java
+++ b/zy-acs-manager/src/main/java/com/zy/acs/manager/common/CodeBuilder.java
@@ -49,7 +49,6 @@
/**
*
# dual
-
TRUNCATE man_bus;
TRUNCATE man_task;
TRUNCATE man_travel;
@@ -62,7 +61,6 @@
TRUNCATE man_veh_fault_rec;
# log
-
TRUNCATE man_bus_log;
TRUNCATE man_jam_log;
TRUNCATE man_lane;
@@ -73,7 +71,6 @@
TRUNCATE man_action_log;
# init
-
TRUNCATE man_code;
TRUNCATE man_code_gap;
TRUNCATE man_route;
diff --git a/zy-acs-manager/src/main/java/com/zy/acs/manager/core/controller/NewsController.java b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/controller/NewsController.java
index ce6eca9..af609e4 100644
--- a/zy-acs-manager/src/main/java/com/zy/acs/manager/core/controller/NewsController.java
+++ b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/controller/NewsController.java
@@ -11,7 +11,7 @@
*/
@Slf4j
@RestController
-@RequestMapping("/news")
+@RequestMapping("/api/news")
public class NewsController {
@RequestMapping("/print")
diff --git a/zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/TrafficService.java b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/TrafficService.java
index dbf3a7b..98bbfda 100644
--- a/zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/TrafficService.java
+++ b/zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/TrafficService.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.acs.common.utils.GsonUtils;
+import com.zy.acs.common.utils.News;
import com.zy.acs.common.utils.Utils;
import com.zy.acs.framework.common.Cools;
import com.zy.acs.framework.common.SnowflakeIdWorker;
@@ -337,7 +338,7 @@
boolean hasUnavoidableBlocks = blockVehicleList.stream().anyMatch(blockVehicleDto -> !blockVehicleDto.isAvoidable());
if (hasUnavoidableBlocks && pathList.size() <= MapDataConstant.MIN_SLICE_PATH_LENGTH) {
- log.info("AGV[{}] waiting in place, because the path list is too short...", agvNo);
+ News.info("AGV[{}] waiting in place, because the path list is too short...", agvNo);
pathList.clear();
}
--
Gitblit v1.9.1