张世豪
17 小时以前 5ae9bbe3583384afab8eb95a134ccb74aee6487a
曾加修改密码功能
已修改4个文件
已添加1个文件
723 ■■■■■ 文件已修改
set.properties 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Mqttmessage/Client.java 308 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/lujing/AoxinglujingNoObstacle.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/set/Sets.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/set/xiugaimima.java 276 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
set.properties
@@ -1,5 +1,5 @@
#Mower Configuration Properties - Updated
#Thu Dec 25 12:21:15 CST 2025
#Thu Dec 25 13:40:36 CST 2025
appVersion=-1
boundaryLengthVisible=false
currentWorkLandNumber=LAND1
@@ -8,7 +8,7 @@
handheldMarkerId=1872
idleTrailDurationSeconds=60
manualBoundaryDrawingMode=false
mapScale=0.78
mapScale=0.93
measurementModeEnabled=false
mowerId=6258
serialAutoConnect=true
src/Mqttmessage/Client.java
@@ -3,6 +3,7 @@
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import set.Setsys;
import user.Usrdell;
@@ -19,6 +20,10 @@
    private MqttClient client;
    private MqttConnectOptions options;
    
    // é™æ€å˜é‡ç”¨äºŽå­˜å‚¨å®¢æˆ·ç«¯å®žä¾‹
    private static Client gpsClient;
    private static Client responseClient;
    /**
     * æž„造函数
     * @param host MQTT服务器地址,格式:tcp://ip:port
@@ -26,12 +31,19 @@
     * @param clientId å®¢æˆ·ç«¯ID,不能重复
     */
    public Client(String host, String topic, String clientId) {
        this.host = host;
        this.topic = topic;
        this.clientId = clientId;
        this.options = new MqttConnectOptions();
        this.options.setCleanSession(true);
        // è®¾ç½®è¿žæŽ¥è¶…时时间(秒)
        this.options.setConnectionTimeout(30);
        // è®¾ç½®KeepAlive间隔(秒),用于保持连接活跃
        this.options.setKeepAliveInterval(60);
        // è®¾ç½®è‡ªåŠ¨é‡è¿ž
        this.options.setAutomaticReconnect(true);
        // è®¾ç½®MQTT版本,使用3.1.1
        this.options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
    }
    
    /**
@@ -40,11 +52,28 @@
     */
    public void connect() throws MqttException {
        if (client != null && client.isConnected()) {
            System.out.println("MQTT客户端已连接,ClientId: " + clientId);
            return;
        }
        client = new MqttClient(host, clientId);
        client.connect(options);
        // å¦‚果客户端已存在但未连接,先关闭
        if (client != null) {
            try {
                client.close();
            } catch (Exception e) {
                // å¿½ç•¥å…³é—­æ—¶çš„异常
            }
            client = null;
        }
        // ä½¿ç”¨å†…存持久化,避免文件锁定问题
        client = new MqttClient(host, clientId, new MemoryPersistence());
        // å…ˆè®¾ç½®å›žè°ƒï¼Œå†è¿žæŽ¥
        client.setCallback(new PushCallback());
        // æ‰§è¡Œè¿žæŽ¥
        client.connect(options);
        System.out.println("MQTT连接成功!ClientId: " + clientId + ", æœåС噍: " + host + ", ä¸»é¢˜: " + topic);
    }
    
    /**
@@ -78,6 +107,23 @@
    }
    
    /**
     * å…³é—­å®¢æˆ·ç«¯å¹¶é‡Šæ”¾èµ„源
     */
    public void close() {
        try {
            if (client != null) {
                if (client.isConnected()) {
                    client.disconnect();
                }
                client.close();
                client = null;
            }
        } catch (Exception e) {
            // å¿½ç•¥å…³é—­æ—¶çš„异常
        }
    }
    /**
     * æ£€æŸ¥æ˜¯å¦å·²è¿žæŽ¥
     * @return true表示已连接,false表示未连接
     */
