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

---
 src/dikuai/ObstacleManagementPage.java   |  759 +++++++++++++++++++++++++++++++++
 dikuai.properties                        |    6 
 src/dikuai/addzhangaiwu.java             |    3 
 src/lujing/MowingPathGenerationPage.java |  121 +++++
 image/gecaojishijiao1.png                |    0 
 image/gecaojishijiao2.png                |    0 
 src/zhuye/MapRenderer.java               |   24 +
 set.properties                           |    4 
 src/zhuye/Shouye.java                    |   22 
 Obstacledge.properties                   |    7 
 src/zhuye/buttonset.java                 |  128 +++-
 src/dikuai/Dikuaiguanli.java             |  256 ++++++----
 12 files changed, 1,168 insertions(+), 162 deletions(-)

diff --git a/Obstacledge.properties b/Obstacledge.properties
index 2bc1283..91afaf5 100644
--- a/Obstacledge.properties
+++ b/Obstacledge.properties
@@ -1,5 +1,5 @@
 # 鍓茶崏鏈哄湴鍧楅殰纰嶇墿閰嶇疆鏂囦欢
-# 鐢熸垚鏃堕棿锛�2025-12-17T12:03:32.881913900
+# 鐢熸垚鏃堕棿锛�2025-12-17T13:28:00.386604500
 # 鍧愭爣绯伙細WGS84锛堝害鍒嗘牸寮忥級
 
 # ============ 鍦板潡鍩哄噯绔欓厤缃� ============
@@ -12,3 +12,8 @@
 # 鏍煎紡锛歱lot.[鍦板潡缂栧彿].obstacle.[闅滅鐗╁悕绉癩.originalCoords=[鍧愭爣涓瞉
 # 鏍煎紡锛歱lot.[鍦板潡缂栧彿].obstacle.[闅滅鐗╁悕绉癩.xyCoords=[鍧愭爣涓瞉
 
+# --- 鍦板潡LAND1鐨勯殰纰嶇墿 ---
+plot.LAND1.obstacle.闅滅鐗�1.shape=1
+plot.LAND1.obstacle.闅滅鐗�1.originalCoords=0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E
+plot.LAND1.obstacle.闅滅鐗�1.xyCoords=81.22,-22.17;81.21,-22.17;81.19,-22.18;81.20,-22.19;81.18,-22.21;81.07,-22.54;80.94,-23.05;80.44,-23.54;79.84,-23.83;79.27,-24.19;78.63,-24.52;78.11,-24.79;77.69,-25.22;77.43,-25.75;77.15,-26.45;77.08,-27.23;77.10,-27.92;77.08,-28.67;77.08,-29.32;77.15,-29.97;77.27,-30.57;77.53,-31.19;77.92,-31.45;78.22,-31.73;78.92,-31.87;79.65,-31.77;80.35,-31.60;80.90,-31.17;81.27,-30.63;81.70,-30.11;81.90,-29.48;81.95,-28.92;81.90,-28.38;81.73,-27.83;81.73,-27.30;81.52,-26.81;81.42,-26.24;81.26,-25.76;81.27,-25.25;81.04,-25.07;81.11,-24.98;81.11,-25.00;81.12,-25.00
+
diff --git a/dikuai.properties b/dikuai.properties
index 026cea7..69f66e5 100644
--- a/dikuai.properties
+++ b/dikuai.properties
@@ -1,5 +1,5 @@
 #Dikuai Properties
-#Wed Dec 17 12:03:32 CST 2025
+#Wed Dec 17 13:50:43 CST 2025
 LAND1.angleThreshold=-1
 LAND1.baseStationCoordinates=3949.90238860,N,11616.75692000,E
 LAND1.boundaryCoordinates=77.19,-32.68;80.71,-54.97;80.99,-55.90;83.54,-56.46;85.04,-55.55;85.94,-53.74;83.24,-35.82;84.55,-34.54;94.02,-31.92;94.10,-31.11;90.88,-20.39;90.35,-19.53;88.33,-19.00;84.12,-19.47;78.92,-22.36;76.63,-25.55;76.93,-29.84;77.06,-31.26;77.19,-32.68
@@ -13,7 +13,7 @@
 LAND1.mowingPattern=骞宠绾�
 LAND1.mowingTrack=
 LAND1.mowingWidth=40
-LAND1.plannedPath=77.17,-29.65;81.28,-55.71;81.70,-55.80;76.93,-25.57;77.26,-25.10;82.12,-55.89;82.54,-55.98;77.59,-24.64;77.92,-24.18;82.96,-56.08;83.38,-56.17;78.25,-23.72;78.59,-23.25;83.76,-56.03;84.13,-55.81;78.92,-22.79;79.27,-22.45;84.50,-55.58;84.87,-55.33;79.64,-22.24;80.01,-22.04;85.18,-54.72;85.48,-54.10;80.39,-21.83;80.76,-21.62;83.00,-35.83;83.34,-35.38;81.13,-21.42;81.50,-21.21;83.69,-35.03;84.04,-34.69;81.88,-21.00;82.25,-20.80;84.39,-34.35;84.77,-34.22;82.62,-20.59;82.99,-20.38;85.16,-34.11;85.55,-34.00;83.37,-20.18;83.74,-19.97;85.94,-33.90;86.32,-33.79;84.11,-19.76;84.50,-19.68;86.71,-33.68;87.10,-33.57;84.90,-19.63;85.30,-19.59;87.49,-33.47;87.88,-33.36;85.70,-19.55;86.09,-19.50;88.26,-33.25;88.65,-33.15;86.49,-19.46;86.89,-19.41;89.04,-33.04;89.43,-32.93;87.29,-19.37;87.69,-19.32;89.82,-32.82;90.20,-32.72;88.08,-19.28;88.49,-19.30;90.59,-32.61;90.98,-32.50;88.91,-19.41;89.34,-19.52;91.37,-32.39;91.76,-32.29;89.76,-19.63;90.18,-19.74;92.14,-32.18;92.53,-32.07;90.76,-20.88;91.62,-23.72;92.92,-31.96;93.31,-31.86;92.47,-26.56;93.33,-29.40;93.70,-31.75
+LAND1.plannedPath=77.45,-31.44;81.28,-55.71;81.70,-55.80;77.91,-31.78;78.05,-31.91;78.17,-31.98;78.35,-32.01;82.12,-55.89;82.54,-55.98;78.77,-32.09;78.95,-32.12;79.17,-32.09;82.96,-56.08;83.38,-56.17;79.57,-32.03;79.96,-31.95;83.76,-56.03;84.13,-55.81;80.35,-31.86;80.50,-31.80;80.72,-31.63;84.50,-55.58;84.87,-55.33;81.08,-31.34;81.41,-30.87;85.18,-54.72;85.48,-54.10;81.75,-30.45;81.89,-30.27;81.94,-30.19;82.05,-29.82;83.00,-35.83;83.34,-35.38;81.43,-23.31;81.47,-22.18;81.43,-22.04;81.32,-21.94;81.21,-21.92;81.13,-21.42;81.50,-21.21;83.69,-35.03;84.04,-34.69;81.88,-21.00;82.25,-20.80;84.39,-34.35;84.77,-34.22;82.62,-20.59;82.99,-20.38;85.16,-34.11;85.55,-34.00;83.37,-20.18;83.74,-19.97;85.94,-33.90;86.32,-33.79;84.11,-19.76;84.50,-19.68;86.71,-33.68;87.10,-33.57;84.90,-19.63;85.30,-19.59;87.49,-33.47;87.88,-33.36;85.70,-19.55;86.09,-19.50;88.26,-33.25;88.65,-33.15;86.49,-19.46;86.89,-19.41;89.04,-33.04;89.43,-32.93;87.29,-19.37;87.69,-19.32;89.82,-32.82;90.20,-32.72;88.08,-19.28;88.49,-19.30;90.59,-32.61;90.98,-32.50;88.91,-19.41;89.34,-19.52;91.37,-32.39;91.76,-32.29;89.76,-19.63;90.18,-19.74;92.14,-32.18;92.53,-32.07;90.76,-20.88;91.62,-23.72;92.92,-31.96;93.31,-31.86;92.47,-26.56;93.33,-29.40;93.70,-31.75;81.40,-22.00;81.27,-21.93;81.21,-21.92;81.08,-21.96;80.98,-22.05;79.85,-23.55;79.64,-22.24;80.01,-22.04;80.22,-23.37;80.58,-23.05;80.39,-21.83;80.76,-21.62;80.87,-22.34;77.96,-24.59;77.63,-24.92;77.59,-24.64;77.92,-24.18;77.99,-24.57;78.36,-24.38;78.25,-23.72;78.59,-23.25;78.73,-24.19;79.11,-23.99;78.92,-22.79;79.27,-22.45;79.48,-23.76;77.99,-24.57;77.93,-24.62;77.51,-25.05;77.47,-25.11;77.20,-25.66;77.01,-26.12;76.93,-25.57;77.26,-25.10;77.31,-25.43
 LAND1.returnPointCoordinates=-1
