From b315a6943e6c0d6bdf0d5f7565c570d719154d6c Mon Sep 17 00:00:00 2001
From: 张世豪 <979909237@qq.com>
Date: 星期三, 17 十二月 2025 14:56:58 +0800
Subject: [PATCH] 新增了障碍物管理页面

---
 src/dikuai/Dikuaiguanli.java |  718 +++++++++++++++++++++++++++++++++++------------------------
 1 files changed, 423 insertions(+), 295 deletions(-)

diff --git a/src/dikuai/Dikuaiguanli.java b/src/dikuai/Dikuaiguanli.java
index 3d01450..5adb9c5 100644
--- a/src/dikuai/Dikuaiguanli.java
+++ b/src/dikuai/Dikuaiguanli.java
@@ -6,6 +6,9 @@
 import java.awt.datatransfer.*;
 import java.awt.datatransfer.StringSelection;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import ui.UIConfig;
@@ -17,8 +20,11 @@
 import java.util.List;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Objects;
+import java.util.Properties;
 
 import lujing.Lunjingguihua;
+import lujing.MowingPathGenerationPage;
 import zhangaiwu.AddDikuai;
 import zhangaiwu.Obstacledge;
 import zhuye.MapRenderer;
@@ -58,6 +64,8 @@
 	private JButton addLandBtn;
 
 	private static String currentWorkLandNumber;
+	private static final String WORK_LAND_KEY = "currentWorkLandNumber";
+	private static final String PROPERTIES_FILE = "set.properties";
 	private static final Map<String, Boolean> boundaryPointVisibility = new HashMap<>();
 	private ImageIcon workSelectedIcon;
 	private ImageIcon workUnselectedIcon;
@@ -218,12 +226,12 @@
 		
 		// 鍦板潡缂栧彿
 		contentPanel.add(createCardInfoItem("鍦板潡缂栧彿:", getDisplayValue(dikuai.getLandNumber(), "鏈煡")));
-	contentPanel.add(Box.createRigidArea(new Dimension(0, 20)));
-		
+	contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
+
 		// 娣诲姞鏃堕棿
 		contentPanel.add(createCardInfoItem("娣诲姞鏃堕棿:", getDisplayValue(dikuai.getCreateTime(), "鏈煡")));
-	contentPanel.add(Box.createRigidArea(new Dimension(0, 20)));
-		
+	contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
+
 		// 鍦板潡闈㈢Н
 		String landArea = dikuai.getLandArea();
 		if (landArea != null && !landArea.equals("-1")) {
@@ -232,14 +240,14 @@
 			landArea = "鏈煡";
 		}
 		contentPanel.add(createCardInfoItem("鍦板潡闈㈢Н:", landArea));
-	contentPanel.add(Box.createRigidArea(new Dimension(0, 20)));
-		
+	contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
+
 		// 杩斿洖鐐瑰潗鏍囷紙甯︿慨鏀规寜閽級
