package com.mes.print; import com.mes.print.model.FlowCardData; import com.mes.print.template.FlowCardRenderer; import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; /** * 流转卡坐标调整工具 * 可视化调整二维码和文字位置(支持项目号、钢印码、客户码) */ public class CoordinateAdjustTool extends JFrame { // 当前坐标(初始值从 FlowCardRenderer 获取) private float qrX = 194.5f; private float qrY = 65.4f - 16.0f; // 已上移 private float qrW = 35.7f; private float qrH = 34.6f; private float projTextX = 291.5f; private float projTextY = 75.0f - 16.0f; // 项目号 private float steelTextX = 291.5f; private float steelTextY = 85.5f - 16.0f; // 钢印码 private float snTextX = 291.5f; private float snTextY = 96.4f - 16.0f; // 客户码 private JLabel previewLabel; private FlowCardData testData; // 拖拽相关 private boolean draggingQr = false; private Point dragStart = null; public CoordinateAdjustTool() { setTitle("流转卡坐标调整工具 - OP170"); setSize(1200, 900); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout(10, 10)); // 测试数据 testData = new FlowCardData(); testData.setSn("+KB60IS3031T729002101"); testData.setSteelSn("T09260729002"); // 左侧:预览区域 previewLabel = new JLabel(); previewLabel.setHorizontalAlignment(JLabel.CENTER); // 添加鼠标拖拽监听 previewLabel.addMouseListener(new java.awt.event.MouseAdapter() { @Override public void mousePressed(java.awt.event.MouseEvent e) { handleMousePressed(e); } @Override public void mouseReleased(java.awt.event.MouseEvent e) { draggingQr = false; dragStart = null; } }); previewLabel.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() { @Override public void mouseDragged(java.awt.event.MouseEvent e) { handleMouseDragged(e); } }); JScrollPane scrollPane = new JScrollPane(previewLabel); add(scrollPane, BorderLayout.CENTER); // 右侧:控制面板 JPanel controlPanel = new JPanel(); controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS)); controlPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // 输入框区域 controlPanel.add(createLabel("=== 测试数据 ===")); JTextField snField = new JTextField(testData.getSn(), 20); JTextField steelSnField = new JTextField(testData.getSteelSn(), 15); JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); inputPanel.add(new JLabel("客户码:")); inputPanel.add(snField); controlPanel.add(inputPanel); JPanel inputPanel2 = new JPanel(new FlowLayout(FlowLayout.LEFT)); inputPanel2.add(new JLabel("钢印码:")); inputPanel2.add(steelSnField); controlPanel.add(inputPanel2); // 项目号显示 JLabel projLabel = new JLabel("项目号: " + testData.getProdCode()); projLabel.setFont(new Font("Microsoft YaHei UI", Font.BOLD, 12)); projLabel.setForeground(new Color(0, 100, 0)); controlPanel.add(projLabel); JButton updateBtn = new JButton("更新预览"); updateBtn.addActionListener(e -> { testData.setSn(snField.getText()); testData.setSteelSn(steelSnField.getText()); projLabel.setText("项目号: " + testData.getProdCode()); updatePreview(); }); controlPanel.add(updateBtn); controlPanel.add(Box.createVerticalStrut(20)); // 二维码坐标控制 controlPanel.add(createLabel("=== 二维码位置 ===")); JTextField qrXField = new JTextField(String.format("%.1f", qrX), 8); JTextField qrYField = new JTextField(String.format("%.1f", qrY), 8); controlPanel.add(createCoordinatePanel("X", qrXField, qrX, 100f, 300f, v -> { qrX = v; qrXField.setText(String.format("%.1f", v)); updatePreview(); })); controlPanel.add(createCoordinatePanel("Y", qrYField, qrY, 30f, 150f, v -> { qrY = v; qrYField.setText(String.format("%.1f", v)); updatePreview(); })); controlPanel.add(Box.createVerticalStrut(20)); // 项目号坐标控制 controlPanel.add(createLabel("=== 项目号位置 ===")); JTextField projXField = new JTextField(String.format("%.1f", projTextX), 8); JTextField projYField = new JTextField(String.format("%.1f", projTextY), 8); controlPanel.add(createCoordinatePanel("X", projXField, projTextX, 200f, 400f, v -> { projTextX = v; projXField.setText(String.format("%.1f", v)); updatePreview(); })); controlPanel.add(createCoordinatePanel("Y", projYField, projTextY, 50f, 150f, v -> { projTextY = v; projYField.setText(String.format("%.1f", v)); updatePreview(); })); controlPanel.add(Box.createVerticalStrut(20)); // 钢印码坐标控制 controlPanel.add(createLabel("=== 钢印码位置 ===")); JTextField steelXField = new JTextField(String.format("%.1f", steelTextX), 8); JTextField steelYField = new JTextField(String.format("%.1f", steelTextY), 8); controlPanel.add(createCoordinatePanel("X", steelXField, steelTextX, 200f, 400f, v -> { steelTextX = v; steelXField.setText(String.format("%.1f", v)); updatePreview(); })); controlPanel.add(createCoordinatePanel("Y", steelYField, steelTextY, 50f, 150f, v -> { steelTextY = v; steelYField.setText(String.format("%.1f", v)); updatePreview(); })); controlPanel.add(Box.createVerticalStrut(20)); // 客户码坐标控制 controlPanel.add(createLabel("=== 客户码位置 ===")); JTextField snXField = new JTextField(String.format("%.1f", snTextX), 8); JTextField snYField = new JTextField(String.format("%.1f", snTextY), 8); controlPanel.add(createCoordinatePanel("X", snXField, snTextX, 200f, 400f, v -> { snTextX = v; snXField.setText(String.format("%.1f", v)); updatePreview(); })); controlPanel.add(createCoordinatePanel("Y", snYField, snTextY, 50f, 150f, v -> { snTextY = v; snYField.setText(String.format("%.1f", v)); updatePreview(); })); controlPanel.add(Box.createVerticalStrut(20)); // 按钮 JButton exportBtn = new JButton("生成代码"); exportBtn.setFont(new Font("Microsoft YaHei UI", Font.BOLD, 14)); exportBtn.addActionListener(e -> exportCode()); controlPanel.add(exportBtn); JButton previewPdfBtn = new JButton("预览 PDF"); previewPdfBtn.setFont(new Font("Microsoft YaHei UI", Font.BOLD, 14)); previewPdfBtn.addActionListener(e -> previewPdf()); controlPanel.add(previewPdfBtn); add(controlPanel, BorderLayout.EAST); // 初始预览 updatePreview(); } private void handleMousePressed(java.awt.event.MouseEvent e) { if (previewLabel.getIcon() == null) return; ImageIcon icon = (ImageIcon) previewLabel.getIcon(); int imgW = icon.getIconWidth(); int imgH = icon.getIconHeight(); int labelW = previewLabel.getWidth(); int labelH = previewLabel.getHeight(); int offsetX = (labelW - imgW) / 2; int offsetY = (labelH - imgH) / 2; int clickX = e.getX() - offsetX; int clickY = e.getY() - offsetY; if (clickX < 0 || clickX >= imgW || clickY < 0 || clickY >= imgH) return; double scale = 300.0 / 72.0; int qrImgX = (int) (qrX * scale); int qrImgY = (int) (qrY * scale); int qrImgW = (int) (qrW * scale); int qrImgH = (int) (qrH * scale); if (clickX >= qrImgX && clickX <= qrImgX + qrImgW && clickY >= qrImgY && clickY <= qrImgY + qrImgH) { draggingQr = true; dragStart = new Point(clickX, clickY); } } private void handleMouseDragged(java.awt.event.MouseEvent e) { if (!draggingQr || dragStart == null) return; if (previewLabel.getIcon() == null) return; ImageIcon icon = (ImageIcon) previewLabel.getIcon(); int imgW = icon.getIconWidth(); int imgH = icon.getIconHeight(); int labelW = previewLabel.getWidth(); int labelH = previewLabel.getHeight(); int offsetX = (labelW - imgW) / 2; int offsetY = (labelH - imgH) / 2; int currentX = e.getX() - offsetX; int currentY = e.getY() - offsetY; int deltaX = currentX - dragStart.x; int deltaY = currentY - dragStart.y; double scale = 300.0 / 72.0; float pdfDeltaX = (float) (deltaX / scale); float pdfDeltaY = (float) (deltaY / scale); qrX += pdfDeltaX; qrY += pdfDeltaY; dragStart = new Point(currentX, currentY); updatePreview(); } private JLabel createLabel(String text) { JLabel label = new JLabel(text); label.setFont(new Font("Microsoft YaHei UI", Font.BOLD, 14)); return label; } private JPanel createCoordinatePanel(String label, JTextField field, float initValue, float min, float max, ValueChangeListener listener) { JPanel panel = new JPanel(new BorderLayout(5, 5)); JLabel nameLabel = new JLabel(label); nameLabel.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 12)); JLabel valueLabel = new JLabel(String.format("%.1f", initValue)); valueLabel.setFont(new Font("Consolas", Font.PLAIN, 12)); valueLabel.setPreferredSize(new Dimension(50, 20)); JSlider slider = new JSlider(JSlider.HORIZONTAL, (int)(min * 10), (int)(max * 10), (int)(initValue * 10)); slider.addChangeListener(e -> { float value = slider.getValue() / 10.0f; valueLabel.setText(String.format("%.1f", value)); listener.onValueChange(value); }); field.addActionListener(e -> { try { float value = Float.parseFloat(field.getText()); slider.setValue((int)(value * 10)); listener.onValueChange(value); } catch (NumberFormatException ex) { field.setText(String.format("%.1f", initValue)); } }); JPanel topPanel = new JPanel(new BorderLayout()); topPanel.add(nameLabel, BorderLayout.WEST); topPanel.add(field, BorderLayout.CENTER); topPanel.add(valueLabel, BorderLayout.EAST); panel.add(topPanel, BorderLayout.NORTH); panel.add(slider, BorderLayout.CENTER); return panel; } private void updatePreview() { try { BufferedImage preview = createPreviewImage(); ImageIcon icon = new ImageIcon(preview); previewLabel.setIcon(icon); } catch (Exception e) { e.printStackTrace(); JOptionPane.showMessageDialog(this, "预览失败: " + e.getMessage()); } } private BufferedImage createPreviewImage() throws Exception { BufferedImage templateBg = FlowCardRenderer.getTemplateImage(); BufferedImage img = new BufferedImage(templateBg.getWidth(), templateBg.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D g2 = img.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2.drawImage(templateBg, 0, 0, null); double scale = 300.0 / 72.0; // 绘制二维码 try { BufferedImage qrCode = FlowCardRenderer.generateQrCode(testData.getSn(), 600); int imgQrX = (int)(qrX * scale); int imgQrY = (int)(qrY * scale); int imgQrW = (int)(qrW * scale); int imgQrH = (int)(qrH * scale); g2.drawImage(qrCode, imgQrX, imgQrY, imgQrW, imgQrH, null); } catch (Exception e) { e.printStackTrace(); } g2.setColor(Color.BLACK); g2.setFont(new Font("SimSun", Font.BOLD, (int)(8 * scale))); // 绘制项目号(蓝色) String projCode = testData.getProdCode(); if (projCode != null && !projCode.isEmpty()) { int imgProjX = (int)(projTextX * scale); int imgProjY = (int)(projTextY * scale); g2.setColor(new Color(0, 0, 200)); g2.drawString(projCode, imgProjX, imgProjY); // 蓝色十字标记 g2.setStroke(new BasicStroke(2)); g2.drawLine(imgProjX - 20, imgProjY, imgProjX + 20, imgProjY); g2.drawLine(imgProjX, imgProjY - 20, imgProjX, imgProjY + 20); } // 绘制钢印码(红色) int imgSteelX = (int)(steelTextX * scale); int imgSteelY = (int)(steelTextY * scale); if (testData.getSteelSn() != null) { g2.setColor(Color.BLACK); g2.drawString(testData.getSteelSn(), imgSteelX, imgSteelY); } g2.setColor(Color.RED); g2.setStroke(new BasicStroke(2)); g2.drawLine(imgSteelX - 20, imgSteelY, imgSteelX + 20, imgSteelY); g2.drawLine(imgSteelX, imgSteelY - 20, imgSteelX, imgSteelY + 20); // 绘制客户码(绿色) int imgSnX = (int)(snTextX * scale); int imgSnY = (int)(snTextY * scale); if (testData.getSn() != null) { g2.setColor(Color.BLACK); g2.drawString(testData.getSn(), imgSnX, imgSnY); } g2.setColor(Color.GREEN.darker()); g2.setStroke(new BasicStroke(2)); g2.drawLine(imgSnX - 20, imgSnY, imgSnX + 20, imgSnY); g2.drawLine(imgSnX, imgSnY - 20, imgSnX, imgSnY + 20); // 显示当前坐标 g2.setColor(Color.BLACK); g2.setFont(new Font("Consolas", Font.BOLD, 24)); g2.drawString(String.format("QR: (%.1f, %.1f)", qrX, qrY), 20, 50); g2.drawString(String.format("项目: (%.1f, %.1f)", projTextX, projTextY), 20, 90); g2.drawString(String.format("钢印: (%.1f, %.1f)", steelTextX, steelTextY), 20, 130); g2.drawString(String.format("客户: (%.1f, %.1f)", snTextX, snTextY), 20, 170); g2.dispose(); return img; } private void exportCode() { String code = String.format( "// 坐标配置(复制到 FlowCardRenderer.java 中)\n" + "private static final float QR_X = %.1ff;\n" + "private static final float QR_Y = %.1ff;\n" + "private static final float QR_W = %.1ff;\n" + "private static final float QR_H = %.1ff;\n\n" + "private static final float PROJ_TEXT_X = %.1ff;\n" + "private static final float PROJ_TEXT_Y = %.1ff;\n" + "private static final float STEEL_TEXT_X = %.1ff;\n" + "private static final float STEEL_TEXT_Y = %.1ff;\n" + "private static final float SN_TEXT_X = %.1ff;\n" + "private static final float SN_TEXT_Y = %.1ff;\n", qrX, qrY, qrW, qrH, projTextX, projTextY, steelTextX, steelTextY, snTextX, snTextY ); JTextArea textArea = new JTextArea(code, 15, 50); textArea.setFont(new Font("Consolas", Font.PLAIN, 12)); textArea.setEditable(false); JOptionPane.showMessageDialog(this, new JScrollPane(textArea), "生成的代码", JOptionPane.INFORMATION_MESSAGE); } private void previewPdf() { try { FlowCardRenderer.preview(testData); JOptionPane.showMessageDialog(this, "PDF 预览已打开"); } catch (Exception e) { JOptionPane.showMessageDialog(this, "预览失败: " + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); } } interface ValueChangeListener { void onValueChange(float value); } public static void main(String[] args) { SwingUtilities.invokeLater(() -> { CoordinateAdjustTool tool = new CoordinateAdjustTool(); tool.setVisible(true); }); } }