| | |
| | | package set; |
| | | |
| | | import baseStation.BaseStation; |
| | | |
| | | import zhuye.MapRenderer; |
| | | import zhuye.Shouye; |
| | | import zhuye.buttonset; |
| | | import zhuye.celiangmoshi; |
| | | import javax.swing.*; |
| | | import javax.swing.filechooser.FileNameExtensionFilter; |
| | | |
| | | import java.awt.*; |
| | | import java.awt.event.*; |
| | | import java.io.File; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 设置对话框 - 参考Shouye.java样式 |
| | | */ |
| | | public class Sets extends JDialog { |
| | | private static final long serialVersionUID = 1L; |
| | | private static final int ROW_HEIGHT = 40; |
| | | private static final int ROW_SPACING = 25; |
| | | private static final int ROW_HEIGHT = 50; // 增加行高以适应分割线 |
| | | private static final int ITEM_PADDING = 16; // 列表项内边距 |
| | | |
| | | // 主题颜色 |
| | | private final Color THEME_COLOR; |
| | | private final Color BACKGROUND_COLOR = new Color(250, 250, 250); |
| | | private final Color PANEL_BACKGROUND = new Color(255, 255, 255); |
| | | private final Color BORDER_COLOR = new Color(220, 220, 220); |
| | | private final Color BACKGROUND_COLOR = new Color(245, 247, 250); // 更柔和的浅灰色背景 |
| | | private final Color PANEL_BACKGROUND = new Color(255, 255, 255); // 白色面板 |
| | | private final Color BORDER_COLOR = new Color(233, 236, 239); // 浅边框色 |
| | | private final Color DIVIDER_COLOR = new Color(233, 236, 239); // 分割线颜色 |
| | | |
| | | // 设置项组件 |
| | | private JLabel mowerIdLabel; |
| | | private JLabel baseStationIdLabel; |
| | | private JLabel handheldMarkerLabel; |
| | | private JLabel simCardNumberLabel; |
| | | private JLabel baseStationSimLabel; |
| | | private JLabel firmwareVersionLabel; |
| | | private JLabel appVersionLabel; |
| | | private JLabel idleTrailDurationLabel; |
| | | private JLabel boundaryLengthVisibleLabel; |
| | | private JLabel measurementModeEnabledLabel; |
| | | |
| | | private JButton mowerIdEditBtn; |
| | | private JButton baseStationIdEditBtn; |
| | | private JButton handheldEditBtn; |
| | | private JButton checkUpdateBtn; |
| | | private JButton feedbackButton; |
| | | private JButton idleTrailEditBtn; |
| | | |
| | | // 数据模型 |
| | | private Setsys setData; |
| | | private final BaseStation baseStation; |
| | | |
| | | public Sets(JFrame parent, Color themeColor) { |
| | | super(parent, "系统设置", true); |
| | | this.THEME_COLOR = themeColor; |
| | | this.setData = new Setsys(); |
| | | this.baseStation = new BaseStation(); |
| | | initializeUI(); |
| | | loadData(); |
| | | } |
| | |
| | | super(parent, "系统设置", true); |
| | | this.THEME_COLOR = themeColor; |
| | | this.setData = new Setsys(); |
| | | this.baseStation = new BaseStation(); |
| | | initializeUI(); |
| | | loadData(); |
| | | } |
| | |
| | | setLocationRelativeTo(getParent()); |
| | | setResizable(false); |
| | | |
| | | // 创建主内容面板 |
| | | // 创建主内容面板(使用更柔和的浅灰色背景) |
| | | JPanel mainPanel = new JPanel(); |
| | | mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); |
| | | mainPanel.setLayout(new BorderLayout()); |
| | | mainPanel.setBackground(BACKGROUND_COLOR); |
| | | mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); |
| | | |
| | | // 创建设置项面板 |
| | | // 创建设置项面板(圆角白色面板) |
| | | JPanel settingsPanel = createSettingsPanel(); |
| | | |
| | | // 添加组件到主面板 |
| | | mainPanel.add(settingsPanel); |
| | | mainPanel.add(Box.createVerticalGlue()); |
| | | mainPanel.add(settingsPanel, BorderLayout.CENTER); |
| | | |
| | | add(mainPanel, BorderLayout.CENTER); |
| | | } |
| | | |
| | | private JPanel createSettingsPanel() { |
| | | // 创建圆角白色面板容器 |
| | | JPanel container = new JPanel() { |
| | | @Override |
| | | protected void paintComponent(Graphics g) { |
| | | Graphics2D g2d = (Graphics2D) g.create(); |
| | | g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
| | | g2d.setColor(PANEL_BACKGROUND); |
| | | // 绘制圆角矩形背景 |
| | | g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 16, 16); |
| | | g2d.dispose(); |
| | | } |
| | | }; |
| | | container.setLayout(new BorderLayout()); |
| | | container.setOpaque(false); |
| | | |
| | | // 内容面板 |
| | | JPanel panel = new JPanel(); |
| | | panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); |
| | | panel.setBackground(PANEL_BACKGROUND); |
| | | panel.setBorder(BorderFactory.createCompoundBorder( |
| | | BorderFactory.createLineBorder(BORDER_COLOR), |
| | | BorderFactory.createEmptyBorder(20, 20, 20, 20) |
| | | )); |
| | | panel.setOpaque(false); |
| | | panel.setBorder(BorderFactory.createEmptyBorder(8, 0, 8, 0)); |
| | | |
| | | // 割草机编号 |
| | | JPanel mowerIdPanel = createSettingItemPanel("割草机编号", |
| | | setData.getMowerId() != null ? setData.getMowerId() : "未设置", true); |
| | | mowerIdLabel = (JLabel) mowerIdPanel.getClientProperty("valueLabel"); |
| | | mowerIdEditBtn = (JButton) mowerIdPanel.getClientProperty("editButton"); |
| | | |
| | | JPanel baseStationIdPanel = createSettingItemPanel("差分基准站编号", |
| | | resolveBaseStationId(), true); |
| | | baseStationIdLabel = (JLabel) baseStationIdPanel.getClientProperty("valueLabel"); |
| | | baseStationIdEditBtn = (JButton) baseStationIdPanel.getClientProperty("editButton"); |
| | | |
| | | JPanel handheldPanel = createSettingItemPanel("便携打点器编号", |
| | | setData.getHandheldMarkerId() != null ? setData.getHandheldMarkerId() : "未设置", true); |
| | | handheldMarkerLabel = (JLabel) handheldPanel.getClientProperty("valueLabel"); |
| | | handheldEditBtn = (JButton) handheldPanel.getClientProperty("editButton"); |
| | | |
| | | // 物联卡号 |
| | | JPanel simCardPanel = createSettingItemPanel("物联卡号", |
| | | JPanel simCardPanel = createSettingItemPanel("割草机物联网卡号", |
| | | setData.getSimCardNumber() != null ? setData.getSimCardNumber() : "未设置", false); |
| | | simCardNumberLabel = (JLabel) simCardPanel.getClientProperty("valueLabel"); |
| | | |
| | | JPanel baseStationSimPanel = createSettingItemPanel("基准站物联网卡号", |
| | | resolveBaseStationSimCard(), false); |
| | | baseStationSimLabel = (JLabel) baseStationSimPanel.getClientProperty("valueLabel"); |
| | | |
| | | // 固件版本 |
| | | JPanel firmwarePanel = createSettingItemPanel("固件版本", |
| | | setData.getFirmwareVersion() != null ? setData.getFirmwareVersion() : "未设置", false); |
| | | firmwareVersionLabel = (JLabel) firmwarePanel.getClientProperty("valueLabel"); |
| | | |
| | | JPanel idleTrailPanel = createSettingItemPanel("轨迹拖尾时长", |
| | | formatIdleTrailDurationValue(), true); |
| | | idleTrailDurationLabel = (JLabel) idleTrailPanel.getClientProperty("valueLabel"); |
| | | idleTrailEditBtn = (JButton) idleTrailPanel.getClientProperty("editButton"); |
| | | |
| | | // 显示边界距离设置项 |
| | | JPanel boundaryLengthPanel = createBoundaryLengthPanel(); |
| | | boundaryLengthVisibleLabel = (JLabel) boundaryLengthPanel.getClientProperty("valueLabel"); |
| | | |
| | | // 开启测量模式设置项 |
| | | JPanel measurementModePanel = createMeasurementModePanel(); |
| | | measurementModeEnabledLabel = (JLabel) measurementModePanel.getClientProperty("valueLabel"); |
| | | |
| | | JPanel feedbackPanel = createFeedbackPanel(); |
| | | |
| | | // APP版本 |
| | | JPanel appVersionPanel = createAppVersionPanel(); |
| | | |
| | | addRowWithSpacing(panel, mowerIdPanel); |
| | | addRowWithSpacing(panel, simCardPanel); |
| | | addRowWithSpacing(panel, firmwarePanel); |
| | | panel.add(appVersionPanel); |
| | | // 添加设置项,使用分割线分隔 |
| | | addSettingItem(panel, mowerIdPanel, true); |
| | | addSettingItem(panel, baseStationIdPanel, true); |
| | | addSettingItem(panel, handheldPanel, true); |
| | | addSettingItem(panel, simCardPanel, true); |
| | | addSettingItem(panel, baseStationSimPanel, true); |
| | | addSettingItem(panel, firmwarePanel, true); |
| | | addSettingItem(panel, idleTrailPanel, true); |
| | | addSettingItem(panel, boundaryLengthPanel, true); |
| | | addSettingItem(panel, measurementModePanel, true); |
| | | addSettingItem(panel, feedbackPanel, true); |
| | | addSettingItem(panel, appVersionPanel, false); // 最后一项不加分割线 |
| | | |
| | | return panel; |
| | | container.add(panel, BorderLayout.CENTER); |
| | | return container; |
| | | } |
| | | |
| | | /** |
| | | * 添加设置项(带分割线) |
| | | */ |
| | | private void addSettingItem(JPanel container, JPanel itemPanel, boolean showDivider) { |
| | | container.add(itemPanel); |
| | | if (showDivider) { |
| | | // 添加分割线 |
| | | JSeparator divider = new JSeparator(); |
| | | divider.setForeground(DIVIDER_COLOR); |
| | | divider.setMaximumSize(new Dimension(Integer.MAX_VALUE, 1)); |
| | | divider.setAlignmentX(Component.LEFT_ALIGNMENT); |
| | | container.add(divider); |
| | | } |
| | | } |
| | | |
| | | private void addRowWithSpacing(JPanel container, JPanel row) { |
| | | container.add(row); |
| | | container.add(Box.createRigidArea(new Dimension(0, ROW_SPACING))); |
| | | private String formatIdleTrailDurationValue() { |
| | | int seconds = setData != null ? setData.getIdleTrailDurationSeconds() : Setsys.DEFAULT_IDLE_TRAIL_DURATION_SECONDS; |
| | | if (seconds <= 0) { |
| | | seconds = Setsys.DEFAULT_IDLE_TRAIL_DURATION_SECONDS; |
| | | } |
| | | return seconds + "秒"; |
| | | } |
| | | |
| | | |
| | | private JPanel createSettingItemPanel(String title, String value, boolean editable) { |
| | | JPanel panel = new JPanel(new GridBagLayout()); |
| | | panel.setBackground(PANEL_BACKGROUND); |
| | | panel.setOpaque(false); // 透明背景,由容器绘制 |
| | | panel.setAlignmentX(Component.LEFT_ALIGNMENT); |
| | | panel.setMaximumSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT)); |
| | | panel.setPreferredSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT)); |
| | | panel.setMinimumSize(new Dimension(0, ROW_HEIGHT)); |
| | | panel.setBorder(BorderFactory.createEmptyBorder(ITEM_PADDING, ITEM_PADDING, ITEM_PADDING, ITEM_PADDING)); |
| | | |
| | | GridBagConstraints gbc = new GridBagConstraints(); |
| | | |
| | |
| | | |
| | | private JPanel createAppVersionPanel() { |
| | | JPanel panel = new JPanel(new GridBagLayout()); |
| | | panel.setBackground(PANEL_BACKGROUND); |
| | | panel.setOpaque(false); // 透明背景 |
| | | panel.setAlignmentX(Component.LEFT_ALIGNMENT); |
| | | panel.setMaximumSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT)); |
| | | panel.setPreferredSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT)); |
| | | panel.setMinimumSize(new Dimension(0, ROW_HEIGHT)); |
| | | panel.setBorder(BorderFactory.createEmptyBorder(ITEM_PADDING, ITEM_PADDING, ITEM_PADDING, ITEM_PADDING)); |
| | | |
| | | GridBagConstraints gbc = new GridBagConstraints(); |
| | | |
| | |
| | | gbc.anchor = GridBagConstraints.EAST; |
| | | panel.add(appVersionLabel, gbc); |
| | | |
| | | checkUpdateBtn = new JButton("检查更新"); |
| | | checkUpdateBtn = buttonset.createStyledButton("检查更新", THEME_COLOR); |
| | | checkUpdateBtn.setFont(new Font("微软雅黑", Font.PLAIN, 12)); |
| | | checkUpdateBtn.setBackground(THEME_COLOR); |
| | | checkUpdateBtn.setForeground(Color.WHITE); |
| | | checkUpdateBtn.setBorder(BorderFactory.createEmptyBorder(0, 12, 0, 12)); |
| | | checkUpdateBtn.setPreferredSize(new Dimension(90, 25)); |
| | | checkUpdateBtn.setMinimumSize(new Dimension(90, 25)); |
| | | checkUpdateBtn.setMaximumSize(new Dimension(90, 25)); |
| | | checkUpdateBtn.setFocusPainted(false); |
| | | |
| | | checkUpdateBtn.addMouseListener(new MouseAdapter() { |
| | | public void mouseEntered(MouseEvent e) { |
| | | checkUpdateBtn.setBackground(new Color( |
| | | Math.max(THEME_COLOR.getRed() - 20, 0), |
| | | Math.max(THEME_COLOR.getGreen() - 20, 0), |
| | | Math.max(THEME_COLOR.getBlue() - 20, 0) |
| | | )); |
| | | } |
| | | public void mouseExited(MouseEvent e) { |
| | | checkUpdateBtn.setBackground(THEME_COLOR); |
| | | } |
| | | }); |
| | | |
| | | gbc = new GridBagConstraints(); |
| | | gbc.gridx = 2; |
| | |
| | | |
| | | return panel; |
| | | } |
| | | |
| | | private JPanel createFeedbackPanel() { |
| | | JPanel panel = new JPanel(new GridBagLayout()); |
| | | panel.setOpaque(false); // 透明背景 |
| | | panel.setAlignmentX(Component.LEFT_ALIGNMENT); |
| | | panel.setMaximumSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT)); |
| | | panel.setPreferredSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT)); |
| | | panel.setMinimumSize(new Dimension(0, ROW_HEIGHT)); |
| | | panel.setBorder(BorderFactory.createEmptyBorder(ITEM_PADDING, ITEM_PADDING, ITEM_PADDING, ITEM_PADDING)); |
| | | |
| | | GridBagConstraints gbc = new GridBagConstraints(); |
| | | |
| | | JLabel titleLabel = new JLabel("问题反馈咨询"); |
| | | titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); |
| | | titleLabel.setForeground(Color.BLACK); |
| | | titleLabel.setHorizontalAlignment(SwingConstants.RIGHT); |
| | | gbc.gridx = 0; |
| | | gbc.gridy = 0; |
| | | gbc.weightx = 0; |
| | | gbc.anchor = GridBagConstraints.EAST; |
| | | gbc.insets = new Insets(0, 0, 0, 12); |
| | | panel.add(titleLabel, gbc); |
| | | |
| | | feedbackButton = buttonset.createStyledButton("反馈", THEME_COLOR); |
| | | feedbackButton.setFont(new Font("微软雅黑", Font.PLAIN, 12)); |
| | | |
| | | gbc = new GridBagConstraints(); |
| | | gbc.gridx = 1; |
| | | gbc.gridy = 0; |
| | | gbc.weightx = 1.0; |
| | | gbc.anchor = GridBagConstraints.EAST; |
| | | panel.add(feedbackButton, gbc); |
| | | |
| | | return panel; |
| | | } |
| | | |
| | | /** |
| | | * 创建显示边界距离设置面板 |
| | | */ |
| | | private JPanel createBoundaryLengthPanel() { |
| | | JPanel panel = new JPanel(new GridBagLayout()); |
| | | panel.setOpaque(false); // 透明背景 |
| | | panel.setAlignmentX(Component.LEFT_ALIGNMENT); |
| | | panel.setMaximumSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT)); |
| | | panel.setPreferredSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT)); |
| | | panel.setMinimumSize(new Dimension(0, ROW_HEIGHT)); |
| | | panel.setBorder(BorderFactory.createEmptyBorder(ITEM_PADDING, ITEM_PADDING, ITEM_PADDING, ITEM_PADDING)); |
| | | |
| | | GridBagConstraints gbc = new GridBagConstraints(); |
| | | |
| | | JLabel titleLabel = new JLabel("显示边界距离"); |
| | | titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); |
| | | titleLabel.setForeground(Color.BLACK); |
| | | titleLabel.setHorizontalAlignment(SwingConstants.RIGHT); |
| | | gbc.gridx = 0; |
| | | gbc.gridy = 0; |
| | | gbc.weightx = 0; |
| | | gbc.anchor = GridBagConstraints.EAST; |
| | | gbc.insets = new Insets(0, 0, 0, 12); |
| | | panel.add(titleLabel, gbc); |
| | | |
| | | boundaryLengthVisibleLabel = new JLabel(setData.isBoundaryLengthVisible() ? "已开启" : "已关闭"); |
| | | boundaryLengthVisibleLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); |
| | | boundaryLengthVisibleLabel.setForeground(Color.DARK_GRAY); |
| | | gbc = new GridBagConstraints(); |
| | | gbc.gridx = 1; |
| | | gbc.gridy = 0; |
| | | gbc.weightx = 1.0; |
| | | gbc.anchor = GridBagConstraints.EAST; |
| | | panel.add(boundaryLengthVisibleLabel, gbc); |
| | | |
| | | panel.putClientProperty("valueLabel", boundaryLengthVisibleLabel); |
| | | |
| | | // 创建切换按钮(使用图标) |
| | | JButton toggleBtn = createBoundaryLengthToggleButton(); |
| | | gbc = new GridBagConstraints(); |
| | | gbc.gridx = 2; |
| | | gbc.gridy = 0; |
| | | gbc.weightx = 0; |
| | | gbc.anchor = GridBagConstraints.EAST; |
| | | panel.add(toggleBtn, gbc); |
| | | panel.putClientProperty("toggleButton", toggleBtn); |
| | | |
| | | return panel; |
| | | } |
| | | |
| | | /** |
| | | * 创建开启测量模式设置面板 |
| | | */ |
| | | private JPanel createMeasurementModePanel() { |
| | | JPanel panel = new JPanel(new GridBagLayout()); |
| | | panel.setOpaque(false); // 透明背景 |
| | | panel.setAlignmentX(Component.LEFT_ALIGNMENT); |
| | | panel.setMaximumSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT)); |
| | | panel.setPreferredSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT)); |
| | | panel.setMinimumSize(new Dimension(0, ROW_HEIGHT)); |
| | | panel.setBorder(BorderFactory.createEmptyBorder(ITEM_PADDING, ITEM_PADDING, ITEM_PADDING, ITEM_PADDING)); |
| | | |
| | | GridBagConstraints gbc = new GridBagConstraints(); |
| | | |
| | | JLabel titleLabel = new JLabel("开启测量模式"); |
| | | titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); |
| | | titleLabel.setForeground(Color.BLACK); |
| | | titleLabel.setHorizontalAlignment(SwingConstants.RIGHT); |
| | | gbc.gridx = 0; |
| | | gbc.gridy = 0; |
| | | gbc.weightx = 0; |
| | | gbc.anchor = GridBagConstraints.EAST; |
| | | gbc.insets = new Insets(0, 0, 0, 12); |
| | | panel.add(titleLabel, gbc); |
| | | |
| | | measurementModeEnabledLabel = new JLabel(setData.isMeasurementModeEnabled() ? "已开启" : "已关闭"); |
| | | measurementModeEnabledLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); |
| | | measurementModeEnabledLabel.setForeground(Color.DARK_GRAY); |
| | | gbc = new GridBagConstraints(); |
| | | gbc.gridx = 1; |
| | | gbc.gridy = 0; |
| | | gbc.weightx = 1.0; |
| | | gbc.anchor = GridBagConstraints.EAST; |
| | | panel.add(measurementModeEnabledLabel, gbc); |
| | | |
| | | panel.putClientProperty("valueLabel", measurementModeEnabledLabel); |
| | | |
| | | // 创建切换按钮(使用图标) |
| | | JButton toggleBtn = createMeasurementModeToggleButton(); |
| | | gbc = new GridBagConstraints(); |
| | | gbc.gridx = 2; |
| | | gbc.gridy = 0; |
| | | gbc.weightx = 0; |
| | | gbc.anchor = GridBagConstraints.EAST; |
| | | panel.add(toggleBtn, gbc); |
| | | panel.putClientProperty("toggleButton", toggleBtn); |
| | | |
| | | return panel; |
| | | } |
| | | |
| | | /** |
| | | * 创建测量模式切换按钮 |
| | | */ |
| | | private JButton createMeasurementModeToggleButton() { |
| | | JButton button = new JButton(); |
| | | button.setContentAreaFilled(false); |
| | | button.setBorder(null); |
| | | button.setFocusPainted(false); |
| | | button.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| | | button.setPreferredSize(new Dimension(32, 32)); |
| | | button.setMinimumSize(new Dimension(32, 32)); |
| | | button.setMaximumSize(new Dimension(32, 32)); |
| | | |
| | | updateMeasurementModeToggleButton(button); |
| | | |
| | | button.addActionListener(e -> toggleMeasurementMode(button)); |
| | | |
| | | return button; |
| | | } |
| | | |
| | | /** |
| | | * 更新测量模式切换按钮图标 |
| | | */ |
| | | private void updateMeasurementModeToggleButton(JButton button) { |
| | | boolean isEnabled = setData.isMeasurementModeEnabled(); |
| | | try { |
| | | String iconPath = isEnabled ? "image/open.png" : "image/close.png"; |
| | | ImageIcon icon = new ImageIcon(iconPath); |
| | | if (icon.getIconWidth() > 0) { |
| | | Image scaledImage = icon.getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH); |
| | | button.setIcon(new ImageIcon(scaledImage)); |
| | | button.setText(null); |
| | | } else { |
| | | button.setIcon(null); |
| | | button.setText(isEnabled ? "开" : "关"); |
| | | } |
| | | } catch (Exception e) { |
| | | button.setIcon(null); |
| | | button.setText(isEnabled ? "开" : "关"); |
| | | System.err.println("无法加载测量模式图标: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 切换测量模式状态 |
| | | */ |
| | | private void toggleMeasurementMode(JButton button) { |
| | | boolean newValue = !setData.isMeasurementModeEnabled(); |
| | | setData.setMeasurementModeEnabled(newValue); |
| | | |
| | | // 保存到配置文件 |
| | | setData.updateProperty("measurementModeEnabled", String.valueOf(newValue)); |
| | | |
| | | // 更新UI |
| | | if (measurementModeEnabledLabel != null) { |
| | | measurementModeEnabledLabel.setText(newValue ? "已开启" : "已关闭"); |
| | | } |
| | | updateMeasurementModeToggleButton(button); |
| | | |
| | | // 通知MapRenderer更新 |
| | | Shouye shouye = Shouye.getInstance(); |
| | | if (shouye != null) { |
| | | MapRenderer renderer = shouye.getMapRenderer(); |
| | | if (renderer != null) { |
| | | renderer.setMeasurementMode(newValue); |
| | | } |
| | | if (newValue) { |
| | | celiangmoshi.start(); |
| | | } else { |
| | | celiangmoshi.stop(); |
| | | } |
| | | // 刷新地图显示(通过MapRenderer触发重绘) |
| | | if (renderer != null) { |
| | | renderer.repaint(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 创建边界距离显示切换按钮 |
| | | */ |
| | | private JButton createBoundaryLengthToggleButton() { |
| | | JButton button = new JButton(); |
| | | button.setContentAreaFilled(false); |
| | | button.setBorder(null); |
| | | button.setFocusPainted(false); |
| | | button.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| | | button.setPreferredSize(new Dimension(32, 32)); |
| | | button.setMinimumSize(new Dimension(32, 32)); |
| | | button.setMaximumSize(new Dimension(32, 32)); |
| | | |
| | | updateBoundaryLengthToggleButton(button); |
| | | |
| | | button.addActionListener(e -> toggleBoundaryLengthVisible(button)); |
| | | |
| | | return button; |
| | | } |
| | | |
| | | /** |
| | | * 更新边界距离切换按钮图标 |
| | | */ |
| | | private void updateBoundaryLengthToggleButton(JButton button) { |
| | | boolean isVisible = setData.isBoundaryLengthVisible(); |
| | | try { |
| | | String iconPath = isVisible ? "image/open.png" : "image/close.png"; |
| | | ImageIcon icon = new ImageIcon(iconPath); |
| | | if (icon.getIconWidth() > 0) { |
| | | Image scaledImage = icon.getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH); |
| | | button.setIcon(new ImageIcon(scaledImage)); |
| | | button.setText(null); |
| | | } else { |
| | | button.setIcon(null); |
| | | button.setText(isVisible ? "开" : "关"); |
| | | } |
| | | } catch (Exception e) { |
| | | button.setIcon(null); |
| | | button.setText(isVisible ? "开" : "关"); |
| | | System.err.println("无法加载边界距离图标: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 切换边界距离显示状态 |
| | | */ |
| | | private void toggleBoundaryLengthVisible(JButton button) { |
| | | boolean newValue = !setData.isBoundaryLengthVisible(); |
| | | setData.setBoundaryLengthVisible(newValue); |
| | | |
| | | // 保存到配置文件 |
| | | setData.updateProperty("boundaryLengthVisible", String.valueOf(newValue)); |
| | | |
| | | // 更新UI |
| | | if (boundaryLengthVisibleLabel != null) { |
| | | boundaryLengthVisibleLabel.setText(newValue ? "已开启" : "已关闭"); |
| | | } |
| | | updateBoundaryLengthToggleButton(button); |
| | | |
| | | // 通知MapRenderer更新 |
| | | Shouye shouye = Shouye.getInstance(); |
| | | if (shouye != null) { |
| | | MapRenderer renderer = shouye.getMapRenderer(); |
| | | if (renderer != null) { |
| | | renderer.setBoundaryLengthVisible(newValue); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private JButton createEditButton() { |
| | | JButton button = new JButton(); |
| | |
| | | private void loadData() { |
| | | // 从Setsys类加载数据 |
| | | setData.initializeFromProperties(); |
| | | baseStation.load(); |
| | | updateDisplay(); |
| | | // 加载并应用上次保存的视图中心坐标 |
| | | loadViewCenterFromProperties(); |
| | | } |
| | | |
| | | /** |
| | | * 从配置文件加载视图中心坐标并应用到MapRenderer |
| | | */ |
| | | private void loadViewCenterFromProperties() { |
| | | Shouye shouye = Shouye.getInstance(); |
| | | if (shouye == null) { |
| | | return; |
| | | } |
| | | MapRenderer renderer = shouye.getMapRenderer(); |
| | | if (renderer == null) { |
| | | return; |
| | | } |
| | | |
| | | // 从配置文件读取视图中心坐标 |
| | | String viewCenterXValue = Setsys.getPropertyValue("viewCenterX"); |
| | | String viewCenterYValue = Setsys.getPropertyValue("viewCenterY"); |
| | | |
| | | double savedTranslateX = 0.0; |
| | | double savedTranslateY = 0.0; |
| | | |
| | | if (viewCenterXValue != null && !viewCenterXValue.trim().isEmpty()) { |
| | | try { |
| | | savedTranslateX = Double.parseDouble(viewCenterXValue.trim()); |
| | | } catch (NumberFormatException e) { |
| | | savedTranslateX = 0.0; |
| | | } |
| | | } |
| | | if (viewCenterYValue != null && !viewCenterYValue.trim().isEmpty()) { |
| | | try { |
| | | savedTranslateY = Double.parseDouble(viewCenterYValue.trim()); |
| | | } catch (NumberFormatException e) { |
| | | savedTranslateY = 0.0; |
| | | } |
| | | } |
| | | |
| | | // 应用视图中心坐标(保持当前缩放比例) |
| | | double currentScale = renderer.getScale(); |
| | | renderer.setViewTransform(currentScale, savedTranslateX, savedTranslateY); |
| | | } |
| | | |
| | | private void updateDisplay() { |
| | |
| | | if (mowerIdLabel != null) { |
| | | mowerIdLabel.setText(setData.getMowerId() != null ? setData.getMowerId() : "未设置"); |
| | | } |
| | | |
| | | if (baseStationIdLabel != null) { |
| | | baseStationIdLabel.setText(resolveBaseStationId()); |
| | | } |
| | | |
| | | if (handheldMarkerLabel != null) { |
| | | handheldMarkerLabel.setText(setData.getHandheldMarkerId() != null ? setData.getHandheldMarkerId() : "未设置"); |
| | | } |
| | | |
| | | // 更新物联卡号显示 |
| | | if (simCardNumberLabel != null) { |
| | | simCardNumberLabel.setText(setData.getSimCardNumber() != null ? |
| | | setData.getSimCardNumber() : "未设置"); |
| | | } |
| | | |
| | | if (baseStationSimLabel != null) { |
| | | baseStationSimLabel.setText(resolveBaseStationSimCard()); |
| | | } |
| | | |
| | | // 更新固件版本显示 |
| | | if (firmwareVersionLabel != null) { |
| | | firmwareVersionLabel.setText(setData.getFirmwareVersion() != null ? |
| | | setData.getFirmwareVersion() : "未设置"); |
| | | } |
| | | |
| | | if (idleTrailDurationLabel != null) { |
| | | idleTrailDurationLabel.setText(formatIdleTrailDurationValue()); |
| | | } |
| | | |
| | | // 更新显示边界距离状态 |
| | | if (boundaryLengthVisibleLabel != null) { |
| | | boundaryLengthVisibleLabel.setText(setData.isBoundaryLengthVisible() ? "已开启" : "已关闭"); |
| | | } |
| | | // 更新切换按钮图标 |
| | | JPanel boundaryLengthPanel = (JPanel) boundaryLengthVisibleLabel.getParent(); |
| | | if (boundaryLengthPanel != null) { |
| | | JButton toggleBtn = (JButton) boundaryLengthPanel.getClientProperty("toggleButton"); |
| | | if (toggleBtn != null) { |
| | | updateBoundaryLengthToggleButton(toggleBtn); |
| | | } |
| | | } |
| | | |
| | | // 更新测量模式状态 |
| | | if (measurementModeEnabledLabel != null) { |
| | | measurementModeEnabledLabel.setText(setData.isMeasurementModeEnabled() ? "已开启" : "已关闭"); |
| | | } |
| | | // 更新测量模式切换按钮图标 |
| | | JPanel measurementModePanel = (JPanel) measurementModeEnabledLabel.getParent(); |
| | | if (measurementModePanel != null) { |
| | | JButton toggleBtn = (JButton) measurementModePanel.getClientProperty("toggleButton"); |
| | | if (toggleBtn != null) { |
| | | updateMeasurementModeToggleButton(toggleBtn); |
| | | } |
| | | } |
| | | |
| | | // 更新APP版本显示 |
| | | if (appVersionLabel != null) { |
| | |
| | | setData.getAppVersion() : "未设置"); |
| | | } |
| | | } |
| | | |
| | | private String resolveBaseStationSimCard() { |
| | | if (baseStation == null) { |
| | | return "未设置"; |
| | | } |
| | | String value = baseStation.getIotSimCardNumber(); |
| | | if (value == null) { |
| | | return "未设置"; |
| | | } |
| | | String trimmed = value.trim(); |
| | | if (trimmed.isEmpty() || "-1".equals(trimmed)) { |
| | | return "未设置"; |
| | | } |
| | | return trimmed; |
| | | } |
| | | |
| | | private String resolveBaseStationId() { |
| | | if (baseStation == null) { |
| | | return "未设置"; |
| | | } |
| | | String value = baseStation.getDeviceId(); |
| | | if (value == null) { |
| | | return "未设置"; |
| | | } |
| | | String trimmed = value.trim(); |
| | | if (trimmed.isEmpty() || "-1".equals(trimmed)) { |
| | | return "未设置"; |
| | | } |
| | | return trimmed; |
| | | } |
| | | |
| | | private void setupEventHandlers() { |
| | | // 割草机编号编辑按钮事件 |
| | | if (mowerIdEditBtn != null) { |
| | | mowerIdEditBtn.addActionListener(e -> editMowerId()); |
| | | } |
| | | |
| | | if (baseStationIdEditBtn != null) { |
| | | baseStationIdEditBtn.addActionListener(e -> editBaseStationId()); |
| | | } |
| | | |
| | | // 检查更新按钮事件 |
| | | if (checkUpdateBtn != null) { |
| | | checkUpdateBtn.addActionListener(e -> checkForUpdates()); |
| | | } |
| | | |
| | | if (handheldEditBtn != null) { |
| | | handheldEditBtn.addActionListener(e -> editHandheldMarkerId()); |
| | | } |
| | | |
| | | if (feedbackButton != null) { |
| | | feedbackButton.addActionListener(e -> showFeedbackDialog()); |
| | | } |
| | | |
| | | if (idleTrailEditBtn != null) { |
| | | idleTrailEditBtn.addActionListener(e -> editIdleTrailDuration()); |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void editHandheldMarkerId() { |
| | | String currentValue = setData.getHandheldMarkerId() != null ? setData.getHandheldMarkerId() : ""; |
| | | String newValue = (String) JOptionPane.showInputDialog(this, |
| | | "请输入便携打点器编号:", |
| | | "修改便携打点器编号", |
| | | JOptionPane.QUESTION_MESSAGE, |
| | | null, |
| | | null, |
| | | currentValue); |
| | | |
| | | if (newValue == null) { |
| | | return; |
| | | } |
| | | |
| | | newValue = newValue.trim(); |
| | | if (!newValue.isEmpty()) { |
| | | if (setData.updateProperty("handheldMarkerId", newValue)) { |
| | | if (handheldMarkerLabel != null) { |
| | | handheldMarkerLabel.setText(newValue); |
| | | } |
| | | JOptionPane.showMessageDialog(this, "便携打点器编号更新成功", "成功", JOptionPane.INFORMATION_MESSAGE); |
| | | } else { |
| | | JOptionPane.showMessageDialog(this, "便携打点器编号更新失败", "错误", JOptionPane.ERROR_MESSAGE); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void editBaseStationId() { |
| | | String currentValue = "未设置".equals(resolveBaseStationId()) ? "" : resolveBaseStationId(); |
| | | String newValue = (String) JOptionPane.showInputDialog(this, |
| | | "请输入差分基准站编号:", |
| | | "修改差分基准站编号", |
| | | JOptionPane.QUESTION_MESSAGE, |
| | | null, |
| | | null, |
| | | currentValue); |
| | | |
| | | if (newValue == null) { |
| | | return; |
| | | } |
| | | |
| | | newValue = newValue.trim(); |
| | | if (newValue.isEmpty()) { |
| | | JOptionPane.showMessageDialog(this, "差分基准站编号不能为空", "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | baseStation.updateByDeviceId(newValue, |
| | | baseStation.getInstallationCoordinates(), |
| | | baseStation.getIotSimCardNumber(), |
| | | baseStation.getDeviceActivationTime(), |
| | | baseStation.getDataUpdateTime()); |
| | | baseStation.load(); |
| | | if (baseStationIdLabel != null) { |
| | | baseStationIdLabel.setText(resolveBaseStationId()); |
| | | } |
| | | JOptionPane.showMessageDialog(this, "差分基准站编号更新成功", "成功", JOptionPane.INFORMATION_MESSAGE); |
| | | } catch (IllegalArgumentException ex) { |
| | | JOptionPane.showMessageDialog(this, ex.getMessage(), "输入错误", JOptionPane.WARNING_MESSAGE); |
| | | } catch (Exception ex) { |
| | | ex.printStackTrace(); |
| | | JOptionPane.showMessageDialog(this, "差分基准站编号更新失败", "错误", JOptionPane.ERROR_MESSAGE); |
| | | } |
| | | } |
| | | |
| | | private void editIdleTrailDuration() { |
| | | int currentSeconds = setData != null ? setData.getIdleTrailDurationSeconds() : Setsys.DEFAULT_IDLE_TRAIL_DURATION_SECONDS; |
| | | if (currentSeconds <= 0) { |
| | | currentSeconds = Setsys.DEFAULT_IDLE_TRAIL_DURATION_SECONDS; |
| | | } |
| | | |
| | | String input = JOptionPane.showInputDialog(this, |
| | | "请输入轨迹拖尾时长(单位:秒)", |
| | | currentSeconds); |
| | | |
| | | if (input == null) { |
| | | return; |
| | | } |
| | | |
| | | String trimmed = input.trim(); |
| | | if (trimmed.isEmpty()) { |
| | | JOptionPane.showMessageDialog(this, "轨迹拖尾时长不能为空", "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | int parsedSeconds; |
| | | try { |
| | | parsedSeconds = Integer.parseInt(trimmed); |
| | | } catch (NumberFormatException ex) { |
| | | JOptionPane.showMessageDialog(this, "请输入有效的整数秒数", "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | if (parsedSeconds < 5 || parsedSeconds > 600) { |
| | | JOptionPane.showMessageDialog(this, "请输入5到600之间的秒数", "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | if (setData.updateProperty("idleTrailDurationSeconds", String.valueOf(parsedSeconds))) { |
| | | int appliedSeconds = setData.getIdleTrailDurationSeconds(); |
| | | if (idleTrailDurationLabel != null) { |
| | | idleTrailDurationLabel.setText(appliedSeconds + "秒"); |
| | | } |
| | | MapRenderer renderer = null; |
| | | Shouye shouye = Shouye.getInstance(); |
| | | if (shouye != null) { |
| | | renderer = shouye.getMapRenderer(); |
| | | } |
| | | if (renderer != null) { |
| | | renderer.setIdleTrailDurationSeconds(appliedSeconds); |
| | | } |
| | | JOptionPane.showMessageDialog(this, "轨迹拖尾时长已更新为 " + appliedSeconds + " 秒", "成功", JOptionPane.INFORMATION_MESSAGE); |
| | | } else { |
| | | JOptionPane.showMessageDialog(this, "轨迹拖尾时长更新失败", "错误", JOptionPane.ERROR_MESSAGE); |
| | | } |
| | | } |
| | | |
| | | private void showFeedbackDialog() { |
| | | JDialog dialog = new JDialog(this, "问题反馈咨询", true); |
| | | dialog.setLayout(new BorderLayout(0, 12)); |
| | | dialog.setResizable(false); |
| | | dialog.setPreferredSize(new Dimension(400, 800)); |
| | | dialog.setSize(new Dimension(400, 800)); |
| | | dialog.setMinimumSize(new Dimension(400, 800)); |
| | | |
| | | JPanel content = new JPanel(); |
| | | content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS)); |
| | | content.setBorder(BorderFactory.createEmptyBorder(20, 24, 20, 24)); |
| | | dialog.add(content, BorderLayout.CENTER); |
| | | |
| | | JLabel descriptionLabel = new JLabel("问题描述:"); |
| | | descriptionLabel.setFont(new Font("微软雅黑", Font.BOLD, 13)); |
| | | descriptionLabel.setAlignmentX(Component.LEFT_ALIGNMENT); |
| | | content.add(descriptionLabel); |
| | | |
| | | content.add(Box.createRigidArea(new Dimension(0, 8))); |
| | | |
| | | JTextArea descriptionArea = new JTextArea(5, 30); |
| | | descriptionArea.setLineWrap(true); |
| | | descriptionArea.setWrapStyleWord(true); |
| | | descriptionArea.setFont(new Font("微软雅黑", Font.PLAIN, 13)); |
| | | JScrollPane scrollPane = new JScrollPane(descriptionArea); |
| | | scrollPane.setAlignmentX(Component.LEFT_ALIGNMENT); |
| | | content.add(scrollPane); |
| | | |
| | | content.add(Box.createRigidArea(new Dimension(0, 16))); |
| | | |
| | | JPanel photoControls = new JPanel(new FlowLayout(FlowLayout.LEFT, 12, 0)); |
| | | photoControls.setAlignmentX(Component.LEFT_ALIGNMENT); |
| | | JLabel photoLabel = new JLabel("选择照片(最多6张):"); |
| | | photoLabel.setFont(new Font("微软雅黑", Font.BOLD, 13)); |
| | | JButton selectPhotosButton = buttonset.createStyledButton("选择照片", THEME_COLOR); |
| | | selectPhotosButton.setFont(new Font("微软雅黑", Font.PLAIN, 12)); |
| | | photoControls.add(photoLabel); |
| | | photoControls.add(selectPhotosButton); |
| | | content.add(photoControls); |
| | | |
| | | content.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | |
| | | JPanel photoGrid = new JPanel(new GridLayout(2, 3, 12, 12)); |
| | | photoGrid.setAlignmentX(Component.LEFT_ALIGNMENT); |
| | | photoGrid.setMaximumSize(new Dimension(Integer.MAX_VALUE, 220)); |
| | | List<JLabel> photoSlots = new ArrayList<>(); |
| | | for (int i = 0; i < 6; i++) { |
| | | JLabel slot = buildPhotoSlot(); |
| | | photoGrid.add(slot); |
| | | photoSlots.add(slot); |
| | | } |
| | | content.add(photoGrid); |
| | | |
| | | content.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | |
| | | JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); |
| | | JButton cancelButton = buttonset.createStyledButton("放弃", new Color(128, 128, 128)); |
| | | cancelButton.setFont(new Font("微软雅黑", Font.PLAIN, 12)); |
| | | JButton submitButton = buttonset.createStyledButton("提交", THEME_COLOR); |
| | | submitButton.setFont(new Font("微软雅黑", Font.PLAIN, 12)); |
| | | |
| | | buttonPanel.add(cancelButton); |
| | | buttonPanel.add(submitButton); |
| | | dialog.add(buttonPanel, BorderLayout.SOUTH); |
| | | |
| | | List<File> selectedPhotos = new ArrayList<>(); |
| | | |
| | | selectPhotosButton.addActionListener(e -> { |
| | | JFileChooser chooser = new JFileChooser(); |
| | | chooser.setMultiSelectionEnabled(true); |
| | | chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); |
| | | chooser.setFileFilter(new FileNameExtensionFilter("图片文件", "jpg", "jpeg", "png", "bmp", "gif", "webp")); |
| | | |
| | | if (chooser.showOpenDialog(dialog) == JFileChooser.APPROVE_OPTION) { |
| | | File[] files = chooser.getSelectedFiles(); |
| | | if (files != null) { |
| | | if (selectedPhotos.size() >= 6) { |
| | | JOptionPane.showMessageDialog(dialog, "已达到6张照片上限", "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | for (File file : files) { |
| | | if (selectedPhotos.size() >= 6) { |
| | | break; |
| | | } |
| | | if (file != null && file.exists() && file.isFile()) { |
| | | selectedPhotos.add(file); |
| | | } |
| | | } |
| | | updatePhotoPreview(photoSlots, selectedPhotos); |
| | | } |
| | | } |
| | | }); |
| | | |
| | | cancelButton.addActionListener(e -> dialog.dispose()); |
| | | |
| | | submitButton.addActionListener(e -> { |
| | | String description = descriptionArea.getText() != null ? descriptionArea.getText().trim() : ""; |
| | | if (description.isEmpty()) { |
| | | JOptionPane.showMessageDialog(dialog, "请填写问题描述", "提示", JOptionPane.WARNING_MESSAGE); |
| | | descriptionArea.requestFocusInWindow(); |
| | | return; |
| | | } |
| | | |
| | | // 提交逻辑占位 |
| | | JOptionPane.showMessageDialog(dialog, |
| | | "反馈已提交,感谢您的支持!\n描述字数:" + description.length() + ",照片数量:" + selectedPhotos.size(), |
| | | "提交成功", |
| | | JOptionPane.INFORMATION_MESSAGE); |
| | | dialog.dispose(); |
| | | }); |
| | | |
| | | dialog.pack(); |
| | | dialog.setLocationRelativeTo(this); |
| | | dialog.setVisible(true); |
| | | } |
| | | |
| | | private JLabel buildPhotoSlot() { |
| | | JLabel slot = new JLabel(); |
| | | slot.setPreferredSize(new Dimension(100, 100)); |
| | | slot.setMinimumSize(new Dimension(100, 100)); |
| | | slot.setOpaque(true); |
| | | slot.setBackground(new Color(245, 245, 245)); |
| | | slot.setHorizontalAlignment(SwingConstants.CENTER); |
| | | slot.setVerticalAlignment(SwingConstants.CENTER); |
| | | slot.setBorder(BorderFactory.createLineBorder(new Color(220, 220, 220), 1)); |
| | | slot.setText("+"); |
| | | slot.setFont(new Font("微软雅黑", Font.BOLD, 18)); |
| | | slot.setForeground(new Color(180, 180, 180)); |
| | | return slot; |
| | | } |
| | | |
| | | private void updatePhotoPreview(List<JLabel> slots, List<File> photos) { |
| | | for (int i = 0; i < slots.size(); i++) { |
| | | JLabel slot = slots.get(i); |
| | | if (i < photos.size()) { |
| | | File photo = photos.get(i); |
| | | ImageIcon icon = new ImageIcon(photo.getAbsolutePath()); |
| | | Image scaled = icon.getImage().getScaledInstance(100, 100, Image.SCALE_SMOOTH); |
| | | slot.setIcon(new ImageIcon(scaled)); |
| | | slot.setText(null); |
| | | } else { |
| | | slot.setIcon(null); |
| | | slot.setText("+"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void checkForUpdates() { |
| | | // 模拟检查更新过程 |