-		contentPanel.add(createCardInfoItemWithButton("杩斿洖鐐瑰潗鏍�:", 
-			getDisplayValue(dikuai.getReturnPointCoordinates(), "鏈缃�"), 
+		contentPanel.add(createCardInfoItemWithButton("杩斿洖鐐瑰潗鏍�:",
+			getDisplayValue(dikuai.getReturnPointCoordinates(), "鏈缃�"),
 			"淇敼", e -> editReturnPoint(dikuai)));
-	contentPanel.add(Box.createRigidArea(new Dimension(0, 20)));
-		
+	contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
+
 		// 鍦板潡杈圭晫鍧愭爣锛堝甫鏄剧ず椤剁偣鎸夐挳锛�
 		JPanel boundaryPanel = createBoundaryInfoItem(dikuai,
 			getTruncatedValue(dikuai.getBoundaryCoordinates(), 12, "鏈缃�"));
@@ -248,27 +256,31 @@
 			() -> editBoundaryCoordinates(dikuai),
 			"鐐瑰嚮鏌ョ湅/缂栬緫鍦板潡杈圭晫鍧愭爣");
 		contentPanel.add(boundaryPanel);
-	contentPanel.add(Box.createRigidArea(new Dimension(0, 20)));
-		
+	contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
+
 		ObstacleSummary obstacleSummary = getObstacleSummaryFromCache(dikuai.getLandNumber());
 		JPanel obstaclePanel = createCardInfoItemWithButton("闅滅鐗�:",
 			obstacleSummary.buildDisplayValue(),
 			"鏂板",
 			e -> addNewObstacle(dikuai));
 		setInfoItemTooltip(obstaclePanel, obstacleSummary.buildTooltip());
+		// 璁╅殰纰嶇墿鏍囬鍙偣鍑伙紝鎵撳紑闅滅鐗╃鐞嗛〉闈�
+		configureInteractiveLabel(getInfoItemTitleLabel(obstaclePanel),
+			() -> showObstacleManagementPage(dikuai),
+			"鐐瑰嚮鏌ョ湅/绠$悊闅滅鐗�");
 		contentPanel.add(obstaclePanel);
-	contentPanel.add(Box.createRigidArea(new Dimension(0, 20)));
+	contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
 
 		// 璺緞鍧愭爣锛堝甫鏌ョ湅鎸夐挳锛�
-		JPanel pathPanel = createCardInfoItemWithButton("璺緞鍧愭爣:", 
-			getTruncatedValue(dikuai.getPlannedPath(), 12, "鏈缃�"), 
+		JPanel pathPanel = createCardInfoItemWithButton("璺緞鍧愭爣:",
+			getTruncatedValue(dikuai.getPlannedPath(), 12, "鏈缃�"),
 			"澶嶅埗", e -> copyCoordinatesAction("璺緞鍧愭爣", dikuai.getPlannedPath()));
 		setInfoItemTooltip(pathPanel, dikuai.getPlannedPath());
 		configureInteractiveLabel(getInfoItemTitleLabel(pathPanel),
 			() -> editPlannedPath(dikuai),
 			"鐐瑰嚮鏌ョ湅/缂栬緫璺緞鍧愭爣");
 		contentPanel.add(pathPanel);
-	contentPanel.add(Box.createRigidArea(new Dimension(0, 20)));
+	contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
 
 		JPanel baseStationPanel = createCardInfoItemWithButton("鍩虹珯鍧愭爣:",
 			getTruncatedValue(dikuai.getBaseStationCoordinates(), 12, "鏈缃�"),
@@ -278,7 +290,7 @@
 			() -> editBaseStationCoordinates(dikuai),
 			"鐐瑰嚮鏌ョ湅/缂栬緫鍩虹珯鍧愭爣");
 		contentPanel.add(baseStationPanel);
-	contentPanel.add(Box.createRigidArea(new Dimension(0, 20)));
+	contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
 
 		JPanel boundaryOriginalPanel = createCardInfoItemWithButton("杈圭晫鍘熷鍧愭爣:",
 			getTruncatedValue(dikuai.getBoundaryOriginalCoordinates(), 12, "鏈缃�"),
@@ -288,7 +300,7 @@
 			() -> editBoundaryOriginalCoordinates(dikuai),
 			"鐐瑰嚮鏌ョ湅/缂栬緫杈圭晫鍘熷鍧愭爣");
 		contentPanel.add(boundaryOriginalPanel);
-		contentPanel.add(Box.createRigidArea(new Dimension(0, 20)));
+		contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
 
 		JPanel mowingPatternPanel = createCardInfoItemWithButton("鍓茶崏妯″紡:",
 			getTruncatedValue(dikuai.getMowingPattern(), 12, "鏈缃�"),
@@ -298,7 +310,7 @@
 			() -> editMowingPattern(dikuai),
 			"鐐瑰嚮鏌ョ湅/缂栬緫鍓茶崏妯″紡");
 		contentPanel.add(mowingPatternPanel);
-		contentPanel.add(Box.createRigidArea(new Dimension(0, 20)));
+		contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
 
 		String mowingWidthValue = dikuai.getMowingWidth();
 		String widthSource = null;
@@ -311,7 +323,7 @@
 			"缂栬緫", e -> editMowingWidth(dikuai));
 		setInfoItemTooltip(mowingWidthPanel, widthSource);
 		contentPanel.add(mowingWidthPanel);
-		contentPanel.add(Box.createRigidArea(new Dimension(0, 20)));
+		contentPanel.add(Box.createRigidArea(new Dimension(0, 15)));
 
 		JPanel completedTrackPanel = createCardInfoItemWithButton("宸插畬鎴愬壊鑽夎矾寰�:",
 			getTruncatedValue(dikuai.getMowingTrack(), 12, "鏈褰�"),
@@ -327,8 +339,8 @@
 		JButton deleteBtn = createDeleteButton();
 		deleteBtn.addActionListener(e -> deleteDikuai(dikuai));
 
-		JButton generatePathBtn = createPrimaryFooterButton("鐢熸垚鍓茶崏璺緞");
-		generatePathBtn.addActionListener(e -> generateMowingPath(dikuai));
+		JButton generatePathBtn = createPrimaryFooterButton("璺緞瑙勫垝");
+		generatePathBtn.addActionListener(e -> showPathPlanningPage(dikuai));
 
 		JPanel footerPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
 		footerPanel.setBackground(CARD_BACKGROUND);
@@ -363,7 +375,10 @@
 	private JPanel createCardInfoItemWithButton(String label, String value, String buttonText, ActionListener listener) {
 		JPanel itemPanel = new JPanel(new BorderLayout());
 		itemPanel.setBackground(CARD_BACKGROUND);
-		itemPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 20));
+		// 澧炲姞楂樺害浠ョ‘淇濇寜閽畬鏁存樉绀猴紙鎸夐挳楂樺害绾�24-28鍍忕礌锛屽姞涓婁笂涓嬭竟璺濓級
+		itemPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 35));
+		itemPanel.setPreferredSize(new Dimension(Integer.MAX_VALUE, 30));
+		itemPanel.setMinimumSize(new Dimension(0, 28));
 		
 		JLabel labelComp = new JLabel(label);
 		labelComp.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
@@ -371,13 +386,14 @@
 		
 		JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0));
 		rightPanel.setBackground(CARD_BACKGROUND);
+		// 娣诲姞鍨傜洿鍐呰竟璺濅互纭繚鎸夐挳涓嶈瑁佸壀
+		rightPanel.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 0));
 		
 		JLabel valueComp = new JLabel(value);
 		valueComp.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
 		valueComp.setForeground(TEXT_COLOR);
 		
-		JButton button = createSmallButton(buttonText);
-		button.addActionListener(listener);
+		JButton button = createSmallLinkButton(buttonText, listener);
 		
 		rightPanel.add(valueComp);
 		rightPanel.add(button);
