From b518f895dec5264fd25e22a68300c40ceba6f43d Mon Sep 17 00:00:00 2001
From: 826220679@qq.com <826220679@qq.com>
Date: 星期六, 20 十二月 2025 15:30:20 +0800
Subject: [PATCH] 新增了按钮功能

---
 src/dikuai/addzhangaiwu.java | 1492 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 1,420 insertions(+), 72 deletions(-)

diff --git a/src/dikuai/addzhangaiwu.java b/src/dikuai/addzhangaiwu.java
index c936a6b..662c5c1 100644
--- a/src/dikuai/addzhangaiwu.java
+++ b/src/dikuai/addzhangaiwu.java
@@ -10,8 +10,11 @@
 import java.awt.Font;
 import java.awt.Image;
 import java.awt.Window;
+import java.awt.Container;
+import java.awt.event.ActionListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.io.File;
@@ -36,15 +39,24 @@
 import javax.swing.JPanel;
 import javax.swing.SwingConstants;
 import javax.swing.SwingUtilities;
+import javax.swing.JTextField;
 import javax.swing.border.Border;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
 
 import baseStation.BaseStation;
 import gecaoji.Device;
+import set.Setsys;
 import ui.UIConfig;
 import zhuye.Coordinate;
+import zhuye.MapRenderer;
 import zhuye.Shouye;
 import zhangaiwu.AddDikuai;
 import zhangaiwu.Obstacledge;
