Explorar el Código

全屏后页面优化

17491 hace 2 días
padre
commit
2620e19a58

+ 0 - 4
.codex/skills/java-client-file-logging/agents/openai.yaml

@@ -1,4 +0,0 @@
-interface:
-  display_name: "Java Client File Logging"
-  short_description: "Add durable file logs to Java desktop clients"
-  default_prompt: "Use $java-client-file-logging to add client-side file logging to this Java desktop application."

+ 0 - 88
.codex/skills/java-client-file-logging/assets/AppLog.java

@@ -1,88 +0,0 @@
-package com.example.client.util;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.PrintStream;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-/**
- * Lightweight local logging for a standalone Java desktop client.
- * Change APP_NAME and package before use.
- */
-public final class AppLog {
-    private static final String APP_NAME = "client";
-    private static final Object LOCK = new Object();
-    private static final SimpleDateFormat TIME = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
-    private static PrintStream out;
-    private static volatile String exitReason;
-    private static volatile String uncaughtThread;
-    private static volatile Throwable uncaughtException;
-
-    private AppLog() { }
-
-    public static void init() {
-        synchronized (LOCK) {
-            if (out != null) return;
-            try {
-                File code = new File(AppLog.class.getProtectionDomain().getCodeSource().getLocation().toURI());
-                File base = code.isFile() ? code.getParentFile() : new File(System.getProperty("user.dir"));
-                File dir = new File(base, "logs");
-                if (!dir.exists() && !dir.mkdirs()) return;
-
-                String day = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
-                out = new PrintStream(new FileOutputStream(new File(dir, APP_NAME + "-" + day + ".log"), true), true, "UTF-8");
-                Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
-                    public void uncaughtException(Thread thread, Throwable exception) {
-                        exitReason = "UNCAUGHT_EXCEPTION";
-                        uncaughtThread = thread.getName();
-                        uncaughtException = exception;
-                    }
-                });
-                Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
-                    public void run() {
-                        if ("USER_CLOSE".equals(exitReason)) {
-                            log("INFO", "Client exited, reason=user-close", null);
-                        } else if ("UNCAUGHT_EXCEPTION".equals(exitReason)) {
-                            log("ERROR", "Client exited, reason=uncaught-exception, thread=" + clean(uncaughtThread), uncaughtException);
-                        }
-                    }
-                }, APP_NAME + "-log-shutdown"));
-            } catch (Exception ignored) {
-                // Logging is best-effort and must not stop client startup.
-            }
-        }
-    }
-
-    public static void login(String userId, String method, boolean success) {
-        log(success ? "INFO" : "WARN", "Login " + (success ? "succeeded" : "failed")
-                + ", method=" + clean(method) + ", user=" + clean(userId), null);
-    }
-
-    public static void scan(String type, String code) {
-        log("INFO", "Scan, type=" + clean(type) + ", code=" + clean(code), null);
-    }
-
-    public static void work(String action, String code, boolean success) {
-        log(success ? "INFO" : "WARN", "Operation " + (success ? "succeeded" : "failed")
-                + ", action=" + clean(action) + ", code=" + clean(code), null);
-    }
-
-    public static void markUserClose() {
-        exitReason = "USER_CLOSE";
-    }
-
-    private static String clean(String value) {
-        return value == null ? "" : value.replace('\r', ' ').replace('\n', ' ');
-    }
-
-    private static void log(String level, String message, Throwable exception) {
-        synchronized (LOCK) {
-            if (out == null) return;
-            out.println("[" + TIME.format(new Date()) + "][" + level + "]["
-                    + Thread.currentThread().getName() + "] " + message);
-            if (exception != null) exception.printStackTrace(out);
-            out.flush();
-        }
-    }
-}

+ 13 - 0
logs/mes-client-2026-07-14.log

@@ -18,3 +18,16 @@
 [2026-07-14 14:17:28.207][INFO][mes-log-shutdown] 客户端退出,原因=用户手动关闭
 [2026-07-14 14:19:48.487][INFO][AWT-EventQueue-0] 登录成功,方式=权限校验,用户=system
 [2026-07-14 14:19:56.347][INFO][mes-log-shutdown] 客户端退出,原因=用户手动关闭