-LAND1.updateTime=2025-12-17 12\:03\:32
+LAND1.updateTime=2025-12-17 13\:50\:43
 LAND1.userId=-1
diff --git a/image/gecaojishijiao1.png b/image/gecaojishijiao1.png
new file mode 100644
index 0000000..17c4c56
--- /dev/null
+++ b/image/gecaojishijiao1.png
Binary files differ
diff --git a/image/gecaojishijiao2.png b/image/gecaojishijiao2.png
new file mode 100644
index 0000000..a73111b
--- /dev/null
+++ b/image/gecaojishijiao2.png
Binary files differ
diff --git a/set.properties b/set.properties
index 4e56433..835a658 100644
--- a/set.properties
+++ b/set.properties
@@ -1,12 +1,12 @@
 #Mower Configuration Properties - Updated
-#Wed Dec 17 12:04:00 CST 2025
+#Wed Dec 17 14:56:25 CST 2025
 appVersion=-1
 currentWorkLandNumber=LAND1
 cuttingWidth=200
 firmwareVersion=-1
 handheldMarkerId=
 idleTrailDurationSeconds=60
-mapScale=15.31
+mapScale=12.92
 mowerId=1234
 serialAutoConnect=true
 serialBaudRate=115200
diff --git a/src/dikuai/Dikuaiguanli.java b/src/dikuai/Dikuaiguanli.java
index 5f0218e..5adb9c5 100644
--- a/src/dikuai/Dikuaiguanli.java
+++ b/src/dikuai/Dikuaiguanli.java
@@ -226,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")) {
@@ -240,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, "鏈缃�"));
@@ -256,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, "鏈缃�"),
@@ -286,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, "鏈缃�"),
@@ -296,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, "鏈缃�"),
@@ -306,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;
@@ -319,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, "鏈褰�"),
@@ -371,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));
@@ -379,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);
@@ -401,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));
@@ -838,6 +848,12 @@
 		}
 
 		Window owner = SwingUtilities.getWindowAncestor(this);
+		
+		// 鑾峰彇鍦板潡绠$悊瀵硅瘽妗嗭紝鍑嗗鍦ㄦ墦寮�璺緞瑙勫垝椤甸潰鏃跺叧闂�
+		Window managementWindow = null;
+		if (owner instanceof JDialog) {
+			managementWindow = owner;
+		}
 
 		// 鑾峰彇鍦板潡鍩烘湰鏁版嵁
 		String baseStationValue = prepareCoordinateForEditor(dikuai.getBaseStationCoordinates());
@@ -897,6 +913,11 @@
 			callback
 		);
 
+		// 鍏抽棴鍦板潡绠$悊椤甸潰
+		if (managementWindow != null) {
+			managementWindow.dispose();
+		}
+
 		dialog.setVisible(true);
 	}
 
@@ -1292,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) {
@@ -1507,6 +1520,8 @@
 				boolean showBoundaryPoints = sanitizedLandNumber != null && boundaryPointVisibility.getOrDefault(sanitizedLandNumber, false);
 				renderer.setBoundaryPointsVisible(showBoundaryPoints);
 				renderer.setBoundaryPointSizeScale(showBoundaryPoints ? 0.5d : 1.0d);
+				// 閫�鍑洪瑙堝悗锛屼笉鏄剧ず闅滅鐗╃偣锛堥殰纰嶇墿鐐瑰彧鍦ㄩ瑙堟椂鏄剧ず锛�
+				renderer.setObstaclePointsVisible(false);
 			}
 			shouye.refreshMowingIndicators();
 		}