@@ -94,29 +140,249 @@
    }
    
    /**
     * ç¤ºä¾‹ç”¨æ³•
     * è¿žæŽ¥MQTT服务器的工具方法
     * ä¾›å…¶ä»–类直接调用,连接GPS主题和响应主题
     * @return true表示连接成功,false表示连接失败
     */
    public static void lianjiemqqt()  {
    public static boolean connectMQTT() {
        // å…ˆæ–­å¼€ä¹‹å‰çš„连接
        disconnectAll();
        boolean gpsSuccess = false;
        boolean responseSuccess = false;
        try {
            String host = "tcp://39.99.43.227:1883";
            String deiveID=Setsys.getMowerIdValue();
            String clientId =Usrdell.getUserEmail()+"mower";
            String clientId2 =Usrdell.getUserEmail()+"response";
            String topic = "mower/"+deiveID+"/gps";
            String topic2 = "mower/"+deiveID+"/response";
            Client mqttClient = new Client(host, topic, clientId);
            Client mqttClient1 = new Client(host, topic2, clientId2);
            mqttClient.connect();
            mqttClient.subscribe();
            String deiveID = Setsys.getMowerIdValue();
            // æ·»åŠ æ—¶é—´æˆ³ç¡®ä¿å®¢æˆ·ç«¯ID唯一
            long timestamp = System.currentTimeMillis();
            String clientId = Usrdell.getUserEmail() + "mower" + "_" + timestamp;
            String clientId2 = Usrdell.getUserEmail() + "response" + "_" + timestamp;
            String topic = "mower/" + deiveID + "/gps";
            String topic2 = "mower/" + deiveID + "/response";
            // è¿žæŽ¥GPS主题
            try {
                gpsClient = new Client(host, topic, clientId);
                gpsClient.connect();
                // ç¨ä½œå»¶è¿Ÿï¼Œç¡®ä¿è¿žæŽ¥ç¨³å®š
                Thread.sleep(100);
                gpsClient.subscribe();
                gpsSuccess = true;
                System.out.println("GPS主题MQTT连接并订阅成功");
            } catch (MqttException e) {
                System.err.println("GPS主题MQTT连接失败: " + e.getMessage());
                if (e.getCause() != null) {
                    System.err.println("失败原因: " + e.getCause().getMessage());
                }
                e.printStackTrace();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                System.err.println("连接过程被中断");
            }
            mqttClient1.connect();
            mqttClient1.subscribe();
            // è¿žæŽ¥å“åº”主题
            try {
                responseClient = new Client(host, topic2, clientId2);
                responseClient.connect();
                // ç¨ä½œå»¶è¿Ÿï¼Œç¡®ä¿è¿žæŽ¥ç¨³å®š
                Thread.sleep(100);
                responseClient.subscribe();
                responseSuccess = true;
                System.out.println("响应主题MQTT连接并订阅成功");
            } catch (MqttException e) {
                System.err.println("响应主题MQTT连接失败: " + e.getMessage());
                if (e.getCause() != null) {
                    System.err.println("失败原因: " + e.getCause().getMessage());
                }
                e.printStackTrace();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                System.err.println("连接过程被中断");
            }
            // ä¿æŒç¨‹åºè¿è¡Œ
//           Thread.sleep(Long.MAX_VALUE);
        } catch (MqttException e) {
            throw new RuntimeException(e);
            if (gpsSuccess && responseSuccess) {
                System.out.println("所有MQTT主题连接并订阅成功!");
                return true;
            } else if (gpsSuccess || responseSuccess) {
                System.out.println("部分MQTT主题连接成功");
                return true;
            } else {
                System.err.println("所有MQTT主题连接失败");
                return false;
            }
        } catch (Exception e) {
            System.err.println("MQTT连接过程发生异常: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    /**
     * è¿žæŽ¥MQTT服务器的工具方法(带参数版本)
     * @param host MQTT服务器地址,格式:tcp://ip:port
     * @param deviceId è®¾å¤‡ID
     * @param userEmail ç”¨æˆ·é‚®ç®±
     * @return true表示连接成功,false表示连接失败
     */
    public static boolean connectMQTT(String host, String deviceId, String userEmail) {
        // å…ˆæ–­å¼€ä¹‹å‰çš„连接
        disconnectAll();
        boolean gpsSuccess = false;
        boolean responseSuccess = false;
        try {
            // æ·»åŠ æ—¶é—´æˆ³ç¡®ä¿å®¢æˆ·ç«¯ID唯一
            long timestamp = System.currentTimeMillis();
            String clientId = userEmail + "mower" + "_" + timestamp;
            String clientId2 = userEmail + "response" + "_" + timestamp;
            String topic = "mower/" + deviceId + "/gps";
            String topic2 = "mower/" + deviceId + "/response";
            // è¿žæŽ¥GPS主题
            try {
                gpsClient = new Client(host, topic, clientId);
                gpsClient.connect();
                // ç¨ä½œå»¶è¿Ÿï¼Œç¡®ä¿è¿žæŽ¥ç¨³å®š
                Thread.sleep(100);
                gpsClient.subscribe();
                gpsSuccess = true;
                System.out.println("GPS主题MQTT连接并订阅成功");
            } catch (MqttException e) {
                System.err.println("GPS主题MQTT连接失败: " + e.getMessage());
                if (e.getCause() != null) {
                    System.err.println("失败原因: " + e.getCause().getMessage());
                }
                e.printStackTrace();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                System.err.println("连接过程被中断");
            }
            // è¿žæŽ¥å“åº”主题
            try {
                responseClient = new Client(host, topic2, clientId2);
                responseClient.connect();
                // ç¨ä½œå»¶è¿Ÿï¼Œç¡®ä¿è¿žæŽ¥ç¨³å®š
                Thread.sleep(100);
                responseClient.subscribe();
                responseSuccess = true;
                System.out.println("响应主题MQTT连接并订阅成功");
            } catch (MqttException e) {
                System.err.println("响应主题MQTT连接失败: " + e.getMessage());
                if (e.getCause() != null) {
                    System.err.println("失败原因: " + e.getCause().getMessage());
                }
                e.printStackTrace();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                System.err.println("连接过程被中断");
            }
            if (gpsSuccess && responseSuccess) {
                System.out.println("所有MQTT主题连接并订阅成功!");
                return true;
            } else if (gpsSuccess || responseSuccess) {
                System.out.println("部分MQTT主题连接成功");
                return true;
            } else {
                System.err.println("所有MQTT主题连接失败");
                return false;
            }
        } catch (Exception e) {
            System.err.println("MQTT连接过程发生异常: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    /**
     * åˆ›å»ºå¹¶è¿žæŽ¥MQTT客户端的工具方法
     * @param host MQTT服务器地址
     * @param topic è®¢é˜…主题
     * @param clientId å®¢æˆ·ç«¯ID
     * @param qos æœåŠ¡è´¨é‡ç­‰çº§ï¼Œé»˜è®¤2
     * @return Client实例,连接失败返回null
     */
    public static Client createAndConnect(String host, String topic, String clientId, int qos) {
        try {
            Client mqttClient = new Client(host, topic, clientId);
            mqttClient.connect();
            mqttClient.subscribe(qos);
            System.out.println("MQTT客户端创建并订阅成功,主题: " + topic + ", ClientId: " + clientId);
            return mqttClient;
        } catch (MqttException e) {
            System.err.println("MQTT客户端创建失败: " + e.getMessage() + ", ä¸»é¢˜: " + topic);
            e.printStackTrace();
            return null;
        }
    }
    /**
     * åˆ›å»ºå¹¶è¿žæŽ¥MQTT客户端的工具方法(默认QoS为2)
     * @param host MQTT服务器地址
     * @param topic è®¢é˜…主题
     * @param clientId å®¢æˆ·ç«¯ID
     * @return Client实例,连接失败返回null
     */
    public static Client createAndConnect(String host, String topic, String clientId) {
        return createAndConnect(host, topic, clientId, 2);
    }
    /**
     * æ–­å¼€æ‰€æœ‰MQTT连接
     */
    public static void disconnectAll() {
        try {
            if (gpsClient != null) {
                gpsClient.close();
                System.out.println("GPS主题MQTT连接已断开");
                gpsClient = null;
            }
            if (responseClient != null) {
                responseClient.close();
                System.out.println("响应主题MQTT连接已断开");
                responseClient = null;
            }
        } catch (Exception e) {
            System.err.println("断开MQTT连接失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
    /**
     * èŽ·å–GPS客户端实例
     * @return GPS客户端实例
     */
    public static Client getGpsClient() {
        return gpsClient;
    }
    /**
     * èŽ·å–å“åº”å®¢æˆ·ç«¯å®žä¾‹
     * @return å“åº”客户端实例
     */
    public static Client getResponseClient() {
        return responseClient;
    }
    /**
     * æ£€æŸ¥MQTT连接状态(静态方法)
     * @return true表示已连接,false表示未连接
     */
    public static boolean areClientsConnected() {
        boolean gpsConnected = gpsClient != null && gpsClient.isConnected();
        boolean responseConnected = responseClient != null && responseClient.isConnected();
        return gpsConnected || responseConnected;
    }
    /**
     * ç¤ºä¾‹ç”¨æ³•(保留向后兼容)
     * @deprecated è¯·ä½¿ç”¨ connectMQTT() æ–¹æ³•替代
     */
    @Deprecated
    public static void lianjiemqqt() {
        connectMQTT();
    }
}
src/lujing/AoxinglujingNoObstacle.java
@@ -38,6 +38,14 @@
        return planPathCore(originalPolygon, width, margin);
    }
    /**
     * æ ¸å¿ƒè·¯å¾„规划逻辑
     *
     * @param originalPolygon åŽŸå§‹å¤šè¾¹å½¢é¡¶ç‚¹åˆ—è¡¨
     * @param width å‰²è‰å®½åº¦
     * @param margin å®‰å…¨è¾¹è·
     * @return è§„划好的路径段列表
     */
    private static List<PathSegment> planPathCore(List<Point> originalPolygon, double width, double margin) {
        if (originalPolygon.size() < 3) return new ArrayList<>();
@@ -74,6 +82,11 @@
    /**
     * å¯»æ‰¾å¼“字形的第一条线的起点
     *
     * @param polygon å¤šè¾¹å½¢é¡¶ç‚¹åˆ—表
     * @param angle æ‰«æè§’度
     * @param width å‰²è‰å®½åº¦
     * @return æ‰«æèµ·ç‚¹çš„坐标
     */
    private static Point getFirstScanStartPoint(List<Point> polygon, double angle, double width) {
        List<Point> rotated = rotatePolygon(polygon, -angle);
@@ -90,6 +103,10 @@
    /**
     * é‡ç»„多边形顶点,使得索引0的点最靠近填充起点
     *
     * @param polygon å¤šè¾¹å½¢é¡¶ç‚¹åˆ—表
     * @param target ç›®æ ‡ç‚¹ï¼ˆå¡«å……起点)
     * @return é‡ç»„后的多边形顶点列表
     */
    private static List<Point> alignBoundaryToStart(List<Point> polygon, Point target) {
        int bestIdx = 0;
@@ -108,6 +125,15 @@
        return aligned;
    }
    /**
     * ç”Ÿæˆå¼“字形扫描路径
     *
     * @param polygon å¤šè¾¹å½¢é¡¶ç‚¹åˆ—表
     * @param angle æ‰«æè§’度
     * @param width å‰²è‰å®½åº¦
     * @param startPoint èµ·å§‹ç‚¹
     * @return å¼“字形路径段列表
     */
    private static List<PathSegment> generateZigZagPath(List<Point> polygon, double angle, double width, Point startPoint) {
        List<PathSegment> result = new ArrayList<>();
        List<Point> rotated = rotatePolygon(polygon, -angle);
@@ -143,6 +169,13 @@
        return result;
    }
    /**
     * èŽ·å–æ‰«æçº¿ä¸Žå¤šè¾¹å½¢çš„äº¤ç‚¹X坐标列表
     *
     * @param rotatedPoly æ—‹è½¬åŽçš„多边形
     * @param y æ‰«æçº¿çš„Y坐标
     * @return äº¤ç‚¹X坐标列表
     */
    private static List<Double> getXIntersections(List<Point> rotatedPoly, double y) {
        List<Double> xIntersections = new ArrayList<>();
        int n = rotatedPoly.size();
@@ -159,6 +192,13 @@
    // --- å‡ ä½•基础工具 ---
    /**
     * å¤šè¾¹å½¢å†…缩(计算安全工作区域)
     *
     * @param polygon åŽŸå§‹å¤šè¾¹å½¢
     * @param margin å†…缩距离
     * @return å†…缩后的多边形
     */
    private static List<Point> shrinkPolygon(List<Point> polygon, double margin) {
        List<Point> result = new ArrayList<>();
        int n = polygon.size();
@@ -187,6 +227,12 @@
        return result;
    }
    /**
     * å¯»æ‰¾æœ€ä¼˜æ‰«æè§’度(使扫描线数量最少)
     *
     * @param polygon å¤šè¾¹å½¢
     * @return æœ€ä¼˜è§’度(弧度)
     */
    private static double findOptimalScanAngle(List<Point> polygon) {
        double minH = Double.MAX_VALUE;
        double bestA = 0;
@@ -199,6 +245,13 @@
        return bestA;
    }
    /**
     * è®¡ç®—多边形在特定角度下的高度(投影长度)
     *
     * @param poly å¤šè¾¹å½¢
     * @param angle è§’度
     * @return é«˜åº¦
     */
    private static double calculatePolygonHeightAtAngle(List<Point> poly, double angle) {
        double minY = Double.MAX_VALUE, maxY = -Double.MAX_VALUE;
        double sin = Math.sin(-angle), cos = Math.cos(-angle);
@@ -209,17 +262,36 @@
        return maxY - minY;
    }
    /**
     * æ—‹è½¬ç‚¹
     *
     * @param p ç‚¹
     * @param angle æ—‹è½¬è§’度
     * @return æ—‹è½¬åŽçš„点
     */
    private static Point rotatePoint(Point p, double angle) {
        double c = Math.cos(angle), s = Math.sin(angle);
        return new Point(p.x * c - p.y * s, p.x * s + p.y * c);
    }
    /**
     * æ—‹è½¬å¤šè¾¹å½¢
     *
     * @param poly å¤šè¾¹å½¢
     * @param angle æ—‹è½¬è§’度
     * @return æ—‹è½¬åŽçš„多边形
     */
    private static List<Point> rotatePolygon(List<Point> poly, double angle) {
        List<Point> res = new ArrayList<>();
        for (Point p : poly) res.add(rotatePoint(p, angle));
        return res;
    }
    /**
     * ç¡®ä¿å¤šè¾¹å½¢é¡¶ç‚¹ä¸ºé€†æ—¶é’ˆé¡ºåº
     *
     * @param poly å¤šè¾¹å½¢
     */
    private static void ensureCCW(List<Point> poly) {
        double s = 0;
        for (int i = 0; i < poly.size(); i++) {
@@ -229,6 +301,12 @@
        if (s > 0) Collections.reverse(poly);
    }
    /**
     * è§£æžåæ ‡å­—符串
     *
     * @param s åæ ‡å­—符串 (格式: "x1,y1;x2,y2;...")
     * @return ç‚¹åˆ—表
     */
    private static List<Point> parseCoords(String s) {
        List<Point> list = new ArrayList<>();
        for (String p : s.split(";")) {
src/set/Sets.java
@@ -193,6 +193,9 @@
        JPanel manualBoundaryDrawingPanel = createManualBoundaryDrawingPanel();
        manualBoundaryDrawingModeLabel = (JLabel) manualBoundaryDrawingPanel.getClientProperty("valueLabel");
        // ä¿®æ”¹å¯†ç è®¾ç½®é¡¹
        JPanel changePasswordPanel = createChangePasswordPanel();
        JPanel feedbackPanel = createFeedbackPanel();
        
        // APP版本
@@ -214,6 +217,7 @@
        addSettingItem(panel, boundaryLengthPanel, true);
        addSettingItem(panel, measurementModePanel, true);
        addSettingItem(panel, manualBoundaryDrawingPanel, true);
        addSettingItem(panel, changePasswordPanel, true);
        addSettingItem(panel, feedbackPanel, true);
        addSettingItem(panel, appVersionPanel, true);
        addSettingItem(panel, logoutPanel, false);  // æœ€åŽä¸€é¡¹ä¸åŠ åˆ†å‰²çº¿
@@ -1860,4 +1864,57 @@
        
        return panel;
    }
    /**
     * åˆ›å»ºä¿®æ”¹å¯†ç è®¾ç½®é¢æ¿
     */
    private JPanel createChangePasswordPanel() {
        JPanel panel = new JPanel(new GridBagLayout());
        panel.setOpaque(false);
        panel.setAlignmentX(Component.LEFT_ALIGNMENT);
        panel.setMaximumSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT));
        panel.setPreferredSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT));
        panel.setMinimumSize(new Dimension(0, ROW_HEIGHT));
        panel.setBorder(BorderFactory.createEmptyBorder(ITEM_PADDING, ITEM_PADDING, ITEM_PADDING, ITEM_PADDING));
        GridBagConstraints gbc = new GridBagConstraints();
        JLabel titleLabel = new JLabel("修改密码");
        titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 14));
        titleLabel.setForeground(Color.BLACK);
        titleLabel.setHorizontalAlignment(SwingConstants.RIGHT);
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.weightx = 0;
        gbc.anchor = GridBagConstraints.EAST;
        gbc.insets = new Insets(0, 0, 0, 12);
        panel.add(titleLabel, gbc);
        JLabel valueLabel = new JLabel("******");
        valueLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
        valueLabel.setForeground(Color.DARK_GRAY);
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 0;
        gbc.weightx = 1.0;
        gbc.anchor = GridBagConstraints.EAST;
        panel.add(valueLabel, gbc);
        JButton editBtn = createEditButton();
        editBtn.addActionListener(e -> {
            SwingUtilities.invokeLater(() -> {
                xiugaimima dialog = new xiugaimima((Frame) SwingUtilities.getWindowAncestor(this));
                dialog.setVisible(true);
            });
        });
        gbc = new GridBagConstraints();
        gbc.gridx = 2;
        gbc.gridy = 0;
        gbc.weightx = 0;
        gbc.anchor = GridBagConstraints.EAST;
        panel.add(editBtn, gbc);
        return panel;
    }
}
src/set/xiugaimima.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,276 @@
package set;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import user.Usrdell;
public class xiugaimima extends JDialog {
    private static final long serialVersionUID = 1L;
    private JPasswordField oldPasswordField;
    private JPasswordField newPasswordField;
    private JPasswordField confirmPasswordField;
    private JButton saveButton;
    private JButton cancelButton;
    private JLabel errorLabel;
    private final Color THEME_COLOR = new Color(46, 139, 87);
    public xiugaimima(Frame owner) {
        super(owner, "修改密码", true);
        initializeUI();
    }
    private void initializeUI() {
        setLayout(new BorderLayout());
        setSize(400, 350);
        setLocationRelativeTo(getOwner());
        setResizable(false);
        JPanel mainPanel = new JPanel(new GridBagLayout());
        mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
        mainPanel.setBackground(Color.WHITE);
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(10, 10, 10, 10);
        gbc.fill = GridBagConstraints.HORIZONTAL;
        // åŽŸå¯†ç 
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.weightx = 0;
        mainPanel.add(new JLabel("原密码:"), gbc);
        oldPasswordField = createStyledPasswordField();
        gbc.gridx = 1;
        gbc.gridy = 0;
        gbc.weightx = 1.0;
        mainPanel.add(oldPasswordField, gbc);
        // æ–°å¯†ç 
        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.weightx = 0;
        mainPanel.add(new JLabel("新密码:"), gbc);
        JPanel newPasswordPanel = createPasswordPanelWithEye(newPasswordField = createStyledPasswordField());
        gbc.gridx = 1;
        gbc.gridy = 1;
        gbc.weightx = 1.0;
        mainPanel.add(newPasswordPanel, gbc);
        // ç¡®è®¤å¯†ç 
        gbc.gridx = 0;
        gbc.gridy = 2;
        gbc.weightx = 0;
        mainPanel.add(new JLabel("确认密码:"), gbc);
        JPanel confirmPasswordPanel = createPasswordPanelWithEye(confirmPasswordField = createStyledPasswordField());
        gbc.gridx = 1;
        gbc.gridy = 2;
        gbc.weightx = 1.0;
        mainPanel.add(confirmPasswordPanel, gbc);
        // é”™è¯¯æç¤ºä¿¡æ¯
        errorLabel = new JLabel("密码长度不能小于6个字符");
        errorLabel.setForeground(Color.GRAY);
        errorLabel.setFont(new Font("PingFang SC", Font.PLAIN, 12));
        gbc.gridx = 1;
        gbc.gridy = 3;
        gbc.gridwidth = 2;
        gbc.insets = new Insets(0, 10, 10, 10);
        mainPanel.add(errorLabel, gbc);
        // æ·»åŠ å¯†ç è¾“å…¥ç›‘å¬
        DocumentListener passwordListener = new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) { checkPasswords(); }
            @Override
            public void removeUpdate(DocumentEvent e) { checkPasswords(); }
            @Override
            public void changedUpdate(DocumentEvent e) { checkPasswords(); }
        };
        newPasswordField.getDocument().addDocumentListener(passwordListener);
        confirmPasswordField.getDocument().addDocumentListener(passwordListener);
        add(mainPanel, BorderLayout.CENTER);
        // æŒ‰é’®é¢æ¿
        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10));
        buttonPanel.setBackground(Color.WHITE);
        saveButton = new JButton("保存");
        saveButton.setBackground(THEME_COLOR);
        saveButton.setForeground(Color.WHITE);
        saveButton.setFocusPainted(false);
        saveButton.setPreferredSize(new Dimension(100, 35));
        saveButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                handleSave();
            }
        });
        cancelButton = new JButton("取消");
        cancelButton.setBackground(new Color(240, 240, 240));
        cancelButton.setForeground(Color.BLACK);
        cancelButton.setFocusPainted(false);
        cancelButton.setPreferredSize(new Dimension(100, 35));
        cancelButton.addActionListener(e -> dispose());
        buttonPanel.add(saveButton);
        buttonPanel.add(cancelButton);
        add(buttonPanel, BorderLayout.SOUTH);
    }
    private JPasswordField createStyledPasswordField() {
        JPasswordField field = new JPasswordField(15);
        field.setPreferredSize(new Dimension(200, 38)); // è®¾ç½®é«˜åº¦ä¸º38,与登录页面一致
        field.setFont(new Font("PingFang SC", Font.PLAIN, 14));
        field.setBorder(BorderFactory.createCompoundBorder(
            BorderFactory.createLineBorder(new Color(200, 200, 200)),
            BorderFactory.createEmptyBorder(8, 10, 8, 10)
        ));
        field.setForeground(new Color(60, 60, 60));
        return field;
    }
    private JPanel createPasswordPanelWithEye(JPasswordField passwordField) {
        JPanel panel = new JPanel(new BorderLayout());
        panel.setBackground(Color.WHITE);
        // å°†è¾¹æ¡†ç§»åŠ¨åˆ° Panel ä¸Šï¼Œæ¨¡æ‹Ÿæ–‡æœ¬æ¡†å¤–è§‚
        panel.setBorder(BorderFactory.createCompoundBorder(
            BorderFactory.createLineBorder(new Color(200, 200, 200)),
            BorderFactory.createEmptyBorder(0, 0, 0, 5)
        ));
        panel.setPreferredSize(new Dimension(200, 38));
        // ç§»é™¤ Field çš„边框,使其融入 Panel
        passwordField.setBorder(BorderFactory.createEmptyBorder(8, 10, 8, 0));
        passwordField.setPreferredSize(null); // è®© BorderLayout ç®¡ç†å¤§å°
        panel.add(passwordField, BorderLayout.CENTER);
        JLabel eyeLabel = new JLabel();
        eyeLabel.setPreferredSize(new Dimension(30, 38));
        eyeLabel.setHorizontalAlignment(SwingConstants.CENTER);
        eyeLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
        // é»˜è®¤é—­çœ¼å›¾æ ‡
        eyeLabel.setText("👁");
        eyeLabel.setForeground(Color.GRAY);
        eyeLabel.addMouseListener(new MouseAdapter() {
            private boolean isVisible = false;
            @Override
            public void mouseClicked(MouseEvent e) {
                isVisible = !isVisible;
                if (isVisible) {
                    passwordField.setEchoChar((char) 0);
                    eyeLabel.setForeground(THEME_COLOR);
                } else {
                    passwordField.setEchoChar('•');
                    eyeLabel.setForeground(Color.GRAY);
                }
            }
        });
        panel.add(eyeLabel, BorderLayout.EAST);
        return panel;
    }
    private void checkPasswords() {
        String newPass = new String(newPasswordField.getPassword());
        String confirmPass = new String(confirmPasswordField.getPassword());
        // é»˜è®¤æç¤º
        if (newPass.isEmpty() && confirmPass.isEmpty()) {
            errorLabel.setText("密码长度不能小于6个字符");
            errorLabel.setForeground(Color.GRAY);
            return;
        }
        // é•¿åº¦æ£€æŸ¥
        if (newPass.length() > 0 && newPass.length() < 6) {
            errorLabel.setText("密码长度不能小于6个字符");
            errorLabel.setForeground(Color.RED);
            return;
        }
        // ä¸€è‡´æ€§æ£€æŸ¥
        if (confirmPass.length() > 0) {
            if (confirmPass.length() == newPass.length()) {
                if (!newPass.equals(confirmPass)) {
                    errorLabel.setText("两次输入的新密码不一致");
                    errorLabel.setForeground(Color.RED);
                } else {
                    errorLabel.setText(" "); // å¯†ç ä¸€è‡´ä¸”长度符合要求
                }
            } else if (confirmPass.length() < newPass.length()) {
                // æ­£åœ¨è¾“入中,如果之前有错误提示,可以清除或恢复默认
                if (newPass.length() >= 6) {
                    errorLabel.setText(" ");
                }
            } else {
                // ç¡®è®¤å¯†ç æ¯”新密码长,肯定不一致
                errorLabel.setText("两次输入的新密码不一致");
                errorLabel.setForeground(Color.RED);
            }
        } else {
            // ç¡®è®¤å¯†ç ä¸ºç©ºï¼Œå¦‚果新密码符合长度,清除错误(或者显示默认提示)
            if (newPass.length() >= 6) {
                errorLabel.setText(" ");
            }
        }
    }
    private void handleSave() {
        // æ¸…除之前的错误信息
        // errorLabel.setText(" "); // ä¸å†å¼ºåˆ¶æ¸…除,依赖 checkPasswords çš„状态,或者重新检查
        String oldPass = new String(oldPasswordField.getPassword());
        String newPass = new String(newPasswordField.getPassword());
        String confirmPass = new String(confirmPasswordField.getPassword());
        if (oldPass.isEmpty() || newPass.isEmpty() || confirmPass.isEmpty()) {
            errorLabel.setText("请填写所有字段");
            errorLabel.setForeground(Color.RED);
            return;
        }
        if (newPass.length() < 6) {
            errorLabel.setText("新密码长度不能小于6个字符");
            errorLabel.setForeground(Color.RED);
            return;
        }
        String currentStoredPassword = Usrdell.getProperty("password");
        if (currentStoredPassword == null) {
             currentStoredPassword = "";
        }
        if (!oldPass.equals(currentStoredPassword)) {
            errorLabel.setText("原密码错误");
            errorLabel.setForeground(Color.RED);
            return;
        }
        if (!newPass.equals(confirmPass)) {
            errorLabel.setText("两次输入的新密码不一致");
            errorLabel.setForeground(Color.RED);
            return;
        }
        // æ›´æ–°å¯†ç 
        Usrdell.updateProperty("password", newPass);
        JOptionPane.showMessageDialog(this, "密码修改成功", "提示", JOptionPane.INFORMATION_MESSAGE);
        dispose();
    }
}