+[2026-07-14 15:16:21.955][INFO][AWT-EventQueue-0] 登录成功,方式=权限校验,用户=system
+[2026-07-14 15:16:43.804][INFO][AWT-EventQueue-0] 业务成功,操作=镭雕完成,编码=JS260625191
+[2026-07-14 15:16:43.841][INFO][pool-2-thread-1] 业务成功,操作=生成客户编码,编码=+KB60IS3031T714000201
+[2026-07-14 15:16:43.847][INFO][pool-2-thread-1] 业务成功,操作=镭雕结果提交(OK),编码=+KB60IS3031T714000201               
+[2026-07-14 15:16:51.462][INFO][mes-log-shutdown] 客户端退出,原因=用户手动关闭
+[2026-07-14 16:14:48.216][INFO][AWT-EventQueue-0] 登录成功,方式=权限校验,用户=system
+[2026-07-14 16:33:34.889][INFO][AWT-EventQueue-0] 登录成功,方式=权限校验,用户=system
+[2026-07-14 16:34:00.347][INFO][AWT-EventQueue-0] 业务成功,操作=镭雕完成,编码=JS260625192
+[2026-07-14 16:34:00.386][INFO][pool-2-thread-1] 业务成功,操作=生成客户编码,编码=+KB60IS3031T714000301
+[2026-07-14 16:34:00.393][INFO][pool-2-thread-1] 业务成功,操作=镭雕结果提交(OK),编码=+KB60IS3031T714000301               
+[2026-07-14 16:34:30.018][WARN][main] 检测到上一次客户端未正常关闭,可能是强制结束、崩溃、断电或系统终止
+[2026-07-14 16:34:34.316][INFO][AWT-EventQueue-0] 登录成功,方式=权限校验,用户=system
+[2026-07-14 16:35:02.860][INFO][mes-log-shutdown] 客户端退出,原因=用户手动关闭

+ 4 - 0
src/com/mes/ui/LoginFarme.java