@@ -1614,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);
diff --git a/src/dikuai/ObstacleManagementPage.java b/src/dikuai/ObstacleManagementPage.java
new file mode 100644
index 0000000..43cd9e4
--- /dev/null
+++ b/src/dikuai/ObstacleManagementPage.java
@@ -0,0 +1,759 @@
+package dikuai;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.plaf.basic.BasicScrollBarUI;
+import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.geom.RoundRectangle2D;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import zhangaiwu.Obstacledge;
+import zhuye.Shouye;
+
+/**
+ * 闅滅鐗╃鐞嗛〉闈� - UI浼樺寲鐗�
+ */
+public class ObstacleManagementPage extends JDialog {
+    private static final long serialVersionUID = 1L;
+    
+    // --- 灏哄甯搁噺 (淇濇寔涓嶅彉) ---
+    private static final int SCREEN_WIDTH = 400;
+    private static final int SCREEN_HEIGHT = 800;
+    
+    // --- 閰嶈壊鏂规 (鏇寸幇浠g殑鑾叞杩�/鎵佸钩椋庢牸) ---
+    private static final Color PRIMARY_COLOR = new Color(46, 139, 87);     // 娴疯椈缁� (涓昏壊)
+    private static final Color PRIMARY_HOVER = new Color(60, 179, 113);    // 鎮仠缁�
+    private static final Color DANGER_COLOR = new Color(220, 53, 69);      // 璀﹀憡绾�
+    private static final Color DANGER_HOVER = new Color(200, 35, 51);
+    
+    private static final Color TEXT_PRIMARY = new Color(33, 37, 41);       // 涓昏鏂囧瓧
+    private static final Color TEXT_SECONDARY = new Color(108, 117, 125);  // 娆¤鏂囧瓧
+    
+    private static final Color BG_MAIN = new Color(248, 249, 250);         // 鏁翠綋鑳屾櫙 (鐏扮櫧)
+    private static final Color BG_CARD = Color.WHITE;                      // 鍗$墖鑳屾櫙
+    private static final Color BG_INPUT = new Color(241, 243, 245);        // 杈撳叆妗嗚儗鏅�
+    
+    private static final Color BORDER_LIGHT = new Color(233, 236, 239);    // 娴呰竟妗�
+    
+    private final Dikuai dikuai;
+    private JPanel cardsPanel;
+    private JScrollPane scrollPane;
+    
+    public ObstacleManagementPage(Window owner, Dikuai dikuai) {
+        super(owner, "闅滅鐗╃鐞�", Dialog.ModalityType.APPLICATION_MODAL);
+        this.dikuai = dikuai;
+        
+        initializeWindow();
+        initializeUI();
+        
+        // 纭繚甯冨眬瀹屾垚鍚庡啀鏄剧ず鏁版嵁
+        SwingUtilities.invokeLater(this::loadAndDisplayObstacles);
+    }
+    
+    private void initializeWindow() {
+        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+        setLayout(new BorderLayout());
+        getContentPane().setBackground(BG_MAIN);
+        setSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
+        setResizable(false); 
+        setLocationRelativeTo(getOwner());
+    }
+    
+    private void initializeUI() {
+        // 1. 椤堕儴瀵艰埅鏍�
+        add(createHeader(), BorderLayout.NORTH);
+        
+        // 2. 涓棿婊氬姩鍖哄煙
+        cardsPanel = new JPanel();
+        cardsPanel.setLayout(new BoxLayout(cardsPanel, BoxLayout.Y_AXIS));
+        cardsPanel.setBackground(BG_MAIN);
+        cardsPanel.setBorder(new EmptyBorder(15, 15, 15, 15)); // 澶栬竟璺�
+        
+        scrollPane = new JScrollPane(cardsPanel);
+        scrollPane.setBorder(null); // 鏃犺竟妗�
+        scrollPane.setBackground(BG_MAIN);
+        scrollPane.getVerticalScrollBar().setUnitIncrement(20);
+        
+        // 鑷畾涔夋粴鍔ㄦ潯鏍峰紡
+        customizeScrollBar(scrollPane);
+        
+        add(scrollPane, BorderLayout.CENTER);
+    }
+    
+    /**
+     * 鍒涘缓椤堕儴鏍囬鏍�
+     */
+    private JPanel createHeader() {
+        JPanel header = new JPanel(new BorderLayout());
+        header.setBackground(Color.WHITE);
+        header.setPreferredSize(new Dimension(SCREEN_WIDTH, 60));
+        header.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, BORDER_LIGHT)); // 搴曢儴缁嗙嚎
+        
+        // 宸︿晶淇℃伅
+        JPanel infoPanel = new JPanel(new GridLayout(2, 1));
+        infoPanel.setBackground(Color.WHITE);
+        infoPanel.setBorder(new EmptyBorder(8, 20, 8, 0));
+        
+        String landName = getDisplayValue(dikuai.getLandName(), "鏈煡鍦板潡");
+        String landNumber = getDisplayValue(dikuai.getLandNumber(), "--");
+        
+        JLabel titleLabel = new JLabel(landName);
+        titleLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
+        titleLabel.setForeground(TEXT_PRIMARY);
+        
+        JLabel subTitleLabel = new JLabel("缂栧彿: " + landNumber);
+        subTitleLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+        subTitleLabel.setForeground(TEXT_SECONDARY);
+        
+        infoPanel.add(titleLabel);
+        infoPanel.add(subTitleLabel);
+        
+        header.add(infoPanel, BorderLayout.CENTER);
+        
+        // 鍙充晶鍏抽棴鎸夐挳
+        JButton closeBtn = new JButton("鉁�");
+        closeBtn.setFont(new Font("Arial", Font.BOLD, 18));
+        closeBtn.setForeground(TEXT_SECONDARY);
+        closeBtn.setBorder(new EmptyBorder(0, 15, 0, 20));
+        closeBtn.setContentAreaFilled(false);
+        closeBtn.setFocusPainted(false);
+        closeBtn.setCursor(new Cursor(Cursor.HAND_CURSOR));
+        closeBtn.addMouseListener(new MouseAdapter() {
+            public void mouseEntered(MouseEvent e) { closeBtn.setForeground(TEXT_PRIMARY); }
+            public void mouseExited(MouseEvent e) { closeBtn.setForeground(TEXT_SECONDARY); }
+        });
+        closeBtn.addActionListener(e -> dispose());
+        
+        header.add(closeBtn, BorderLayout.EAST);
+        
+        return header;
+    }
+    
+    /**
+     * 鑷畾涔夋粴鍔ㄦ潯鏍峰紡锛堟墎骞冲寲銆佺粏鏉★級
+     */
+    private void customizeScrollBar(JScrollPane scrollPane) {
+        scrollPane.getVerticalScrollBar().setUI(new BasicScrollBarUI() {
+            @Override
+            protected void configureScrollBarColors() {
+                this.thumbColor = new Color(200, 200, 200);
+                this.trackColor = BG_MAIN;
+            }
+            
+            @Override
+            protected JButton createDecreaseButton(int orientation) {
+                return createZeroButton();
+            }
+            
+            @Override
+            protected JButton createIncreaseButton(int orientation) {
+                return createZeroButton();
+            }
+            
+            private JButton createZeroButton() {
+                JButton jbutton = new JButton();
+                jbutton.setPreferredSize(new Dimension(0, 0));
+                jbutton.setMinimumSize(new Dimension(0, 0));
+                jbutton.setMaximumSize(new Dimension(0, 0));
+                return jbutton;
+            }
+            
+            @Override
+            protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) {
+                Graphics2D g2 = (Graphics2D) g.create();
+                g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+                g2.setColor(thumbColor);
+                g2.fillRoundRect(thumbBounds.x + 2, thumbBounds.y, thumbBounds.width - 4, thumbBounds.height, 6, 6);
+                g2.dispose();
+            }
+        });
+        scrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(8, 0));
+    }
+    
+    /**
+     * 鍔犺浇鏁版嵁
+     */
+    private void loadAndDisplayObstacles() {
+        cardsPanel.removeAll();
+        
+        List<Obstacledge.Obstacle> obstacles = loadObstacles();
+        
+        if (obstacles.isEmpty()) {
+            showEmptyState();
+        } else {
+            for (Obstacledge.Obstacle obstacle : obstacles) {
+                cardsPanel.add(createObstacleCard(obstacle));
+                cardsPanel.add(Box.createVerticalStrut(15)); // 鍗$墖闂磋窛
+            }
+        }
+        
+        cardsPanel.revalidate();
+        cardsPanel.repaint();
+    }
+    
+    private void showEmptyState() {
+        JPanel emptyPanel = new JPanel();
+        emptyPanel.setLayout(new BoxLayout(emptyPanel, BoxLayout.Y_AXIS));
+        emptyPanel.setBackground(BG_MAIN);
+        emptyPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
+        
+        JLabel iconLabel = new JLabel("馃敳"); // 绠�鍗曠殑鍗犱綅绗︼紝濡傛灉鏈夊浘鐗囨洿濂�
+        iconLabel.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 48));
+        iconLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
+        
+        JLabel textLabel = new JLabel("鏆傛棤闅滅鐗╂暟鎹�");
+        textLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+        textLabel.setForeground(TEXT_SECONDARY);
+        textLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
+        
+        emptyPanel.add(Box.createVerticalStrut(50));
+        emptyPanel.add(iconLabel);
+        emptyPanel.add(Box.createVerticalStrut(10));
+        emptyPanel.add(textLabel);
+        
+        cardsPanel.add(emptyPanel);
+    }
+    
+    /**
+     * 鍒涘缓鍗曚釜闅滅鐗╁崱鐗�
+     */
+    private JPanel createObstacleCard(Obstacledge.Obstacle obstacle) {
+        // 鍗$墖瀹瑰櫒 - 浣跨敤鍦嗚鐭╁舰鑳屾櫙
+        JPanel card = new JPanel() {
+            @Override
+            protected void paintComponent(Graphics g) {
+                Graphics2D g2 = (Graphics2D) g.create();
+                g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+                g2.setColor(BG_CARD);
+                // 缁樺埗鍦嗚鐭╁舰鑳屾櫙
+                g2.fillRoundRect(0, 0, getWidth() - 1, getHeight() - 1, 12, 12);
+                // 缁樺埗娣℃贰鐨勮竟妗�
+                g2.setColor(BORDER_LIGHT);
+                g2.drawRoundRect(0, 0, getWidth() - 1, getHeight() - 1, 12, 12);
+                g2.dispose();
+            }
+        };
+        card.setLayout(new BoxLayout(card, BoxLayout.Y_AXIS));
+        card.setOpaque(false); // 閫忔槑浠ユ樉绀� paintComponent 鏁堟灉
+        card.setBorder(new EmptyBorder(15, 15, 15, 15)); // 鍗$墖鍐呴儴鍐呰竟璺�
+        card.setMaximumSize(new Dimension(Integer.MAX_VALUE, 380)); // 闄愬埗楂樺害锛岄槻姝㈡媺浼�
+        
+        // 1. 鍗$墖鏍囬琛� (鍚嶇О + 褰㈢姸 + 鍒犻櫎)
+        JPanel titleRow = new JPanel(new BorderLayout());
+        titleRow.setOpaque(false);
+        
+        String name = (obstacle.getObstacleName() == null || obstacle.getObstacleName().isEmpty()) 
+                      ? "鏈懡鍚�" : obstacle.getObstacleName();
+        JLabel nameLabel = new JLabel(name);
+        nameLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 15));
+        nameLabel.setForeground(TEXT_PRIMARY);
+        // 娣诲姞涓�涓皬鍥炬爣瑁呴グ
+        nameLabel.setIcon(new Icon() {
+            public void paintIcon(Component c, Graphics g, int x, int y) {
+                g.setColor(PRIMARY_COLOR);
+                g.fillOval(x, y + 4, 8, 8);
+            }
+            public int getIconWidth() { return 12; }
+            public int getIconHeight() { return 16; }
+        });
+        
+        // 娣诲姞褰㈢姸鏄剧ず锛堝湪鍚嶇О鍙宠竟锛�
+        JLabel shapeLabel = createShapeLabel(obstacle.getShape());
+        
+        // 宸︿晶闈㈡澘锛氬悕绉� + 褰㈢姸
+        JPanel leftPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 0));
+        leftPanel.setOpaque(false);
+        leftPanel.add(nameLabel);
+        leftPanel.add(shapeLabel);
+        
+        JButton deleteBtn = createIconButton("馃棏", DANGER_COLOR); // 浣跨敤Unicode鍨冨溇妗�
+        deleteBtn.addActionListener(e -> deleteObstacle(obstacle));
+        
+        titleRow.add(leftPanel, BorderLayout.CENTER);
+        titleRow.add(deleteBtn, BorderLayout.EAST);
+        
+        card.add(titleRow);
+        card.add(Box.createVerticalStrut(12));
+        
+        // 鍒嗗壊绾�
+        JSeparator sep = new JSeparator();
+        sep.setForeground(BORDER_LIGHT);
+        sep.setMaximumSize(new Dimension(Integer.MAX_VALUE, 1));
+        card.add(sep);
+        card.add(Box.createVerticalStrut(12));
+        
+        // 2. 鍘熷鍧愭爣鍖哄煙
+        String originalCoords = extractOriginalCoordinates(obstacle);
+        card.add(createDataField("鍘熷缁忕含搴︽暟鎹�", originalCoords, 2));
+        card.add(Box.createVerticalStrut(10));
+        
+        // 3. 鐢熸垚鍧愭爣鍖哄煙
+        String genCoords = extractObstacleCoordinates(obstacle);
+        JTextArea xyArea = createDataTextArea(genCoords, 3); // 寮曠敤浠ヤ究鏇存柊
+        JScrollPane scrollXY = new JScrollPane(xyArea);
+        scrollXY.setBorder(BorderFactory.createEmptyBorder()); // 澶栭儴鐢盤anel鎻愪緵杈规
+        
+        JPanel xyWrapper = createWrapperPanel("鐢熸垚鍧愭爣 (XY绫�)", scrollXY);
+        card.add(xyWrapper);
+        card.add(Box.createVerticalStrut(15));
+        
+        // 4. 鎿嶄綔鎸夐挳琛�
+        JPanel actionPanel = new JPanel();
+        actionPanel.setLayout(new BoxLayout(actionPanel, BoxLayout.X_AXIS));
+        actionPanel.setOpaque(false);
+        
+        JButton generateBtn = createStyledButton("閲嶆柊鐢熸垚鍧愭爣", PRIMARY_COLOR, true);
+        generateBtn.addActionListener(e -> generateObstacleCoordinates(obstacle, xyArea));
+        
+        JButton previewBtn = createStyledButton("棰勮", TEXT_SECONDARY, false);
+        previewBtn.setPreferredSize(new Dimension(70, 36)); // 绋嶅井绐勪竴鐐�
+        previewBtn.addActionListener(e -> previewObstacle(obstacle));
+        
+        actionPanel.add(generateBtn);
+        actionPanel.add(Box.createHorizontalStrut(10));
+        actionPanel.add(previewBtn);
+        
+        card.add(actionPanel);
+        
+        return card;
+    }
+    
+    /**
+     * 杈呭姪鏂规硶锛氬垱寤哄甫鏍囬鐨勬暟鎹睍绀哄潡
+     */
+    private JPanel createDataField(String title, String content, int rows) {
+        JTextArea area = createDataTextArea(content, rows);
+        JScrollPane scroll = new JScrollPane(area);
+        scroll.setBorder(BorderFactory.createEmptyBorder());
+        return createWrapperPanel(title, scroll);
+    }
+
+    private JPanel createWrapperPanel(String title, JComponent content) {
+        JPanel panel = new JPanel(new BorderLayout(0, 5));
+        panel.setOpaque(false);
+        
+        JLabel label = new JLabel(title);
+        label.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+        label.setForeground(TEXT_SECONDARY);
+        
+        JPanel contentBox = new JPanel(new BorderLayout());
+        contentBox.setBackground(BG_INPUT);
+        contentBox.setBorder(BorderFactory.createCompoundBorder(
+            BorderFactory.createLineBorder(new Color(230, 230, 230), 1),
+            BorderFactory.createEmptyBorder(5, 5, 5, 5)
+        ));
+        contentBox.add(content, BorderLayout.CENTER);
+        
+        panel.add(label, BorderLayout.NORTH);
+        panel.add(contentBox, BorderLayout.CENTER);
+        return panel;
+    }
+    
+    private JTextArea createDataTextArea(String text, int rows) {
+        JTextArea area = new JTextArea(text);
+        area.setRows(rows);
+        area.setEditable(false);
+        area.setLineWrap(true);
+        area.setWrapStyleWord(true);
+        area.setBackground(BG_INPUT);
+        area.setForeground(new Color(50, 50, 50));
+        // 浣跨敤绛夊瀛椾綋鏄剧ず鏁版嵁锛岀湅璧锋潵鏇翠笓涓�
+        area.setFont(new Font("Monospaced", Font.PLAIN, 12)); 
+        return area;
+    }
+
+    /**
+     * 鍒涘缓鐜颁唬椋庢牸鎸夐挳 (瀹炲績鎴栬疆寤�)
+     */
+    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(new Color(baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue(), 230)); // 寰�忔槑
+                    else g2.setColor(baseColor);
+                    g2.fill(new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), 8, 8));
+                    g2.setColor(Color.WHITE);
+                } else {
+                    g2.setColor(BG_CARD); // 鑳屾櫙鐧�
+                    g2.fill(new RoundRectangle2D.Double(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.draw(new RoundRectangle2D.Double(0, 0, getWidth()-1, getHeight()-1, 8, 8));
+                    
+                    g2.setColor(isRollover ? baseColor : TEXT_SECONDARY);
+                }
+                
+                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();
+            }
+        };
+        btn.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 12));
+        btn.setFocusPainted(false);
+        btn.setContentAreaFilled(false);
+        btn.setBorderPainted(false);
+        btn.setCursor(new Cursor(Cursor.HAND_CURSOR));
+        btn.setMaximumSize(new Dimension(Integer.MAX_VALUE, 36));
+        return btn;
+    }
+
+    /**
+     * 鍒涘缓褰㈢姸鏍囩锛堟樉绀哄渾褰㈡垨澶氳竟褰㈠浘鏍囷級
+     */
+    private JLabel createShapeLabel(Obstacledge.ObstacleShape shape) {
+        JLabel shapeLabel = new JLabel();
+        shapeLabel.setPreferredSize(new Dimension(24, 24));
+        
+        if (shape == null) {
+            return shapeLabel;
+        }
+        
+        // 鏍规嵁褰㈢姸鍒涘缓涓嶅悓鐨勫浘鏍�
+        Icon shapeIcon;
+        if (shape == Obstacledge.ObstacleShape.CIRCLE) {
+            // 鍦嗗舰鍥炬爣
+            shapeIcon = new Icon() {
+                @Override
+                public void paintIcon(Component c, Graphics g, int x, int y) {
+                    Graphics2D g2 = (Graphics2D) g.create();
+                    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+                    g2.setColor(PRIMARY_COLOR);
+                    g2.setStroke(new BasicStroke(2.0f));
+                    g2.drawOval(x + 2, y + 2, getIconWidth() - 4, getIconHeight() - 4);
+                    g2.dispose();
+                }
+                
+                @Override
+                public int getIconWidth() {
+                    return 20;
+                }
+                
+                @Override
+                public int getIconHeight() {
+                    return 20;
+                }
+            };
+            shapeLabel.setToolTipText("鍦嗗舰");
+        } else if (shape == Obstacledge.ObstacleShape.POLYGON) {
+            // 澶氳竟褰㈠浘鏍囷紙鍏竟褰級
+            shapeIcon = new Icon() {
+                @Override
+                public void paintIcon(Component c, Graphics g, int x, int y) {
+                    Graphics2D g2 = (Graphics2D) g.create();
+                    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+                    g2.setColor(PRIMARY_COLOR);
+                    g2.setStroke(new BasicStroke(2.0f));
+                    
+                    // 缁樺埗鍏竟褰�
+                    int centerX = x + getIconWidth() / 2;
+                    int centerY = y + getIconHeight() / 2;
+                    int radius = 8;
+                    int[] xPoints = new int[6];
+                    int[] yPoints = new int[6];
+                    
+                    for (int i = 0; i < 6; i++) {
+                        double angle = Math.PI / 3.0 * i - Math.PI / 6.0; // 浠庨《閮ㄥ紑濮�
+                        xPoints[i] = centerX + (int) (radius * Math.cos(angle));
+                        yPoints[i] = centerY + (int) (radius * Math.sin(angle));
+                    }
+                    
+                    g2.drawPolygon(xPoints, yPoints, 6);
+                    g2.dispose();
+                }
+                
+                @Override
+                public int getIconWidth() {
+                    return 20;
+                }
+                
+                @Override
+                public int getIconHeight() {
+                    return 20;
+                }
+            };
+            shapeLabel.setToolTipText("澶氳竟褰�");
+        } else {
+            return shapeLabel; // 鏈煡褰㈢姸锛岃繑鍥炵┖鏍囩
+        }
+        
+        shapeLabel.setIcon(shapeIcon);
+        return shapeLabel;
+    }
+    
+    /**
+     * 鍒涘缓绾浘鏍囨寜閽紙鐢ㄤ簬鍒犻櫎锛�
+     */
+    private JButton createIconButton(String iconText, Color hoverColor) {
+        JButton btn = new JButton(iconText);
+        btn.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 16));
+        btn.setForeground(TEXT_SECONDARY);
+        btn.setBorder(null);
+        btn.setContentAreaFilled(false);
+        btn.setFocusPainted(false);
+        btn.setCursor(new Cursor(Cursor.HAND_CURSOR));
+        btn.setPreferredSize(new Dimension(30, 30));
+        
+        btn.addMouseListener(new MouseAdapter() {
+            public void mouseEntered(MouseEvent e) { btn.setForeground(hoverColor); }
+            public void mouseExited(MouseEvent e) { btn.setForeground(TEXT_SECONDARY); }
+        });
+        return btn;
+    }
+
+    // =================================================================================
+    // 浠ヤ笅涓哄師鏈変笟鍔¢�昏緫浠g爜锛屼繚鎸佷笉鍙橈紝浠呰皟鏁撮儴鍒嗙┖鍊煎垽鏂互閫傞厤鏂癠I
+    // =================================================================================
+
+    private void previewObstacle(Obstacledge.Obstacle obstacle) {
+        if (obstacle == null) return;
+        
+        String landNumber = dikuai.getLandNumber();
+        String landName = dikuai.getLandName();
+        String boundary = dikuai.getBoundaryCoordinates();
+        String obstacleCoords = extractObstacleCoordinates(obstacle);
+        
+        if (obstacleCoords == null || obstacleCoords.trim().isEmpty()) {
+            JOptionPane.showMessageDialog(this, "璇ラ殰纰嶇墿娌℃湁鍧愭爣鏁版嵁", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+            return;
+        }
+        
+        List<Obstacledge.Obstacle> allObstacles = loadObstacles();
+        String allObstaclesCoords = buildAllObstaclesCoordinates(allObstacles);
+        
+        setVisible(false);
+        
+        SwingUtilities.invokeLater(() -> {
+            Shouye shouye = Shouye.getInstance();
+            if (shouye != null) {
+                shouye.startMowingPathPreview(
+                    landNumber,
+                    landName,
+                    boundary,
+                    allObstaclesCoords,
+                    null,
+                    () -> SwingUtilities.invokeLater(() -> setVisible(true))
+                );
+            } else {
+                JOptionPane.showMessageDialog(null, "鏃犳硶鎵撳紑涓婚〉闈㈣繘琛岄瑙�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+                setVisible(true);
+            }
+        });
+    }
+
+    private String buildAllObstaclesCoordinates(List<Obstacledge.Obstacle> obstacles) {
+        if (obstacles == null || obstacles.isEmpty()) return null;
+        List<String> coordStrings = new ArrayList<>();
+        for (Obstacledge.Obstacle obstacle : obstacles) {
+            String coords = extractObstacleCoordinates(obstacle);
+            if (coords != null && !coords.trim().isEmpty()) {
+                coordStrings.add(coords.trim());
+            }
+        }
+        return coordStrings.isEmpty() ? null : String.join(" ", coordStrings);
+    }
+    
+    private String extractObstacleCoordinates(Obstacledge.Obstacle obstacle) {
+        if (obstacle == null) return "";
+        String xy = obstacle.getXyCoordsString();
+        return isMeaningfulValue(xy) ? xy.trim() : "";
+    }
+    
+    private String extractOriginalCoordinates(Obstacledge.Obstacle obstacle) {
+        if (obstacle == null) return "";
+        String original = obstacle.getOriginalCoordsString();
+        return isMeaningfulValue(original) ? original.trim() : "";
+    }
+    
+    private void generateObstacleCoordinates(Obstacledge.Obstacle obstacle, JTextArea coordArea) {
+        if (obstacle == null || coordArea == null) return;
+        
+        String originalCoords = extractOriginalCoordinates(obstacle);
+        if (!isMeaningfulValue(originalCoords)) {
+            JOptionPane.showMessageDialog(this, "鏃犲師濮嬪潗鏍囨暟鎹紝鏃犳硶鐢熸垚", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+            return;
+        }
+        
+        String baseStation = dikuai.getBaseStationCoordinates();
+        if (!isMeaningfulValue(baseStation)) {
+            JOptionPane.showMessageDialog(this, "鍦板潡鏈缃熀绔欏潗鏍�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+        
+        try {
+            List<Obstacledge.DMCoordinate> originalCoordsList = obstacle.getOriginalCoordinates();
+            if (originalCoordsList == null || originalCoordsList.isEmpty()) {
+                JOptionPane.showMessageDialog(this, "鍘熷鍧愭爣鏁版嵁鏃犳晥", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+                return;
+            }
+            
+            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());
+            
+            List<Obstacledge.XYCoordinate> xyCoords = new ArrayList<>();
+            for (int i = 0; i < originalCoordsList.size(); i += 2) {
+                if (i + 1 >= originalCoordsList.size()) break;
+                double lat = originalCoordsList.get(i).toDecimalDegree();
+                double lon = originalCoordsList.get(i + 1).toDecimalDegree();
+                
+                if (Double.isFinite(lat) && Double.isFinite(lon)) {
+                    double[] localXY = convertLatLonToLocal(lat, lon, baseLat, baseLon);
+                    xyCoords.add(new Obstacledge.XYCoordinate(localXY[0], localXY[1]));
+                }
+            }
+            
+            if (xyCoords.isEmpty()) {
+                JOptionPane.showMessageDialog(this, "鍧愭爣杞崲澶辫触", "閿欒", JOptionPane.ERROR_MESSAGE);
+                return;
+            }
+            
+            obstacle.setXyCoordinates(xyCoords);
+            saveObstacleUpdate(obstacle, coordArea);
+            
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            JOptionPane.showMessageDialog(this, "閿欒: " + ex.getMessage(), "閿欒", JOptionPane.ERROR_MESSAGE);
+        }
+    }
+    
+    private void saveObstacleUpdate(Obstacledge.Obstacle obstacle, JTextArea coordArea) {
+        String landNumber = dikuai.getLandNumber();
+        try {
+            File configFile = new File("Obstacledge.properties");
+            Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager();
+            if (configFile.exists()) manager.loadFromFile(configFile.getAbsolutePath());
+            
+            Obstacledge.Plot plot = manager.getPlotById(landNumber.trim());
+            if (plot != null) {
+                plot.removeObstacleByName(obstacle.getObstacleName());
+                plot.addObstacle(obstacle);
+                manager.saveToFile(configFile.getAbsolutePath());
+                
+                Dikuai.updateField(landNumber.trim(), "updateTime", getCurrentTime());
+                Dikuai.saveToProperties();
+                
+                coordArea.setText(obstacle.getXyCoordsString());
+                JOptionPane.showMessageDialog(this, "鍧愭爣宸茬敓鎴愬苟淇濆瓨", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    private double parseDMToDecimal(String dmm, String direction) {
+        if (dmm == null || dmm.trim().isEmpty()) return Double.NaN;
+        try {
+            String trimmed = dmm.trim();
+            int dotIndex = trimmed.indexOf('.');
+            if (dotIndex < 2) return Double.NaN;
+            int degrees = Integer.parseInt(trimmed.substring(0, dotIndex - 2));
+            double minutes = Double.parseDouble(trimmed.substring(dotIndex - 2));
+            double decimal = degrees + minutes / 60.0;
+            if ("S".equalsIgnoreCase(direction) || "W".equalsIgnoreCase(direction)) decimal = -decimal;
+            return decimal;
+        } catch (NumberFormatException ex) {
+            return Double.NaN;
+        }
+    }
+    
+    private double[] convertLatLonToLocal(double lat, double lon, double baseLat, double baseLon) {
+        double deltaLat = lat - baseLat;
+        double deltaLon = lon - baseLon;
+        double meanLatRad = Math.toRadians((baseLat + lat) / 2.0);
+        double METERS_PER_DEGREE_LAT = 111320.0;
+        double eastMeters = deltaLon * METERS_PER_DEGREE_LAT * Math.cos(meanLatRad);
+        double northMeters = deltaLat * METERS_PER_DEGREE_LAT;
+        return new double[]{eastMeters, northMeters};
+    }
+    
+    private void deleteObstacle(Obstacledge.Obstacle obstacle) {
+        if (obstacle == null) return;
+        String name = obstacle.getObstacleName();
+        
+        int choice = JOptionPane.showConfirmDialog(this,
+            "纭畾瑕佸垹闄ら殰纰嶇墿 \"" + name + "\" 鍚楋紵",
+            "鍒犻櫎纭", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
+            
+        if (choice != JOptionPane.YES_OPTION) return;
+        
+        String landNumber = dikuai.getLandNumber();
+        try {
+            File configFile = new File("Obstacledge.properties");
+            Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager();
+            if (configFile.exists()) manager.loadFromFile(configFile.getAbsolutePath());
+            
+            Obstacledge.Plot plot = manager.getPlotById(landNumber.trim());
+            if (plot != null && plot.removeObstacleByName(name)) {
+                manager.saveToFile(configFile.getAbsolutePath());
+                Dikuai.updateField(landNumber.trim(), "updateTime", getCurrentTime());
+                Dikuai.saveToProperties();
+                Dikuaiguanli.notifyExternalCreation(landNumber.trim());
+                
+                JOptionPane.showMessageDialog(this, "鍒犻櫎鎴愬姛");
+                loadAndDisplayObstacles();
+            } else {
+                JOptionPane.showMessageDialog(this, "鍒犻櫎澶辫触锛氭湭鎵惧埌璁板綍", "閿欒", JOptionPane.ERROR_MESSAGE);
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            JOptionPane.showMessageDialog(this, "寮傚父: " + ex.getMessage(), "閿欒", JOptionPane.ERROR_MESSAGE);
+        }
+    }
+    
+    private String getCurrentTime() {
+        return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date());
+    }
+    
+    private List<Obstacledge.Obstacle> loadObstacles() {
+        if (dikuai == null) return Collections.emptyList();
+        String landNumber = dikuai.getLandNumber();
+        if (landNumber == null || landNumber.trim().isEmpty()) return Collections.emptyList();
+        
+        try {
+            File configFile = new File("Obstacledge.properties");
+            if (!configFile.exists()) return Collections.emptyList();
+            
+            Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager();
+            if (!manager.loadFromFile(configFile.getAbsolutePath())) return Collections.emptyList();
+            
+            Obstacledge.Plot plot = manager.getPlotById(landNumber.trim());
+            return plot != null ? new ArrayList<>(plot.getObstacles()) : Collections.emptyList();
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            return Collections.emptyList();
+        }
+    }
+    
+    private String getDisplayValue(String value, String defaultValue) {
+        return (value != null && !value.equals("-1") && !value.trim().isEmpty()) ? value : defaultValue;
+    }
+    
+    private boolean isMeaningfulValue(String value) {
+        return value != null && !value.trim().isEmpty() && !"-1".equals(value.trim());
+    }
+}
diff --git a/src/dikuai/addzhangaiwu.java b/src/dikuai/addzhangaiwu.java
index cd6d982..5f31288 100644
--- a/src/dikuai/addzhangaiwu.java
+++ b/src/dikuai/addzhangaiwu.java
@@ -564,6 +564,9 @@
 
         prevButton = createSecondaryButton("涓婁竴姝�");
         nextButton = createPrimaryButton("涓嬩竴姝�", 16);
+        // 璁剧疆涓嬩竴姝ユ寜閽搴︿负300鍍忕礌
+        nextButton.setPreferredSize(new Dimension(300, nextButton.getPreferredSize().height));
+        nextButton.setMaximumSize(new Dimension(300, nextButton.getPreferredSize().height));
         saveButton = createPrimaryButton("淇濆瓨", 16);
         saveButton.setVisible(false);
 
diff --git a/src/lujing/MowingPathGenerationPage.java b/src/lujing/MowingPathGenerationPage.java
index 8f96af2..5c080b0 100644
--- a/src/lujing/MowingPathGenerationPage.java
+++ b/src/lujing/MowingPathGenerationPage.java
@@ -1,6 +1,7 @@
 package lujing;
 
 import javax.swing.*;
+import javax.swing.SwingUtilities;
 import java.awt.*;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
@@ -143,14 +144,17 @@
         buttonPanel.setBackground(BACKGROUND_COLOR);
         
         JButton generateBtn = createPrimaryFooterButton("鐢熸垚鍓茶崏璺緞");
+        JButton previewBtn = createPrimaryFooterButton("棰勮");
         JButton saveBtn = createPrimaryFooterButton("淇濆瓨璺緞");
         JButton cancelBtn = createPrimaryFooterButton("鍙栨秷");
         
         generateBtn.addActionListener(e -> generatePath(modeValue));
+        previewBtn.addActionListener(e -> previewPath());
         saveBtn.addActionListener(e -> savePath());
         cancelBtn.addActionListener(e -> dispose());
         
         buttonPanel.add(generateBtn);
+        buttonPanel.add(previewBtn);
         buttonPanel.add(saveBtn);
         buttonPanel.add(cancelBtn);
         add(buttonPanel, BorderLayout.SOUTH);
@@ -187,6 +191,123 @@
     }
     
     /**
+     * 棰勮璺緞
+     */
+    private void previewPath() {
+        // 鍏堜繚瀛樺綋鍓嶈矾寰勫埌鍦板潡锛堜复鏃朵繚瀛橈紝鐢ㄤ簬棰勮锛�
+        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(this, "璇峰厛鐢熸垚鍓茶崏璺緞", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+            return;
+        }
+        
+        // 涓存椂淇濆瓨璺緞鍒板湴鍧楀璞★紙涓嶆寔涔呭寲锛�
+        if (saveCallback != null) {
+            saveCallback.savePlannedPath(dikuai, pathNormalized);
+        }
+        
+        // 淇濆瓨褰撳墠椤甸潰鐘舵�侊紝鐢ㄤ簬杩斿洖鏃舵仮澶�
+        String currentBaseStation = baseStationField.getText();
+        String currentBoundary = boundaryArea.getText();
+        String currentObstacle = obstacleArea.getText();
+        String currentWidth = widthField.getText();
+        String currentPath = pathArea.getText();
+        
+        // 鑾峰彇鍦板潡淇℃伅
+        String landNumber = dikuai.getLandNumber();
+        String landName = dikuai.getLandName();
+        
+        // 澶勭悊杈圭晫鍧愭爣锛岀‘淇濆彉閲忔槸 effectively final
+        String boundaryInput = normalizeCoordinateInput(boundaryArea.getText());
+        final String boundary;
+        if (!"-1".equals(boundaryInput)) {
+            String processed = boundaryInput.replace("\r\n", ";")
+                .replace('\r', ';')
+                .replace('\n', ';')
+                .replaceAll(";+", ";")
+                .replaceAll("\\s*;\\s*", ";")
+                .trim();
+            if (processed.isEmpty()) {
+                boundary = dikuai.getBoundaryCoordinates();
+            } else {
+                boundary = processed;
+            }
+        } else {
+            boundary = dikuai.getBoundaryCoordinates();
+        }
+        
+        // 澶勭悊闅滅鐗╁潗鏍囷紝纭繚鍙橀噺鏄� effectively final
+        String obstaclesInput = normalizeCoordinateInput(obstacleArea.getText());
+        final String obstacles;
+        if (!"-1".equals(obstaclesInput)) {
+            String processed = obstaclesInput.replace("\r\n", " ")
+                .replace('\r', ' ')
+                .replace('\n', ' ')
+                .replaceAll("\\s{2,}", " ")
+                .trim();
+            if (processed.isEmpty()) {
+                obstacles = null;
+            } else {
+                obstacles = processed;
+            }
+        } else {
+            obstacles = null;
+        }
+        
+        // 淇濆瓨鏈�缁堝�煎埌 final 鍙橀噺锛屼互渚垮湪 lambda 涓娇鐢�
+        final String finalPathNormalized = pathNormalized;
+        final String finalLandNumber = landNumber;
+        final String finalLandName = landName;
+        
+        // 鍏抽棴璺緞瑙勫垝椤甸潰
+        setVisible(false);
+        
+        // 鎵撳紑涓婚〉闈㈠苟鏄剧ず璺緞棰勮
+        SwingUtilities.invokeLater(() -> {
+            zhuye.Shouye shouye = zhuye.Shouye.getInstance();
+            if (shouye != null) {
+                // 鏄剧ず璺緞棰勮锛屽苟璁剧疆杩斿洖鍥炶皟
+                shouye.startMowingPathPreview(
+                    finalLandNumber,
+                    finalLandName,
+                    boundary,
+                    obstacles,
+                    finalPathNormalized,
+                    () -> {
+                        // 杩斿洖鍥炶皟锛氶噸鏂版墦寮�璺緞瑙勫垝椤甸潰
+                        SwingUtilities.invokeLater(() -> {
+                            setVisible(true);
+                            // 鎭㈠涔嬪墠鐨勭姸鎬�
+                            baseStationField.setText(currentBaseStation);
+                            boundaryArea.setText(currentBoundary);
+                            obstacleArea.setText(currentObstacle);
+                            widthField.setText(currentWidth);
+                            pathArea.setText(currentPath);
+                        });
+                    }
+                );
+            } else {
+                // 濡傛灉涓婚〉闈笉瀛樺湪锛屾彁绀虹敤鎴峰苟閲嶆柊鏄剧ず璺緞瑙勫垝椤甸潰
+                JOptionPane.showMessageDialog(null, "鏃犳硶鎵撳紑涓婚〉闈㈣繘琛岄瑙�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+                setVisible(true);
+            }
+        });
+    }
+    
+    /**
      * 淇濆瓨璺緞
      */
     private void savePath() {
diff --git a/src/zhuye/MapRenderer.java b/src/zhuye/MapRenderer.java
index e36d3c3..3c79352 100644
--- a/src/zhuye/MapRenderer.java
+++ b/src/zhuye/MapRenderer.java
@@ -52,6 +52,7 @@
     private static final Color GRASS_FILL_COLOR = new Color(144, 238, 144, 120);
     private static final Color GRASS_BORDER_COLOR = new Color(60, 179, 113);
     private static final Color BOUNDARY_POINT_COLOR = new Color(128, 0, 128);
+    private static final Color OBSTACLE_POINT_COLOR = new Color(255, 140, 0); // 姗欒壊锛岀敤浜庡尯鍒嗛殰纰嶇墿鐐�
     private static final Color CIRCLE_SAMPLE_COLOR = new Color(220, 20, 60, 230);
     private static final double CIRCLE_SAMPLE_SIZE = 0.54d;
     private static final double BOUNDARY_POINT_MERGE_THRESHOLD = 0.05;
@@ -71,6 +72,7 @@
     private String currentObstacleLandNumber;
     private String boundaryName;
     private boolean boundaryPointsVisible;
+    private boolean obstaclePointsVisible;
     private double boundaryPointSizeScale = 1.0d;
     private boolean previewSizingEnabled;
     private String currentBoundaryLandNumber;
@@ -385,6 +387,22 @@
             );
         }
 
+        // 缁樺埗闅滅鐗╁潗鏍囩偣
+        if (obstaclePointsVisible && hasObstacles) {
+            List<Point2D.Double> obstaclePoints = Obstacledraw.getObstaclePoints(currentObstacles);
+            if (obstaclePoints != null && !obstaclePoints.isEmpty()) {
+                double markerScale = boundaryPointSizeScale * (previewSizingEnabled ? PREVIEW_BOUNDARY_MARKER_SCALE : 1.0d);
+                pointandnumber.drawBoundaryPoints(
+                    g2d,
+                    obstaclePoints,
+                    scale,
+                    BOUNDARY_POINT_MERGE_THRESHOLD,
+                    OBSTACLE_POINT_COLOR,
+                    markerScale
+                );
+            }
+        }
+
         if (shouldRenderIdleTrail()) {
             tuowei.draw(g2d, idleMowerTrail, scale);
         }
@@ -1746,6 +1764,7 @@
         obstacleBounds = null;
         selectedObstacleName = null;
         currentObstacleLandNumber = null;
+        obstaclePointsVisible = false;
     }
 
     private List<Obstacledge.Obstacle> parseObstacles(String obstaclesData, String landNumber) {
@@ -2033,6 +2052,11 @@
         visualizationPanel.repaint();
     }
 
+    public void setObstaclePointsVisible(boolean visible) {
+        this.obstaclePointsVisible = visible;
+        visualizationPanel.repaint();
+    }
+
     public void setBoundaryPointSizeScale(double sizeScale) {
         double normalized = (Double.isFinite(sizeScale) && sizeScale > 0.0d) ? sizeScale : 1.0d;
         if (Math.abs(boundaryPointSizeScale - normalized) < 1e-6) {
diff --git a/src/zhuye/Shouye.java b/src/zhuye/Shouye.java
index 1aa16f7..e76f5c7 100644
--- a/src/zhuye/Shouye.java
+++ b/src/zhuye/Shouye.java
@@ -320,7 +320,7 @@
 		}
 		// 鍏抽棴妯″紡鏃朵笉闇�瑕佸仛浠讳綍鎿嶄綔锛岀敤鎴峰凡缁忓彲浠ヨ嚜鐢辩Щ鍔ㄥ湴鍥�
 		
-		// 鏇存柊宸ュ叿鎻愮ず
+		// 鏇存柊鍥炬爣鏄剧ず锛堥噸缁樹互鍒囨崲鍥炬爣锛�
 		if (visualizationPanel != null) {
 			visualizationPanel.repaint();
 		}
@@ -564,14 +564,16 @@
 
 		// 鍙鍖栧尯鍩� - 浣跨敤MapRenderer杩涜缁樺埗
 		visualizationPanel = new JPanel() {
-			private ImageIcon gecaojiIcon = null;
+			private ImageIcon gecaojiIcon1 = null;  // 榛樿鍥炬爣
+			private ImageIcon gecaojiIcon2 = null;  // 浠ュ壊鑽夋満涓轰腑蹇冩ā寮忓浘鏍�
 			private static final int GECAOJI_ICON_X = 37;
 			private static final int GECAOJI_ICON_Y = 10;
 			private static final int GECAOJI_ICON_SIZE = 20;
 			
 			{
 				// 鍔犺浇鍓茶崏鏈哄浘鏍囷紝澶у皬20x20鍍忕礌
-				gecaojiIcon = loadScaledIcon("image/gecaoji.png", GECAOJI_ICON_SIZE, GECAOJI_ICON_SIZE);
+				gecaojiIcon1 = loadScaledIcon("image/gecaojishijiao1.png", GECAOJI_ICON_SIZE, GECAOJI_ICON_SIZE);
+				gecaojiIcon2 = loadScaledIcon("image/gecaojishijiao2.png", GECAOJI_ICON_SIZE, GECAOJI_ICON_SIZE);
 			}
 			
 			/**
@@ -607,11 +609,15 @@
 				if (isMowerOutsideBoundary && warningIconVisible) {
 					// 缁樺埗绾㈣壊涓夎褰㈣鍛婂浘鏍囷紙甯﹀徆鍙凤級
 					drawWarningIcon(g, GECAOJI_ICON_X, GECAOJI_ICON_Y, GECAOJI_ICON_SIZE);
-				} else if (gecaojiIcon != null) {
-					// 缁樺埗姝e父鐨勫壊鑽夋満鍥炬爣
-					// 姘村钩鏂瑰悜涓庨�熷害鎸囩ず鍣ㄥ榻愶紙x=37锛�
-					// 鍨傜洿鏂瑰悜涓庡崼鏄熺姸鎬佸浘鏍囧榻愶紙y=10锛岄�熷害鎸囩ず鍣ㄩ潰鏉块《閮ㄨ竟璺�10鍍忕礌锛屼娇鍥炬爣涓績瀵归綈锛�
-					g.drawImage(gecaojiIcon.getImage(), GECAOJI_ICON_X, GECAOJI_ICON_Y, null);
+				} else {
+					// 鏍规嵁妯″紡閫夋嫨涓嶅悓鐨勫浘鏍�
+					ImageIcon iconToDraw = centerOnMowerMode ? gecaojiIcon2 : gecaojiIcon1;
+					if (iconToDraw != null) {
+						// 缁樺埗鍓茶崏鏈哄浘鏍�
+						// 姘村钩鏂瑰悜涓庨�熷害鎸囩ず鍣ㄥ榻愶紙x=37锛�
+						// 鍨傜洿鏂瑰悜涓庡崼鏄熺姸鎬佸浘鏍囧榻愶紙y=10锛岄�熷害鎸囩ず鍣ㄩ潰鏉块《閮ㄨ竟璺�10鍍忕礌锛屼娇鍥炬爣涓績瀵归綈锛�
+						g.drawImage(iconToDraw.getImage(), GECAOJI_ICON_X, GECAOJI_ICON_Y, null);
+					}
 				}
 			}
 			
diff --git a/src/zhuye/buttonset.java b/src/zhuye/buttonset.java
index 50bad9c..2d6df18 100644
--- a/src/zhuye/buttonset.java
+++ b/src/zhuye/buttonset.java
@@ -1,70 +1,118 @@
 package zhuye;
 
+import java.awt.BasicStroke;
 import java.awt.Color;
+import java.awt.Cursor;
 import java.awt.Dimension;
 import java.awt.Font;
-
-import javax.swing.BorderFactory;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.RoundRectangle2D;
 import javax.swing.JButton;
+import javax.swing.border.EmptyBorder;
 
 /**
- * 鎻愪緵缁熶竴鎸夐挳鏍峰紡鐨勫伐鍘傛柟娉曘��
+ * 鎻愪緵缁熶竴鎸夐挳鏍峰紡鐨勫伐鍘傛柟娉曪紙缇庡寲鐗堬級銆�
+ * 椋庢牸涓� ObstacleManagementPage 淇濇寔涓�鑷达細鍦嗚銆佹墎骞冲寲銆佹姉閿娇銆�
  */
 public final class buttonset {
+    
+    // 榛樿鎸夐挳灏哄
     private static final Dimension DEFAULT_SIZE = new Dimension(90, 36);
+    // 鍦嗚鍗婂緞
+    private static final int CORNER_RADIUS = 8;
 
     private buttonset() {
         // 宸ュ叿绫讳笉闇�瑕佸疄渚嬪寲
     }
 
     /**
-     * 鍒涘缓甯︽湁缁熶竴鏍峰紡鐨勬寜閽紝渚涘涓晫闈㈠鐢ㄣ��
+     * 鍒涘缓甯︽湁鐜颁唬鍖栫粺涓�鏍峰紡鐨勬寜閽紙鍦嗚銆佹墎骞抽鏍硷級銆�
      *
      * @param text 鎸夐挳鏄剧ず鏂囨湰
-     * @param backgroundColor 鎸夐挳鑳屾櫙鑹�
+     * @param backgroundColor 鎸夐挳涓昏壊璋冿紙濡傛灉涓簄ull锛岄粯璁や娇鐢ㄧ伆钃濊壊锛�
      * @return 宸插簲鐢ㄦ牱寮忕殑 JButton
      */
     public static JButton createStyledButton(String text, Color backgroundColor) {
+        // 纭畾鍩虹棰滆壊锛屽鏋滄湭鎸囧畾鍒欎娇鐢ㄩ粯璁ょ殑閽㈣摑鑹�
         final Color baseColor = backgroundColor != null ? backgroundColor : new Color(70, 130, 180);
 
-        JButton button = new JButton(text);
+        // 鍒涘缓鑷畾涔夌粯鍒剁殑鎸夐挳
+        JButton button = new JButton(text) {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            protected void paintComponent(Graphics g) {
+                Graphics2D g2 = (Graphics2D) g.create();
+                // 寮�鍚姉閿娇锛屼娇鍦嗚鍜屾枃瀛楁洿骞虫粦
+                g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+                g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+
+                // 鑾峰彇鎸夐挳鐘舵��
+                boolean isPressed = getModel().isPressed();
+                boolean isRollover = getModel().isRollover();
+                int w = getWidth();
+                int h = getHeight();
+
+                // 璁$畻褰撳墠鑳屾櫙鑹�
+                if (isPressed) {
+                    g2.setColor(baseColor.darker()); // 鎸変笅鍙樻繁
+                } else if (isRollover) {
+                    // 鎮仠鏃剁◢寰�忔槑鎴栧彉浜紝澧炲姞浜や簰鎰�
+                    g2.setColor(new Color(baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue(), 220)); 
+                } else {
+                    g2.setColor(baseColor); // 姝e父棰滆壊
+                }
+
+                // 缁樺埗鍦嗚鐭╁舰鑳屾櫙
+                g2.fill(new RoundRectangle2D.Double(0, 0, w, h, CORNER_RADIUS, CORNER_RADIUS));
+
+                // 濡傛灉闇�瑕佸湪鎸変笅鏃舵湁杞诲井鐨勮竟妗嗘晥鏋滐紙鍙�夛紝澧炲姞绔嬩綋鎰燂級
+                if (isPressed) {
+                    g2.setColor(new Color(0, 0, 0, 30));
+                    g2.draw(new RoundRectangle2D.Double(0.5, 0.5, w - 1, h - 1, CORNER_RADIUS, CORNER_RADIUS));
+                }
+
+                // 缁樺埗鏂囧瓧
+                g2.setColor(Color.WHITE);
+                g2.setFont(getFont());
+                FontMetrics fm = g2.getFontMetrics();
+                
+                // 绮剧‘璁$畻鏂囧瓧灞呬腑浣嶇疆
+                int textX = (w - fm.stringWidth(getText())) / 2;
+                // 娉ㄦ剰锛歽鍧愭爣鏄熀绾夸綅缃紝闇�瑕佸姞涓� Ascent 骞跺噺鍘婚珮搴︾殑涓�鍗�
+                int textY = (h - fm.getHeight()) / 2 + fm.getAscent();
+                
+                g2.drawString(getText(), textX, textY);
+
+                g2.dispose();
+            }
+        };
+
+        // 鍩虹灞炴�ц缃�
         button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
-        button.setBackground(baseColor);
         button.setForeground(Color.WHITE);
-        button.setFocusPainted(false);
-        button.setBorder(BorderFactory.createEmptyBorder(8, 18, 8, 18));
+        button.setFocusPainted(false);      // 鍘婚櫎鐒︾偣铏氱嚎妗�
+        button.setContentAreaFilled(false); // 鍘婚櫎榛樿鑳屾櫙缁樺埗锛堢敱paintComponent鎺ョ锛�
+        button.setBorderPainted(false);     // 鍘婚櫎榛樿杈规
+        button.setCursor(new Cursor(Cursor.HAND_CURSOR)); // 榧犳爣鍙樻垚鎵嬪瀷
+        
+        // 璁剧疆鍐呰竟璺濓紙铏界劧涓昏鐢眕aintComponent鎺у埗锛屼絾瀵瑰竷灞�璁$畻鏈夊府鍔╋級
+        button.setBorder(new EmptyBorder(8, 18, 8, 18));
 
-    Dimension preferred = button.getPreferredSize();
-    int width = Math.max(preferred.width, DEFAULT_SIZE.width);
-    int height = Math.max(preferred.height, DEFAULT_SIZE.height);
-    Dimension adjustedSize = new Dimension(width, height);
-
-    button.setPreferredSize(adjustedSize);
-    button.setMinimumSize(new Dimension(DEFAULT_SIZE.width, DEFAULT_SIZE.height));
-    button.setMaximumSize(adjustedSize);
-
-        button.addMouseListener(new java.awt.event.MouseAdapter() {
-            @Override
-            public void mouseEntered(java.awt.event.MouseEvent event) {
-                button.setBackground(brightenColor(baseColor));
-            }
-
-            @Override
-            public void mouseExited(java.awt.event.MouseEvent event) {
-                button.setBackground(baseColor);
-            }
-        });
+        // 灏哄璁$畻閫昏緫
+        Dimension preferred = button.getPreferredSize();
+        // 纭繚瀹藉害涓嶅皬浜庨粯璁ゅ�硷紝涓旀牴鎹枃瀛楅暱搴﹁嚜閫傚簲
+        int width = Math.max(preferred.width + 20, DEFAULT_SIZE.width); 
+        int height = Math.max(preferred.height, DEFAULT_SIZE.height);
+        
+        Dimension finalSize = new Dimension(width, height);
+        button.setPreferredSize(finalSize);
+        button.setMinimumSize(DEFAULT_SIZE);
+        button.setMaximumSize(finalSize); // 闃叉鍦ㄦ煇浜涘竷灞�涓杩囧害鎷変几
 
         return button;
     }
-
-    private static Color brightenColor(Color color) {
-        if (color == null) {
-            return new Color(200, 200, 200);
-        }
-        int r = Math.min(255, color.getRed() + 30);
-        int g = Math.min(255, color.getGreen() + 30);
-        int b = Math.min(255, color.getBlue() + 30);
-        return new Color(r, g, b);
-    }
-}
+}
\ No newline at end of file

--
Gitblit v1.10.0