| | |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import ui.UIConfig; |
| | | import ui.UIUtils; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | import java.util.Map; |
| | |
| | | import zhuye.MapRenderer; |
| | | import zhuye.Shouye; |
| | | import zhuye.Coordinate; |
| | | import zhuye.buttonset; |
| | | import zhuye.Fuzhibutton; |
| | | import zhuye.Lookbutton; |
| | | import gecaoji.Device; |
| | | |
| | | /** |
| | | * 地块管理面板 - 卡片式布局设计 |
| | |
| | | private ImageIcon workUnselectedIcon; |
| | | private ImageIcon boundaryVisibleIcon; |
| | | private ImageIcon boundaryHiddenIcon; |
| | | private static final int BOUNDARY_TOGGLE_ICON_SIZE = 48; |
| | | private static final int BOUNDARY_TOGGLE_ICON_SIZE = 24; |
| | | private Map<String, ObstacleSummary> obstacleSummaryCache = Collections.emptyMap(); |
| | | |
| | | public Dikuaiguanli(String landNumber) { |
| | |
| | | for (Dikuai dikuai : allDikuai.values()) { |
| | | JPanel card = createDikuaiCard(dikuai); |
| | | cardsPanel.add(card); |
| | | cardsPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | cardsPanel.add(Box.createRigidArea(new Dimension(0, 10))); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | headerPanel.add(nameLabel, BorderLayout.WEST); |
| | | |
| | | // 右侧区域:状态文字 + 按钮 |
| | | JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0)); |
| | | rightPanel.setBackground(CARD_BACKGROUND); |
| | | rightPanel.setOpaque(false); |
| | | |
| | | // 状态文字标签(根据是否选中显示/隐藏) |
| | | JLabel statusLabel = new JLabel("已设置为当前地块"); |
| | | statusLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13)); |
| | | statusLabel.setForeground(PRIMARY_COLOR); |
| | | boolean isCurrent = dikuai.getLandNumber() != null && dikuai.getLandNumber().equals(currentWorkLandNumber); |
| | | statusLabel.setVisible(isCurrent); |
| | | |
| | | JButton workToggleBtn = createWorkToggleButton(dikuai); |
| | | headerPanel.add(workToggleBtn, BorderLayout.EAST); |
| | | |
| | | // 将状态标签和按钮关联,以便在按钮状态变化时更新标签 |
| | | workToggleBtn.putClientProperty("statusLabel", statusLabel); |
| | | |
| | | rightPanel.add(statusLabel); |
| | | rightPanel.add(workToggleBtn); |
| | | |
| | | headerPanel.add(rightPanel, BorderLayout.EAST); |
| | | |
| | | card.add(headerPanel, BorderLayout.NORTH); |
| | | |
| | |
| | | |
| | | // 地块编号 |
| | | contentPanel.add(createCardInfoItem("地块编号:", getDisplayValue(dikuai.getLandNumber(), "未知"))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); |
| | | |
| | | // 添加时间 |
| | | contentPanel.add(createCardInfoItem("添加时间:", getDisplayValue(dikuai.getCreateTime(), "未知"))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); |
| | | |
| | | // 地块面积 |
| | | String landArea = dikuai.getLandArea(); |
| | |
| | | landArea = "未知"; |
| | | } |
| | | contentPanel.add(createCardInfoItem("地块面积:", landArea)); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); |
| | | |
| | | // 返回点坐标(带修改按钮) |
| | | contentPanel.add(createCardInfoItemWithButton("返回点坐标:", |
| | | getDisplayValue(dikuai.getReturnPointCoordinates(), "未设置"), |
| | | "修改", e -> editReturnPoint(dikuai))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | JPanel mowingPatternPanel = createCardInfoItem("割草模式:", |
| | | formatMowingPatternForDisplay(dikuai.getMowingPattern())); |
| | | configureInteractiveLabel(getInfoItemTitleLabel(mowingPatternPanel), |
| | | () -> editMowingPattern(dikuai), |
| | | "点击查看/编辑割草模式"); |
| | | contentPanel.add(mowingPatternPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); |
| | | |
| | | // 地块边界坐标(带显示顶点按钮) |
| | | JPanel boundaryPanel = createBoundaryInfoItem(dikuai, |
| | | getTruncatedValue(dikuai.getBoundaryCoordinates(), 12, "未设置")); |
| | | setInfoItemTooltip(boundaryPanel, dikuai.getBoundaryCoordinates()); |
| | | configureInteractiveLabel(getInfoItemTitleLabel(boundaryPanel), |
| | | () -> editBoundaryCoordinates(dikuai), |
| | | "点击查看/编辑地块边界坐标"); |
| | | contentPanel.add(boundaryPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | // 割草机割刀宽度 |
| | | String mowingBladeWidthValue = dikuai.getMowingBladeWidth(); |
| | | String displayBladeWidth = "未设置"; |
| | | if (mowingBladeWidthValue != null && !"-1".equals(mowingBladeWidthValue) && !mowingBladeWidthValue.trim().isEmpty()) { |
| | | try { |
| | | double bladeWidthMeters = Double.parseDouble(mowingBladeWidthValue.trim()); |
| | | double bladeWidthCm = bladeWidthMeters * 100.0; |
| | | displayBladeWidth = String.format("%.2f厘米", bladeWidthCm); |
| | | } catch (NumberFormatException e) { |
| | | displayBladeWidth = "未设置"; |
| | | } |
| | | } |
| | | JPanel mowingBladeWidthPanel = createCardInfoItem("割草机割刀宽度:", displayBladeWidth); |
| | | contentPanel.add(mowingBladeWidthPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); |
| | | |
| | | String mowingWidthValue = dikuai.getMowingWidth(); |
| | | String displayWidth = "未设置"; |
| | | if (mowingWidthValue != null && !"-1".equals(mowingWidthValue) && !mowingWidthValue.trim().isEmpty()) { |
| | | displayWidth = mowingWidthValue + "厘米"; |
| | | } |
| | | JPanel mowingWidthPanel = createCardInfoItem("割草宽度:", displayWidth); |
| | | contentPanel.add(mowingWidthPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); |
| | | |
| | | // 割草安全距离 |
| | | String displaySafetyDistance = "未设置"; |
| | | String safetyDistanceValue = dikuai.getMowingSafetyDistance(); |
| | | if (safetyDistanceValue != null && !"-1".equals(safetyDistanceValue) && !safetyDistanceValue.trim().isEmpty()) { |
| | | try { |
| | | double distanceMeters = Double.parseDouble(safetyDistanceValue.trim()); |
| | | // 如果值大于100,认为是厘米,需要转换为米 |
| | | if (distanceMeters > 100) { |
| | | distanceMeters = distanceMeters / 100.0; |
| | | } |
| | | displaySafetyDistance = String.format("%.2f米", distanceMeters); |
| | | } catch (NumberFormatException e) { |
| | | displaySafetyDistance = "未设置"; |
| | | } |
| | | } |
| | | JPanel mowingSafetyDistancePanel = createCardInfoItem("割草安全距离:", displaySafetyDistance); |
| | | contentPanel.add(mowingSafetyDistancePanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); |
| | | |
| | | // 往返点路径(带查看图标按钮) |
| | | JPanel returnPathPanel = createCardInfoItemWithButton("往返点路径:", |
| | | getDisplayValue(dikuai.getReturnPathCoordinates(), "未设置"), |
| | | createViewButton(e -> editReturnPath(dikuai))); |
| | | configureInteractiveLabel(getInfoItemTitleLabel(returnPathPanel), |
| | | () -> editReturnPath(dikuai), |
| | | "点击查看/编辑往返点路径"); |
| | | contentPanel.add(returnPathPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); |
| | | |
| | | ObstacleSummary obstacleSummary = getObstacleSummaryFromCache(dikuai.getLandNumber()); |
| | | JPanel obstaclePanel = createCardInfoItemWithButton("障碍物:", |
| | |
| | | () -> showObstacleManagementPage(dikuai), |
| | | "点击查看/管理障碍物"); |
| | | contentPanel.add(obstaclePanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); |
| | | |
| | | // 地块边界坐标(带显示顶点按钮) |
| | | JPanel boundaryPanel = createBoundaryInfoItem(dikuai); |
| | | configureInteractiveLabel(getInfoItemTitleLabel(boundaryPanel), |
| | | () -> editBoundaryCoordinates(dikuai), |
| | | "点击查看/编辑地块边界坐标"); |
| | | contentPanel.add(boundaryPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); |
| | | |
| | | // 路径坐标(带查看按钮) |
| | | JPanel pathPanel = createCardInfoItemWithButton("路径坐标:", |
| | | getTruncatedValue(dikuai.getPlannedPath(), 12, "未设置"), |
| | | "复制", e -> copyCoordinatesAction("路径坐标", dikuai.getPlannedPath())); |
| | | setInfoItemTooltip(pathPanel, dikuai.getPlannedPath()); |
| | | JPanel pathPanel = createCardInfoItemWithIconButton("路径坐标:", |
| | | createViewButton(e -> editPlannedPath(dikuai))); |
| | | configureInteractiveLabel(getInfoItemTitleLabel(pathPanel), |
| | | () -> editPlannedPath(dikuai), |
| | | "点击查看/编辑路径坐标"); |
| | | contentPanel.add(pathPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); |
| | | |
| | | JPanel baseStationPanel = createCardInfoItemWithButton("基站坐标:", |
| | | getTruncatedValue(dikuai.getBaseStationCoordinates(), 12, "未设置"), |
| | | "复制", e -> copyCoordinatesAction("基站坐标", dikuai.getBaseStationCoordinates())); |
| | | setInfoItemTooltip(baseStationPanel, dikuai.getBaseStationCoordinates()); |
| | | configureInteractiveLabel(getInfoItemTitleLabel(baseStationPanel), |
| | | () -> editBaseStationCoordinates(dikuai), |
| | | "点击查看/编辑基站坐标"); |
| | | contentPanel.add(baseStationPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | JPanel baseStationPanel = createCardInfoItemWithIconButton("基站坐标:", |
| | | createViewButton(e -> editBaseStationCoordinates(dikuai))); |
| | | configureInteractiveLabel(getInfoItemTitleLabel(baseStationPanel), |
| | | () -> editBaseStationCoordinates(dikuai), |
| | | "点击查看/编辑基站坐标"); |
| | | contentPanel.add(baseStationPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); |
| | | |
| | | JPanel boundaryOriginalPanel = createCardInfoItemWithButton("边界原始坐标:", |
| | | getTruncatedValue(dikuai.getBoundaryOriginalCoordinates(), 12, "未设置"), |
| | | "复制", e -> copyCoordinatesAction("边界原始坐标", dikuai.getBoundaryOriginalCoordinates())); |
| | | setInfoItemTooltip(boundaryOriginalPanel, dikuai.getBoundaryOriginalCoordinates()); |
| | | configureInteractiveLabel(getInfoItemTitleLabel(boundaryOriginalPanel), |
| | | () -> editBoundaryOriginalCoordinates(dikuai), |
| | | "点击查看/编辑边界原始坐标"); |
| | | contentPanel.add(boundaryOriginalPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | |
| | | JPanel mowingPatternPanel = createCardInfoItemWithButton("割草模式:", |
| | | getTruncatedValue(dikuai.getMowingPattern(), 12, "未设置"), |
| | | "复制", e -> copyCoordinatesAction("割草模式", dikuai.getMowingPattern())); |
| | | setInfoItemTooltip(mowingPatternPanel, dikuai.getMowingPattern()); |
| | | configureInteractiveLabel(getInfoItemTitleLabel(mowingPatternPanel), |
| | | () -> editMowingPattern(dikuai), |
| | | "点击查看/编辑割草模式"); |
| | | contentPanel.add(mowingPatternPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | |
| | | 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); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | JPanel boundaryOriginalPanel = createCardInfoItemWithIconButton("边界原始坐标:", |
| | | createViewButton(e -> editBoundaryOriginalCoordinates(dikuai))); |
| | | configureInteractiveLabel(getInfoItemTitleLabel(boundaryOriginalPanel), |
| | | () -> editBoundaryOriginalCoordinates(dikuai), |
| | | "点击查看/编辑边界原始坐标"); |
| | | contentPanel.add(boundaryOriginalPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); |
| | | |
| | | JPanel completedTrackPanel = createCardInfoItemWithButton("已完成割草路径:", |
| | | getTruncatedValue(dikuai.getMowingTrack(), 12, "未记录"), |
| | | "查看", e -> showCompletedMowingTrackDialog(dikuai)); |
| | | createViewButton(e -> showCompletedMowingTrackDialog(dikuai))); |
| | | setInfoItemTooltip(completedTrackPanel, dikuai.getMowingTrack()); |
| | | configureInteractiveLabel(getInfoItemTitleLabel(completedTrackPanel), |
| | | () -> showCompletedMowingTrackDialog(dikuai), |
| | |
| | | JButton generatePathBtn = createPrimaryFooterButton("路径规划"); |
| | | generatePathBtn.addActionListener(e -> showPathPlanningPage(dikuai)); |
| | | |
| | | JButton navigationPreviewBtn = createPrimaryFooterButton("导航预览"); |
| | | navigationPreviewBtn.addActionListener(e -> startNavigationPreview(dikuai)); |
| | | |
| | | JPanel footerPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); |
| | | footerPanel.setBackground(CARD_BACKGROUND); |
| | | footerPanel.setBorder(BorderFactory.createEmptyBorder(15, 0, 0, 0)); |
| | | footerPanel.add(generatePathBtn); |
| | | footerPanel.add(Box.createHorizontalStrut(12)); |
| | | footerPanel.add(navigationPreviewBtn); |
| | | footerPanel.add(Box.createHorizontalStrut(12)); |
| | | footerPanel.add(deleteBtn); |
| | | card.add(footerPanel, BorderLayout.SOUTH); |
| | | |
| | |
| | | |
| | | itemPanel.add(labelComp, BorderLayout.WEST); |
| | | itemPanel.add(valueComp, BorderLayout.EAST); |
| | | itemPanel.putClientProperty("titleLabel", labelComp); |
| | | |
| | | return itemPanel; |
| | | } |
| | |
| | | return itemPanel; |
| | | } |
| | | |
| | | private JPanel createBoundaryInfoItem(Dikuai dikuai, String displayValue) { |
| | | private JPanel createCardInfoItemWithButton(String label, String value, JButton button) { |
| | | JPanel itemPanel = new JPanel(new BorderLayout()); |
| | | itemPanel.setBackground(CARD_BACKGROUND); |
| | | // 增加高度以确保按钮完整显示(按钮高度约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)); |
| | | labelComp.setForeground(LIGHT_TEXT); |
| | | |
| | | 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); |
| | | |
| | | rightPanel.add(valueComp); |
| | | rightPanel.add(button); |
| | | |
| | | itemPanel.add(labelComp, BorderLayout.WEST); |
| | | itemPanel.add(rightPanel, BorderLayout.CENTER); |
| | | itemPanel.putClientProperty("valueLabel", valueComp); |
| | | itemPanel.putClientProperty("titleLabel", labelComp); |
| | | |
| | | return itemPanel; |
| | | } |
| | | |
| | | private JPanel createCardInfoItemWithButtonOnly(String label, String buttonText, ActionListener listener) { |
| | | JPanel itemPanel = new JPanel(new BorderLayout()); |
| | | itemPanel.setBackground(CARD_BACKGROUND); |
| | | // 增加高度以确保按钮完整显示(按钮高度约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)); |
| | | labelComp.setForeground(LIGHT_TEXT); |
| | | |
| | | JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0)); |
| | | rightPanel.setBackground(CARD_BACKGROUND); |
| | | // 添加垂直内边距以确保按钮不被裁剪 |
| | | rightPanel.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 0)); |
| | | |
| | | JButton button = createSmallLinkButton(buttonText, listener); |
| | | |
| | | rightPanel.add(button); |
| | | |
| | | itemPanel.add(labelComp, BorderLayout.WEST); |
| | | itemPanel.add(rightPanel, BorderLayout.CENTER); |
| | | itemPanel.putClientProperty("titleLabel", labelComp); |
| | | |
| | | return itemPanel; |
| | | } |
| | | |
| | | private JPanel createCardInfoItemWithIconButton(String label, JButton button) { |
| | | JPanel itemPanel = new JPanel(new BorderLayout()); |
| | | itemPanel.setBackground(CARD_BACKGROUND); |
| | | // 增加高度以确保按钮完整显示(按钮高度约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)); |
| | | labelComp.setForeground(LIGHT_TEXT); |
| | | |
| | | JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0)); |
| | | rightPanel.setBackground(CARD_BACKGROUND); |
| | | // 添加垂直内边距以确保按钮不被裁剪 |
| | | rightPanel.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 0)); |
| | | |
| | | rightPanel.add(button); |
| | | |
| | | itemPanel.add(labelComp, BorderLayout.WEST); |
| | | itemPanel.add(rightPanel, BorderLayout.CENTER); |
| | | itemPanel.putClientProperty("titleLabel", labelComp); |
| | | |
| | | return itemPanel; |
| | | } |
| | | |
| | | private JPanel createBoundaryInfoItem(Dikuai dikuai) { |
| | | JPanel itemPanel = new JPanel(new BorderLayout()); |
| | | itemPanel.setBackground(CARD_BACKGROUND); |
| | | // 增加高度以确保按钮下边缘完整显示(按钮高度56,加上上下边距) |
| | | int rowHeight = Math.max(60, BOUNDARY_TOGGLE_ICON_SIZE + 16); |
| | | // 增加高度以确保按钮下边缘完整显示(按钮高度28,加上上下边距) |
| | | int rowHeight = Math.max(30, BOUNDARY_TOGGLE_ICON_SIZE + 8); |
| | | Dimension rowDimension = new Dimension(Integer.MAX_VALUE, rowHeight); |
| | | itemPanel.setMaximumSize(rowDimension); |
| | | itemPanel.setPreferredSize(rowDimension); |
| | | itemPanel.setMinimumSize(new Dimension(0, 56)); |
| | | itemPanel.setMinimumSize(new Dimension(0, 28)); |
| | | |
| | | JLabel labelComp = new JLabel("地块边界:"); |
| | | labelComp.setFont(new Font("微软雅黑", Font.PLAIN, 14)); |
| | |
| | | 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); |
| | | // 状态提示文字标签 |
| | | JLabel statusLabel = new JLabel(); |
| | | statusLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13)); |
| | | statusLabel.setForeground(LIGHT_TEXT); |
| | | |
| | | JButton toggleButton = createBoundaryToggleButton(dikuai); |
| | | // 将状态标签和按钮关联,以便在按钮状态变化时更新标签 |
| | | toggleButton.putClientProperty("statusLabel", statusLabel); |
| | | |
| | | rightPanel.add(valueComp); |
| | | // 初始化状态文字 |
| | | String landNumber = dikuai.getLandNumber(); |
| | | boolean isVisible = boundaryPointVisibility.getOrDefault(landNumber, false); |
| | | updateBoundaryStatusLabel(statusLabel, isVisible); |
| | | |
| | | rightPanel.add(statusLabel); |
| | | rightPanel.add(toggleButton); |
| | | |
| | | itemPanel.add(labelComp, BorderLayout.WEST); |
| | | itemPanel.add(rightPanel, BorderLayout.CENTER); |
| | | itemPanel.putClientProperty("valueLabel", valueComp); |
| | | itemPanel.putClientProperty("titleLabel", labelComp); |
| | | |
| | | return itemPanel; |
| | |
| | | button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); |
| | | button.setMargin(new Insets(0, 0, 0, 0)); |
| | | button.setIconTextGap(0); |
| | | button.setPreferredSize(new Dimension(56, 56)); |
| | | button.setPreferredSize(new Dimension(28, 28)); |
| | | |
| | | String landNumber = dikuai.getLandNumber(); |
| | | boolean isVisible = boundaryPointVisibility.getOrDefault(landNumber, false); |
| | |
| | | button.setOpaque(true); |
| | | } |
| | | button.setToolTipText(active ? "隐藏边界点序号" : "显示边界点序号"); |
| | | |
| | | // 更新状态提示文字 |
| | | Object statusLabelObj = button.getClientProperty("statusLabel"); |
| | | if (statusLabelObj instanceof JLabel) { |
| | | JLabel statusLabel = (JLabel) statusLabelObj; |
| | | updateBoundaryStatusLabel(statusLabel, active); |
| | | } |
| | | } |
| | | |
| | | private void updateBoundaryStatusLabel(JLabel statusLabel, boolean active) { |
| | | if (statusLabel == null) { |
| | | return; |
| | | } |
| | | if (active) { |
| | | statusLabel.setText("已开启边界点显示"); |
| | | } else { |
| | | statusLabel.setText("已关闭边界点显示"); |
| | | } |
| | | } |
| | | |
| | | private void ensureBoundaryToggleIconsLoaded() { |
| | |
| | | contentPanel.add(scrollPane, BorderLayout.CENTER); |
| | | |
| | | JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); |
| | | JButton okButton = new JButton("确定"); |
| | | JButton cancelButton = new JButton("取消"); |
| | | JButton copyButton = new JButton("复制"); |
| | | |
| | | // 判断是否是往返点路径对话框 |
| | | boolean isReturnPathDialog = title != null && title.contains("往返点路径"); |
| | | JButton okButton; |
| | | JButton cancelButton; |
| | | JButton copyButton; |
| | | |
| | | if (isReturnPathDialog) { |
| | | // 往返点路径对话框:使用 buttonset 风格的确定按钮,图标按钮 |
| | | okButton = buttonset.createStyledButton("去绘制", new Color(70, 130, 220)); |
| | | |
| | | // 取消按钮使用 closepage.png 图标 |
| | | cancelButton = new JButton(); |
| | | ImageIcon closeIcon = loadIcon("image/closepage.png", 25, 25); |
| | | if (closeIcon != null) { |
| | | cancelButton.setIcon(closeIcon); |
| | | } else { |
| | | cancelButton.setText("关闭"); |
| | | } |
| | | cancelButton.setFont(new Font("微软雅黑", Font.PLAIN, 11)); |
| | | cancelButton.setForeground(PRIMARY_COLOR); |
| | | cancelButton.setBorder(BorderFactory.createEmptyBorder()); |
| | | cancelButton.setContentAreaFilled(false); |
| | | cancelButton.setFocusPainted(false); |
| | | cancelButton.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| | | cancelButton.addMouseListener(new MouseAdapter() { |
| | | public void mouseEntered(MouseEvent e) { cancelButton.setOpaque(true); cancelButton.setBackground(new Color(255, 240, 240)); } |
| | | public void mouseExited(MouseEvent e) { cancelButton.setOpaque(false); } |
| | | }); |
| | | |
| | | // 使用 Fuzhibutton 创建复制按钮 |
| | | copyButton = Fuzhibutton.createCopyButton( |
| | | (java.util.function.Supplier<String>) () -> { |
| | | String text = textArea.getText(); |
| | | if (text == null) { |
| | | text = ""; |
| | | } |
| | | String trimmed = text.trim(); |
| | | if (trimmed.isEmpty() || "-1".equals(trimmed)) { |
| | | return null; // 返回null会触发"未设置要复制的内容"提示 |
| | | } |
| | | return text; |
| | | }, |
| | | "复制" + title, |
| | | new Color(230, 250, 240) |
| | | ); |
| | | |
| | | } else { |
| | | // 其他对话框保持原有样式 |
| | | okButton = new JButton("确定"); |
| | | cancelButton = new JButton("取消"); |
| | | copyButton = new JButton("复制"); |
| | | |
| | | // 其他对话框的复制按钮逻辑 |
| | | copyButton.addActionListener(e -> { |
| | | String text = textArea.getText(); |
| | | if (text == null) { |
| | | text = ""; |
| | | } |
| | | String trimmed = text.trim(); |
| | | if (trimmed.isEmpty() || "-1".equals(trimmed)) { |
| | | JOptionPane.showMessageDialog(dialog, title + " 未设置", "提示", JOptionPane.INFORMATION_MESSAGE); |
| | | return; |
| | | } |
| | | try { |
| | | StringSelection selection = new StringSelection(text); |
| | | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); |
| | | clipboard.setContents(selection, selection); |
| | | JOptionPane.showMessageDialog(dialog, title + " 已复制到剪贴板", "提示", JOptionPane.INFORMATION_MESSAGE); |
| | | } catch (Exception ex) { |
| | | JOptionPane.showMessageDialog(dialog, "复制失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | final boolean[] confirmed = new boolean[] {false}; |
| | | final String[] resultHolder = new String[1]; |
| | |
| | | |
| | | cancelButton.addActionListener(e -> dialog.dispose()); |
| | | |
| | | copyButton.addActionListener(e -> { |
| | | String text = textArea.getText(); |
| | | if (text == null) { |
| | | text = ""; |
| | | } |
| | | String trimmed = text.trim(); |
| | | if (trimmed.isEmpty() || "-1".equals(trimmed)) { |
| | | JOptionPane.showMessageDialog(dialog, title + " 未设置", "提示", JOptionPane.INFORMATION_MESSAGE); |
| | | return; |
| | | } |
| | | try { |
| | | StringSelection selection = new StringSelection(text); |
| | | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); |
| | | clipboard.setContents(selection, selection); |
| | | JOptionPane.showMessageDialog(dialog, title + " 已复制到剪贴板", "提示", JOptionPane.INFORMATION_MESSAGE); |
| | | } catch (Exception ex) { |
| | | JOptionPane.showMessageDialog(dialog, "复制失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); |
| | | } |
| | | }); |
| | | |
| | | buttonPanel.add(okButton); |
| | | buttonPanel.add(cancelButton); |
| | | buttonPanel.add(copyButton); |
| | |
| | | dialog.setContentPane(contentPanel); |
| | | dialog.getRootPane().setDefaultButton(okButton); |
| | | dialog.pack(); |
| | | |
| | | // 如果是往返点路径对话框,设置宽度为首页的90%,高度保持不变 |
| | | if (isReturnPathDialog) { |
| | | Shouye shouye = Shouye.getInstance(); |
| | | if (shouye != null && shouye.getWidth() > 0) { |
| | | int homeWidth = shouye.getWidth(); |
| | | int dialogWidth = (int)(homeWidth * 0.9); |
| | | Dimension currentSize = dialog.getSize(); |
| | | dialog.setSize(dialogWidth, currentSize.height); |
| | | } |
| | | } |
| | | |
| | | dialog.setLocationRelativeTo(this); |
| | | dialog.setVisible(true); |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * 启动导航预览 |
| | | */ |
| | | private void startNavigationPreview(Dikuai dikuai) { |
| | | if (dikuai == null) { |
| | | return; |
| | | } |
| | | |
| | | Window owner = SwingUtilities.getWindowAncestor(this); |
| | | |
| | | // 获取地块管理对话框,准备在打开导航预览时关闭 |
| | | Window managementWindow = null; |
| | | if (owner instanceof JDialog) { |
| | | managementWindow = owner; |
| | | } |
| | | |
| | | // 关闭地块管理页面 |
| | | if (managementWindow != null) { |
| | | managementWindow.dispose(); |
| | | } |
| | | |
| | | // 启动导航预览 |
| | | daohangyulan.getInstance().startNavigationPreview(dikuai); |
| | | } |
| | | |
| | | /** |
| | | * 显示路径规划页面 |
| | | */ |
| | | private void showPathPlanningPage(Dikuai dikuai) { |
| | |
| | | dialog.setVisible(true); |
| | | } |
| | | |
| | | |
| | | private void generateMowingPath(Dikuai dikuai) { |
| | | if (dikuai == null) { |
| | | return; |
| | |
| | | return section; |
| | | } |
| | | |
| | | private String formatMowingPatternForDisplay(String patternValue) { |
| | | String sanitized = sanitizeValueOrNull(patternValue); |
| | | if (sanitized == null) { |
| | | return "未设置"; |
| | | } |
| | | String normalized = normalizeExistingMowingPattern(sanitized); |
| | | if ("parallel".equals(normalized)) { |
| | | return "平行模式 (parallel)"; |
| | | } |
| | | if ("spiral".equals(normalized)) { |
| | | return "螺旋模式 (spiral)"; |
| | | } |
| | | return sanitized; |
| | | } |
| | | |
| | | private String formatMowingPatternForDialog(String patternValue) { |
| | | String sanitized = sanitizeValueOrNull(patternValue); |
| | | if (sanitized == null) { |
| | |
| | | } |
| | | |
| | | private JButton createDeleteButton() { |
| | | JButton button = createStyledButton("删除", RED_COLOR, false); // 轮廓风格 |
| | | ImageIcon deleteIcon = loadIcon("image/delete.png", 16, 16); |
| | | JButton button = new JButton(); |
| | | ImageIcon deleteIcon = loadIcon("image/delete.png", 25, 25); |
| | | if (deleteIcon != null) { |
| | | button.setIcon(deleteIcon); |
| | | button.setIconTextGap(6); |
| | | } else { |
| | | button.setText("删除"); |
| | | } |
| | | button.setFont(new Font("微软雅黑", Font.PLAIN, 11)); |
| | | button.setForeground(RED_COLOR); |
| | | button.setBorder(BorderFactory.createEmptyBorder()); |
| | | button.setContentAreaFilled(false); |
| | | button.setFocusPainted(false); |
| | | button.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| | | button.addMouseListener(new MouseAdapter() { |
| | | public void mouseEntered(MouseEvent e) { button.setOpaque(true); button.setBackground(new Color(255, 240, 240)); } |
| | | public void mouseExited(MouseEvent e) { button.setOpaque(false); } |
| | | }); |
| | | return button; |
| | | } |
| | | |
| | | private JButton createViewButton(ActionListener listener) { |
| | | // 使用 Lookbutton 类创建查看按钮 |
| | | return Lookbutton.createViewButton(listener, new Color(230, 250, 240)); |
| | | } |
| | | |
| | | private JButton createPrimaryFooterButton(String text) { |
| | | return createStyledButton(text, PRIMARY_COLOR, true); // 实心风格 |
| | | } |
| | |
| | | button.setText(isCurrent ? "当前地块" : "设为当前"); |
| | | } |
| | | button.setToolTipText(isCurrent ? "取消当前作业地块" : "设为当前作业地块"); |
| | | |
| | | // 更新状态文字标签的显示/隐藏 |
| | | Object statusLabelObj = button.getClientProperty("statusLabel"); |
| | | if (statusLabelObj instanceof JLabel) { |
| | | JLabel statusLabel = (JLabel) statusLabelObj; |
| | | statusLabel.setVisible(isCurrent); |
| | | } |
| | | } |
| | | |
| | | private void ensureWorkIconsLoaded() { |
| | |
| | | }); |
| | | } |
| | | |
| | | private void editReturnPoint(Dikuai dikuai) { |
| | | FanhuiDialog fd = new FanhuiDialog(SwingUtilities.getWindowAncestor(this), dikuai); |
| | | fd.setVisible(true); |
| | | // 如果对话框已更新数据,刷新显示 |
| | | if (fd.isUpdated()) { |
| | | loadDikuaiData(); |
| | | private void editReturnPath(Dikuai dikuai) { |
| | | if (dikuai == null) { |
| | | return; |
| | | } |
| | | String edited = promptCoordinateEditing("查看 / 编辑往返点路径", dikuai.getReturnPathCoordinates()); |
| | | if (edited == null) { |
| | | return; |
| | | } |
| | | String normalized = normalizeCoordinateInput(edited); |
| | | if (!saveFieldAndRefresh(dikuai, "returnPathCoordinates", normalized)) { |
| | | JOptionPane.showMessageDialog(this, "无法更新往返点路径", "错误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | String message = "-1".equals(normalized) ? "往返点路径已清空" : "往返点路径已更新"; |
| | | JOptionPane.showMessageDialog(this, message, "成功", JOptionPane.INFORMATION_MESSAGE); |
| | | } |
| | | |
| | | /** |