package dikuai; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.awt.datatransfer.*; import java.awt.datatransfer.StringSelection; import java.math.BigDecimal; import ui.UIConfig; import ui.UIUtils; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; import zhuye.MapRenderer; import zhuye.Shouye; /** * 地块管理面板 - 卡片式布局设计 * 改为JPanel以适应主界面嵌入,参考Shouye尺寸 */ public class Dikuaiguanli extends JPanel { private static final long serialVersionUID = 1L; private static Dikuaiguanli latestInstance; // 6.5寸竖屏尺寸 private final int SCREEN_WIDTH = 400; private final int SCREEN_HEIGHT = 800; // 主题颜色 private final Color PRIMARY_COLOR = new Color(46, 139, 87); private final Color PRIMARY_DARK = new Color(30, 107, 69); private final Color RED_COLOR = new Color(255, 107, 107); private final Color RED_DARK = new Color(255, 82, 82); private final Color TEXT_COLOR = new Color(51, 51, 51); private final Color LIGHT_TEXT = new Color(119, 119, 119); private final Color WHITE = Color.WHITE; private final Color BORDER_COLOR = new Color(200, 200, 200); private final Color BACKGROUND_COLOR = new Color(250, 250, 250); private final Color CARD_BACKGROUND = new Color(255, 255, 255); private final Color CARD_SHADOW = new Color(220, 220, 220); // 组件 private JPanel mainPanel; private JPanel cardsPanel; private JScrollPane scrollPane; // 按钮 private JButton addLandBtn; private static String currentWorkLandNumber; private static final Map boundaryPointVisibility = new HashMap<>(); private ImageIcon workSelectedIcon; private ImageIcon workUnselectedIcon; private ImageIcon boundaryVisibleIcon; private ImageIcon boundaryHiddenIcon; private static final int BOUNDARY_TOGGLE_ICON_SIZE = 48; public Dikuaiguanli(String landNumber) { latestInstance = this; initializeUI(landNumber); setupEventHandlers(); } private void copyCoordinatesAction(String title, String coordinates) { if (coordinates == null || coordinates.equals("-1") || coordinates.trim().isEmpty()) { JOptionPane.showMessageDialog(this, title + " 未设置", "提示", JOptionPane.INFORMATION_MESSAGE); return; } try { StringSelection selection = new StringSelection(coordinates); Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); clipboard.setContents(selection, selection); JOptionPane.showMessageDialog(this, title + " 已复制到剪贴板", "提示", JOptionPane.INFORMATION_MESSAGE); } catch (Exception ex) { JOptionPane.showMessageDialog(this, "复制失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); } } private void initializeUI(String landNumber) { setLayout(new BorderLayout()); setBackground(BACKGROUND_COLOR); // 使用与Shouye相同的6.5寸竖屏尺寸 setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT)); createMainPanel(); // 添加到主面板 add(mainPanel, BorderLayout.CENTER); // 加载地块数据 loadDikuaiData(); } private void createMainPanel() { mainPanel = new JPanel(); mainPanel.setLayout(new BorderLayout()); mainPanel.setBackground(BACKGROUND_COLOR); mainPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); // 顶部:新增按钮 JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); topPanel.setBackground(BACKGROUND_COLOR); addLandBtn = createActionButton("+ 新增地块", PRIMARY_COLOR); addLandBtn.setPreferredSize(new Dimension(120, 40)); addLandBtn.setFont(new Font("微软雅黑", Font.BOLD, 14)); topPanel.add(addLandBtn); mainPanel.add(topPanel, BorderLayout.NORTH); // 中部:卡片区域 cardsPanel = new JPanel(); cardsPanel.setLayout(new BoxLayout(cardsPanel, BoxLayout.Y_AXIS)); cardsPanel.setBackground(BACKGROUND_COLOR); scrollPane = new JScrollPane(cardsPanel); scrollPane.setBorder(BorderFactory.createEmptyBorder()); scrollPane.getVerticalScrollBar().setUnitIncrement(16); scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); mainPanel.add(scrollPane, BorderLayout.CENTER); } private void loadDikuaiData() { Dikuai.initFromProperties(); // 清空现有卡片 cardsPanel.removeAll(); Map allDikuai = Dikuai.getAllDikuai(); if (allDikuai.isEmpty()) { // 显示空状态 JPanel emptyPanel = createEmptyStatePanel(); cardsPanel.add(emptyPanel); setCurrentWorkLand(null, null); } else { if (allDikuai.size() == 1) { Dikuai onlyDikuai = allDikuai.values().iterator().next(); setCurrentWorklandIfNeeded(onlyDikuai); } // 为每个地块创建卡片 for (Dikuai dikuai : allDikuai.values()) { JPanel card = createDikuaiCard(dikuai); cardsPanel.add(card); cardsPanel.add(Box.createRigidArea(new Dimension(0, 15))); } } cardsPanel.revalidate(); cardsPanel.repaint(); } private JPanel createEmptyStatePanel() { JPanel emptyPanel = new JPanel(); emptyPanel.setLayout(new BorderLayout()); emptyPanel.setBackground(BACKGROUND_COLOR); emptyPanel.setBorder(BorderFactory.createEmptyBorder(50, 0, 0, 0)); JLabel emptyLabel = new JLabel("暂无地块数据", JLabel.CENTER); emptyLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16)); emptyLabel.setForeground(LIGHT_TEXT); emptyPanel.add(emptyLabel, BorderLayout.CENTER); return emptyPanel; } private JPanel createDikuaiCard(Dikuai dikuai) { JPanel card = new JPanel(); card.setLayout(new BorderLayout()); card.setBackground(CARD_BACKGROUND); card.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createLineBorder(CARD_SHADOW, 1), BorderFactory.createEmptyBorder(15, 15, 15, 15) )); card.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)); // 卡片头部:地块名称和删除按钮 JPanel headerPanel = new JPanel(new BorderLayout()); headerPanel.setBackground(CARD_BACKGROUND); // 地块名称 String landName = dikuai.getLandName(); if (landName == null || landName.equals("-1")) { landName = "未知地块"; } JLabel nameLabel = new JLabel(landName); nameLabel.setFont(new Font("微软雅黑", Font.BOLD, 16)); nameLabel.setForeground(TEXT_COLOR); headerPanel.add(nameLabel, BorderLayout.WEST); JButton workToggleBtn = createWorkToggleButton(dikuai); headerPanel.add(workToggleBtn, BorderLayout.EAST); card.add(headerPanel, BorderLayout.NORTH); // 卡片内容:地块信息 JPanel contentPanel = new JPanel(); contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); contentPanel.setBackground(CARD_BACKGROUND); contentPanel.setBorder(BorderFactory.createEmptyBorder(15, 0, 0, 0)); // 地块编号 contentPanel.add(createCardInfoItem("地块编号:", getDisplayValue(dikuai.getLandNumber(), "未知"))); contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); // 添加时间 contentPanel.add(createCardInfoItem("添加时间:", getDisplayValue(dikuai.getCreateTime(), "未知"))); contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); // 地块面积 String landArea = dikuai.getLandArea(); if (landArea != null && !landArea.equals("-1")) { landArea += "㎡"; } else { landArea = "未知"; } contentPanel.add(createCardInfoItem("地块面积:", landArea)); contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); // 返回点坐标(带修改按钮) contentPanel.add(createCardInfoItemWithButton("返回点坐标:", getDisplayValue(dikuai.getReturnPointCoordinates(), "未设置"), "修改", e -> editReturnPoint(dikuai))); contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); // 地块边界坐标(带显示顶点按钮) JPanel boundaryPanel = createBoundaryInfoItem(dikuai, getTruncatedValue(dikuai.getBoundaryCoordinates(), 12, "未设置")); setInfoItemTooltip(boundaryPanel, dikuai.getBoundaryCoordinates()); contentPanel.add(boundaryPanel); contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); // 障碍物坐标(带新增和查看按钮) JPanel obstaclePanel = createCardInfoItemWithButton("障碍物坐标:", getTruncatedValue(dikuai.getObstacleCoordinates(), 12, "未设置"), "新增", e -> addNewObstacle(dikuai)); setInfoItemTooltip(obstaclePanel, dikuai.getObstacleCoordinates()); contentPanel.add(obstaclePanel); contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); // 路径坐标(带查看按钮) JPanel pathPanel = createCardInfoItemWithButton("路径坐标:", getTruncatedValue(dikuai.getPlannedPath(), 12, "未设置"), "复制", e -> copyCoordinatesAction("路径坐标", dikuai.getPlannedPath())); setInfoItemTooltip(pathPanel, dikuai.getPlannedPath()); contentPanel.add(pathPanel); contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); JPanel baseStationPanel = createCardInfoItemWithButton("基站坐标:", getTruncatedValue(dikuai.getBaseStationCoordinates(), 12, "未设置"), "复制", e -> copyCoordinatesAction("基站坐标", dikuai.getBaseStationCoordinates())); setInfoItemTooltip(baseStationPanel, dikuai.getBaseStationCoordinates()); contentPanel.add(baseStationPanel); contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); JPanel boundaryOriginalPanel = createCardInfoItemWithButton("边界原始坐标:", getTruncatedValue(dikuai.getBoundaryOriginalCoordinates(), 12, "未设置"), "复制", e -> copyCoordinatesAction("边界原始坐标", dikuai.getBoundaryOriginalCoordinates())); setInfoItemTooltip(boundaryOriginalPanel, dikuai.getBoundaryOriginalCoordinates()); contentPanel.add(boundaryOriginalPanel); contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); JPanel mowingPatternPanel = createCardInfoItemWithButton("割草模式:", getTruncatedValue(dikuai.getMowingPattern(), 12, "未设置"), "复制", e -> copyCoordinatesAction("割草模式", dikuai.getMowingPattern())); setInfoItemTooltip(mowingPatternPanel, dikuai.getMowingPattern()); contentPanel.add(mowingPatternPanel); contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); String mowingWidthValue = dikuai.getMowingWidth(); String widthSource = null; if (mowingWidthValue != null && !"-1".equals(mowingWidthValue) && !mowingWidthValue.trim().isEmpty()) { widthSource = mowingWidthValue + "厘米"; } String displayWidth = getTruncatedValue(widthSource, 12, "未设置"); JPanel mowingWidthPanel = createCardInfoItemWithButton("割草宽度:", displayWidth, "编辑", e -> editMowingWidth(dikuai)); setInfoItemTooltip(mowingWidthPanel, widthSource); contentPanel.add(mowingWidthPanel); card.add(contentPanel, BorderLayout.CENTER); JButton deleteBtn = createDeleteButton(); deleteBtn.addActionListener(e -> deleteDikuai(dikuai)); JPanel footerPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); footerPanel.setBackground(CARD_BACKGROUND); footerPanel.setBorder(BorderFactory.createEmptyBorder(15, 0, 0, 0)); footerPanel.add(deleteBtn); card.add(footerPanel, BorderLayout.SOUTH); return card; } private JPanel createCardInfoItem(String label, String value) { JPanel itemPanel = new JPanel(new BorderLayout()); itemPanel.setBackground(CARD_BACKGROUND); itemPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 20)); JLabel labelComp = new JLabel(label); labelComp.setFont(new Font("微软雅黑", Font.PLAIN, 14)); labelComp.setForeground(LIGHT_TEXT); JLabel valueComp = new JLabel(value); valueComp.setFont(new Font("微软雅黑", Font.PLAIN, 14)); valueComp.setForeground(TEXT_COLOR); itemPanel.add(labelComp, BorderLayout.WEST); itemPanel.add(valueComp, BorderLayout.EAST); return itemPanel; } 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)); JLabel labelComp = new JLabel(label); labelComp.setFont(new Font("微软雅黑", Font.PLAIN, 14)); labelComp.setForeground(LIGHT_TEXT); JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0)); rightPanel.setBackground(CARD_BACKGROUND); JLabel valueComp = new JLabel(value); valueComp.setFont(new Font("微软雅黑", Font.PLAIN, 14)); valueComp.setForeground(TEXT_COLOR); JButton button = createSmallButton(buttonText); button.addActionListener(listener); rightPanel.add(valueComp); rightPanel.add(button); itemPanel.add(labelComp, BorderLayout.WEST); itemPanel.add(rightPanel, BorderLayout.CENTER); itemPanel.putClientProperty("valueLabel", valueComp); return itemPanel; } 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); Dimension rowDimension = new Dimension(Integer.MAX_VALUE, rowHeight); itemPanel.setMaximumSize(rowDimension); itemPanel.setPreferredSize(rowDimension); itemPanel.setMinimumSize(new Dimension(0, 32)); 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); JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0)); rightPanel.setBackground(CARD_BACKGROUND); rightPanel.setBorder(BorderFactory.createEmptyBorder(verticalPadding, 0, verticalPadding, 0)); JLabel valueComp = new JLabel(displayValue); valueComp.setFont(new Font("微软雅黑", Font.PLAIN, 14)); valueComp.setForeground(TEXT_COLOR); JButton toggleButton = createBoundaryToggleButton(dikuai); rightPanel.add(valueComp); rightPanel.add(toggleButton); itemPanel.add(labelComp, BorderLayout.WEST); itemPanel.add(rightPanel, BorderLayout.CENTER); itemPanel.putClientProperty("valueLabel", valueComp); return itemPanel; } private JButton createBoundaryToggleButton(Dikuai dikuai) { JButton button = new JButton(); button.setContentAreaFilled(false); button.setBorder(BorderFactory.createEmptyBorder()); button.setFocusPainted(false); button.setOpaque(false); button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); button.setMargin(new Insets(0, 0, 0, 0)); button.setIconTextGap(0); button.setPreferredSize(new Dimension(56, 56)); String landNumber = dikuai.getLandNumber(); boolean isVisible = boundaryPointVisibility.getOrDefault(landNumber, false); updateBoundaryToggleButton(button, isVisible); button.addActionListener(e -> toggleBoundaryPointVisualization(dikuai, button)); return button; } private void updateBoundaryToggleButton(JButton button, boolean active) { ensureBoundaryToggleIconsLoaded(); ImageIcon icon = active ? boundaryVisibleIcon : boundaryHiddenIcon; if (icon != null) { button.setIcon(icon); button.setText(null); button.setContentAreaFilled(false); button.setOpaque(false); } else { button.setIcon(null); button.setText(active ? "关闭" : "开启"); button.setContentAreaFilled(true); button.setBackground(PRIMARY_COLOR); button.setForeground(WHITE); button.setOpaque(true); } button.setToolTipText(active ? "隐藏边界点序号" : "显示边界点序号"); } private void ensureBoundaryToggleIconsLoaded() { if (boundaryVisibleIcon == null) { boundaryVisibleIcon = loadIcon("image/open.png", BOUNDARY_TOGGLE_ICON_SIZE, BOUNDARY_TOGGLE_ICON_SIZE); } if (boundaryHiddenIcon == null) { boundaryHiddenIcon = loadIcon("image/close.png", BOUNDARY_TOGGLE_ICON_SIZE, BOUNDARY_TOGGLE_ICON_SIZE); } } private void toggleBoundaryPointVisualization(Dikuai dikuai, JButton button) { if (dikuai == null) { return; } String landNumber = dikuai.getLandNumber(); if (landNumber == null || landNumber.trim().isEmpty()) { return; } boolean currentState = boundaryPointVisibility.getOrDefault(landNumber, false); boolean desiredState = !currentState; if (desiredState) { String boundary = dikuai.getBoundaryCoordinates(); if (boundary == null || boundary.trim().isEmpty() || "-1".equals(boundary.trim())) { JOptionPane.showMessageDialog(this, "当前地块暂无边界数据", "提示", JOptionPane.INFORMATION_MESSAGE); desiredState = false; } } boundaryPointVisibility.put(landNumber, desiredState); updateBoundaryToggleButton(button, desiredState); Shouye shouye = Shouye.getInstance(); if (shouye != null) { MapRenderer renderer = shouye.getMapRenderer(); if (renderer != null) { boolean isCurrent = currentWorkLandNumber != null && currentWorkLandNumber.equals(landNumber); if (isCurrent) { renderer.setBoundaryPointsVisible(desiredState); } } } } private void setInfoItemTooltip(JPanel itemPanel, String rawValue) { if (itemPanel == null || rawValue == null || rawValue.trim().isEmpty() || "-1".equals(rawValue.trim())) { return; } Object valueComp = itemPanel.getClientProperty("valueLabel"); if (valueComp instanceof JLabel) { ((JLabel) valueComp).setToolTipText(rawValue); } } private String getDisplayValue(String value, String defaultValue) { if (value == null || value.equals("-1") || value.trim().isEmpty()) { return defaultValue; } return value; } private String getTruncatedValue(String value, int maxLength, String defaultValue) { if (value == null || value.equals("-1") || value.trim().isEmpty()) { return defaultValue; } if (value.length() > maxLength) { return value.substring(0, maxLength) + "..."; } return value; } private JButton createSmallButton(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(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(PRIMARY_DARK); } public void mouseExited(MouseEvent e) { button.setBackground(PRIMARY_COLOR); } }); return button; } 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)); // 悬停效果 button.addMouseListener(new MouseAdapter() { public void mouseEntered(MouseEvent e) { if (color == RED_COLOR) { button.setBackground(RED_DARK); } else { button.setBackground(PRIMARY_DARK); } } public void mouseExited(MouseEvent e) { if (color == RED_COLOR) { button.setBackground(RED_COLOR); } else { button.setBackground(PRIMARY_COLOR); } } }); return button; } 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)); 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 createWorkToggleButton(Dikuai dikuai) { JButton button = new JButton(); button.setContentAreaFilled(false); button.setBorder(BorderFactory.createEmptyBorder()); button.setFocusPainted(false); button.setOpaque(false); button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); button.setPreferredSize(new Dimension(56, 56)); boolean isCurrent = dikuai.getLandNumber() != null && dikuai.getLandNumber().equals(currentWorkLandNumber); updateWorkToggleButton(button, isCurrent); button.addActionListener(e -> handleWorkToggle(dikuai)); return button; } private void updateWorkToggleButton(JButton button, boolean isCurrent) { ensureWorkIconsLoaded(); ImageIcon icon = isCurrent ? workSelectedIcon : workUnselectedIcon; if (icon != null) { button.setIcon(icon); button.setText(null); } else { button.setText(isCurrent ? "当前地块" : "设为当前"); } button.setToolTipText(isCurrent ? "取消当前作业地块" : "设为当前作业地块"); } private void ensureWorkIconsLoaded() { if (workSelectedIcon == null) { workSelectedIcon = loadIcon("image/open.png", 48, 48); } if (workUnselectedIcon == null) { workUnselectedIcon = loadIcon("image/close.png", 48, 48); } } private void handleWorkToggle(Dikuai dikuai) { String landNumber = dikuai.getLandNumber(); if (landNumber == null || landNumber.trim().isEmpty()) { return; } boolean isCurrent = landNumber.equals(currentWorkLandNumber); if (isCurrent) { setCurrentWorkLand(null, null); } else { setCurrentWorkLand(landNumber, dikuai.getLandName()); } loadDikuaiData(); } private void setCurrentWorklandIfNeeded(Dikuai dikuai) { if (dikuai == null) { return; } String landNumber = dikuai.getLandNumber(); if (landNumber == null || landNumber.trim().isEmpty()) { return; } setCurrentWorkLand(landNumber, dikuai.getLandName()); } public static void setCurrentWorkLand(String landNumber, String landName) { currentWorkLandNumber = landNumber; Dikuai dikuai = null; if (landNumber != null) { dikuai = Dikuai.getDikuai(landNumber); if (dikuai != null && (landName == null || "-1".equals(landName) || landName.trim().isEmpty())) { landName = dikuai.getLandName(); } } if (landName != null && ("-1".equals(landName) || landName.trim().isEmpty())) { landName = "未知地块"; } Shouye shouye = Shouye.getInstance(); if (shouye != null) { if (landNumber == null) { shouye.updateCurrentAreaName(null); } else { shouye.updateCurrentAreaName(landName); } MapRenderer renderer = shouye.getMapRenderer(); if (renderer != null) { String boundary = (dikuai != null) ? dikuai.getBoundaryCoordinates() : null; String plannedPath = (dikuai != null) ? dikuai.getPlannedPath() : null; renderer.setCurrentBoundary(boundary, landNumber, landNumber == null ? null : landName); renderer.setCurrentPlannedPath(plannedPath); boolean showBoundaryPoints = landNumber != null && boundaryPointVisibility.getOrDefault(landNumber, false); renderer.setBoundaryPointsVisible(showBoundaryPoints); } } } private ImageIcon loadIcon(String path, int width, int height) { try { ImageIcon rawIcon = new ImageIcon(path); if (rawIcon.getIconWidth() <= 0 || rawIcon.getIconHeight() <= 0) { return null; } Image scaled = rawIcon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH); return new ImageIcon(scaled); } catch (Exception ex) { System.err.println("无法加载图标: " + path + " - " + ex.getMessage()); return null; } } private void setupEventHandlers() { // 新增地块按钮 addLandBtn.addActionListener(e -> { Window ownerWindow = SwingUtilities.getWindowAncestor(this); Window addDialogOwner = ownerWindow != null ? ownerWindow.getOwner() : null; Component parentComponent = addDialogOwner instanceof Component ? (Component) addDialogOwner : ownerWindow; if (ownerWindow instanceof JDialog) { ownerWindow.dispose(); } latestInstance = null; SwingUtilities.invokeLater(() -> AddDikuai.showAddDikuaiDialog(parentComponent)); }); } private void editReturnPoint(Dikuai dikuai) { FanhuiDialog fd = new FanhuiDialog(SwingUtilities.getWindowAncestor(this), dikuai); fd.setVisible(true); // 如果对话框已更新数据,刷新显示 if (fd.isUpdated()) { loadDikuaiData(); } } private void addNewObstacle(Dikuai dikuai) { String obstacleCoords = JOptionPane.showInputDialog(this, "请输入障碍物坐标(格式: 纬度1,经度1;纬度2,经度2;...):"); if (obstacleCoords != null && !obstacleCoords.trim().isEmpty()) { // 更新障碍物坐标 Dikuai.updateField(dikuai.getLandNumber(), "obstacleCoordinates", obstacleCoords.trim()); Dikuai.updateField(dikuai.getLandNumber(), "updateTime", getCurrentTime()); // 保存到文件 Dikuai.saveToProperties(); // 刷新显示 loadDikuaiData(); JOptionPane.showMessageDialog(this, "障碍物坐标已添加!", "成功", JOptionPane.INFORMATION_MESSAGE); } } private void editMowingWidth(Dikuai dikuai) { String currentWidth = dikuai.getMowingWidth(); if (currentWidth == null || "-1".equals(currentWidth)) { currentWidth = ""; } String input = JOptionPane.showInputDialog(this, "请输入割草宽度(单位: 厘米)", currentWidth); if (input == null) { return; } String trimmed = input.trim(); if (trimmed.isEmpty()) { JOptionPane.showMessageDialog(this, "割草宽度不能为空", "提示", JOptionPane.WARNING_MESSAGE); return; } String sanitized = trimmed; if (sanitized.endsWith("厘米")) { sanitized = sanitized.substring(0, sanitized.length() - 2).trim(); } if (sanitized.endsWith("cm") || sanitized.endsWith("CM")) { sanitized = sanitized.substring(0, sanitized.length() - 2).trim(); } double widthValue; try { widthValue = Double.parseDouble(sanitized); } catch (NumberFormatException ex) { JOptionPane.showMessageDialog(this, "请输入有效的数字", "提示", JOptionPane.WARNING_MESSAGE); return; } if (widthValue <= 0) { JOptionPane.showMessageDialog(this, "割草宽度必须大于0", "提示", JOptionPane.WARNING_MESSAGE); return; } String normalized = BigDecimal.valueOf(widthValue).stripTrailingZeros().toPlainString(); if (!Dikuai.updateField(dikuai.getLandNumber(), "mowingWidth", normalized)) { JOptionPane.showMessageDialog(this, "无法更新割草宽度", "错误", JOptionPane.ERROR_MESSAGE); return; } Dikuai.updateField(dikuai.getLandNumber(), "updateTime", getCurrentTime()); Dikuai.saveToProperties(); loadDikuaiData(); JOptionPane.showMessageDialog(this, "割草宽度已更新", "成功", JOptionPane.INFORMATION_MESSAGE); } private void deleteDikuai(Dikuai dikuai) { int result = JOptionPane.showConfirmDialog(this, "确定要删除地块 \"" + dikuai.getLandName() + "\" 吗?此操作无法撤销。", "确认删除", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if (result == JOptionPane.YES_OPTION) { // 从内存中移除并同步到文件 if (Dikuai.removeDikuai(dikuai.getLandNumber())) { if (dikuai.getLandNumber() != null && dikuai.getLandNumber().equals(currentWorkLandNumber)) { setCurrentWorkLand(null, null); } boundaryPointVisibility.remove(dikuai.getLandNumber()); Dikuai.saveToProperties(); loadDikuaiData(); JOptionPane.showMessageDialog(this, "地块删除成功!", "成功", JOptionPane.INFORMATION_MESSAGE); } else { JOptionPane.showMessageDialog(this, "未找到要删除的地块。", "提示", JOptionPane.WARNING_MESSAGE); } } } private String getCurrentTime() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(new Date()); } /** * 获取地块管理面板实例 * @param landNumber 地块编号 */ public static Dikuaiguanli createDikuaiManagementPanel(String landNumber) { // 确保地块数据已初始化 Dikuai.initFromProperties(); return new Dikuaiguanli(landNumber); } /** * 显示地块管理对话框(向后兼容) * @param parent 父组件 * @param landNumber 地块编号 */ public static void showDikuaiManagement(Component parent, String landNumber) { // 确保地块数据已初始化 Dikuai.initFromProperties(); JDialog dialog = new JDialog(); dialog.setTitle("地块管理"); dialog.setModal(true); dialog.setSize(400, 800); dialog.setLocationRelativeTo(parent); dialog.setResizable(false); Dikuaiguanli managementPanel = new Dikuaiguanli(landNumber); dialog.add(managementPanel); dialog.setVisible(true); } public static void notifyExternalCreation(String landNumber) { if (latestInstance == null) { return; } SwingUtilities.invokeLater(() -> latestInstance.loadDikuaiData()); } public static void updateBoundaryPointVisibility(String landNumber, boolean visible) { if (landNumber == null || landNumber.trim().isEmpty()) { return; } boundaryPointVisibility.put(landNumber, visible); } }