| | |
| | | |
| | | // 地块编号 |
| | | 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(); |
| | |
| | | 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(), "未设置"), |
| | | "修改", e -> editReturnPoint(dikuai))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | |
| | | // 地块边界坐标(带显示顶点按钮) |
| | | JPanel boundaryPanel = createBoundaryInfoItem(dikuai, |
| | |
| | | () -> 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("障碍物:", |
| | |
| | | "新增", |
| | | 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("路径坐标:", |
| | |
| | | () -> 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, "未设置"), |
| | |
| | | () -> 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, "未设置"), |
| | |
| | | () -> 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, "未设置"), |
| | |
| | | () -> 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; |
| | |
| | | "编辑", 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, "未记录"), |
| | |
| | | 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)); |
| | |
| | | |
| | | 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); |
| | |
| | | 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)); |
| | |
| | | |
| | | Window owner = SwingUtilities.getWindowAncestor(this); |
| | | |
| | | // 获取地块管理对话框,准备在打开路径规划页面时关闭 |
| | | Window managementWindow = null; |
| | | if (owner instanceof JDialog) { |
| | | managementWindow = owner; |
| | | } |
| | | |
| | | // 获取地块基本数据 |
| | | String baseStationValue = prepareCoordinateForEditor(dikuai.getBaseStationCoordinates()); |
| | | String boundaryValue = prepareCoordinateForEditor(dikuai.getBoundaryCoordinates()); |
| | |
| | | callback |
| | | ); |
| | | |
| | | // 关闭地块管理页面 |
| | | if (managementWindow != null) { |
| | | managementWindow.dispose(); |
| | | } |
| | | |
| | | dialog.setVisible(true); |
| | | } |
| | | |
| | |
| | | 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); |
| | | } else { |
| | | button.setBackground(PRIMARY_DARK); |
| | | } |
| | | } |
| | | public void mouseExited(MouseEvent e) { |
| | | if (color == RED_COLOR) { |
| | | button.setBackground(RED_COLOR); |
| | | } else { |
| | | button.setBackground(PRIMARY_COLOR); |
| | | } |
| | | } |
| | | }); |
| | | /** |
| | | * 创建现代风格按钮 (实心/轮廓) |
| | | */ |
| | | 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); |
| | | |
| | | return button; |
| | | 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 { |
| | | 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(); |
| | | } |
| | | }; |
| | | 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) { |
| | |
| | | boolean showBoundaryPoints = sanitizedLandNumber != null && boundaryPointVisibility.getOrDefault(sanitizedLandNumber, false); |
| | | renderer.setBoundaryPointsVisible(showBoundaryPoints); |
| | | renderer.setBoundaryPointSizeScale(showBoundaryPoints ? 0.5d : 1.0d); |
| | | // 退出预览后,不显示障碍物点(障碍物点只在预览时显示) |
| | | renderer.setObstaclePointsVisible(false); |
| | | } |
| | | shouye.refreshMowingIndicators(); |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 显示障碍物管理页面 |
| | | */ |
| | | 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); |