+import zhangaiwu.yulanzhangaiwu;
+import zhuye.buttonset;
+import bianjie.bianjieguihua2;
+import bianjie.ThreePointCircle;
 
 /**
  * 闅滅鐗╂柊澧�/缂栬緫瀵硅瘽妗嗐�傝璁¤瑷�鍙傝�� {@link AddDikuai}锛屾敮鎸侀�氳繃瀹炲湴缁樺埗閲囬泦闅滅鐗╁潗鏍囥��
@@ -61,6 +73,8 @@
     private final Color MEDIUM_GRAY = new Color(233, 236, 239);
     private final Color TEXT_COLOR = new Color(33, 37, 41);
     private final Color LIGHT_TEXT = new Color(108, 117, 125);
+    private final Color DANGER_COLOR = new Color(220, 53, 69);
+    private final Color DANGER_LIGHT = new Color(255, 235, 238);
 
     private final Dikuai targetDikuai;
     private final List<ExistingObstacle> existingObstacles;
@@ -79,7 +93,13 @@
     private JPanel selectedMethodPanel;
     private JPanel selectedShapePanel;
     private JButton drawButton;
+    private JButton generateBoundaryButton;  // 鐢熸垚闅滅鐗╄竟鐣屾寜閽�
+    private JButton previewButton;  // 棰勮鎸夐挳
     private JLabel drawingStatusLabel;
+    private JLabel boundaryStatusLabel;  // 鐢熸垚闅滅鐗╄竟鐣岀姸鎬佹爣绛�
+    private JTextField obstacleNameField;
+    private JPanel existingObstacleListPanel;
+    private JPanel step1NextButtonRow;
 
     private int currentStep = 1;
     private boolean drawingInProgress;
@@ -102,8 +122,11 @@
         if (target == null) {
             throw new IllegalArgumentException("targetDikuai 涓嶈兘涓虹┖");
         }
-    this.targetDikuai = target;
-    this.existingObstacles = Collections.unmodifiableList(resolveExistingObstacles(target, obstacleNames));
+        Coordinate.coordinates.clear();
+        Coordinate.setStartSaveGngga(false);
+        Coordinate.clearActiveDeviceIdFilter();
+        this.targetDikuai = target;
+        this.existingObstacles = new ArrayList<>(resolveExistingObstacles(target, obstacleNames));
         initializeUI();
         setupEventHandlers();
         preloadData();
@@ -161,7 +184,11 @@
     infoContainer.add(createInfoRow("鍦板潡缂栧彿锛�", safeValue(targetDikuai.getLandNumber(), "鏈缃�")));
     infoContainer.add(Box.createRigidArea(new Dimension(0, 16)));
     infoContainer.add(createInfoRow("鍦板潡鍚嶇О锛�", safeValue(targetDikuai.getLandName(), "鏈懡鍚�")));
-    infoContainer.add(Box.createRigidArea(new Dimension(0, 20)));
+    infoContainer.add(Box.createRigidArea(new Dimension(0, 16)));
+    infoContainer.add(createObstacleNameRow());
+    infoContainer.add(Box.createRigidArea(new Dimension(0, 12)));
+    infoContainer.add(createStep1NextButtonRow());
+    infoContainer.add(Box.createRigidArea(new Dimension(0, 12)));
     infoContainer.add(buildExistingObstacleSection());
 
     stepPanel.add(infoContainer);
@@ -182,32 +209,64 @@
         section.add(titleLabel);
         section.add(Box.createRigidArea(new Dimension(0, 8)));
 
+        existingObstacleListPanel = new JPanel();
+        existingObstacleListPanel.setLayout(new BoxLayout(existingObstacleListPanel, BoxLayout.Y_AXIS));
+        existingObstacleListPanel.setOpaque(false);
+        existingObstacleListPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+        section.add(existingObstacleListPanel);
+        refreshExistingObstacleList();
+        return section;
+    }
+
+    private JPanel createStep1NextButtonRow() {
+        if (step1NextButtonRow == null) {
+            step1NextButtonRow = new JPanel();
+            step1NextButtonRow.setLayout(new BoxLayout(step1NextButtonRow, BoxLayout.X_AXIS));
+            step1NextButtonRow.setOpaque(false);
+            step1NextButtonRow.setAlignmentX(Component.LEFT_ALIGNMENT);
+            step1NextButtonRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, 36));
+            step1NextButtonRow.add(Box.createHorizontalGlue());
+        }
+        return step1NextButtonRow;
+    }
+
+    private void attachNextButtonToStep1Row() {
+        if (step1NextButtonRow == null || nextButton == null) {
+            return;
+        }
+        step1NextButtonRow.removeAll();
+        step1NextButtonRow.add(Box.createHorizontalGlue());
+        nextButton.setAlignmentX(Component.RIGHT_ALIGNMENT);
+        step1NextButtonRow.add(nextButton);
+        step1NextButtonRow.revalidate();
+        step1NextButtonRow.repaint();
+    }
+
+    private void refreshExistingObstacleList() {
+        if (existingObstacleListPanel == null) {
+            return;
+        }
+        existingObstacleListPanel.removeAll();
         if (existingObstacles.isEmpty()) {
             JLabel emptyLabel = new JLabel("鏆傛棤");
             emptyLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
             emptyLabel.setForeground(LIGHT_TEXT);
             emptyLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
-            section.add(emptyLabel);
-            return section;
-        }
-
-        JPanel listPanel = new JPanel();
-        listPanel.setLayout(new BoxLayout(listPanel, BoxLayout.Y_AXIS));
-        listPanel.setOpaque(false);
-        listPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
-
-        for (int i = 0; i < existingObstacles.size(); i++) {
-            ExistingObstacle obstacle = existingObstacles.get(i);
-            JPanel row = createObstacleSummaryRow(obstacle);
-            row.setAlignmentX(Component.LEFT_ALIGNMENT);
-            listPanel.add(row);
-            if (i < existingObstacles.size() - 1) {
-                listPanel.add(Box.createRigidArea(new Dimension(0, 6)));
+            existingObstacleListPanel.add(emptyLabel);
+        } else {
+            for (int i = 0; i < existingObstacles.size(); i++) {
+                ExistingObstacle obstacle = existingObstacles.get(i);
+                JPanel row = createObstacleSummaryRow(obstacle);
+                row.setAlignmentX(Component.LEFT_ALIGNMENT);
+                existingObstacleListPanel.add(row);
+                if (i < existingObstacles.size() - 1) {
+                    existingObstacleListPanel.add(Box.createRigidArea(new Dimension(0, 6)));
+                }
             }
         }
-
-        section.add(listPanel);
-        return section;
+        existingObstacleListPanel.revalidate();
+        existingObstacleListPanel.repaint();
     }
 
     private JPanel createObstacleSummaryRow(ExistingObstacle obstacle) {
@@ -225,20 +284,22 @@
         JButton editButton = createInlineButton("淇敼");
         editButton.addActionListener(e -> populateObstacleForEditing(obstacle));
 
+        JButton deleteButton = createInlineIconButton("image/delete.png", "鍒犻櫎闅滅鐗�");
+        deleteButton.addActionListener(e -> handleDeleteExistingObstacle(obstacle));
+
         row.add(infoLabel);
         row.add(Box.createHorizontalGlue());
         row.add(editButton);
+        row.add(Box.createRigidArea(new Dimension(6, 0)));
+        row.add(deleteButton);
         return row;
     }
 
     private JButton createInlineButton(String text) {
-        JButton button = new JButton(text);
+        JButton button = buttonset.createStyledButton(text, PRIMARY_COLOR);
         button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 12));
-        button.setForeground(WHITE);
-        button.setBackground(PRIMARY_COLOR);
         button.setBorder(BorderFactory.createEmptyBorder(6, 16, 6, 16));
         button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
-        button.setFocusPainted(false);
         Dimension size = new Dimension(72, 28);
         button.setPreferredSize(size);
         button.setMinimumSize(size);
@@ -257,10 +318,56 @@
         return button;
     }
 
+    private JButton createInlineIconButton(String iconPath, String tooltip) {
+        JButton button = new JButton();
+        button.setPreferredSize(new Dimension(32, 28));
+        button.setMinimumSize(new Dimension(32, 28));
+        button.setMaximumSize(new Dimension(32, 28));
+        button.setBackground(WHITE);
+        button.setBorder(BorderFactory.createLineBorder(DANGER_COLOR));
+        button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+        button.setFocusPainted(false);
+        button.setFocusable(false);
+        button.setOpaque(true);
+        button.setContentAreaFilled(true);
+        if (tooltip != null && !tooltip.trim().isEmpty()) {
+            button.setToolTipText(tooltip);
+        }
+
+        ImageIcon icon = null;
+        if (iconPath != null && !iconPath.trim().isEmpty()) {
+            ImageIcon rawIcon = new ImageIcon(iconPath);
+            if (rawIcon.getIconWidth() > 0 && rawIcon.getIconHeight() > 0) {
+                Image scaled = rawIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH);
+                icon = new ImageIcon(scaled);
+            }
+        }
+        if (icon != null) {
+            button.setIcon(icon);
+        } else {
+            button.setText("鍒�");
+            button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 12));
+            button.setForeground(DANGER_COLOR);
+        }
+
+        button.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseEntered(MouseEvent e) {
+                button.setBackground(DANGER_LIGHT);
+            }
+
+            @Override
+            public void mouseExited(MouseEvent e) {
+                button.setBackground(WHITE);
+            }
+        });
+        return button;
+    }
+
     private String buildObstacleSummaryText(ExistingObstacle obstacle) {
         String name = obstacle.getName();
         String shape = obstacle.getShapeDisplay();
-        String coordPreview = buildCoordinatePreview(obstacle.getCoordinates(), 5);
+    String coordPreview = buildCoordinatePreview(obstacle.getDisplayCoordinates(), 5);
         return String.format(Locale.CHINA, "%s锛�%s锛屽潗鏍�:%s", name, shape, coordPreview);
     }
 
@@ -278,16 +385,70 @@
         return sanitized.substring(0, keepLength) + "...";
     }
 
+    private boolean deleteObstacleFromConfig(String obstacleName) {
+        String landNumber = targetDikuai != null ? targetDikuai.getLandNumber() : null;
+        if (!isMeaningfulValue(landNumber) || !isMeaningfulValue(obstacleName)) {
+            return false;
+        }
+        try {
+            File configFile = new File("Obstacledge.properties");
+            if (!configFile.exists()) {
+                return false;
+            }
+            Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager();
+            if (!manager.loadFromFile(configFile.getAbsolutePath())) {
+                return false;
+            }
+            Obstacledge.Plot plot = manager.getPlotById(landNumber.trim());
+            if (plot == null) {
+                return false;
+            }
+            if (!plot.removeObstacleByName(obstacleName.trim())) {
+                return false;
+            }
+            if (!manager.saveToFile(configFile.getAbsolutePath())) {
+                return false;
+            }
+            Dikuai.updateField(landNumber.trim(), "updateTime", currentTime());
+            Dikuai.saveToProperties();
+            Dikuaiguanli.notifyExternalCreation(landNumber.trim());
+            return true;
+        } catch (Exception ex) {
+            System.err.println("鍒犻櫎闅滅鐗╁け璐�: " + ex.getMessage());
+            return false;
+        }
+    }
+
     private void populateObstacleForEditing(ExistingObstacle obstacle) {
         if (obstacle == null) {
             return;
         }
-        String coords = obstacle.getCoordinates();
-        if (isMeaningfulValue(coords)) {
-            formData.put("obstacleCoordinates", coords.trim());
+        String name = obstacle.getName();
+        if (isMeaningfulValue(name)) {
+            formData.put("obstacleName", name.trim());
+            formData.put("editingObstacleName", name.trim());
+        } else {
+            formData.remove("obstacleName");
+            formData.remove("editingObstacleName");
+        }
+        if (obstacleNameField != null) {
+            obstacleNameField.setText(isMeaningfulValue(name) ? name.trim() : "");
+        }
+        String xyCoords = obstacle.getXyCoordinates();
+        String displayCoords = obstacle.getDisplayCoordinates();
+        if (isMeaningfulValue(xyCoords)) {
+            formData.put("obstacleCoordinates", xyCoords.trim());
+        } else if (isMeaningfulValue(displayCoords)) {
+            formData.put("obstacleCoordinates", displayCoords.trim());
         } else {
             formData.remove("obstacleCoordinates");
         }
+        String originalCoords = obstacle.getOriginalCoordinates();
+        if (isMeaningfulValue(originalCoords)) {
+            formData.put("obstacleOriginalCoordinates", originalCoords.trim());
+        } else {
+            formData.remove("obstacleOriginalCoordinates");
+        }
         String shapeKey = obstacle.getShapeKey();
         if (shapeKey != null) {
             JPanel shapePanel = shapeOptionPanels.get(shapeKey);
@@ -297,6 +458,58 @@
         showStep(2);
     }
 
+    private void handleDeleteExistingObstacle(ExistingObstacle obstacle) {
+        if (obstacle == null) {
+            return;
+        }
+        String obstacleName = obstacle.getName();
+        if (!isMeaningfulValue(obstacleName)) {
+            JOptionPane.showMessageDialog(this, "鏃犳硶鍒犻櫎锛氶殰纰嶇墿鍚嶇О鏃犳晥", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+        int choice = JOptionPane.showConfirmDialog(this,
+                "纭畾瑕佸垹闄ら殰纰嶇墿 \"" + obstacleName + "\" 鍚楋紵姝ゆ搷浣滄棤娉曟挙閿�銆�",
+                "鍒犻櫎纭",
+                JOptionPane.YES_NO_OPTION,
+                JOptionPane.WARNING_MESSAGE);
+        if (choice != JOptionPane.YES_OPTION) {
+            return;
+        }
+        boolean removedFromConfig = deleteObstacleFromConfig(obstacleName);
+    boolean hasPersistedData = obstacle.hasPersistedData() || obstacle.getShapeKey() != null;
+        if (!removedFromConfig && hasPersistedData) {
+            JOptionPane.showMessageDialog(this, "鍒犻櫎澶辫触锛屾湭鑳藉湪閰嶇疆涓Щ闄よ闅滅鐗┿��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+        if (!existingObstacles.remove(obstacle)) {
+            existingObstacles.removeIf(item -> obstacleName.equals(item != null ? item.getName() : null));
+        }
+        refreshExistingObstacleList();
+        clearEditingStateIfDeleted(obstacleName);
+        JOptionPane.showMessageDialog(this, "闅滅鐗╁凡鍒犻櫎", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+    }
+
+    private void clearEditingStateIfDeleted(String obstacleName) {
+        if (!isMeaningfulValue(obstacleName)) {
+            return;
+        }
+        String editingName = formData.get("editingObstacleName");
+        if (isMeaningfulValue(editingName) && obstacleName.equals(editingName)) {
+            formData.remove("editingObstacleName");
+            formData.remove("obstacleCoordinates");
+            formData.remove("obstacleOriginalCoordinates");
+            formData.remove("generatedBoundaryCoordinates");  // 娓呴櫎鐢熸垚鐨勮竟鐣屽潗鏍�
+            if (obstacleNameField != null) {
+                obstacleNameField.setText("");
+            } else {
+                formData.remove("obstacleName");
+            }
+            updateDrawingStatus();
+            updateSaveButtonState();
+            updatePreviewButtonState();
+        }
+    }
+
     private JPanel createStep2Panel() {
         JPanel stepPanel = new JPanel();
         stepPanel.setLayout(new BoxLayout(stepPanel, BoxLayout.Y_AXIS));
@@ -322,7 +535,7 @@
         stepPanel.add(methodOptionsPanel);
 
         stepPanel.add(Box.createRigidArea(new Dimension(0, 20)));
-        stepPanel.add(createSectionHeader("闅滅鐗╁舰鐘�", "澶氳竟褰㈤渶閲囬泦澶氫釜鐐癸紝鍦嗗舰鍙渶鍦嗗績涓庡渾鍛ㄤ笂涓�鐐�"));
+    stepPanel.add(createSectionHeader("闅滅鐗╁舰鐘�", "澶氳竟褰㈤渶閲囬泦澶氫釜鐐癸紝鍦嗗舰闇�鍦ㄥ渾鍛ㄤ笂閲囬泦鑷冲皯涓変釜鐐�"));
         stepPanel.add(Box.createRigidArea(new Dimension(0, 10)));
 
         shapeOptionsPanel = new JPanel();
@@ -331,15 +544,29 @@
         shapeOptionsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
     shapeOptionsPanel.add(createShapeOption("polygon", "澶氳竟褰�", "渚濇閲囬泦杞粨涓婄殑澶氫釜鐐规垨鑰呮部闅滅鐗╄竟缂樿蛋涓�鍦�"));
         shapeOptionsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
-        shapeOptionsPanel.add(createShapeOption("circle", "鍦嗗舰", "鍏堥噰闆嗗渾蹇冨潗鏍囷紝鍐嶉噰闆嗗渾鍛ㄤ笂涓�鐐�"));
+    shapeOptionsPanel.add(createShapeOption("circle", "鍦嗗舰", "娌垮渾鍛ㄤ换鎰忛噰闆嗕笁涓偣鑷姩鐢熸垚鍦�"));
         stepPanel.add(shapeOptionsPanel);
 
         stepPanel.add(Box.createRigidArea(new Dimension(0, 24)));
 
+        // 鎸夐挳瀹瑰櫒锛氶噸鏂扮粯鍒� + 鐢熸垚闅滅鐗╄竟鐣�
+        JPanel buttonRow = new JPanel();
+        buttonRow.setLayout(new BoxLayout(buttonRow, BoxLayout.X_AXIS));
+        buttonRow.setOpaque(false);
+        buttonRow.setAlignmentX(Component.LEFT_ALIGNMENT);
+        
         drawButton = createPrimaryButton("寮�濮嬬粯鍒�", 16);
-        drawButton.setAlignmentX(Component.LEFT_ALIGNMENT);
         drawButton.addActionListener(e -> startDrawingWorkflow());
-        stepPanel.add(drawButton);
+        buttonRow.add(drawButton);
+        
+        buttonRow.add(Box.createRigidArea(new Dimension(10, 0)));
+        
+        generateBoundaryButton = createPrimaryButton("鐢熸垚闅滅鐗╄竟鐣�", 16);
+        generateBoundaryButton.setVisible(false);  // 鍒濆闅愯棌锛岀粯鍒跺畬鎴愬悗鏄剧ず
+        generateBoundaryButton.addActionListener(e -> generateObstacleBoundary());
+        buttonRow.add(generateBoundaryButton);
+        
+        stepPanel.add(buttonRow);
 
         stepPanel.add(Box.createRigidArea(new Dimension(0, 12)));
 
@@ -348,6 +575,15 @@
         drawingStatusLabel.setForeground(LIGHT_TEXT);
         drawingStatusLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
         stepPanel.add(drawingStatusLabel);
+        
+        // 娣诲姞鐢熸垚闅滅鐗╄竟鐣岀姸鎬佹爣绛�
+        stepPanel.add(Box.createRigidArea(new Dimension(0, 8)));
+        boundaryStatusLabel = new JLabel("");
+        boundaryStatusLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
+        boundaryStatusLabel.setForeground(LIGHT_TEXT);
+        boundaryStatusLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+        boundaryStatusLabel.setVisible(false);  // 鍒濆闅愯棌
+        stepPanel.add(boundaryStatusLabel);
 
         stepPanel.add(Box.createVerticalGlue());
         return stepPanel;
@@ -361,15 +597,28 @@
 
         prevButton = createSecondaryButton("涓婁竴姝�");
         nextButton = createPrimaryButton("涓嬩竴姝�", 16);
+        // 璁剧疆涓嬩竴姝ユ寜閽搴︿负300鍍忕礌
+        nextButton.setPreferredSize(new Dimension(300, nextButton.getPreferredSize().height));
+        nextButton.setMaximumSize(new Dimension(300, nextButton.getPreferredSize().height));
+        
+        previewButton = createSecondaryButton("棰勮");
+        previewButton.setVisible(false);  // 鍒濆闅愯棌锛岀敓鎴愯竟鐣屽悗鏄剧ず
+        previewButton.setOpaque(true);
+        previewButton.setContentAreaFilled(true);
+        previewButton.addActionListener(e -> previewObstacleBoundary());
+        
         saveButton = createPrimaryButton("淇濆瓨", 16);
         saveButton.setVisible(false);
 
         buttonPanel.add(prevButton);
         buttonPanel.add(Box.createHorizontalGlue());
-        buttonPanel.add(nextButton);
         buttonPanel.add(Box.createRigidArea(new Dimension(12, 0)));
+        buttonPanel.add(previewButton);
+        buttonPanel.add(Box.createRigidArea(new Dimension(10, 0)));
         buttonPanel.add(saveButton);
 
+        attachNextButtonToStep1Row();
+
         return buttonPanel;
     }
 
@@ -547,6 +796,10 @@
         if (option == null) {
             return;
         }
+        if (userTriggered && "handheld".equalsIgnoreCase(type) && !hasConfiguredHandheldMarker()) {
+            JOptionPane.showMessageDialog(this, "璇峰厛鍘荤郴缁熻缃坊鍔犱究鎼烘墦鐐瑰櫒缂栧彿", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
         if (selectedMethodPanel != null && selectedMethodPanel != option) {
             resetOptionAppearance(selectedMethodPanel);
         }
@@ -558,6 +811,11 @@
         }
     }
 
+    private boolean hasConfiguredHandheldMarker() {
+        String handheldId = Setsys.getPropertyValue("handheldMarkerId");
+        return handheldId != null && !handheldId.trim().isEmpty();
+    }
+
     private void selectShapeOption(JPanel option, String type, boolean userTriggered) {
         if (option == null) {
             return;
@@ -616,6 +874,22 @@
 
         nextButton.addActionListener(e -> {
             if (currentStep == 1) {
+                String name = obstacleNameField != null ? obstacleNameField.getText() : null;
+                if (!isMeaningfulValue(name)) {
+                    JOptionPane.showMessageDialog(this, "璇峰厛濉啓闅滅鐗╁悕绉�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+                    if (obstacleNameField != null) {
+                        obstacleNameField.requestFocusInWindow();
+                    }
+                    return;
+                }
+                if (!isObstacleNameUnique(name)) {
+                    JOptionPane.showMessageDialog(this, "闅滅鐗╁悕绉板凡瀛樺湪锛岃杈撳叆鍞竴鍚嶇О", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+                    if (obstacleNameField != null) {
+                        obstacleNameField.requestFocusInWindow();
+                    }
+                    return;
+                }
+                formData.put("obstacleName", name.trim());
                 Coordinate.coordinates.clear();
                 showStep(2);
             }
@@ -637,6 +911,23 @@
         }
 
         activeDrawingShape = null;
+        
+        // 閲嶆柊缁樺埗鏃舵竻闄や箣鍓嶇敓鎴愮殑杈圭晫鍧愭爣
+        formData.remove("generatedBoundaryCoordinates");
+        if (previewButton != null) {
+            previewButton.setVisible(false);
+        }
+        // 娓呴櫎杈圭晫鐘舵�佹爣绛�
+        if (boundaryStatusLabel != null) {
+            boundaryStatusLabel.setVisible(false);
+            boundaryStatusLabel.setText("");
+        }
+        // 瀵逛簬鍦嗗舰闅滅鐗╋紝闅愯棌鐢熸垚杈圭晫鎸夐挳
+        String shapeKey = formData.get("obstacleShape");
+        boolean isCircle = "circle".equals(shapeKey);
+        if (isCircle && generateBoundaryButton != null) {
+            generateBoundaryButton.setVisible(false);
+        }
 
         String method = formData.get("drawingMethod");
         if (!isMeaningfulValue(method)) {
@@ -656,14 +947,37 @@
             return;
         }
 
+    String deviceId = resolveDrawingDeviceId(method);
+    if (!isMeaningfulValue(deviceId)) {
+        String idLabel = "handheld".equalsIgnoreCase(method) ? "鎵嬫寔璁惧缂栧彿" : "鍓茶崏鏈虹紪鍙�";
+        JOptionPane.showMessageDialog(this,
+            "鏈幏鍙栧埌鏈夋晥鐨�" + idLabel + "锛岃鍏堝湪绯荤粺璁剧疆涓畬鎴愰厤缃�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+
         activeDrawingShape = shape.toLowerCase(Locale.ROOT);
 
+        Shouye shouyeInstance = Shouye.getInstance();
+        if (shouyeInstance != null) {
+            shouyeInstance.setHandheldMowerIconActive("handheld".equalsIgnoreCase(method));
+            MapRenderer renderer = shouyeInstance.getMapRenderer();
+            if (renderer != null) {
+                renderer.clearIdleTrail();
+            }
+        }
+
         Coordinate.coordinates.clear();
+        Coordinate.setActiveDeviceIdFilter(deviceId);
         Coordinate.setStartSaveGngga(true);
+        yulanzhangaiwu.startPreview(activeDrawingShape, baseStation);
         drawingInProgress = true;
         drawButton.setText("姝e湪缁樺埗...");
         drawButton.setEnabled(false);
-        drawingStatusLabel.setText("姝e湪閲囬泦闅滅鐗╁潗鏍囷紝璇峰湪涓荤晫闈㈠畬鎴愮粯鍒躲��");
+        if ("circle".equals(activeDrawingShape)) {
+            drawingStatusLabel.setText("姝e湪閲囬泦鍦嗗舰闅滅鐗╋紝璇锋部鍦嗗懆閲囬泦鑷冲皯涓変釜鐐瑰悗鍦ㄤ富鐣岄潰缁撴潫缁樺埗銆�");
+        } else {
+            drawingStatusLabel.setText("姝e湪閲囬泦闅滅鐗╁潗鏍囷紝璇峰湪涓荤晫闈㈠畬鎴愮粯鍒躲��");
+        }
 
         if (activeSession == null) {
             activeSession = new ObstacleDrawingSession();
@@ -692,6 +1006,8 @@
 
     public static void finishDrawingSession() {
         Coordinate.setStartSaveGngga(false);
+        Coordinate.clearActiveDeviceIdFilter();
+        yulanzhangaiwu.stopPreview();
 
         Shouye shouye = Shouye.getInstance();
         if (shouye != null) {
@@ -710,11 +1026,20 @@
         showDialog(parent, activeSession.target);
     }
 
+    public static String getActiveSessionBaseStation() {
+        if (activeSession != null && isMeaningfulValue(activeSession.baseStation)) {
+            return activeSession.baseStation.trim();
+        }
+        return null;
+    }
+
     /**
      * Stops an in-progress circle capture and reopens the wizard on step 2.
      */
     public static void abortCircleDrawingAndReturn(String message) {
         Coordinate.setStartSaveGngga(false);
+        Coordinate.clearActiveDeviceIdFilter();
+        yulanzhangaiwu.stopPreview();
         Coordinate.coordinates.clear();
 
         if (activeSession == null || activeSession.target == null) {
@@ -750,6 +1075,13 @@
             return;
         }
 
+        String originalCoordStr = buildOriginalCoordinateString(captured);
+        if (isMeaningfulValue(originalCoordStr)) {
+            session.data.put("obstacleOriginalCoordinates", originalCoordStr);
+        } else {
+            session.data.remove("obstacleOriginalCoordinates");
+        }
+
         List<double[]> xyPoints = convertToLocalXY(captured, session.baseStation);
         if (xyPoints.isEmpty()) {
             session.captureSuccessful = false;
@@ -759,18 +1091,25 @@
 
         String shape = session.data.get("obstacleShape");
         if ("circle".equals(shape)) {
-            if (xyPoints.size() < 2) {
+            if (xyPoints.size() < 3) {
                 session.captureSuccessful = false;
-                session.captureMessage = "鍦嗗舰闅滅鐗╄嚦灏戦渶瑕佷袱涓噰闆嗙偣锛堝渾蹇冨拰鍦嗗懆鐐癸級";
+                session.captureMessage = "鍦嗗舰闅滅鐗╄嚦灏戦渶瑕佷笁涓噰闆嗙偣锛堝渾鍛ㄤ笂鐨勭偣锛�";
                 return;
             }
-            double[] center = xyPoints.get(0);
-            double[] radiusPoint = xyPoints.get(xyPoints.size() - 1);
+            CircleFitResult circle = fitCircleFromPoints(xyPoints);
+            if (circle == null) {
+                session.captureSuccessful = false;
+                session.captureMessage = "鏃犳硶鏍规嵁閲囬泦鐨勭偣鐢熸垚鍦嗭紝璇风‘淇濋�夋嫨浜嗕笁涓潪鍏辩嚎鐨勫渾鍛ㄧ偣";
+                return;
+            }
+            // 浣跨敤璁$畻鍑虹殑鍗婂緞鐢熸垚涓�涓湡姝g殑鍦嗕笂鐐癸紙鍦嗗績鍙充晶鐨勭偣锛夛紝纭繚棰勮鏃惰绠楃殑鍗婂緞鍜岀粨鏉熺粯鍒舵椂鐨勫崐寰勪竴鑷�
+            double radiusX = circle.centerX + circle.radius;
+            double radiusY = circle.centerY;
             String result = String.format(Locale.US, "%.2f,%.2f;%.2f,%.2f",
-                    center[0], center[1], radiusPoint[0], radiusPoint[1]);
+                    circle.centerX, circle.centerY, radiusX, radiusY);
             session.data.put("obstacleCoordinates", result);
             session.captureSuccessful = true;
-            session.captureMessage = "宸查噰闆嗗渾褰㈤殰纰嶇墿鍧愭爣";
+            session.captureMessage = "宸查噰闆嗗渾褰㈤殰纰嶇墿锛屽叡 " + xyPoints.size() + " 涓偣";
         } else {
             if (xyPoints.size() < 3) {
                 session.captureSuccessful = false;
@@ -816,6 +1155,67 @@
         return result;
     }
 
+    private static String buildOriginalCoordinateString(List<Coordinate> coords) {
+        if (coords == null || coords.isEmpty()) {
+            return null;
+        }
+        StringBuilder sb = new StringBuilder();
+        for (Coordinate coord : coords) {
+            if (coord == null) {
+                continue;
+            }
+            String latToken = sanitizeCoordinateToken(coord.getLatitude());
+            String lonToken = sanitizeCoordinateToken(coord.getLongitude());
+            if (latToken == null || lonToken == null) {
+                continue;
+            }
+            char latDir = sanitizeDirection(coord.getLatDirection(), 'N');
+            char lonDir = sanitizeDirection(coord.getLonDirection(), 'E');
+            if (sb.length() > 0) {
+                sb.append(";");
+            }
+            sb.append(latToken)
+              .append(",")
+              .append(latDir)
+              .append(",")
+              .append(lonToken)
+              .append(",")
+              .append(lonDir);
+        }
+        return sb.length() > 0 ? sb.toString() : null;
+    }
+
+    private static String sanitizeCoordinateToken(String token) {
+        if (token == null) {
+            return null;
+        }
+        String trimmed = token.trim();
+        if (trimmed.isEmpty()) {
+            return null;
+        }
+        try {
+            Double.parseDouble(trimmed);
+            return trimmed;
+        } catch (NumberFormatException ex) {
+            return null;
+        }
+    }
+
+    private static char sanitizeDirection(String direction, char fallback) {
+        if (direction == null) {
+            return fallback;
+        }
+        String trimmed = direction.trim();
+        if (trimmed.isEmpty()) {
+            return fallback;
+        }
+        char ch = Character.toUpperCase(trimmed.charAt(0));
+        if (ch != 'N' && ch != 'S' && ch != 'E' && ch != 'W') {
+            return fallback;
+        }
+        return ch;
+    }
+
     private static double parseDMToDecimal(String dmm, String direction) {
         if (dmm == null || dmm.trim().isEmpty()) {
             return Double.NaN;
@@ -847,6 +1247,101 @@
         return new double[]{eastMeters, northMeters};
     }
 
+    private static CircleFitResult fitCircleFromPoints(List<double[]> points) {
+        if (points == null || points.size() < 3) {
+            return null;
+        }
+        CircleFitResult best = null;
+        double bestScore = 0.0;
+        int n = points.size();
+        for (int i = 0; i < n - 2; i++) {
+            double[] p1 = points.get(i);
+            for (int j = i + 1; j < n - 1; j++) {
+                double[] p2 = points.get(j);
+                for (int k = j + 1; k < n; k++) {
+                    double[] p3 = points.get(k);
+                    CircleFitResult candidate = circleFromThreePoints(p1, p2, p3);
+                    if (candidate == null || candidate.radius <= 0) {
+                        continue;
+                    }
+                    double minEdge = Math.min(distance(p1, p2), Math.min(distance(p2, p3), distance(p1, p3)));
+                    if (minEdge > bestScore) {
+                        bestScore = minEdge;
+                        best = candidate;
+                    }
+                }
+            }
+        }
+        return best;
+    }
+
+    private static CircleFitResult circleFromThreePoints(double[] p1, double[] p2, double[] p3) {
+        double x1 = p1[0];
+        double y1 = p1[1];
+        double x2 = p2[0];
+        double y2 = p2[1];
+        double x3 = p3[0];
+        double y3 = p3[1];
+
+        double determinant = 2.0 * (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2));
+        if (Math.abs(determinant) < 1e-6) {
+            return null;
+        }
+
+        double x1Sq = x1 * x1 + y1 * y1;
+        double x2Sq = x2 * x2 + y2 * y2;
+        double x3Sq = x3 * x3 + y3 * y3;
+
+        double centerX = (x1Sq * (y2 - y3) + x2Sq * (y3 - y1) + x3Sq * (y1 - y2)) / determinant;
+        double centerY = (x1Sq * (x3 - x2) + x2Sq * (x1 - x3) + x3Sq * (x2 - x1)) / determinant;
+        double radius = Math.hypot(centerX - x1, centerY - y1);
+
+        if (!Double.isFinite(centerX) || !Double.isFinite(centerY) || !Double.isFinite(radius)) {
+            return null;
+        }
+        if (radius < 0.05) {
+            return null;
+        }
+
+        return new CircleFitResult(centerX, centerY, radius, new double[]{x1, y1});
+    }
+
+    private static double[] pickRadiusPoint(List<double[]> points, CircleFitResult circle) {
+        if (circle == null || points == null || points.isEmpty()) {
+            return null;
+        }
+        double[] best = null;
+        double bestDistance = 0.0;
+        for (double[] pt : points) {
+            double dist = Math.hypot(pt[0] - circle.centerX, pt[1] - circle.centerY);
+            if (dist > bestDistance) {
+                bestDistance = dist;
+                best = pt;
+            }
+        }
+        return best;
+    }
+
+    private static double distance(double[] a, double[] b) {
+        double dx = a[0] - b[0];
+        double dy = a[1] - b[1];
+        return Math.hypot(dx, dy);
+    }
+
+    private static final class CircleFitResult {
+        final double centerX;
+        final double centerY;
+        final double radius;
+        final double[] referencePoint;
+
+        CircleFitResult(double centerX, double centerY, double radius, double[] referencePoint) {
+            this.centerX = centerX;
+            this.centerY = centerY;
+            this.radius = radius;
+            this.referencePoint = referencePoint;
+        }
+    }
+
     private List<ExistingObstacle> resolveExistingObstacles(Dikuai target, List<String> providedNames) {
         String landNumber = target != null ? target.getLandNumber() : null;
         Map<String, ExistingObstacle> configMap = loadObstacleDetailsFromConfig(landNumber);
@@ -864,7 +1359,7 @@
         }
         if (!configMap.isEmpty()) {
             List<ExistingObstacle> remaining = new ArrayList<>(configMap.values());
-            remaining.sort(Comparator.comparing(ExistingObstacle::getName, String.CASE_INSENSITIVE_ORDER));
+            remaining.sort((a, b) -> String.CASE_INSENSITIVE_ORDER.compare(a.getName(), b.getName()));
             result.addAll(remaining);
         }
         if (result.isEmpty()) {
@@ -914,11 +1409,11 @@
                 }
                 String trimmedName = name.trim();
                 Obstacledge.ObstacleShape shape = obstacle.getShape();
-                String xyCoords = obstacle.getXyCoordsString();
-                String original = obstacle.getOriginalCoordsString();
-                String coords = isMeaningfulValue(xyCoords) ? xyCoords.trim()
-                        : (isMeaningfulValue(original) ? original.trim() : "");
-                details.put(trimmedName, new ExistingObstacle(trimmedName, shape, coords));
+        String xyCoords = obstacle.getXyCoordsString();
+        String original = obstacle.getOriginalCoordsString();
+        String xy = isMeaningfulValue(xyCoords) ? xyCoords.trim() : "";
+        String originalTrimmed = isMeaningfulValue(original) ? original.trim() : "";
+        details.put(trimmedName, new ExistingObstacle(trimmedName, shape, xy, originalTrimmed));
             }
         } catch (Exception ex) {
             System.err.println("鍔犺浇闅滅鐗╄鎯呭け璐�: " + ex.getMessage());
@@ -927,11 +1422,17 @@
     }
 
     private void preloadData() {
-        String existing = targetDikuai.getObstacleCoordinates();
-        if (isMeaningfulValue(existing)) {
-            formData.put("obstacleCoordinates", existing.trim());
-        }
+        formData.remove("obstacleCoordinates");
+        formData.remove("obstacleOriginalCoordinates");
+        formData.remove("generatedBoundaryCoordinates");  // 娓呴櫎鐢熸垚鐨勮竟鐣屽潗鏍�
+        formData.remove("editingObstacleName");
         updateDrawingStatus();
+        updatePreviewButtonState();
+        // 娓呴櫎杈圭晫鐘舵�佹爣绛�
+        if (boundaryStatusLabel != null) {
+            boundaryStatusLabel.setVisible(false);
+            boundaryStatusLabel.setText("");
+        }
     }
 
     private void updateDrawingStatus() {
@@ -939,21 +1440,140 @@
             return;
         }
         String coords = formData.get("obstacleCoordinates");
+        String shapeKey = formData.get("obstacleShape");
+        boolean isCircle = "circle".equals(shapeKey);
+        
         if (isMeaningfulValue(coords)) {
-            int count = countCoordinatePairs(coords);
-            drawingStatusLabel.setText("宸查噰闆嗛殰纰嶇墿鏁版嵁锛岀偣鏁帮細" + count);
+            if (isCircle) {
+                // 瀵逛簬鍦嗗舰闅滅鐗╋紝鏄剧ず鍦嗗績鍧愭爣鍜屽崐寰�
+                String statusText = parseCircleStatusText(coords);
+                drawingStatusLabel.setText(statusText);
+            } else {
+                // 瀵逛簬澶氳竟褰㈤殰纰嶇墿锛屾樉绀虹偣鏁�
+                int count = countCoordinatePairs(coords);
+                drawingStatusLabel.setText("宸查噰闆嗛殰纰嶇墿鏁版嵁锛岀偣鏁帮細" + count);
+            }
             if (!drawingInProgress && drawButton != null) {
                 drawButton.setText("閲嶆柊缁樺埗");
                 drawButton.setEnabled(true);
             }
+            // 瀵逛簬鍦嗗舰闅滅鐗╋紝涓嶆樉绀�"鐢熸垚闅滅鐗╄竟鐣�"鎸夐挳
+            if (generateBoundaryButton != null) {
+                generateBoundaryButton.setVisible(!isCircle);
+            }
         } else {
             drawingStatusLabel.setText("灏氭湭閲囬泦闅滅鐗╁潗鏍�");
             if (!drawingInProgress && drawButton != null) {
                 drawButton.setText("寮�濮嬬粯鍒�");
                 drawButton.setEnabled(true);
             }
+            // 鏈粯鍒舵椂闅愯棌"鐢熸垚闅滅鐗╄竟鐣�"鎸夐挳
+            if (generateBoundaryButton != null) {
+                generateBoundaryButton.setVisible(false);
+            }
+            // 鏈敓鎴愯竟鐣屾椂闅愯棌棰勮鎸夐挳
+            if (previewButton != null) {
+                previewButton.setVisible(false);
+            }
+            // 闅愯棌杈圭晫鐘舵�佹爣绛�
+            if (boundaryStatusLabel != null) {
+                boundaryStatusLabel.setVisible(false);
+            }
         }
         updateSaveButtonState();
+        updatePreviewButtonState();
+    }
+    
+    /**
+     * 鏇存柊杈圭晫鐘舵�佹爣绛�
+     */
+    private void updateBoundaryStatusLabel(int pointCount) {
+        if (boundaryStatusLabel == null) {
+            return;
+        }
+        if (pointCount > 0) {
+            boundaryStatusLabel.setText("鐢熸垚闅滅鐗╄竟鐣岀偣鏁帮細" + pointCount);
+            boundaryStatusLabel.setVisible(true);
+        } else {
+            boundaryStatusLabel.setText("");
+            boundaryStatusLabel.setVisible(false);
+        }
+    }
+    
+    /**
+     * 鏇存柊棰勮鎸夐挳鐨勬樉绀虹姸鎬�
+     */
+    private void updatePreviewButtonState() {
+        if (previewButton == null) {
+            return;
+        }
+        
+        // 瀵逛簬鍦嗗舰闅滅鐗╋紝涓嶆樉绀洪瑙堟寜閽�
+        String shapeKey = formData.get("obstacleShape");
+        boolean isCircle = "circle".equals(shapeKey);
+        if (isCircle) {
+            previewButton.setVisible(false);
+            previewButton.setEnabled(false);
+            return;
+        }
+        
+        // 鍙湁鍦ㄧ敓鎴愯竟鐣屽悗鎵嶆樉绀洪瑙堟寜閽�
+        String generatedBoundary = formData.get("generatedBoundaryCoordinates");
+        boolean hasGeneratedBoundary = isMeaningfulValue(generatedBoundary);
+
+        if (hasGeneratedBoundary) {
+            // 鐢熸垚杈圭晫鍚庯紝閲嶆柊鍒涘缓缁胯壊鐨勯瑙堟寜閽�
+            // 鑾峰彇鎸夐挳鐨勭埗瀹瑰櫒鍜屼綅缃�
+            Container parent = previewButton.getParent();
+            if (parent != null) {
+                // 淇濆瓨鍘熸湁鐨凙ctionListener
+                ActionListener[] listeners = previewButton.getActionListeners();
+                
+                // 绉婚櫎鏃ф寜閽�
+                parent.remove(previewButton);
+                
+                // 鍒涘缓鏂扮殑缁胯壊棰勮鎸夐挳锛堜娇鐢ㄤ富鎸夐挳鏍峰紡锛�
+                previewButton = createPrimaryButton("棰勮", 16);
+                previewButton.setVisible(true);
+                previewButton.setEnabled(true);
+                
+                // 鎭㈠ActionListener
+                for (ActionListener listener : listeners) {
+                    previewButton.addActionListener(listener);
+                }
+                
+                // 娣诲姞鍒扮埗瀹瑰櫒锛堝湪淇濆瓨鎸夐挳涔嬪墠锛�
+                int previewIndex = -1;
+                Component[] components = parent.getComponents();
+                for (int i = 0; i < components.length; i++) {
+                    if (components[i] instanceof JButton) {
+                        JButton btn = (JButton) components[i];
+                        if ("淇濆瓨".equals(btn.getText())) {
+                            previewIndex = i;
+                            break;
+                        }
+                    }
+                }
+                
+                if (previewIndex >= 0) {
+                    parent.add(previewButton, previewIndex);
+                    parent.add(Box.createRigidArea(new Dimension(10, 0)), previewIndex + 1);
+                } else {
+                    // 濡傛灉鎵句笉鍒颁繚瀛樻寜閽紝娣诲姞鍒版湯灏�
+                    parent.add(Box.createRigidArea(new Dimension(12, 0)));
+                    parent.add(previewButton);
+                    parent.add(Box.createRigidArea(new Dimension(10, 0)));
+                }
+                
+                // 鍒锋柊甯冨眬
+                parent.revalidate();
+                parent.repaint();
+            }
+        } else {
+            // 鏈敓鎴愯竟鐣屾椂锛岄殣钘忔寜閽�
+            previewButton.setVisible(false);
+            previewButton.setEnabled(false);
+        }
     }
 
     private int countCoordinatePairs(String coords) {
@@ -976,14 +1596,102 @@
         }
         return count;
     }