@@ -203,7 +203,11 @@ public class LoginFarme extends JFrame {
                     //登录成功
                     MesClient.user_menu.setText(user_id);
                     MesClient.welcomeWin.setVisible(false);
+                    // Apply the maximized state when the main window is actually shown after login.
+                    MesClient.mesClientFrame.setExtendedState(JFrame.MAXIMIZED_BOTH);
                     MesClient.mesClientFrame.setVisible(true);
+                    MesClient.mesClientFrame.revalidate();
+                    MesClient.mesClientFrame.repaint();
 
                     if(MesClient.jfxPanel == null){
                         String url = "http://"+ MesClient.mes_server_ip+":8980/js/a/mes/mesProductRecord/work?__sid="+MesClient.sessionid+"&oprno="+MesClient.mes_gw+"&lineSn="+MesClient.mes_line_sn;

+ 51 - 14
src/com/mes/ui/MesClient.java

@@ -38,9 +38,45 @@ import java.util.Timer;
 import java.util.TimerTask;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.IdentityHashMap;
+import java.util.Map;
 
 public class MesClient extends JFrame {
 
+    private static class CenteredPanel extends JPanel {
+        private final int contentWidth;
+        private final int contentHeight;
+        private final Map<Component, Rectangle> designBounds = new IdentityHashMap<>();
+
+        CenteredPanel(int contentWidth, int contentHeight) {
+            this.contentWidth = contentWidth;
+            this.contentHeight = contentHeight;
+            setLayout(null);
+            setPreferredSize(new Dimension(contentWidth, contentHeight));
+        }
+
+        @Override
+        public void doLayout() {
+            double scale = Math.min((double) getWidth() / contentWidth,
+                    (double) getHeight() / contentHeight);
+            int width = (int) Math.round(contentWidth * scale);
+            int height = (int) Math.round(contentHeight * scale);
+            int x = (getWidth() - width) / 2;
+            int y = (getHeight() - height) / 2;
+            for (Component component : getComponents()) {
+                Rectangle bounds = designBounds.get(component);
+                if (bounds == null) {
+                    bounds = component.getBounds();
+                    designBounds.put(component, new Rectangle(bounds));
+                }
+                component.setBounds(x + (int) Math.round(bounds.x * scale),
+                        y + (int) Math.round(bounds.y * scale),
+                        (int) Math.round(bounds.width * scale),
+                        (int) Math.round(bounds.height * scale));
+            }
+        }
+    }
+
     private static String Drivde = "org.sqlite.JDBC";
 
     public static int mes_auth = 0; // 权限级别 0=无权限 1=操作工人 2=管理员
@@ -745,7 +781,7 @@ public class MesClient extends JFrame {
         contentPane.add(tabbedPane);
 
         //首页
-        JPanel indexPanelA = new JPanel();
+        JPanel indexPanelA = new CenteredPanel(972, 450);
         indexScrollPaneA = new JScrollPane(indexPanelA);
         indexPanelA.setLayout(null);
 
@@ -1029,35 +1065,37 @@ public class MesClient extends JFrame {
         tabbedPane.addTab("工作面板", new ImageIcon(MesClient.class.getResource("/bg/a_side.png")), indexScrollPaneA, null);
         tabbedPane.setEnabledAt(0, true);
 
-        JPanel unbindPanel = new JPanel();
-        JScrollPane unbindScrollPane = new JScrollPane(unbindPanel);
-        unbindPanel.setLayout(null);
+        JPanel unbindPanel = new CenteredPanel(810, 370);
+        JPanel unbindContainer = new JPanel(new GridBagLayout());
+        unbindContainer.add(unbindPanel);
+        JScrollPane unbindScrollPane = new JScrollPane(unbindContainer);
+        unbindScrollPane.getVerticalScrollBar().setUnitIncrement(16);
 
         JLabel unbindTitle = new JLabel("钢印码解绑");
         unbindTitle.setHorizontalAlignment(SwingConstants.CENTER);
         unbindTitle.setForeground(Color.BLACK);
         unbindTitle.setFont(new Font("微软雅黑", Font.PLAIN, 28));
-        unbindTitle.setBounds(260, 80, 420, 50);
+        unbindTitle.setBounds(195, 80, 420, 50);
         unbindPanel.add(unbindTitle);
 
         JLabel unbindInputLabel = new JLabel("钢印码");
         unbindInputLabel.setHorizontalAlignment(SwingConstants.RIGHT);
         unbindInputLabel.setForeground(Color.BLACK);
         unbindInputLabel.setFont(new Font("微软雅黑", Font.PLAIN, 22));
-        unbindInputLabel.setBounds(170, 170, 120, 50);
+        unbindInputLabel.setBounds(160, 170, 120, 50);
         unbindPanel.add(unbindInputLabel);
 
         final JTextField unbindSteelSnInput = new JTextField();
         unbindSteelSnInput.setHorizontalAlignment(SwingConstants.CENTER);
         unbindSteelSnInput.setFont(new Font("微软雅黑", Font.PLAIN, 24));
         unbindSteelSnInput.setToolTipText("输入11位钢印码");
-        unbindSteelSnInput.setBounds(320, 170, 340, 50);
+        unbindSteelSnInput.setBounds(310, 170, 340, 50);
         unbindPanel.add(unbindSteelSnInput);
 
         final JButton unbindSteelSnBt = new JButton("解绑钢印码");
         unbindSteelSnBt.setEnabled(false);
         unbindSteelSnBt.setFont(new Font("微软雅黑", Font.PLAIN, 24));
-        unbindSteelSnBt.setBounds(320, 250, 220, 60);
+        unbindSteelSnBt.setBounds(295, 250, 220, 60);
         unbindSteelSnBt.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent e) {
                 requestUnbindSteelSn(unbindSteelSnInput.getText(), unbindSteelSnInput);
@@ -1089,22 +1127,19 @@ public class MesClient extends JFrame {
 
 //		searchScrollPane = new JScrollPane((Component) null);
 
-        indexPanelC = new JPanel();
+        indexPanelC = new JPanel(new BorderLayout());
         searchScrollPaneDj = new JScrollPane(indexPanelC);
-        indexPanelC.setLayout(null);
 
         tabbedPane.addTab("开班点检", new ImageIcon(MesClient.class.getResource("/bg/menu_data_preprocess.png")), searchScrollPaneDj, null);
 
 
-        indexPanelB = new JPanel();
+        indexPanelB = new JPanel(new BorderLayout());
         searchScrollPane = new JScrollPane(indexPanelB);
-        indexPanelB.setLayout(null);
 
 		tabbedPane.addTab("工作记录", new ImageIcon(MesClient.class.getResource("/bg/menu_data_preprocess.png")), searchScrollPane, null);
 
-        indexPanelD = new JPanel();
+        indexPanelD = new JPanel(new BorderLayout());
         JScrollPane relationScrollPane = new JScrollPane(indexPanelD);
-        indexPanelD.setLayout(null);
 
         tabbedPane.addTab("绑定关系", new ImageIcon(MesClient.class.getResource("/bg/menu_data_preprocess.png")), relationScrollPane, null);
 
@@ -1182,6 +1217,8 @@ public class MesClient extends JFrame {
         // 确保窗口布局正确
         pack();
         setSize(1024, 768);
+        setMaximizedBounds(GraphicsEnvironment.getLocalGraphicsEnvironment()
+                .getMaximumWindowBounds());
         validate();
         repaint();
     }

+ 38 - 0
src/com/mes/util/AppLog.java

@@ -3,8 +3,13 @@ package com.mes.util;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.PrintStream;
+import java.io.FileWriter;
+import java.io.IOException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 public final class AppLog {
     private static final Object LOCK = new Object();
@@ -12,6 +17,8 @@ public final class AppLog {
     private static volatile String exitReason;
     private static volatile String uncaughtThread;
     private static volatile Throwable uncaughtException;
+    private static File stateFile;
+    private static ScheduledExecutorService heartbeat;
     private static final SimpleDateFormat TIME = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
 
     private AppLog() { }
@@ -26,6 +33,17 @@ public final class AppLog {
                 if (!dir.exists()) dir.mkdirs();
                 String day = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
                 out = new PrintStream(new FileOutputStream(new File(dir, "mes-client-" + day + ".log"), true), true, "UTF-8");
+                stateFile = new File(base, "mes-client.running");
+                if (stateFile.exists()) {
+                    log("WARN", "检测到上一次客户端未正常关闭,可能是强制结束、崩溃、断电或系统终止", null);
+                }
+                writeState();
+                heartbeat = Executors.newSingleThreadScheduledExecutor(r -> {
+                    Thread t = new Thread(r, "mes-log-heartbeat");
+                    t.setDaemon(true);
+                    return t;
+                });
+                heartbeat.scheduleAtFixedRate(() -> writeState(), 5, 5, TimeUnit.SECONDS);
                 Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                     public void uncaughtException(Thread t, Throwable e) {
                         exitReason = "UNCAUGHT_EXCEPTION";
@@ -39,7 +57,11 @@ public final class AppLog {
                             log("INFO", "客户端退出,原因=用户手动关闭", null);
                         } else if ("UNCAUGHT_EXCEPTION".equals(exitReason)) {
                             log("ERROR", "客户端退出,原因=未处理异常,线程=" + clean(uncaughtThread), uncaughtException);
+                        } else {
+                            log("WARN", "客户端退出时未收到正常关闭标记", null);
                         }
+                        if (heartbeat != null) heartbeat.shutdownNow();
+                        if ("USER_CLOSE".equals(exitReason)) stateFile.delete();
                     }
                 }, "mes-log-shutdown"));
             } catch (Exception e) { e.printStackTrace(); }
@@ -62,6 +84,22 @@ public final class AppLog {
 
     public static void markUserClose() { exitReason = "USER_CLOSE"; }
 
+    private static void writeState() {
+        synchronized (LOCK) {
+            if (stateFile == null) return;
+            try (FileWriter writer = new FileWriter(stateFile, false)) {
+                writer.write("pid=" + getPid() + "\n");
+                writer.write("heartbeat=" + TIME.format(new Date()) + "\n");
+            } catch (IOException ignored) { }
+        }
+    }
+
+    private static String getPid() {
+        String name = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
+        int separator = name.indexOf('@');
+        return separator > 0 ? name.substring(0, separator) : name;
+    }
+
     private static String clean(String value) {
         return value == null ? "" : value.replace('\r', ' ').replace('\n', ' ');
     }