|
|
@@ -0,0 +1,431 @@
|
|
|
+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);
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|