@@ -393,17 +409,19 @@
 		private JPanel createBoundaryInfoItem(Dikuai dikuai, String displayValue) {
 			JPanel itemPanel = new JPanel(new BorderLayout());
 			itemPanel.setBackground(CARD_BACKGROUND);
-			int rowHeight = Math.max(36, BOUNDARY_TOGGLE_ICON_SIZE + 12);
+			// 澧炲姞楂樺害浠ョ‘淇濇寜閽笅杈圭紭瀹屾暣鏄剧ず锛堟寜閽珮搴�56锛屽姞涓婁笂涓嬭竟璺濓級
+			int rowHeight = Math.max(60, BOUNDARY_TOGGLE_ICON_SIZE + 16);
 			Dimension rowDimension = new Dimension(Integer.MAX_VALUE, rowHeight);
 			itemPanel.setMaximumSize(rowDimension);
 			itemPanel.setPreferredSize(rowDimension);
-			itemPanel.setMinimumSize(new Dimension(0, 32));
+			itemPanel.setMinimumSize(new Dimension(0, 56));
 
 			JLabel labelComp = new JLabel("鍦板潡杈圭晫:");
 			labelComp.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
 			labelComp.setForeground(LIGHT_TEXT);
 
-			int verticalPadding = Math.max(0, (rowHeight - BOUNDARY_TOGGLE_ICON_SIZE) / 2);
+			// 纭繚鎸夐挳鏈夎冻澶熺殑涓婁笅杈硅窛锛岄伩鍏嶄笅杈圭紭琚鍓�
+			int verticalPadding = Math.max(2, (rowHeight - BOUNDARY_TOGGLE_ICON_SIZE) / 2);
 			JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0));
 			rightPanel.setBackground(CARD_BACKGROUND);
 			rightPanel.setBorder(BorderFactory.createEmptyBorder(verticalPadding, 0, verticalPadding, 0));
@@ -504,6 +522,7 @@
 					boolean isCurrent = currentWorkLandNumber != null && currentWorkLandNumber.equals(landNumber);
 					if (isCurrent) {
 						renderer.setBoundaryPointsVisible(desiredState);
+						renderer.setBoundaryPointSizeScale(desiredState ? 0.5d : 1.0d);
 					}
 				}
 			}
@@ -583,17 +602,69 @@
 		JScrollPane scrollPane = new JScrollPane(textArea);
 		scrollPane.setPreferredSize(new Dimension(360, 240));
 
-		int option = JOptionPane.showConfirmDialog(
-			this,
-			scrollPane,
-			title,
-			JOptionPane.OK_CANCEL_OPTION,
-			JOptionPane.PLAIN_MESSAGE);
-
-		if (option == JOptionPane.OK_OPTION) {
-			return textArea.getText();
+		Window owner = SwingUtilities.getWindowAncestor(this);
+		JDialog dialog;
+		if (owner instanceof Frame) {
+			dialog = new JDialog((Frame) owner, title, true);
+		} else if (owner instanceof Dialog) {
+			dialog = new JDialog((Dialog) owner, title, true);
+		} else {
+			dialog = new JDialog((Frame) null, title, true);
 		}
-		return null;
+		dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+
+		JPanel contentPanel = new JPanel(new BorderLayout());
+		contentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+		contentPanel.add(scrollPane, BorderLayout.CENTER);
+
+		JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+		JButton okButton = new JButton("纭畾");
+		JButton cancelButton = new JButton("鍙栨秷");
+		JButton copyButton = new JButton("澶嶅埗");
+
+		final boolean[] confirmed = new boolean[] {false};
+		final String[] resultHolder = new String[1];
+
+		okButton.addActionListener(e -> {
+			resultHolder[0] = textArea.getText();
+			confirmed[0] = true;
+			dialog.dispose();
+		});
+
+		cancelButton.addActionListener(e -> dialog.dispose());
+
+		copyButton.addActionListener(e -> {
+			String text = textArea.getText();
+			if (text == null) {
+				text = "";
+			}
+			String trimmed = text.trim();
+			if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+				JOptionPane.showMessageDialog(dialog, title + " 鏈缃�", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+				return;
+			}
+			try {
+				StringSelection selection = new StringSelection(text);
+				Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+				clipboard.setContents(selection, selection);
+				JOptionPane.showMessageDialog(dialog, title + " 宸插鍒跺埌鍓创鏉�", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+			} catch (Exception ex) {
+				JOptionPane.showMessageDialog(dialog, "澶嶅埗澶辫触: " + ex.getMessage(), "閿欒", JOptionPane.ERROR_MESSAGE);
+			}
+		});
+
+		buttonPanel.add(okButton);
+		buttonPanel.add(cancelButton);
+		buttonPanel.add(copyButton);
+
+		contentPanel.add(buttonPanel, BorderLayout.SOUTH);
+		dialog.setContentPane(contentPanel);
+		dialog.getRootPane().setDefaultButton(okButton);
+		dialog.pack();
+		dialog.setLocationRelativeTo(this);
+		dialog.setVisible(true);
+
+		return confirmed[0] ? resultHolder[0] : null;
 	}
 
 	private boolean saveFieldAndRefresh(Dikuai dikuai, String fieldName, String value) {
@@ -733,10 +804,26 @@
 		switch (trimmed) {
 			case "1":
 			case "spiral":
+			case "铻烘棆":
+			case "铻烘棆妯″紡":
 				return "spiral";
 			case "0":
 			case "parallel":
+			case "骞宠":
+			case "骞宠妯″紡":
 			default:
+				if (trimmed.contains("铻烘棆")) {
+					return "spiral";
+				}
+				if (trimmed.contains("spiral")) {
+					return "spiral";
+				}
+				if (trimmed.contains("parallel")) {
+					return "parallel";
+				}
+				if (trimmed.contains("骞宠")) {
+					return "parallel";
+				}
 				return "parallel";
 		}
 	}
@@ -752,6 +839,88 @@
 		return trimmed;
 	}
 
