新增日志面板,ws连接状态

This commit is contained in:
lifangliang 2025-07-11 17:58:17 +08:00
parent e525e06d7a
commit b0b6c6dfad
7 changed files with 768 additions and 3 deletions

View File

@ -33,6 +33,8 @@ public class PrintQueueGUI implements LocaleChangeListener {
private final PrintStatisticsPanel statisticsPanel;
private final PrintTaskSearchPanel searchPanel;
private final PrintSettingsPanel settingsPanel;
private final WebSocketStatusPanel webSocketStatusPanel;
private final SystemLogPanel systemLogPanel;
private JFrame frame;
private JTable currentTaskTable;
private JTable queuedTasksTable;
@ -45,11 +47,14 @@ public class PrintQueueGUI implements LocaleChangeListener {
@Autowired
public PrintQueueGUI(PrintQueueService printQueueService,
PrintStatisticsPanel statisticsPanel, PrintTaskSearchPanel searchPanel,
PrintSettingsPanel settingsPanel) {
PrintSettingsPanel settingsPanel, WebSocketStatusPanel webSocketStatusPanel,
SystemLogPanel systemLogPanel) {
this.printQueueService = printQueueService;
this.statisticsPanel = statisticsPanel;
this.searchPanel = searchPanel;
this.settingsPanel = settingsPanel;
this.webSocketStatusPanel = webSocketStatusPanel;
this.systemLogPanel = systemLogPanel;
// 注册语言变更监听器
LocaleManager.getInstance().addLocaleChangeListener(this);
@ -123,6 +128,12 @@ public class PrintQueueGUI implements LocaleChangeListener {
// 添加设置面板
tabbedPane.addTab(MessageUtils.getMessage("tab.settings"), settingsPanel);
// 添加WebSocket状态面板
tabbedPane.addTab(MessageUtils.getMessage("tab.websocket.status"), webSocketStatusPanel);
// 添加系统日志面板
tabbedPane.addTab(MessageUtils.getMessage("tab.system.log"), systemLogPanel);
frame.add(tabbedPane, BorderLayout.CENTER);

View File

@ -0,0 +1,64 @@
package com.goeing.printserver.main.gui;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
import lombok.extern.slf4j.Slf4j;
/**
* 自定义日志Appender将日志输出到Swing界面
*/
public class SwingLogAppender extends AppenderBase<ILoggingEvent> {
private static SystemLogPanel logPanel;
/**
* 设置日志面板
*/
public static void setLogPanel(SystemLogPanel panel) {
logPanel = panel;
}
@Override
protected void append(ILoggingEvent event) {
if (logPanel != null && isStarted()) {
try {
// 转换日志级别
SystemLogPanel.LogLevel level = convertLogLevel(event.getLevel());
// 格式化日志消息
String message = String.format("[%s] %s",
event.getLoggerName(),
event.getFormattedMessage());
// 如果有异常信息添加到消息中
if (event.getThrowableProxy() != null) {
message += "\n" + event.getThrowableProxy().getMessage();
}
// 添加到日志面板
logPanel.addLogEntry(level, message);
} catch (Exception e) {
// 避免日志记录本身出错导致的循环问题
System.err.println("SwingLogAppender error: " + e.getMessage());
}
}
}
/**
* 转换日志级别
*/
private SystemLogPanel.LogLevel convertLogLevel(ch.qos.logback.classic.Level level) {
switch (level.toInt()) {
case ch.qos.logback.classic.Level.ERROR_INT:
return SystemLogPanel.LogLevel.ERROR;
case ch.qos.logback.classic.Level.WARN_INT:
return SystemLogPanel.LogLevel.WARN;
case ch.qos.logback.classic.Level.INFO_INT:
return SystemLogPanel.LogLevel.INFO;
case ch.qos.logback.classic.Level.DEBUG_INT:
case ch.qos.logback.classic.Level.TRACE_INT:
default:
return SystemLogPanel.LogLevel.DEBUG;
}
}
}

View File

@ -0,0 +1,332 @@
package com.goeing.printserver.main.gui;
import com.goeing.printserver.main.utils.LocaleChangeListener;
import com.goeing.printserver.main.utils.LocaleManager;
import com.goeing.printserver.main.utils.MessageUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.swing.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* 系统日志面板
*/
@Component
@Slf4j
public class SystemLogPanel extends JPanel implements LocaleChangeListener {
private static final int MAX_LOG_LINES = 1000; // 最大日志行数
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private final BlockingQueue<LogEntry> logQueue = new LinkedBlockingQueue<>();
// UI组件
private JLabel titleLabel;
private JTextPane logTextPane;
private JScrollPane scrollPane;
private JButton clearButton;
private JButton saveButton;
private JComboBox<LogLevel> logLevelComboBox;
private JCheckBox autoScrollCheckBox;
// 样式
private SimpleAttributeSet infoStyle;
private SimpleAttributeSet warnStyle;
private SimpleAttributeSet errorStyle;
private SimpleAttributeSet debugStyle;
// 当前日志级别过滤
private LogLevel currentLogLevel = LogLevel.INFO;
public SystemLogPanel() {
initializeStyles();
initializeUI();
startLogProcessor();
initializeLogPanel();
// 注册语言变更监听器
LocaleManager.getInstance().addLocaleChangeListener(this);
}
/**
* 初始化文本样式
*/
private void initializeStyles() {
infoStyle = new SimpleAttributeSet();
StyleConstants.setForeground(infoStyle, Color.BLACK);
warnStyle = new SimpleAttributeSet();
StyleConstants.setForeground(warnStyle, Color.ORANGE);
StyleConstants.setBold(warnStyle, true);
errorStyle = new SimpleAttributeSet();
StyleConstants.setForeground(errorStyle, Color.RED);
StyleConstants.setBold(errorStyle, true);
debugStyle = new SimpleAttributeSet();
StyleConstants.setForeground(debugStyle, Color.GRAY);
StyleConstants.setItalic(debugStyle, true);
}
/**
* 初始化用户界面
*/
private void initializeUI() {
setLayout(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
// 创建顶部面板
JPanel topPanel = new JPanel(new BorderLayout());
// 标题
titleLabel = new JLabel(MessageUtils.getMessage("log.panel.title"));
titleLabel.setFont(new Font(titleLabel.getFont().getName(), Font.BOLD, 16));
titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
topPanel.add(titleLabel, BorderLayout.WEST);
// 控制面板
JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
// 日志级别过滤
JLabel levelLabel = new JLabel(MessageUtils.getMessage("log.level.filter"));
controlPanel.add(levelLabel);
logLevelComboBox = new JComboBox<>(LogLevel.values());
logLevelComboBox.setSelectedItem(currentLogLevel);
logLevelComboBox.addActionListener(e -> {
currentLogLevel = (LogLevel) logLevelComboBox.getSelectedItem();
refreshLogDisplay();
});
controlPanel.add(logLevelComboBox);
// 自动滚动
autoScrollCheckBox = new JCheckBox(MessageUtils.getMessage("log.auto.scroll"), true);
controlPanel.add(autoScrollCheckBox);
// 清空按钮
clearButton = new JButton(MessageUtils.getMessage("log.button.clear"));
clearButton.addActionListener(e -> clearLogs());
controlPanel.add(clearButton);
// 保存按钮
saveButton = new JButton(MessageUtils.getMessage("log.button.save"));
saveButton.addActionListener(e -> saveLogs());
controlPanel.add(saveButton);
topPanel.add(controlPanel, BorderLayout.EAST);
add(topPanel, BorderLayout.NORTH);
// 创建日志显示区域
logTextPane = new JTextPane();
logTextPane.setEditable(false);
logTextPane.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
logTextPane.setBackground(Color.WHITE);
scrollPane = new JScrollPane(logTextPane);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
add(scrollPane, BorderLayout.CENTER);
}
/**
* 启动日志处理器
*/
private void startLogProcessor() {
Thread logProcessor = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
LogEntry entry = logQueue.take();
SwingUtilities.invokeLater(() -> appendLogEntry(entry));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
logProcessor.setDaemon(true);
logProcessor.setName("LogProcessor");
logProcessor.start();
}
/**
* 初始化日志面板
*/
private void initializeLogPanel() {
// 注册到SwingLogAppender
SwingLogAppender.setLogPanel(this);
// 添加初始化日志
addLogEntry(LogLevel.INFO, "日志面板已初始化");
}
/**
* 添加日志条目公开方法供SwingLogAppender调用
*/
public void addLogEntry(LogLevel level, String message) {
LogEntry entry = new LogEntry(level, message, LocalDateTime.now());
logQueue.offer(entry);
}
/**
* 添加日志条目到显示区域
*/
private void appendLogEntry(LogEntry entry) {
if (entry.level.ordinal() < currentLogLevel.ordinal()) {
return; // 过滤掉低级别的日志
}
Document doc = logTextPane.getDocument();
try {
// 检查是否超过最大行数
if (doc.getLength() > 0) {
String text = doc.getText(0, doc.getLength());
int lineCount = text.split("\n").length;
if (lineCount > MAX_LOG_LINES) {
// 删除前面的行
int firstLineEnd = text.indexOf('\n');
if (firstLineEnd > 0) {
doc.remove(0, firstLineEnd + 1);
}
}
}
// 格式化日志消息
String formattedMessage = String.format("[%s] [%s] %s\n",
entry.timestamp.format(dateFormatter),
entry.level.name(),
entry.message);
// 选择样式
SimpleAttributeSet style = getStyleForLevel(entry.level);
// 添加到文档
doc.insertString(doc.getLength(), formattedMessage, style);
// 自动滚动到底部
if (autoScrollCheckBox.isSelected()) {
logTextPane.setCaretPosition(doc.getLength());
}
} catch (BadLocationException e) {
log.error("添加日志条目失败", e);
}
}
/**
* 获取日志级别对应的样式
*/
private SimpleAttributeSet getStyleForLevel(LogLevel level) {
switch (level) {
case ERROR:
return errorStyle;
case WARN:
return warnStyle;
case DEBUG:
return debugStyle;
case INFO:
default:
return infoStyle;
}
}
/**
* 刷新日志显示
*/
private void refreshLogDisplay() {
// 这里可以重新过滤和显示日志
// 为简化实现暂时不做复杂的过滤重显示
}
/**
* 清空日志
*/
private void clearLogs() {
int option = JOptionPane.showConfirmDialog(this,
MessageUtils.getMessage("log.clear.confirm"),
MessageUtils.getMessage("dialog.confirm"),
JOptionPane.YES_NO_OPTION);
if (option == JOptionPane.YES_OPTION) {
logTextPane.setText("");
log.info("日志已清空");
}
}
/**
* 保存日志到文件
*/
private void saveLogs() {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle(MessageUtils.getMessage("log.save.dialog.title"));
fileChooser.setSelectedFile(new java.io.File("printserver_logs_" +
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".txt"));
int result = fileChooser.showSaveDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
try {
java.io.File file = fileChooser.getSelectedFile();
java.nio.file.Files.write(file.toPath(), logTextPane.getText().getBytes());
JOptionPane.showMessageDialog(this,
MessageUtils.getMessage("log.save.success", new Object[]{file.getAbsolutePath()}),
MessageUtils.getMessage("dialog.success"),
JOptionPane.INFORMATION_MESSAGE);
} catch (Exception e) {
log.error("保存日志文件失败", e);
JOptionPane.showMessageDialog(this,
MessageUtils.getMessage("log.save.error", new Object[]{e.getMessage()}),
MessageUtils.getMessage("dialog.error"),
JOptionPane.ERROR_MESSAGE);
}
}
}
@Override
public void onLocaleChanged(Locale newLocale) {
SwingUtilities.invokeLater(() -> {
titleLabel.setText(MessageUtils.getMessage("log.panel.title"));
clearButton.setText(MessageUtils.getMessage("log.button.clear"));
saveButton.setText(MessageUtils.getMessage("log.button.save"));
autoScrollCheckBox.setText(MessageUtils.getMessage("log.auto.scroll"));
});
}
/**
* 日志级别枚举
*/
public enum LogLevel {
DEBUG, INFO, WARN, ERROR
}
/**
* 日志条目类
*/
private static class LogEntry {
final LogLevel level;
final String message;
final LocalDateTime timestamp;
LogEntry(LogLevel level, String message, LocalDateTime timestamp) {
this.level = level;
this.message = message;
this.timestamp = timestamp;
}
}
/**
* 清理资源
*/
public void shutdown() {
LocaleManager.getInstance().removeLocaleChangeListener(this);
}
}

View File

@ -0,0 +1,258 @@
package com.goeing.printserver.main.gui;
import com.goeing.printserver.main.sse.PrinterClient;
import com.goeing.printserver.main.utils.LocaleChangeListener;
import com.goeing.printserver.main.utils.LocaleManager;
import com.goeing.printserver.main.utils.MessageUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.swing.*;
import java.awt.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* WebSocket连接状态面板
*/
@Component
@Slf4j
public class WebSocketStatusPanel extends JPanel implements LocaleChangeListener {
private final PrinterClient printerClient;
private final ScheduledExecutorService refreshExecutor = Executors.newSingleThreadScheduledExecutor();
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// UI组件
private JLabel titleLabel;
private JLabel connectionStatusLabel;
private JLabel connectionUrlLabel;
private JLabel lastConnectTimeLabel;
private JLabel reconnectCountLabel;
private JButton reconnectButton;
private JButton disconnectButton;
// 状态数据
private boolean isConnected = false;
private String connectionUrl = "";
private LocalDateTime lastConnectTime;
private int reconnectCount = 0;
@Autowired
public WebSocketStatusPanel(PrinterClient printerClient) {
this.printerClient = printerClient;
initializeUI();
startStatusRefresh();
// 注册语言变更监听器
LocaleManager.getInstance().addLocaleChangeListener(this);
}
/**
* 初始化用户界面
*/
private void initializeUI() {
setLayout(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
// 创建标题
titleLabel = new JLabel(MessageUtils.getMessage("websocket.status.title"));
titleLabel.setFont(new Font(titleLabel.getFont().getName(), Font.BOLD, 16));
titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
add(titleLabel, BorderLayout.NORTH);
// 创建状态信息面板
JPanel statusPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(5, 5, 5, 5);
gbc.anchor = GridBagConstraints.WEST;
gbc.fill = GridBagConstraints.HORIZONTAL;
// 连接状态
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 0.0;
JLabel statusTitleLabel = new JLabel(MessageUtils.getMessage("websocket.status.connection"));
statusPanel.add(statusTitleLabel, gbc);
gbc.gridx = 1;
gbc.weightx = 1.0;
connectionStatusLabel = new JLabel(MessageUtils.getMessage("websocket.status.disconnected"));
connectionStatusLabel.setForeground(Color.RED);
statusPanel.add(connectionStatusLabel, gbc);
// 连接URL
gbc.gridx = 0;
gbc.gridy = 1;
gbc.weightx = 0.0;
JLabel urlTitleLabel = new JLabel(MessageUtils.getMessage("websocket.status.url"));
statusPanel.add(urlTitleLabel, gbc);
gbc.gridx = 1;
gbc.weightx = 1.0;
connectionUrlLabel = new JLabel("-");
statusPanel.add(connectionUrlLabel, gbc);
// 最后连接时间
gbc.gridx = 0;
gbc.gridy = 2;
gbc.weightx = 0.0;
JLabel timeTitleLabel = new JLabel(MessageUtils.getMessage("websocket.status.last.connect"));
statusPanel.add(timeTitleLabel, gbc);
gbc.gridx = 1;
gbc.weightx = 1.0;
lastConnectTimeLabel = new JLabel("-");
statusPanel.add(lastConnectTimeLabel, gbc);
// 重连次数
gbc.gridx = 0;
gbc.gridy = 3;
gbc.weightx = 0.0;
JLabel reconnectTitleLabel = new JLabel(MessageUtils.getMessage("websocket.status.reconnect.count"));
statusPanel.add(reconnectTitleLabel, gbc);
gbc.gridx = 1;
gbc.weightx = 1.0;
reconnectCountLabel = new JLabel("0");
statusPanel.add(reconnectCountLabel, gbc);
// 添加弹性空间
gbc.gridx = 0;
gbc.gridy = 4;
gbc.gridwidth = 2;
gbc.weighty = 1.0;
statusPanel.add(Box.createVerticalGlue(), gbc);
add(statusPanel, BorderLayout.CENTER);
// 创建按钮面板
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
reconnectButton = new JButton(MessageUtils.getMessage("websocket.button.reconnect"));
reconnectButton.addActionListener(e -> reconnectWebSocket());
buttonPanel.add(reconnectButton);
disconnectButton = new JButton(MessageUtils.getMessage("websocket.button.disconnect"));
disconnectButton.addActionListener(e -> disconnectWebSocket());
buttonPanel.add(disconnectButton);
add(buttonPanel, BorderLayout.SOUTH);
}
/**
* 启动状态刷新
*/
private void startStatusRefresh() {
refreshExecutor.scheduleAtFixedRate(this::updateStatus, 0, 1, TimeUnit.SECONDS);
}
/**
* 更新状态信息
*/
private void updateStatus() {
SwingUtilities.invokeLater(() -> {
try {
// 这里需要从PrinterClient获取实际状态
// 由于PrinterClient没有公开状态方法我们使用反射或添加公开方法
updateConnectionStatus();
} catch (Exception e) {
log.error("更新WebSocket状态失败", e);
}
});
}
/**
* 更新连接状态显示
*/
private void updateConnectionStatus() {
// 获取实际的连接状态
boolean actuallyConnected = printerClient.isConnected();
String currentUrl = printerClient.getCurrentConnectionUrl();
if (actuallyConnected) {
connectionStatusLabel.setText(MessageUtils.getMessage("websocket.status.connected"));
connectionStatusLabel.setForeground(Color.GREEN);
reconnectButton.setEnabled(false);
disconnectButton.setEnabled(true);
// 如果之前是断开状态现在连接了更新连接时间
if (!isConnected) {
lastConnectTime = LocalDateTime.now();
}
} else {
connectionStatusLabel.setText(MessageUtils.getMessage("websocket.status.disconnected"));
connectionStatusLabel.setForeground(Color.RED);
reconnectButton.setEnabled(true);
disconnectButton.setEnabled(false);
}
isConnected = actuallyConnected;
connectionUrl = currentUrl != null ? currentUrl : "";
connectionUrlLabel.setText(connectionUrl.isEmpty() ? "-" : connectionUrl);
lastConnectTimeLabel.setText(lastConnectTime != null ? lastConnectTime.format(dateFormatter) : "-");
reconnectCountLabel.setText(String.valueOf(reconnectCount));
}
/**
* 重新连接WebSocket
*/
private void reconnectWebSocket() {
try {
printerClient.reconnect();
reconnectCount++;
lastConnectTime = LocalDateTime.now();
isConnected = true; // 假设连接成功
log.info("手动重新连接WebSocket");
} catch (Exception e) {
log.error("重新连接WebSocket失败", e);
JOptionPane.showMessageDialog(this,
MessageUtils.getMessage("websocket.error.reconnect", new Object[]{e.getMessage()}),
MessageUtils.getMessage("dialog.error"),
JOptionPane.ERROR_MESSAGE);
}
}
/**
* 断开WebSocket连接
*/
private void disconnectWebSocket() {
try {
printerClient.disconnect();
isConnected = false;
log.info("手动断开WebSocket连接");
} catch (Exception e) {
log.error("断开WebSocket连接失败", e);
JOptionPane.showMessageDialog(this,
MessageUtils.getMessage("websocket.error.disconnect", new Object[]{e.getMessage()}),
MessageUtils.getMessage("dialog.error"),
JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void onLocaleChanged(Locale newLocale) {
SwingUtilities.invokeLater(() -> {
titleLabel.setText(MessageUtils.getMessage("websocket.status.title"));
reconnectButton.setText(MessageUtils.getMessage("websocket.button.reconnect"));
disconnectButton.setText(MessageUtils.getMessage("websocket.button.disconnect"));
updateConnectionStatus();
});
}
/**
* 清理资源
*/
public void shutdown() {
if (refreshExecutor != null && !refreshExecutor.isShutdown()) {
refreshExecutor.shutdownNow();
}
LocaleManager.getInstance().removeLocaleChangeListener(this);
}
}

View File

@ -231,6 +231,41 @@ public class PrinterClient implements ApplicationRunner {
isConnecting = false;
connect();
}
/**
* 获取WebSocket连接状态
*/
public boolean isConnected() {
return session != null && session.isOpen();
}
/**
* 获取当前连接的URL
*/
public String getCurrentConnectionUrl() {
if (isConnected()) {
String serverUri = config.getWebsocketUrl();
String printerId = config.getPrinterId();
String apiKey = config.getApiKey();
return serverUri + "?printerId=" + printerId + "&apiKey=" + apiKey;
}
return null;
}
/**
* 断开WebSocket连接
*/
public void disconnect() {
if (session != null && session.isOpen()) {
try {
session.close();
log.info("WebSocket连接已手动断开");
} catch (Exception e) {
log.error("断开WebSocket连接失败", e);
throw new RuntimeException("断开连接失败", e);
}
}
}
@Override
public void run(ApplicationArguments args) {

View File

@ -45,6 +45,8 @@ tab.printer.status=Printer Status
tab.statistics=Statistics
tab.task.search=Task Search
tab.settings=Settings
tab.websocket.status=WebSocket Status
tab.system.log=System Log
# Table Headers
table.fileUrl=File URL
@ -156,4 +158,33 @@ tray.notification.title=Print Server Started
tray.notification.message=Print Server is running in the background, click the tray icon to open the main window
app.status.running=Running
menu.help=Help
menu.help.about=About
menu.help.about=About
# Tab titles
tab.websocket.status=WebSocket Status
tab.system.log=System Log
# WebSocket Status Panel
websocket.status.title=WebSocket Status
websocket.status.connected=Connected
websocket.status.disconnected=Disconnected
websocket.status.url=Connection URL:
websocket.status.last.connect=Last Connect Time:
websocket.status.reconnect.count=Reconnect Count:
websocket.button.reconnect=Reconnect
websocket.button.disconnect=Disconnect
websocket.error.reconnect=Failed to reconnect: {0}
websocket.error.disconnect=Failed to disconnect: {0}
# System Log Panel
log.panel.title=System Log
log.level.all=All
log.level.error=Error
log.level.warn=Warning
log.level.info=Info
log.level.debug=Debug
log.auto.scroll=Auto Scroll
log.button.clear=Clear
log.button.save=Save
log.save.success=Log saved successfully to: {0}
log.save.error=Failed to save log: {0}

View File

@ -43,6 +43,8 @@ tab.current.task=当前任务
tab.queued.tasks=队列任务
tab.printer.status=打印机状态
tab.task.search=任务搜索
tab.websocket.status=WebSocket状态
tab.system.log=系统日志
# 表格标题
table.fileUrl=文件URL
@ -154,4 +156,36 @@ tray.open=打开主窗口
tray.exit=退出
tray.notification.title=打印服务器已启动
tray.notification.message=打印服务器正在后台运行,点击托盘图标可打开主窗口
app.status.running=运行中
app.status.running=运行中
system.tray.tooltip=打印服务器
system.tray.show=显示主窗口
system.tray.hide=隐藏主窗口
system.tray.exit=退出应用程序
# 选项卡标题
tab.websocket.status=WebSocket状态
tab.system.log=系统日志
# WebSocket状态面板
websocket.status.title=WebSocket连接状态
websocket.status.connection=连接状态:
websocket.status.url=服务器地址:
websocket.status.last.connect=最后连接时间:
websocket.status.reconnect.count=重连次数:
websocket.status.connected=已连接
websocket.status.disconnected=未连接
websocket.button.reconnect=重新连接
websocket.button.disconnect=断开连接
websocket.error.reconnect=重新连接失败:{0}
websocket.error.disconnect=断开连接失败:{0}
# 系统日志面板
log.panel.title=系统日志
log.level.filter=日志级别:
log.auto.scroll=自动滚动
log.button.clear=清空
log.button.save=保存到文件
log.clear.confirm=确定要清空所有日志吗?
log.save.dialog.title=保存日志文件
log.save.success=日志文件保存成功:{0}
log.save.error=保存日志文件失败:{0}