+    
+    /**
+     * 瑙f瀽鍦嗗舰闅滅鐗╁潗鏍囷紝鐢熸垚鐘舵�佹枃鏈�
+     * 鏍煎紡锛歝enterX,centerY;radiusX,radiusY
+     * 杩斿洖锛氬綋鍓嶉噰闆嗗渾褰㈠渾蹇冨潗鏍噚,y,鍗婂緞n绫�
+     */
+    private String parseCircleStatusText(String coords) {
+        if (!isMeaningfulValue(coords)) {
+            return "灏氭湭閲囬泦鍦嗗舰闅滅鐗╁潗鏍�";
+        }
+        
+        try {
+            String[] pairs = coords.split(";");
+            if (pairs.length < 2) {
+                return "鍦嗗舰闅滅鐗╁潗鏍囨牸寮忛敊璇�";
+            }
+            
+            // 瑙f瀽鍦嗗績鍧愭爣
+            String[] centerParts = pairs[0].trim().split(",");
+            if (centerParts.length < 2) {
+                return "鍦嗗舰闅滅鐗╁潗鏍囨牸寮忛敊璇�";
+            }
+            double centerX = Double.parseDouble(centerParts[0].trim());
+            double centerY = Double.parseDouble(centerParts[1].trim());
+            
+            // 瑙f瀽鍦嗕笂涓�鐐瑰潗鏍�
+            String[] radiusParts = pairs[1].trim().split(",");
+            if (radiusParts.length < 2) {
+                return "鍦嗗舰闅滅鐗╁潗鏍囨牸寮忛敊璇�";
+            }
+            double radiusX = Double.parseDouble(radiusParts[0].trim());
+            double radiusY = Double.parseDouble(radiusParts[1].trim());
+            
+            // 璁$畻鍗婂緞锛堢背锛�
+            double radius = Math.sqrt(Math.pow(radiusX - centerX, 2) + Math.pow(radiusY - centerY, 2));
+            
+            // 鏍煎紡鍖栨樉绀猴細褰撳墠閲囬泦鍦嗗舰鍦嗗績鍧愭爣x,y,鍗婂緞n绫�
+            return String.format(Locale.US, "褰撳墠閲囬泦鍦嗗舰鍦嗗績鍧愭爣%.2f,%.2f,鍗婂緞%.2f绫�", 
+                    centerX, centerY, radius);
+        } catch (Exception e) {
+            return "鍦嗗舰闅滅鐗╁潗鏍囪В鏋愬け璐�";
+        }
+    }
 
     private void updateSaveButtonState() {
         if (saveButton != null) {
-            saveButton.setEnabled(isMeaningfulValue(formData.get("obstacleCoordinates")));
+            boolean hasCoords = isMeaningfulValue(formData.get("obstacleCoordinates"));
+            boolean hasName = isMeaningfulValue(formData.get("obstacleName"));
+            
+            // 妫�鏌ユ槸鍚︾敓鎴愪簡闅滅鐗╄竟鐣屽潗鏍�
+            String shapeKey = formData.get("obstacleShape");
+            boolean hasGeneratedBoundary = isMeaningfulValue(formData.get("generatedBoundaryCoordinates"));
+            
+            // 濡傛灉鏄杈瑰舰闅滅鐗╋紝蹇呴』鐢熸垚杈圭晫鍧愭爣鎵嶈兘淇濆瓨
+            // 濡傛灉鏄渾褰㈤殰纰嶇墿鎴栧叾浠栧舰鐘讹紝涓嶉渶瑕佺敓鎴愯竟鐣屽氨鑳戒繚瀛�
+            boolean boundaryRequirementMet = true;
+            if ("polygon".equals(shapeKey)) {
+                boundaryRequirementMet = hasGeneratedBoundary;
+            }
+            
+            boolean enabled = hasCoords && hasName && boundaryRequirementMet;
+            saveButton.setEnabled(enabled);
+            if (enabled) {
+                saveButton.setBackground(PRIMARY_COLOR);
+                saveButton.setForeground(WHITE);
+                saveButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+                saveButton.setOpaque(true);
+                saveButton.setContentAreaFilled(true);
+            } else {
+                saveButton.setBackground(MEDIUM_GRAY);
+                saveButton.setForeground(TEXT_COLOR);
+                saveButton.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+                saveButton.setOpaque(true);
+                saveButton.setContentAreaFilled(true);
+            }
         }
     }
 
     private boolean validateStep2() {
+        String name = formData.get("obstacleName");
+        if (!isMeaningfulValue(name)) {
+            JOptionPane.showMessageDialog(this, "璇峰~鍐欓殰纰嶇墿鍚嶇О", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            showStep(1);
+            if (obstacleNameField != null) {
+                obstacleNameField.requestFocusInWindow();
+            }
+            return false;
+        }
+        if (!isObstacleNameUnique(name)) {
+            JOptionPane.showMessageDialog(this, "闅滅鐗╁悕绉板凡瀛樺湪锛岃杈撳叆鍞竴鍚嶇О", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            showStep(1);
+            if (obstacleNameField != null) {
+                obstacleNameField.requestFocusInWindow();
+            }
+            return false;
+        }
         if (!isMeaningfulValue(formData.get("obstacleCoordinates"))) {
             JOptionPane.showMessageDialog(this, "璇峰厛瀹屾垚闅滅鐗╃粯鍒�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
             return false;
@@ -995,34 +1703,504 @@
         if (!validateStep2()) {
             return;
         }
-        String coords = formData.get("obstacleCoordinates").trim();
         String landNumber = targetDikuai.getLandNumber();
-        if (!Dikuai.updateField(landNumber, "obstacleCoordinates", coords)) {
-            JOptionPane.showMessageDialog(this, "鏃犳硶鏇存柊闅滅鐗╁潗鏍�", "閿欒", JOptionPane.ERROR_MESSAGE);
+        String obstacleName = formData.get("obstacleName");
+        String coordsValue = formData.get("obstacleCoordinates");
+        String originalValue = formData.get("obstacleOriginalCoordinates");
+        String generatedBoundaryValue = formData.get("generatedBoundaryCoordinates");  // 鐢熸垚鐨勮竟鐣屽潗鏍�
+        String shapeKey = formData.get("obstacleShape");
+        String previousName = formData.get("editingObstacleName");
+
+        if (!isMeaningfulValue(coordsValue)) {
+            JOptionPane.showMessageDialog(this, "闅滅鐗╁潗鏍囨棤鏁�", "閿欒", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        String coords = coordsValue.trim();
+        String originalCoords = isMeaningfulValue(originalValue) ? originalValue.trim() : null;
+        // 瀵逛簬鍦嗗舰闅滅鐗╋紝鐩存帴浣跨敤obstacleCoordinates锛堝湪绗�3涓偣纭鏃跺凡鐢熸垚锛�
+        // 瀵逛簬澶氳竟褰㈤殰纰嶇墿锛屽鏋滄湁鐢熸垚鐨勮竟鐣屽潗鏍囷紝浣跨敤鐢熸垚鐨勮竟鐣屽潗鏍囷紱鍚﹀垯浣跨敤鍘熷鍧愭爣
+        String finalCoords;
+        if ("circle".equals(shapeKey)) {
+            // 鍦嗗舰闅滅鐗╃洿鎺ヤ娇鐢ㄥ凡鐢熸垚鐨勫潗鏍�
+            finalCoords = coords;
+        } else {
+            // 澶氳竟褰㈤殰纰嶇墿浼樺厛浣跨敤鐢熸垚鐨勮竟鐣屽潗鏍�
+            finalCoords = isMeaningfulValue(generatedBoundaryValue) ? generatedBoundaryValue.trim() : coords;
+        }
+        String trimmedName = isMeaningfulValue(obstacleName) ? obstacleName.trim() : null;
+        if (!isMeaningfulValue(trimmedName)) {
+            JOptionPane.showMessageDialog(this, "闅滅鐗╁悕绉版棤鏁�", "閿欒", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        formData.put("obstacleName", trimmedName);
+        if (isMeaningfulValue(previousName)) {
+            formData.put("editingObstacleName", trimmedName);
+        }
+
+        if (!persistObstacleToConfig(landNumber, previousName, trimmedName, shapeKey, finalCoords, originalCoords)) {
+            JOptionPane.showMessageDialog(this, "鍐欏叆闅滅鐗╅厤缃け璐ワ紝璇烽噸璇�", "閿欒", JOptionPane.ERROR_MESSAGE);
             return;
         }
         Dikuai.updateField(landNumber, "updateTime", currentTime());
         Dikuai.saveToProperties();
         Dikuaiguanli.notifyExternalCreation(landNumber);
 
-        JOptionPane.showMessageDialog(this, "闅滅鐗╁潗鏍囧凡淇濆瓨", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+    JOptionPane.showMessageDialog(this, "闅滅鐗╂暟鎹凡淇濆瓨", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
         activeSession = null;
         dispose();
     }
+    
+    /**
+     * 鐢熸垚闅滅鐗╄竟鐣屽潗鏍�
+     */
+    private void generateObstacleBoundary() {
+        String originalCoords = formData.get("obstacleOriginalCoordinates");
+        if (!isMeaningfulValue(originalCoords)) {
+            JOptionPane.showMessageDialog(this, "鏃犲師濮嬪潗鏍囨暟鎹紝鏃犳硶鐢熸垚杈圭晫", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+            return;
+        }
+        
+        String baseStation = targetDikuai.getBaseStationCoordinates();
+        if (!isMeaningfulValue(baseStation)) {
+            JOptionPane.showMessageDialog(this, "鍦板潡鏈缃熀绔欏潗鏍�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+        
+        String shapeKey = formData.get("obstacleShape");
+        
+        try {
+            String method = formData.get("drawingMethod");
+            
+            // 澶勭悊鍦嗗舰闅滅鐗�
+            if ("circle".equals(shapeKey)) {
+                if (!"mower".equals(method)) {
+                    JOptionPane.showMessageDialog(this, "鍙湁鍓茶崏鏈虹粯鍒剁殑鍦嗗舰闅滅鐗╂墠鏀寔鐢熸垚杈圭晫鍧愭爣", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+                    return;
+                }
+                
+                // 灏嗗師濮嬪潗鏍囪浆鎹负Coordinate瀵硅薄鍒楄〃
+                List<Coordinate> coordinateList = parseOriginalCoordinatesToCoordinateList(originalCoords);
+                if (coordinateList.size() < 3) {
+                    JOptionPane.showMessageDialog(this, "鍦嗗舰闅滅鐗╄嚦灏戦渶瑕佷笁涓噰闆嗙偣", "閿欒", JOptionPane.ERROR_MESSAGE);
+                    return;
+                }
+                
+                // 瑙f瀽鍩虹珯鍧愭爣
+                String[] baseParts = baseStation.split(",");
+                if (baseParts.length < 4) {
+                    JOptionPane.showMessageDialog(this, "鍩虹珯鍧愭爣鏍煎紡鏃犳晥", "閿欒", JOptionPane.ERROR_MESSAGE);
+                    return;
+                }
+                
+                double baseLat = parseDMToDecimal(baseParts[0].trim(), baseParts[1].trim());
+                double baseLon = parseDMToDecimal(baseParts[2].trim(), baseParts[3].trim());
+                
+                // 灏嗗師濮嬪潗鏍囪浆鎹负XY鍧愭爣锛堟湰鍦板潗鏍囩郴锛�
+                List<double[]> xyPoints = new ArrayList<>();
+                for (Coordinate coord : coordinateList) {
+                    double lat = parseDMToDecimal(coord.getLatitude(), coord.getLatDirection());
+                    double lon = parseDMToDecimal(coord.getLongitude(), coord.getLonDirection());
+                    xyPoints.add(convertLatLonToLocal(lat, lon, baseLat, baseLon));
+                }
+                
+                // 浣跨敤ThreePointCircle绠楁硶璁$畻鍦嗗績鍜屽崐寰�
+                if (xyPoints.size() < 3) {
+                    JOptionPane.showMessageDialog(this, "鑷冲皯闇�瑕佷笁涓偣鎵嶈兘鐢熸垚鍦嗗舰杈圭晫", "閿欒", JOptionPane.ERROR_MESSAGE);
+                    return;
+                }
+                
+                // 鍙栧墠涓変釜鐐硅绠楀渾
+                double[] p1 = xyPoints.get(0);
+                double[] p2 = xyPoints.get(1);
+                double[] p3 = xyPoints.get(2);
+                
+                String point1 = String.format(Locale.US, "%.2f,%.2f", p1[0], p1[1]);
+                String point2 = String.format(Locale.US, "%.2f,%.2f", p2[0], p2[1]);
+                String point3 = String.format(Locale.US, "%.2f,%.2f", p3[0], p3[1]);
+                
+                String circleResult = bianjie.ThreePointCircle.getCircleFromPoints(point1, point2, point3);
+                
+                // 瑙f瀽缁撴灉锛氭牸寮忎负 "鍦嗗績: x,y; 鍗婂緞: r"
+                if (circleResult == null || circleResult.startsWith("閿欒")) {
+                    JOptionPane.showMessageDialog(this, "鐢熸垚鍦嗗舰杈圭晫澶辫触: " + circleResult, "閿欒", JOptionPane.ERROR_MESSAGE);
+                    return;
+                }
+                
+                // 瑙f瀽鍦嗗績鍜屽崐寰�
+                String[] parts = circleResult.split(";");
+                if (parts.length < 2) {
+                    JOptionPane.showMessageDialog(this, "瑙f瀽鍦嗗舰杈圭晫缁撴灉澶辫触", "閿欒", JOptionPane.ERROR_MESSAGE);
+                    return;
+                }
+                
+                // 鎻愬彇鍦嗗績鍧愭爣
+                String centerPart = parts[0].trim(); // "鍦嗗績: x,y"
+                String radiusPart = parts[1].trim(); // "鍗婂緞: r"
+                
+                String centerCoords = centerPart.substring(centerPart.indexOf(":") + 1).trim();
+                String radiusStr = radiusPart.substring(radiusPart.indexOf(":") + 1).trim();
+                
+                String[] centerXY = centerCoords.split(",");
+                if (centerXY.length < 2) {
+                    JOptionPane.showMessageDialog(this, "瑙f瀽鍦嗗績鍧愭爣澶辫触", "閿欒", JOptionPane.ERROR_MESSAGE);
+                    return;
+                }
+                
+                double centerX = Double.parseDouble(centerXY[0].trim());
+                double centerY = Double.parseDouble(centerXY[1].trim());
+                double radius = Double.parseDouble(radiusStr.trim());
+                
+                // 璁$畻鍦嗕笂涓�鐐癸紙鍦嗗績鍙充晶鐨勭偣锛�
+                double radiusX = centerX + radius;
+                double radiusY = centerY;
+                
+                // 鐢熸垚杈圭晫鍧愭爣鏍煎紡锛氬渾蹇僗,鍦嗗績Y;鍦嗕笂鐐筙,鍦嗕笂鐐筜
+                String boundaryCoords = String.format(Locale.US, "%.2f,%.2f;%.2f,%.2f", 
+                    centerX, centerY, radiusX, radiusY);
+                
+                // 淇濆瓨鐢熸垚鐨勮竟鐣屽潗鏍�
+                formData.put("generatedBoundaryCoordinates", boundaryCoords);
+                
+                // 鏇存柊杈圭晫鐘舵�佹爣绛炬樉绀猴紙鍦嗗舰鍙湁2涓偣锛氬渾蹇冨拰鍦嗕笂涓�鐐癸級
+                updateBoundaryStatusLabel(2);
+                
+                // 鏇存柊棰勮鎸夐挳鏄剧ず锛堝彉鎴愮豢鑹插彲鐐瑰嚮锛�
+                updatePreviewButtonState();
+                
+                // 鏇存柊淇濆瓨鎸夐挳鐘舵�侊紙鍙樻垚鍙偣鍑伙級
+                updateSaveButtonState();
+                
+                // 寮哄埗鍒锋柊UI
+                SwingUtilities.invokeLater(() -> {
+                    if (previewButton != null) {
+                        previewButton.revalidate();
+                        previewButton.repaint();
+                    }
+                });
+                
+                JOptionPane.showMessageDialog(this, "鍦嗗舰闅滅鐗╄竟鐣屽潗鏍囧凡鐢熸垚", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+                return;
+            }
+            
+            // 澶勭悊澶氳竟褰㈤殰纰嶇墿
+            if (!"polygon".equals(shapeKey)) {
+                JOptionPane.showMessageDialog(this, "鍙湁澶氳竟褰㈡垨鍦嗗舰闅滅鐗╂墠闇�瑕佺敓鎴愯竟鐣屽潗鏍�", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+                return;
+            }
+            
+            // 妫�鏌ョ粯鍒舵柟寮忥紝鍙湁鍓茶崏鏈虹粯鍒剁殑澶氳竟褰㈡墠璋冪敤bianjieguihua2
+            if (!"mower".equals(method)) {
+                JOptionPane.showMessageDialog(this, "鍙湁鍓茶崏鏈虹粯鍒剁殑澶氳竟褰㈡墠鏀寔鐢熸垚杈圭晫鍧愭爣", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+                return;
+            }
+            
+            // 灏嗗師濮嬪潗鏍囪浆鎹负Coordinate瀵硅薄鍒楄〃
+            List<Coordinate> coordinateList = parseOriginalCoordinatesToCoordinateList(originalCoords);
+            if (coordinateList.isEmpty()) {
+                JOptionPane.showMessageDialog(this, "鍘熷鍧愭爣鏁版嵁鏃犳晥", "閿欒", JOptionPane.ERROR_MESSAGE);
+                return;
+            }
+            
+            // 淇濆瓨褰撳墠鐨凜oordinate.coordinates
+            List<Coordinate> savedCoordinates = new ArrayList<>(Coordinate.coordinates);
+            
+            try {
+                // 璁剧疆鍒板叏灞�鍧愭爣鍒楄〃
+                Coordinate.coordinates.clear();
+                Coordinate.coordinates.addAll(coordinateList);
+                
+                // 璋冪敤bianjieguihua2绠楁硶鐢熸垚浼樺寲鍚庣殑澶氳竟褰㈣竟鐣屽潗鏍�
+                String optimizedCoordsStr = bianjieguihua2.processCoordinateListAuto(baseStation);
+                
+                if (optimizedCoordsStr == null || optimizedCoordsStr.trim().isEmpty()) {
+                    JOptionPane.showMessageDialog(this, "鐢熸垚杈圭晫鍧愭爣澶辫触", "閿欒", JOptionPane.ERROR_MESSAGE);
+                    return;
+                }
+                
+                // 淇濆瓨鐢熸垚鐨勮竟鐣屽潗鏍�
+                formData.put("generatedBoundaryCoordinates", optimizedCoordsStr.trim());
+                
+                // 璁$畻鐢熸垚鐨勮竟鐣岀偣鏁�
+                int boundaryPointCount = countCoordinatePairs(optimizedCoordsStr.trim());
+                
+                // 鏇存柊杈圭晫鐘舵�佹爣绛炬樉绀�
+                updateBoundaryStatusLabel(boundaryPointCount);
+                
+                // 鏇存柊棰勮鎸夐挳鏄剧ず锛堝彉鎴愮豢鑹插彲鐐瑰嚮锛�
+                updatePreviewButtonState();
+                
+                // 鏇存柊淇濆瓨鎸夐挳鐘舵�侊紙鍙樻垚鍙偣鍑伙級
+                updateSaveButtonState();
+                
+                // 寮哄埗鍒锋柊UI
+                SwingUtilities.invokeLater(() -> {
+                    if (previewButton != null) {
+                        previewButton.revalidate();
+                        previewButton.repaint();
+                    }
+                });
+                
+                JOptionPane.showMessageDialog(this, "闅滅鐗╄竟鐣屽潗鏍囧凡鐢熸垚", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+                
+            } finally {
+                // 鎭㈠鍘熸潵鐨勫潗鏍囧垪琛�
+                Coordinate.coordinates.clear();
+                Coordinate.coordinates.addAll(savedCoordinates);
+            }
+            
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            JOptionPane.showMessageDialog(this, "鐢熸垚杈圭晫鍧愭爣鏃跺彂鐢熼敊璇�: " + ex.getMessage(), "閿欒", JOptionPane.ERROR_MESSAGE);
+        }
+    }
+    
+    /**
+     * 灏嗗師濮嬪潗鏍囧瓧绗︿覆瑙f瀽涓篊oordinate瀵硅薄鍒楄〃
+     */
+    private List<Coordinate> parseOriginalCoordinatesToCoordinateList(String originalCoords) {
+        List<Coordinate> coordinateList = new ArrayList<>();
+        if (!isMeaningfulValue(originalCoords)) {
+            return coordinateList;
+        }
+        
+        // 鍘熷鍧愭爣鏍煎紡锛氱含搴�1,鏂瑰悜1,缁忓害1,鏂瑰悜1;绾害2,鏂瑰悜2,缁忓害2,鏂瑰悜2;...
+        String[] pointStrings = originalCoords.split(";");
+        for (String pointStr : pointStrings) {
+            pointStr = pointStr.trim();
+            if (pointStr.isEmpty()) continue;
+            
+            String[] parts = pointStr.split(",");
+            if (parts.length >= 4) {
+                try {
+                    String lat = parts[0].trim();
+                    String latDir = parts[1].trim();
+                    String lon = parts[2].trim();
+                    String lonDir = parts[3].trim();
+                    
+                    Coordinate coord = new Coordinate(lat, latDir, lon, lonDir, 0.0);
+                    coordinateList.add(coord);
+                } catch (Exception e) {
+                    // 璺宠繃鏃犳晥鐨勫潗鏍囩偣
+                    continue;
+                }
+            }
+        }
+        
+        return coordinateList;
+    }
+    
+    /**
+     * 棰勮闅滅鐗╄竟鐣�
+     */
+    private void previewObstacleBoundary() {
+        String generatedBoundary = formData.get("generatedBoundaryCoordinates");
+        if (!isMeaningfulValue(generatedBoundary)) {
+            JOptionPane.showMessageDialog(this, "璇峰厛鐢熸垚闅滅鐗╄竟鐣屽潗鏍�", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+            return;
+        }
+        
+        String landNumber = targetDikuai.getLandNumber();
+        String landName = targetDikuai.getLandName();
+        String boundary = targetDikuai.getBoundaryCoordinates();
+        
+        // 鑾峰彇闅滅鐗╁潗鏍囷紙浼樺厛浣跨敤鐢熸垚鐨勮竟鐣屽潗鏍囷紝濡傛灉娌℃湁鍒欎娇鐢ㄥ師濮嬪潗鏍囷級
+        String obstacleCoords = generatedBoundary;
+        String shapeKey = formData.get("obstacleShape");
+        String obstacleName = formData.get("obstacleName");
+        
+        // 瀵逛簬鍦嗗舰闅滅鐗╋紝鐢熸垚鐨勮竟鐣屽潗鏍囨牸寮忓氨鏄殰纰嶇墿鍧愭爣鏍煎紡锛屽彲浠ョ洿鎺ヤ娇鐢�
+        // 瀵逛簬澶氳竟褰㈤殰纰嶇墿锛屼篃闇�瑕佷娇鐢ㄧ敓鎴愮殑杈圭晫鍧愭爣
+        // 濡傛灉鐢熸垚鐨勮竟鐣屽潗鏍囦笉鍙敤锛屽皾璇曚娇鐢ㄥ師濮嬮殰纰嶇墿鍧愭爣
+        if (!isMeaningfulValue(obstacleCoords)) {
+            obstacleCoords = formData.get("obstacleCoordinates");
+            if (!isMeaningfulValue(obstacleCoords)) {
+                JOptionPane.showMessageDialog(this, "鏃犳硶鑾峰彇闅滅鐗╁潗鏍囪繘琛岄瑙�", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+                return;
+            }
+        }
+        
+        // 鏋勫缓闅滅鐗╂暟鎹瓧绗︿覆锛屽寘鍚悕绉般�佸舰鐘跺拰鍧愭爣
+        // 鏍煎紡锛氶殰纰嶇墿鍚嶇О::褰㈢姸::鍧愭爣 鎴� 闅滅鐗╁悕绉�:褰㈢姸:鍧愭爣
+        final String obstacleData;
+        if (isMeaningfulValue(obstacleName) && isMeaningfulValue(shapeKey)) {
+            // 浣跨敤 :: 鍒嗛殧绗︽牸寮忥細鍚嶇О::褰㈢姸::鍧愭爣
+            obstacleData = obstacleName.trim() + "::" + shapeKey.trim() + "::" + obstacleCoords;
+        } else if (isMeaningfulValue(shapeKey)) {
+            // 鍙湁褰㈢姸锛氬舰鐘�::鍧愭爣
+            obstacleData = shapeKey.trim() + "::" + obstacleCoords;
+        } else {
+            // 鍙湁鍧愭爣
+            obstacleData = obstacleCoords;
+        }
+        
+        // 鍏抽棴褰撳墠瀵硅瘽妗�
+        setVisible(false);
+        
+        SwingUtilities.invokeLater(() -> {
+            Shouye shouye = Shouye.getInstance();
+            if (shouye != null) {
+                // 浼犻�掑洖璋冧互閲嶆柊鎵撳紑鏂板闅滅鐗╂楠�2椤甸潰
+                shouye.startMowingPathPreview(
+                    landNumber,
+                    landName,
+                    boundary,
+                    obstacleData,  // 浣跨敤鍖呭惈鍚嶇О鍜屽舰鐘剁殑闅滅鐗╂暟鎹�
+                    null,
+                    () -> SwingUtilities.invokeLater(() -> {
+                        // 閲嶆柊鎵撳紑鏂板闅滅鐗╂楠�2椤甸潰
+                        Window owner = SwingUtilities.getWindowAncestor(shouye);
+                        setVisible(true);
+                    })
+                );
+            } else {
+                JOptionPane.showMessageDialog(null, "鏃犳硶鎵撳紑涓婚〉闈㈣繘琛岄瑙�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+                setVisible(true);
+            }
+        });
+    }
+
+    private boolean persistObstacleToConfig(String landNumber, String previousName, String obstacleName,
+                                            String shapeKey, String xyCoords, String originalCoords) {
+        if (!isMeaningfulValue(landNumber) || !isMeaningfulValue(obstacleName) || !isMeaningfulValue(xyCoords)) {
+            return false;
+        }
+
+        String normalizedLandNumber = landNumber.trim();
+        String normalizedNewName = obstacleName.trim();
+        String normalizedPrevious = isMeaningfulValue(previousName) ? previousName.trim() : null;
+
+        String normalizedXy = xyCoords.trim();
+
+        try {
+            File configFile = new File("Obstacledge.properties");
+            Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager();
+            if (configFile.exists()) {
+                if (!manager.loadFromFile(configFile.getAbsolutePath())) {
+                    return false;
+                }
+            }
+
+            Obstacledge.Plot plot = manager.getPlotById(normalizedLandNumber);
+            if (plot == null) {
+                plot = new Obstacledge.Plot(normalizedLandNumber);
+                manager.addPlot(plot);
+            }
+
+            ensurePlotBaseStation(plot);
+
+            if (normalizedPrevious != null && !normalizedPrevious.equals(normalizedNewName)) {
+                plot.removeObstacleByName(normalizedPrevious);
+            }
+
+            Obstacledge.Obstacle obstacle = plot.getObstacleByName(normalizedNewName);
+            if (obstacle == null) {
+                obstacle = new Obstacledge.Obstacle(normalizedLandNumber, normalizedNewName,
+                        determineObstacleShape(shapeKey, normalizedXy));
+                plot.addObstacle(obstacle);
+            }
+
+            obstacle.setPlotId(normalizedLandNumber);
+            obstacle.setObstacleName(normalizedNewName);
+            obstacle.setShape(determineObstacleShape(shapeKey, normalizedXy));
+
+            obstacle.setXyCoordinates(new ArrayList<Obstacledge.XYCoordinate>());
+            obstacle.setOriginalCoordinates(new ArrayList<Obstacledge.DMCoordinate>());
+
+            obstacle.setXyCoordsString(normalizedXy);
+
+            if (isMeaningfulValue(originalCoords)) {
+                try {
+                    obstacle.setOriginalCoordsString(originalCoords);
+                } catch (Exception parseEx) {
+                    System.err.println("瑙f瀽闅滅鐗╁師濮嬪潗鏍囧け璐ワ紝灏嗕娇鐢ㄥ崰浣嶅��: " + parseEx.getMessage());
+                    obstacle.setOriginalCoordinates(new ArrayList<Obstacledge.DMCoordinate>());
+                }
+            }
+
+            ensurePlaceholderOriginalCoords(obstacle);
+
+            return manager.saveToFile(configFile.getAbsolutePath());
+        } catch (Exception ex) {
+            System.err.println("淇濆瓨闅滅鐗╅厤缃け璐�: " + ex.getMessage());
+            return false;
+        }
+    }
+
+    private Obstacledge.ObstacleShape determineObstacleShape(String shapeKey, String xyCoords) {
+        if (isMeaningfulValue(shapeKey)) {
+            String normalized = shapeKey.trim().toLowerCase(Locale.ROOT);
+            if ("circle".equals(normalized) || "鍦嗗舰".equals(normalized) || "0".equals(normalized)) {
+                return Obstacledge.ObstacleShape.CIRCLE;
+            }
+            if ("polygon".equals(normalized) || "澶氳竟褰�".equals(normalized) || "1".equals(normalized)) {
+                return Obstacledge.ObstacleShape.POLYGON;
+            }
+        }
+
+        int pairCount = countCoordinatePairs(xyCoords);
+        if (pairCount <= 0) {
+            return Obstacledge.ObstacleShape.POLYGON;
+        }
+        if (pairCount <= 2) {
+            return Obstacledge.ObstacleShape.CIRCLE;
+        }
+        return Obstacledge.ObstacleShape.POLYGON;
+    }
+
+    private void ensurePlaceholderOriginalCoords(Obstacledge.Obstacle obstacle) {
+        if (obstacle == null) {
+            return;
+        }
+        List<Obstacledge.DMCoordinate> originals = obstacle.getOriginalCoordinates();
+        if (originals != null && !originals.isEmpty()) {
+            return;
+        }
+        List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
+        int pointCount = (xyCoords != null && !xyCoords.isEmpty()) ? xyCoords.size() : 1;
+        List<Obstacledge.DMCoordinate> placeholders = new ArrayList<>(pointCount * 2);
+        for (int i = 0; i < pointCount; i++) {
+            placeholders.add(new Obstacledge.DMCoordinate(0.0, 'N'));
+            placeholders.add(new Obstacledge.DMCoordinate(0.0, 'E'));
+        }
+        obstacle.setOriginalCoordinates(placeholders);
+    }
+
+    private void ensurePlotBaseStation(Obstacledge.Plot plot) {
+        if (plot == null) {
+            return;
+        }
+        String existing = plot.getBaseStationString();
+        if (isMeaningfulValue(existing)) {
+            return;
+        }
+        String baseStation = resolveBaseStationCoordinates();
+        if (!isMeaningfulValue(baseStation)) {
+            return;
+        }
+        try {
+            plot.setBaseStationString(baseStation.trim());
+        } catch (Exception ex) {
+            System.err.println("璁剧疆鍩哄噯绔欏潗鏍囧け璐�: " + ex.getMessage());
+        }
+    }
 
     private static final class ExistingObstacle {
         private final String name;
         private final Obstacledge.ObstacleShape shape;
-        private final String coordinates;
+        private final String xyCoordinates;
+        private final String originalCoordinates;
 
-        ExistingObstacle(String name, Obstacledge.ObstacleShape shape, String coordinates) {
+        ExistingObstacle(String name, Obstacledge.ObstacleShape shape, String xyCoordinates, String originalCoordinates) {
             this.name = name != null ? name : "";
             this.shape = shape;
-            this.coordinates = coordinates != null ? coordinates : "";
+            this.xyCoordinates = safeCoordString(xyCoordinates);
+            this.originalCoordinates = safeCoordString(originalCoordinates);
         }
 
         static ExistingObstacle placeholder(String name) {
-            return new ExistingObstacle(name, null, "");
+            return new ExistingObstacle(name, null, "", "");
         }
 
         String getName() {
@@ -1047,7 +2225,38 @@
         }
 
         String getCoordinates() {
-            return coordinates;
+            return getDisplayCoordinates();
+        }
+
+        String getDisplayCoordinates() {
+            return hasText(xyCoordinates) ? xyCoordinates : originalCoordinates;
+        }
+
+        String getXyCoordinates() {
+            return xyCoordinates;
+        }
+
+        String getOriginalCoordinates() {
+            return originalCoordinates;
+        }
+
+        boolean hasPersistedData() {
+            return hasText(xyCoordinates) || hasText(originalCoordinates);
+        }
+
+        private static String safeCoordString(String value) {
+            if (value == null) {
+                return "";
+            }
+            return value.trim();
+        }
+
+        private static boolean hasText(String value) {
+            if (value == null) {
+                return false;
+            }
+            String trimmed = value.trim();
+            return !trimmed.isEmpty() && !"-1".equals(trimmed);
         }
     }
 
@@ -1058,10 +2267,25 @@
         if (step < 2) {
             nextButton.setVisible(true);
             saveButton.setVisible(false);
+            if (previewButton != null) {
+                previewButton.setVisible(false);
+            }
         } else {
             nextButton.setVisible(false);
             saveButton.setVisible(true);
             updateDrawingStatus();
+            updatePreviewButtonState();
+            // 瀵逛簬鍦嗗舰闅滅鐗╋紝纭繚棰勮鎸夐挳鍜岀敓鎴愯竟鐣屾寜閽殣钘�
+            String shapeKey = formData.get("obstacleShape");
+            boolean isCircle = "circle".equals(shapeKey);
+            if (isCircle) {
+                if (previewButton != null) {
+                    previewButton.setVisible(false);
+                }
+                if (generateBoundaryButton != null) {
+                    generateBoundaryButton.setVisible(false);
+                }
+            }
         }
         updateSaveButtonState();
         revalidate();
@@ -1076,6 +2300,11 @@
         formData.clear();
         formData.putAll(session.data);
 
+        String sessionName = session.data.get("obstacleName");
+        if (obstacleNameField != null) {
+            obstacleNameField.setText(sessionName != null ? sessionName : "");
+        }
+
         String method = session.data.get("drawingMethod");
         if (method != null) {
             JPanel panel = methodOptionPanels.get(method);
@@ -1092,7 +2321,7 @@
             }
         }
 
-        if (session.captureMessage != null) {
+    if (session.captureMessage != null && !session.captureSuccessful) {
             JOptionPane.showMessageDialog(this,
                     session.captureMessage,
                     session.captureSuccessful ? "鎴愬姛" : "鎻愮ず",
@@ -1100,6 +2329,24 @@
         }
 
         updateDrawingStatus();
+        
+        // 濡傛灉宸叉湁鐢熸垚鐨勮竟鐣屽潗鏍囷紝鏇存柊杈圭晫鐘舵�佹爣绛�
+        String generatedBoundary = session.data.get("generatedBoundaryCoordinates");
+        if (isMeaningfulValue(generatedBoundary)) {
+            int boundaryPointCount = countCoordinatePairs(generatedBoundary);
+            updateBoundaryStatusLabel(boundaryPointCount);
+        } else {
+            // 濡傛灉娌℃湁鐢熸垚鐨勮竟鐣屽潗鏍囷紝闅愯棌杈圭晫鐘舵�佹爣绛�
+            if (boundaryStatusLabel != null) {
+                boundaryStatusLabel.setVisible(false);
+                boundaryStatusLabel.setText("");
+            }
+        }
+        
+        // 鏇存柊鎸夐挳鐘舵��
+        updatePreviewButtonState();
+        updateSaveButtonState();
+        
         currentStep = 2;
         showStep(2);
     }
@@ -1108,6 +2355,93 @@
         return createInfoRow(label, value, null);
     }
 
+    private JPanel createObstacleNameRow() {
+        JPanel row = new JPanel();
+        row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));
+        row.setOpaque(false);
+        row.setAlignmentX(Component.LEFT_ALIGNMENT);
+        row.setMaximumSize(new Dimension(Integer.MAX_VALUE, 36));
+
+        JLabel label = new JLabel("闅滅鐗╁悕绉帮細");
+        label.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
+        label.setForeground(TEXT_COLOR);
+        label.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+        if (obstacleNameField == null) {
+            obstacleNameField = new JTextField();
+            obstacleNameField.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+            obstacleNameField.setColumns(16);
+            Dimension fieldSize = new Dimension(240, 30);
+            obstacleNameField.setPreferredSize(fieldSize);
+            obstacleNameField.setMinimumSize(fieldSize);
+            obstacleNameField.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
+            obstacleNameField.setBorder(BorderFactory.createCompoundBorder(
+                    BorderFactory.createLineBorder(BORDER_COLOR, 1),
+                    BorderFactory.createEmptyBorder(4, 8, 4, 8)));
+            obstacleNameField.getDocument().addDocumentListener(new DocumentListener() {
+                @Override
+                public void insertUpdate(DocumentEvent e) {
+                    handleObstacleNameChanged();
+                }
+
+                @Override
+                public void removeUpdate(DocumentEvent e) {
+                    handleObstacleNameChanged();
+                }
+
+                @Override
+                public void changedUpdate(DocumentEvent e) {
+                    handleObstacleNameChanged();
+                }
+            });
+        }
+
+        String existing = formData.get("obstacleName");
+        if (existing != null && !existing.equals(obstacleNameField.getText())) {
+            obstacleNameField.setText(existing);
+        }
+
+        row.add(label);
+        row.add(Box.createRigidArea(new Dimension(10, 0)));
+        row.add(obstacleNameField);
+        row.add(Box.createHorizontalGlue());
+        return row;
+    }
+
+    private void handleObstacleNameChanged() {
+        if (obstacleNameField == null) {
+            return;
+        }
+        String text = obstacleNameField.getText();
+        if (isMeaningfulValue(text)) {
+            formData.put("obstacleName", text.trim());
+        } else {
+            formData.remove("obstacleName");
+        }
+        updateSaveButtonState();
+    }
+
+    private boolean isObstacleNameUnique(String candidate) {
+        if (!isMeaningfulValue(candidate)) {
+            return false;
+        }
+        String trimmed = candidate.trim().toLowerCase(Locale.ROOT);
+        String original = formData.get("editingObstacleName");
+        if (isMeaningfulValue(original) && trimmed.equals(original.trim().toLowerCase(Locale.ROOT))) {
+            return true;
+        }
+        for (ExistingObstacle obstacle : existingObstacles) {
+            if (obstacle == null) {
+                continue;
+            }
+            String existingName = obstacle.getName();
+            if (isMeaningfulValue(existingName) && trimmed.equals(existingName.trim().toLowerCase(Locale.ROOT))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     private JPanel createInfoRow(String label, String value, String tooltip) {
         JPanel row = new JPanel();
         row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));
@@ -1136,15 +2470,14 @@
     }
 
     private JButton createPrimaryButton(String text, int fontSize) {
-        JButton button = new JButton(text);
+        JButton button = buttonset.createStyledButton(text, PRIMARY_COLOR);
         button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, fontSize));
-        button.setBackground(PRIMARY_COLOR);
-        button.setForeground(WHITE);
         button.setBorder(BorderFactory.createCompoundBorder(
                 BorderFactory.createLineBorder(PRIMARY_DARK, 2),
                 BorderFactory.createEmptyBorder(10, 22, 10, 22)));
         button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
-        button.setFocusPainted(false);
+        button.setOpaque(true);
+        button.setContentAreaFilled(true);
         button.addMouseListener(new MouseAdapter() {
             @Override
             public void mouseEntered(MouseEvent e) {
@@ -1157,6 +2490,9 @@
             public void mouseExited(MouseEvent e) {
                 if (button.isEnabled()) {
                     button.setBackground(PRIMARY_COLOR);
+                } else {
+                    // 绂佺敤鏃朵繚鎸佺伆鑹茶儗鏅�
+                    button.setBackground(MEDIUM_GRAY);
                 }
             }
         });
@@ -1164,15 +2500,15 @@
     }
 
     private JButton createSecondaryButton(String text) {
-        JButton button = new JButton(text);
+        JButton button = buttonset.createStyledButton(text, MEDIUM_GRAY);
         button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
-        button.setBackground(MEDIUM_GRAY);
         button.setForeground(TEXT_COLOR);
         button.setBorder(BorderFactory.createCompoundBorder(
                 BorderFactory.createLineBorder(BORDER_COLOR, 2),
                 BorderFactory.createEmptyBorder(10, 22, 10, 22)));
         button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
-        button.setFocusPainted(false);
+        button.setOpaque(true);
+        button.setContentAreaFilled(true);
         return button;
     }
 
@@ -1206,6 +2542,18 @@
         return null;
     }
 
+    private String resolveDrawingDeviceId(String method) {
+        if (!isMeaningfulValue(method)) {
+            return null;
+        }
+        String key = "handheld".equalsIgnoreCase(method) ? "handheldMarkerId" : "mowerId";
+        String value = Setsys.getPropertyValue(key);
+        if (!isMeaningfulValue(value)) {
+            return null;
+        }
+        return value.trim();
+    }
+
     private String safeValue(String value, String fallback) {
         if (!isMeaningfulValue(value)) {
             return fallback;

--
Gitblit v1.10.0