+	/**
+	 * 鏄剧ず璺緞瑙勫垝椤甸潰
+	 */
+	private void showPathPlanningPage(Dikuai dikuai) {
+		if (dikuai == null) {
+			return;
+		}
+
+		Window owner = SwingUtilities.getWindowAncestor(this);
+		
+		// 鑾峰彇鍦板潡绠$悊瀵硅瘽妗嗭紝鍑嗗鍦ㄦ墦寮�璺緞瑙勫垝椤甸潰鏃跺叧闂�
+		Window managementWindow = null;
+		if (owner instanceof JDialog) {
+			managementWindow = owner;
+		}
+
+		// 鑾峰彇鍦板潡鍩烘湰鏁版嵁
+		String baseStationValue = prepareCoordinateForEditor(dikuai.getBaseStationCoordinates());
+		String boundaryValue = prepareCoordinateForEditor(dikuai.getBoundaryCoordinates());
+		List<Obstacledge.Obstacle> configuredObstacles = getConfiguredObstacles(dikuai);
+		String obstacleValue = determineInitialObstacleValue(dikuai, configuredObstacles);
+		String widthValue = sanitizeWidthString(dikuai.getMowingWidth());
+		if (widthValue != null) {
+			try {
+				double widthCm = Double.parseDouble(widthValue);
+				widthValue = formatWidthForStorage(widthCm);
+			} catch (NumberFormatException ignored) {
+				// 淇濇寔鍘熷瀛楃涓诧紝绋嶅悗鏍¢獙鎻愮ず
+			}
+		}
+		String modeValue = sanitizeValueOrNull(dikuai.getMowingPattern());
+		String existingPath = prepareCoordinateForEditor(dikuai.getPlannedPath());
+
+		// 鍒涘缓淇濆瓨鍥炶皟鎺ュ彛瀹炵幇
+		MowingPathGenerationPage.PathSaveCallback callback = new MowingPathGenerationPage.PathSaveCallback() {
+			@Override
+			public boolean saveBaseStationCoordinates(Dikuai dikuai, String value) {
+				return saveFieldAndRefresh(dikuai, "baseStationCoordinates", value);
+			}
+
+			@Override
+			public boolean saveBoundaryCoordinates(Dikuai dikuai, String value) {
+				return saveFieldAndRefresh(dikuai, "boundaryCoordinates", value);
+			}
+
+			@Override
+			public boolean saveObstacleCoordinates(Dikuai dikuai, String baseStationValue, String obstacleValue) {
+				return persistObstaclesForLand(dikuai, baseStationValue, obstacleValue);
+			}
+
+			@Override
+			public boolean saveMowingWidth(Dikuai dikuai, String value) {
+				return saveFieldAndRefresh(dikuai, "mowingWidth", value);
+			}
+
+			@Override
+			public boolean savePlannedPath(Dikuai dikuai, String value) {
+				return saveFieldAndRefresh(dikuai, "plannedPath", value);
+			}
+		};
+
+		// 鏄剧ず璺緞瑙勫垝椤甸潰
+		MowingPathGenerationPage dialog = new MowingPathGenerationPage(
+			owner,
+			dikuai,
+			baseStationValue,
+			boundaryValue,
+			obstacleValue,
+			widthValue,
+			modeValue,
+			existingPath,
+			callback
+		);
+
+		// 鍏抽棴鍦板潡绠$悊椤甸潰
+		if (managementWindow != null) {
+			managementWindow.dispose();
+		}
+
+		dialog.setVisible(true);
+	}
+
 	private void generateMowingPath(Dikuai dikuai) {
 		if (dikuai == null) {
 			return;
@@ -790,178 +959,48 @@
 		String modeValue,
 		String initialGeneratedPath) {
 		Window owner = SwingUtilities.getWindowAncestor(this);
-		JDialog dialog = new JDialog(owner, "鐢熸垚鍓茶崏璺緞", Dialog.ModalityType.APPLICATION_MODAL);
-		dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
-		dialog.getContentPane().setLayout(new BorderLayout());
-		dialog.getContentPane().setBackground(BACKGROUND_COLOR);
-
-		JPanel contentPanel = new JPanel();
-		contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS));
-		contentPanel.setBackground(BACKGROUND_COLOR);
-		contentPanel.setBorder(BorderFactory.createEmptyBorder(12, 16, 12, 16));
-
-		String landName = getDisplayValue(dikuai.getLandName(), "鏈煡鍦板潡");
-		String landNumber = getDisplayValue(dikuai.getLandNumber(), "鏈煡缂栧彿");
-		JLabel headerLabel = new JLabel(landName + " / " + landNumber);
-		headerLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
-		headerLabel.setForeground(TEXT_COLOR);
-		headerLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
-		contentPanel.add(headerLabel);
-		contentPanel.add(Box.createVerticalStrut(12));
-
-		JTextField baseStationField = createInfoTextField(baseStationValue != null ? baseStationValue : "", true);
-		contentPanel.add(createTextFieldSection("鍩虹珯鍧愭爣", baseStationField));
-
-		JTextArea boundaryArea = createInfoTextArea(boundaryValue != null ? boundaryValue : "", true, 6);
-		contentPanel.add(createTextAreaSection("鍦板潡杈圭晫", boundaryArea));
-
-		JTextArea obstacleArea = createInfoTextArea(obstacleValue != null ? obstacleValue : "", true, 6);
-		contentPanel.add(createTextAreaSection("闅滅鐗╁潗鏍�", obstacleArea));
-
-		JTextField widthField = createInfoTextField(widthValue != null ? widthValue : "", true);
-		contentPanel.add(createTextFieldSection("鍓茶崏瀹藉害 (鍘樼背)", widthField));
-		contentPanel.add(createInfoValueSection("鍓茶崏妯″紡", formatMowingPatternForDialog(modeValue)));
-
-		String existingPath = prepareCoordinateForEditor(dikuai.getPlannedPath());
-		String pathSeed = initialGeneratedPath != null ? initialGeneratedPath : existingPath;
-		JTextArea pathArea = createInfoTextArea(pathSeed != null ? pathSeed : "", true, 10);
-		contentPanel.add(createTextAreaSection("鍓茶崏璺緞鍧愭爣", pathArea));
-
-		JScrollPane dialogScrollPane = new JScrollPane(contentPanel);
-		dialogScrollPane.setBorder(BorderFactory.createEmptyBorder());
-		dialogScrollPane.getVerticalScrollBar().setUnitIncrement(16);
-		dialog.add(dialogScrollPane, BorderLayout.CENTER);
-
-		JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 12, 12));
-		buttonPanel.setBackground(BACKGROUND_COLOR);
-
-		JButton generateBtn = createPrimaryFooterButton("鐢熸垚鍓茶崏璺緞");
-		JButton saveBtn = createPrimaryFooterButton("淇濆瓨璺緞");
-		JButton cancelBtn = createPrimaryFooterButton("鍙栨秷");
-
-		generateBtn.addActionListener(e -> {
-			String sanitizedWidth = sanitizeWidthString(widthField.getText());
-			if (sanitizedWidth != null) {
-				try {
-					double widthCm = Double.parseDouble(sanitizedWidth);
-					widthField.setText(formatWidthForStorage(widthCm));
-					sanitizedWidth = formatWidthForStorage(widthCm);
-				} catch (NumberFormatException ex) {
-					widthField.setText(sanitizedWidth);
-				}
+		
+		// 鍒涘缓淇濆瓨鍥炶皟鎺ュ彛瀹炵幇
+		MowingPathGenerationPage.PathSaveCallback callback = new MowingPathGenerationPage.PathSaveCallback() {
+			@Override
+			public boolean saveBaseStationCoordinates(Dikuai dikuai, String value) {
+				return saveFieldAndRefresh(dikuai, "baseStationCoordinates", value);
 			}
-			String generated = attemptMowingPathPreview(
-				boundaryArea.getText(),
-				obstacleArea.getText(),
-				sanitizedWidth,
-				modeValue,
-				dialog,
-				true
-			);
-			if (generated != null) {
-				pathArea.setText(generated);
-				pathArea.setCaretPosition(0);
+			
+			@Override
+			public boolean saveBoundaryCoordinates(Dikuai dikuai, String value) {
+				return saveFieldAndRefresh(dikuai, "boundaryCoordinates", value);
 			}
-		});
-
-		saveBtn.addActionListener(e -> {
-			String baseStationNormalized = normalizeCoordinateInput(baseStationField.getText());
-			String boundaryNormalized = normalizeCoordinateInput(boundaryArea.getText());
-			if (!"-1".equals(boundaryNormalized)) {
-				boundaryNormalized = boundaryNormalized
-					.replace("\r\n", ";")
-					.replace('\r', ';')
-					.replace('\n', ';')
-					.replaceAll(";+", ";")
-					.replaceAll("\\s*;\\s*", ";")
-					.trim();
-				if (boundaryNormalized.isEmpty()) {
-					boundaryNormalized = "-1";
-				}
+			
+			@Override
+			public boolean saveObstacleCoordinates(Dikuai dikuai, String baseStationValue, String obstacleValue) {
+				return persistObstaclesForLand(dikuai, baseStationValue, obstacleValue);
 			}
-			String obstacleNormalized = normalizeCoordinateInput(obstacleArea.getText());
-			if (!"-1".equals(obstacleNormalized)) {
-				obstacleNormalized = obstacleNormalized
-					.replace("\r\n", " ")
-					.replace('\r', ' ')
-					.replace('\n', ' ')
-					.replaceAll("\\s{2,}", " ")
-					.trim();
-				if (obstacleNormalized.isEmpty()) {
-					obstacleNormalized = "-1";
-				}
+			
+			@Override
+			public boolean saveMowingWidth(Dikuai dikuai, String value) {
+				return saveFieldAndRefresh(dikuai, "mowingWidth", value);
 			}
-			String rawWidthInput = widthField.getText() != null ? widthField.getText().trim() : "";
-			String widthSanitized = sanitizeWidthString(widthField.getText());
-			if (widthSanitized == null) {
-				String message = rawWidthInput.isEmpty() ? "璇峰厛璁剧疆鍓茶崏瀹藉害(鍘樼背)" : "鍓茶崏瀹藉害鏍煎紡涓嶆纭�";
-				JOptionPane.showMessageDialog(dialog, message, "鎻愮ず", JOptionPane.WARNING_MESSAGE);
-				return;
+			
+			@Override
+			public boolean savePlannedPath(Dikuai dikuai, String value) {
+				return saveFieldAndRefresh(dikuai, "plannedPath", value);
 			}
-			double widthCm;
-			try {
-				widthCm = Double.parseDouble(widthSanitized);
-			} catch (NumberFormatException ex) {
-				JOptionPane.showMessageDialog(dialog, "鍓茶崏瀹藉害鏍煎紡涓嶆纭�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
-				return;
-			}
-			if (widthCm <= 0) {
-				JOptionPane.showMessageDialog(dialog, "鍓茶崏瀹藉害蹇呴』澶т簬0", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
-				return;
-			}
-			String widthNormalized = formatWidthForStorage(widthCm);
-			widthField.setText(widthNormalized);
-			String pathNormalized = normalizeCoordinateInput(pathArea.getText());
-			if (!"-1".equals(pathNormalized)) {
-				pathNormalized = pathNormalized
-					.replace("\r\n", ";")
-					.replace('\r', ';')
-					.replace('\n', ';')
-					.replaceAll(";+", ";")
-					.replaceAll("\\s*;\\s*", ";")
-					.trim();
-				if (pathNormalized.isEmpty()) {
-					pathNormalized = "-1";
-				}
-			}
-			if ("-1".equals(pathNormalized)) {
-				JOptionPane.showMessageDialog(dialog, "璇峰厛鐢熸垚鍓茶崏璺緞", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
-				return;
-			}
-			if (!saveFieldAndRefresh(dikuai, "baseStationCoordinates", baseStationNormalized)) {
-				JOptionPane.showMessageDialog(dialog, "鏃犳硶淇濆瓨鍩虹珯鍧愭爣", "閿欒", JOptionPane.ERROR_MESSAGE);
-				return;
-			}
-			if (!saveFieldAndRefresh(dikuai, "boundaryCoordinates", boundaryNormalized)) {
-				JOptionPane.showMessageDialog(dialog, "鏃犳硶淇濆瓨鍦板潡杈圭晫", "閿欒", JOptionPane.ERROR_MESSAGE);
-				return;
-			}
-			if (!persistObstaclesForLand(dikuai, baseStationNormalized, obstacleNormalized)) {
-				JOptionPane.showMessageDialog(dialog, "鏃犳硶淇濆瓨闅滅鐗╁潗鏍�", "閿欒", JOptionPane.ERROR_MESSAGE);
-				return;
-			}
-			if (!saveFieldAndRefresh(dikuai, "mowingWidth", widthNormalized)) {
-				JOptionPane.showMessageDialog(dialog, "鏃犳硶淇濆瓨鍓茶崏瀹藉害", "閿欒", JOptionPane.ERROR_MESSAGE);
-				return;
-			}
-			if (!saveFieldAndRefresh(dikuai, "plannedPath", pathNormalized)) {
-				JOptionPane.showMessageDialog(dialog, "鏃犳硶淇濆瓨鍓茶崏璺緞", "閿欒", JOptionPane.ERROR_MESSAGE);
-				return;
-			}
-			JOptionPane.showMessageDialog(dialog, "鍓茶崏璺緞宸蹭繚瀛�", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
-			dialog.dispose();
-		});
-
-		cancelBtn.addActionListener(e -> dialog.dispose());
-
-		buttonPanel.add(generateBtn);
-		buttonPanel.add(saveBtn);
-		buttonPanel.add(cancelBtn);
-		dialog.add(buttonPanel, BorderLayout.SOUTH);
-
-		dialog.pack();
-		dialog.setSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
-		dialog.setLocationRelativeTo(owner);
+		};
+		
+		// 浣跨敤鏂扮殑鐙珛椤甸潰绫�
+		MowingPathGenerationPage dialog = new MowingPathGenerationPage(
+			owner,
+			dikuai,
+			baseStationValue,
+			boundaryValue,
+			obstacleValue,
+			widthValue,
+			modeValue,
+			initialGeneratedPath,
+			callback
+		);
+		
 		dialog.setVisible(true);
 	}
 
@@ -1274,112 +1313,104 @@
 		return value;
 	}
 
+	/**
+	 * 鍒涘缓绫讳技浜庨摼鎺ョ殑灏忔寜閽�
+	 */
+	private JButton createSmallLinkButton(String text, ActionListener listener) {
+		JButton btn = new JButton(text);
+		btn.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 11));
+		btn.setForeground(PRIMARY_COLOR);
+		btn.setBorder(BorderFactory.createCompoundBorder(
+			BorderFactory.createLineBorder(PRIMARY_COLOR, 1, true),
+			BorderFactory.createEmptyBorder(2, 6, 2, 6)
+		));
+		btn.setContentAreaFilled(false);
+		btn.setFocusPainted(false);
+		btn.setCursor(new Cursor(Cursor.HAND_CURSOR));
+		btn.addMouseListener(new MouseAdapter() {
+			public void mouseEntered(MouseEvent e) { btn.setOpaque(true); btn.setBackground(new Color(230, 250, 240)); }
+			public void mouseExited(MouseEvent e) { btn.setOpaque(false); }
+		});
+		if (listener != null) {
+			btn.addActionListener(listener);
+		}
+		return btn;
+	}
+
 	private JButton createSmallButton(String text) {
-		return createSmallButton(text, PRIMARY_COLOR, PRIMARY_DARK);
+		return createSmallLinkButton(text, null);
 	}
 
 	private JButton createSmallButton(String text, Color backgroundColor, Color hoverColor) {
-		JButton button = new JButton(text);
-		button.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+		// 瀵逛簬闇�瑕佷笉鍚岄鑹茬殑鎸夐挳锛屼娇鐢ㄥ疄蹇冮鏍�
 		Color baseColor = backgroundColor == null ? PRIMARY_COLOR : backgroundColor;
-		Color hover = hoverColor == null ? baseColor : hoverColor;
-		button.setBackground(baseColor);
-		button.setForeground(WHITE);
-		button.setBorder(BorderFactory.createEmptyBorder(2, 10, 2, 10));
-		button.setMargin(new Insets(0, 0, 0, 0));
-		button.setFocusPainted(false);
-		button.setCursor(new Cursor(Cursor.HAND_CURSOR));
-
-		button.addMouseListener(new MouseAdapter() {
-			public void mouseEntered(MouseEvent e) {
-				button.setBackground(hover);
-			}
-			public void mouseExited(MouseEvent e) {
-				button.setBackground(baseColor);
-			}
-		});
-
-		return button;
+		return createStyledButton(text, baseColor, true);
 	}
 
 	private JButton createActionButton(String text, Color color) {
-		JButton button = new JButton(text);
-		button.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
-		button.setBackground(color);
-		button.setForeground(WHITE);
-		button.setBorder(BorderFactory.createEmptyBorder(8, 16, 8, 16));
-		button.setFocusPainted(false);
-		button.setCursor(new Cursor(Cursor.HAND_CURSOR));
+		return createStyledButton(text, color, true); // 瀹炲績椋庢牸
+	}
 
-		// 鎮仠鏁堟灉
-		button.addMouseListener(new MouseAdapter() {
-			public void mouseEntered(MouseEvent e) {
-				if (color == RED_COLOR) {
-					button.setBackground(RED_DARK);
+	/**
+	 * 鍒涘缓鐜颁唬椋庢牸鎸夐挳 (瀹炲績/杞粨)
+	 */
+	private JButton createStyledButton(String text, Color baseColor, boolean filled) {
+		JButton btn = new JButton(text) {
+			@Override
+			protected void paintComponent(Graphics g) {
+				Graphics2D g2 = (Graphics2D) g.create();
+				g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+				
+				boolean isPressed = getModel().isPressed();
+				boolean isRollover = getModel().isRollover();
+				
+				if (filled) {
+					if (isPressed) g2.setColor(baseColor.darker());
+					else if (isRollover) g2.setColor(baseColor.brighter());
+					else g2.setColor(baseColor);
+					g2.fillRoundRect(0, 0, getWidth(), getHeight(), 8, 8);
+					g2.setColor(Color.WHITE);
 				} else {
-					button.setBackground(PRIMARY_DARK);
+					g2.setColor(CARD_BACKGROUND); // 鑳屾櫙
+					g2.fillRoundRect(0, 0, getWidth(), getHeight(), 8, 8);
+					
+					if (isPressed) g2.setColor(baseColor.darker());
+					else if (isRollover) g2.setColor(baseColor);
+					else g2.setColor(new Color(200, 200, 200)); // 榛樿杈规鐏�
+					
+					g2.setStroke(new BasicStroke(1.2f));
+					g2.drawRoundRect(0, 0, getWidth()-1, getHeight()-1, 8, 8);
+					g2.setColor(isRollover ? baseColor : TEXT_COLOR);
 				}
+				
+				FontMetrics fm = g2.getFontMetrics();
+				int x = (getWidth() - fm.stringWidth(getText())) / 2;
+				int y = (getHeight() - fm.getHeight()) / 2 + fm.getAscent();
+				g2.drawString(getText(), x, y);
+				
+				g2.dispose();
 			}
-			public void mouseExited(MouseEvent e) {
-				if (color == RED_COLOR) {
-					button.setBackground(RED_COLOR);
-				} else {
-					button.setBackground(PRIMARY_COLOR);
-				}
-			}
-		});
-
-		return button;
+		};
+		btn.setFocusPainted(false);
+		btn.setContentAreaFilled(false);
+		btn.setBorderPainted(false);
+		btn.setCursor(new Cursor(Cursor.HAND_CURSOR));
+		btn.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 12));
+		return btn;
 	}
 
 	private JButton createDeleteButton() {
-		JButton button = new JButton("鍒犻櫎");
-		button.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
-		button.setBackground(RED_COLOR);
-		button.setForeground(WHITE);
-		button.setBorder(BorderFactory.createEmptyBorder(6, 12, 6, 12));
-		button.setFocusPainted(false);
-		button.setCursor(new Cursor(Cursor.HAND_CURSOR));
-
+		JButton button = createStyledButton("鍒犻櫎", RED_COLOR, false); // 杞粨椋庢牸
 		ImageIcon deleteIcon = loadIcon("image/delete.png", 16, 16);
 		if (deleteIcon != null) {
 			button.setIcon(deleteIcon);
 			button.setIconTextGap(6);
 		}
-
-		// 鎮仠鏁堟灉
-		button.addMouseListener(new MouseAdapter() {
-			public void mouseEntered(MouseEvent e) {
-				button.setBackground(RED_DARK);
-			}
-			public void mouseExited(MouseEvent e) {
-				button.setBackground(RED_COLOR);
-			}
-		});
-
 		return button;
 	}
 
 	private JButton createPrimaryFooterButton(String text) {
-		JButton button = new JButton(text);
-		button.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
-		button.setBackground(PRIMARY_COLOR);
-		button.setForeground(WHITE);
-		button.setBorder(BorderFactory.createEmptyBorder(6, 12, 6, 12));
-		button.setFocusPainted(false);
-		button.setCursor(new Cursor(Cursor.HAND_CURSOR));
-
-		button.addMouseListener(new MouseAdapter() {
-			public void mouseEntered(MouseEvent e) {
-				button.setBackground(PRIMARY_DARK);
-			}
-
-			public void mouseExited(MouseEvent e) {
-				button.setBackground(PRIMARY_COLOR);
-			}
-		});
-
-		return button;
+		return createStyledButton(text, PRIMARY_COLOR, true); // 瀹炲績椋庢牸
 	}
 
 	private JButton createWorkToggleButton(Dikuai dikuai) {
@@ -1446,10 +1477,19 @@
 	}
 
 	public static void setCurrentWorkLand(String landNumber, String landName) {
-		currentWorkLandNumber = landNumber;
+		String sanitizedLandNumber = sanitizeLandNumber(landNumber);
+		boolean changed = !Objects.equals(currentWorkLandNumber, sanitizedLandNumber);
+		if (!changed) {
+			String persisted = readPersistedWorkLandNumber();
+			if (!Objects.equals(persisted, sanitizedLandNumber)) {
+				changed = true;
+			}
+		}
+		currentWorkLandNumber = sanitizedLandNumber;
+
 		Dikuai dikuai = null;
-		if (landNumber != null) {
-			dikuai = Dikuai.getDikuai(landNumber);
+		if (sanitizedLandNumber != null) {
+			dikuai = Dikuai.getDikuai(sanitizedLandNumber);
 			if (dikuai != null && (landName == null || "-1".equals(landName) || landName.trim().isEmpty())) {
 				landName = dikuai.getLandName();
 			}
@@ -1460,7 +1500,7 @@
 
 		Shouye shouye = Shouye.getInstance();
 		if (shouye != null) {
-			if (landNumber == null) {
+			if (sanitizedLandNumber == null) {
 				shouye.updateCurrentAreaName(null);
 			} else {
 				shouye.updateCurrentAreaName(landName);
@@ -1470,24 +1510,87 @@
 				renderer.applyLandMetadata(dikuai);
 				String boundary = (dikuai != null) ? dikuai.getBoundaryCoordinates() : null;
 				String plannedPath = (dikuai != null) ? dikuai.getPlannedPath() : null;
-				List<Obstacledge.Obstacle> configuredObstacles = (landNumber != null) ? loadObstaclesFromConfig(landNumber) : Collections.emptyList();
+				List<Obstacledge.Obstacle> configuredObstacles = (sanitizedLandNumber != null) ? loadObstaclesFromConfig(sanitizedLandNumber) : Collections.emptyList();
 				if (configuredObstacles == null) {
 					configuredObstacles = Collections.emptyList();
 				}
-				renderer.setCurrentBoundary(boundary, landNumber, landNumber == null ? null : landName);
+				renderer.setCurrentBoundary(boundary, sanitizedLandNumber, sanitizedLandNumber == null ? null : landName);
 				renderer.setCurrentPlannedPath(plannedPath);
-				renderer.setCurrentObstacles(configuredObstacles, landNumber);
-				boolean showBoundaryPoints = landNumber != null && boundaryPointVisibility.getOrDefault(landNumber, false);
+				renderer.setCurrentObstacles(configuredObstacles, sanitizedLandNumber);
+				boolean showBoundaryPoints = sanitizedLandNumber != null && boundaryPointVisibility.getOrDefault(sanitizedLandNumber, false);
 				renderer.setBoundaryPointsVisible(showBoundaryPoints);
+				renderer.setBoundaryPointSizeScale(showBoundaryPoints ? 0.5d : 1.0d);
+				// 閫�鍑洪瑙堝悗锛屼笉鏄剧ず闅滅鐗╃偣锛堥殰纰嶇墿鐐瑰彧鍦ㄩ瑙堟椂鏄剧ず锛�
+				renderer.setObstaclePointsVisible(false);
 			}
 			shouye.refreshMowingIndicators();
 		}
+
+		if (changed) {
+			persistCurrentWorkLand(sanitizedLandNumber);
+		}
 	}
 
 	public static String getCurrentWorkLandNumber() {
 		return currentWorkLandNumber;
 	}
 
+	public static String getPersistedWorkLandNumber() {
+		return readPersistedWorkLandNumber();
+	}
+
+	private static String sanitizeLandNumber(String landNumber) {
+		if (landNumber == null) {
+			return null;
+		}
+		String trimmed = landNumber.trim();
+		if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+			return null;
+		}
+		return trimmed;
+	}
+
+	private static void persistCurrentWorkLand(String landNumber) {
+		synchronized (Dikuaiguanli.class) {
+			Properties props = new Properties();
+			try (FileInputStream in = new FileInputStream(PROPERTIES_FILE)) {
+				props.load(in);
+			} catch (IOException ignored) {
+				// Use empty defaults when the configuration file is missing.
+			}
+
+			if (landNumber == null) {
+				props.setProperty(WORK_LAND_KEY, "-1");
+			} else {
+				props.setProperty(WORK_LAND_KEY, landNumber);
+			}
+
+			try (FileOutputStream out = new FileOutputStream(PROPERTIES_FILE)) {
+				props.store(out, "Current work land selection updated");
+			} catch (IOException ex) {
+				System.err.println("鏃犳硶淇濆瓨褰撳墠浣滀笟鍦板潡: " + ex.getMessage());
+			}
+		}
+	}
+
+	private static String readPersistedWorkLandNumber() {
+		Properties props = new Properties();
+		try (FileInputStream in = new FileInputStream(PROPERTIES_FILE)) {
+			props.load(in);
+			String value = props.getProperty(WORK_LAND_KEY);
+			if (value == null) {
+				return null;
+			}
+			String trimmed = value.trim();
+			if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+				return null;
+			}
+			return trimmed;
+		} catch (IOException ex) {
+			return null;
+		}
+	}
+
 	private ImageIcon loadIcon(String path, int width, int height) {
 		try {
 			ImageIcon rawIcon = new ImageIcon(path);
@@ -1526,6 +1629,31 @@
 		}
 	}
 
+	/**
+	 * 鏄剧ず闅滅鐗╃鐞嗛〉闈�
+	 */
+	private void showObstacleManagementPage(Dikuai dikuai) {
+		if (dikuai == null) {
+			return;
+		}
+		Window owner = SwingUtilities.getWindowAncestor(this);
+		
+		// 鑾峰彇鍦板潡绠$悊瀵硅瘽妗嗭紝鍑嗗鍦ㄦ墦寮�闅滅鐗╃鐞嗛〉闈㈡椂鍏抽棴
+		Window managementWindow = null;
+		if (owner instanceof JDialog) {
+			managementWindow = owner;
+		}
+		
+		ObstacleManagementPage managementPage = new ObstacleManagementPage(owner, dikuai);
+		
+		// 鍏抽棴鍦板潡绠$悊椤甸潰
+		if (managementWindow != null) {
+			managementWindow.dispose();
+		}
+		
+		managementPage.setVisible(true);
+	}
+
 	private void addNewObstacle(Dikuai dikuai) {
 		if (dikuai == null) {
 			JOptionPane.showMessageDialog(this, "鏈壘鍒板綋鍓嶅湴鍧楋紝鏃犳硶鏂板闅滅鐗�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);

--
Gitblit v1.10.0