Ver Fonte

新增客户端版本更新功能

hou há 1 dia atrás
pai
commit
4612fc897e

+ 116 - 0
src/com/mes/ui/DataUtil.java

@@ -1,19 +1,25 @@
 package com.mes.ui;
 
+import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
 import com.mes.netty.NettyClient;
 import com.mes.util.DateLocalUtils;
 import com.mes.util.JdbcUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.*;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 
 public class DataUtil {
+    public static final Logger log = LoggerFactory.getLogger(DataUtil.class);
     public static String lastUploadPressRivetMessage = "";
 
     public static Boolean synrTcp(NettyClient nettyClient,String mes_gw){
@@ -303,6 +309,7 @@ public class DataUtil {
     }
 
     public static String doPost(String httpUrl, String param) {
+        httpUrl = appendSid(httpUrl);
         HttpURLConnection connection = null;
         InputStream is = null;
         OutputStream os = null;
@@ -365,6 +372,7 @@ public class DataUtil {
     }
 
     public static String doPostJson(String httpUrl, String json) {
+        httpUrl = appendSid(httpUrl);
         HttpURLConnection connection = null;
         InputStream is = null;
         OutputStream os = null;
@@ -451,4 +459,112 @@ public class DataUtil {
             return null;
         }
     }
+
+    /**
+     * 查询单部件加工记录
+     * @param oprno 工位号(空则查所有工位)
+     * @param sn 精追码(可选,用于搜索)
+     * @param pageNo 页码
+     * @param pageSize 每页条数
+     * @return 工作记录响应
+     */
+    public static WorkRecordResp getWorkRecordList(String oprno, String sn, int pageNo, int pageSize) {
+        WorkRecordResp resp = new WorkRecordResp();
+        try {
+            String mes_server_ip = MesClient.mes_server_ip;
+            String lineSn = MesClient.mes_line_sn == null ? "" : MesClient.mes_line_sn.trim();
+
+            String url = "http://" + mes_server_ip + ":8980/js/a/mes/mesProductDbjRecord/listData";
+            StringBuilder params = new StringBuilder();
+            params.append("__ajax=json");
+            params.append("&lineSn=").append(lineSn);
+            params.append("&pageNo=").append(pageNo);
+            params.append("&pageSize=").append(pageSize);
+            System.out.println("oprno="+oprno);
+            if (oprno != null && !oprno.isEmpty()) {
+                String queryOprno = oprno.trim();
+                if (queryOprno.length() == 5) {
+                    queryOprno = queryOprno + "A";
+                }
+                params.append("&oprno=").append(queryOprno);
+            }
+            if (sn != null && !sn.isEmpty()) {
+                params.append("&sn=").append(sn);
+            }
+
+            log.info("查询单部件加工记录: url=" + url + ", params=" + params.toString());
+            String result = doPost(url, params.toString());
+            log.info("查询单部件加工记录结果: result=" + result);
+
+            if (result == null || result.trim().isEmpty()) {
+                resp.setResult(false);
+                resp.setMessage("请求返回空结果");
+                return resp;
+            }
+
+            JSONObject jsonObj = JSONObject.parseObject(result);
+            if (jsonObj == null) {
+                resp.setResult(false);
+                resp.setMessage("解析响应失败");
+                return resp;
+            }
+            if (jsonObj.containsKey("result") && jsonObj.getJSONArray("list") == null) {
+                resp.setResult(false);
+                String message = jsonObj.getString("message");
+                resp.setMessage(message == null || message.trim().isEmpty()
+                        ? "查询失败,请确认账号有单部件加工记录查看权限"
+                        : message);
+                return resp;
+            }
+
+            resp.setResult(true);
+            resp.setPageNo(jsonObj.getIntValue("pageNo"));
+            resp.setPageSize(jsonObj.getIntValue("pageSize"));
+            resp.setCount(jsonObj.getLongValue("count"));
+
+            List<WorkRecordData> list = new ArrayList<>();
+            JSONArray listArray = jsonObj.getJSONArray("list");
+            if (listArray != null) {
+                for (int i = 0; i < listArray.size(); i++) {
+                    JSONObject item = listArray.getJSONObject(i);
+                    WorkRecordData data = new WorkRecordData();
+                    data.setId(item.getString("id"));
+                    data.setSn(item.getString("sn"));
+                    data.setDbjSn(item.getString("dbjSn"));
+                    data.setOprno(item.getString("oprno"));
+                    String userCode = item.getString("userCode");
+                    data.setUpdateBy(userCode != null && !userCode.isEmpty() ? userCode : item.getString("updateBy"));
+                    String createDate = item.getString("createDate");
+                    data.setUpdateDate(createDate != null && !createDate.isEmpty() ? createDate : item.getString("updateDate"));
+                    String resultVal = item.getString("result");
+                    data.setContent(resultVal != null && !resultVal.isEmpty() ? resultVal : item.getString("content"));
+                    list.add(data);
+                }
+            }
+            resp.setList(list);
+
+        } catch (Exception e) {
+            log.error("查询工作记录异常: " + e.getMessage());
+            e.printStackTrace();
+            resp.setResult(false);
+            resp.setMessage("查询异常: " + e.getMessage());
+        }
+        return resp;
+    }
+
+    private static String appendSid(String url) {
+        if (url == null || url.contains("__sid=") || url.contains("/login")) {
+            return url;
+        }
+        try {
+            String sid = MesClient.sessionid;
+            if (sid == null || sid.length() == 0) {
+                return url;
+            }
+            return url + (url.contains("?") ? "&" : "?") + "__sid=" + sid;
+        } catch (Exception e) {
+            return url;
+        }
+    }
+
 }

+ 5 - 6
src/com/mes/ui/LoginFarme.java

@@ -3,6 +3,7 @@ package com.mes.ui;
 import com.alibaba.fastjson2.JSONObject;
 import com.mes.component.MesRadio;
 import com.mes.component.MesWebView;
+import com.mes.util.ClientUpgrade;
 import com.mes.util.Base64Utils;
 import com.mes.util.HttpUtils;
 
@@ -91,6 +92,7 @@ public class LoginFarme extends JFrame {
 
     //登录
     public static void login() {
+        ClientUpgrade.checkIfConfigured(MesClient.welcomeWin);
         String user_str = userNameTxt.getText().toString();
         String password_str = userPasswordTxt.getText().toString();
         if(user_str.equalsIgnoreCase("")||password_str.equalsIgnoreCase("")) {
@@ -120,6 +122,7 @@ public class LoginFarme extends JFrame {
     }
     //扫码登录
     public static void scanLogin() {
+        ClientUpgrade.checkIfConfigured(MesClient.welcomeWin);
         //userNameTxt.setText("");
         //userPasswordTxt.setText("");
         String scanContent = JOptionPane.showInputDialog(null, "请扫码工牌二维码");
@@ -183,12 +186,8 @@ public class LoginFarme extends JFrame {
                     MesClient.welcomeWin.setVisible(false);
                     MesClient.mesClientFrame.setVisible(true);
 
-                    if(MesClient.jfxPanel == null){
-                        String url = "http://"+ MesClient.mes_server_ip+":8980/js/a/mes/mesProductRecord/work?oprno="+MesClient.mes_gw+"&lineSn="+MesClient.mes_line_sn;
-                        MesClient.jfxPanel = new MesWebView(url);
-                        MesClient.jfxPanel.setSize(990, 550);
-                        MesClient.indexPanelB.add(MesClient.jfxPanel);
-                    }
+                    // 工作记录:登录后按当前工位拉取单部件加工记录
+                    MesClient.refreshWorkRecordPanel();
 
                     if(MesClient.jfxPanel2 == null){
                         String url = "http://"+ MesClient.mes_server_ip+":8980/js/a/mes/mesProcessCheckRecord/ulist?ucode="+user_id+"&oprno="+MesClient.mes_gw+"&lineSn="+MesClient.mes_line_sn;

+ 40 - 9
src/com/mes/ui/MesClient.java

@@ -5,6 +5,8 @@ import com.github.xingshuangs.iot.protocol.modbus.service.ModbusTcp;
 import com.mes.component.MesRadio;
 import com.mes.component.MesWebView;
 import com.mes.netty.NettyClient;
+import com.mes.util.ClientVersionUtils;
+import com.mes.util.ClientUpgrade;
 import com.mes.util.DateLocalUtils;
 import com.mes.util.JdbcUtils;
 import org.slf4j.Logger;
@@ -40,7 +42,8 @@ public class MesClient extends JFrame {
     public static int mes_tcp_port = 3000; // TCP鏈嶅姟绔彛
     public static int mes_heart_beat_cycle = 10; // 蹇冭烦鍛ㄦ湡
     public static int mes_heart_icon_cycle = 1;
-    public static String mes_line_sn = ""; // 浜х嚎缂栧彿
+    public static String mes_line_sn = "";
+    public static String client_version = "0.0.0"; // 打包发布版本,需和后台 JAR 管理版本号对齐 // 浜х嚎缂栧彿
     public static String mes_sn_prefix = ""; // 工件码开头格式,多个用逗号分隔
 
     //TCP杩炴帴
@@ -57,7 +60,7 @@ public class MesClient extends JFrame {
     public static MesClient mesClientFrame;
     public static JTabbedPane tabbedPane;
     public static JScrollPane indexScrollPaneA;
-    public static JScrollPane searchScrollPane;
+    public static JScrollPane searchScrollPane; // 已弃用,改为使用 WorkRecordPanel
     public static JScrollPane searchScrollPaneDj;
     public static JPanel indexPanelA;
 
@@ -80,6 +83,7 @@ public class MesClient extends JFrame {
 
     public static JFrame welcomeWin;
     public static JPanel indexPanelB;
+    public static WorkRecordPanel workRecordPanel;
     public static MesWebView jfxPanel = null;
     public static JPanel indexPanelC;
     public static MesWebView jfxPanel2 = null;
@@ -123,6 +127,9 @@ public class MesClient extends JFrame {
     public static Map<Integer, Map<String, String>> pressRivetMap = new LinkedHashMap<Integer, Map<String, String>>();
 
     public static void main(String[] args) {
+        if (ClientUpgrade.handleInstallArgs(args)) {
+            return;
+        }
         if (LockUtil.getInstance().isAppActive() == true){
 //            JOptionPane.showMessageDialog(null, "宸叉湁涓€涓▼搴忓湪杩愯,绋嬪簭閫€鍑?);
             return;
@@ -133,6 +140,11 @@ public class MesClient extends JFrame {
                 try{
                     //璇绘枃浠堕厤缃?
                     readProperty();
+                    if (ClientUpgrade.recoverIncompleteInstall()) {
+                        return;
+                    }
+                    ClientUpgrade.cleanupInstallLeftovers();
+                    ClientUpgrade.checkIfConfigured(null);
 
                     // 鏄剧ず鐣岄潰
                     mesClientFrame = new MesClient();
@@ -184,12 +196,21 @@ public class MesClient extends JFrame {
 
 
     //璇婚厤缃枃浠?
+
+    private static String parseClientVersion(String value) {
+        if (value == null || value.trim().isEmpty()) {
+            return "0.0.0";
+        }
+        return ClientVersionUtils.normalize(value.trim());
+    }
+
     private static void readProperty() throws IOException{
         String enconding = "UTF-8";
         InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties");
         Properties pro = new Properties();
         BufferedReader br = new BufferedReader(new InputStreamReader(is, enconding));
         pro.load(br);
+        client_version = parseClientVersion(pro.getProperty("mes.client.version"));
         mes_gw =  pro.getProperty("mes.gw");
         mes_server_ip = pro.getProperty("mes.server_ip");
         mes_tcp_port = Integer.parseInt(pro.getProperty("mes.tcp_port"));
@@ -889,11 +910,9 @@ public class MesClient extends JFrame {
         tabbedPane.addTab("开班点检", new ImageIcon(MesClient.class.getResource("/bg/menu_data_preprocess.png")), searchScrollPaneDj, null);
 
 
-        indexPanelB = new JPanel();
-        searchScrollPane = new JScrollPane(indexPanelB);
-        indexPanelB.setLayout(null);
-
-        tabbedPane.addTab("工作记录", new ImageIcon(MesClient.class.getResource("/bg/menu_data_preprocess.png")), searchScrollPane, null);
+        // 工作记录:登录后由客户端表格拉取单部件加工记录
+        indexPanelB = new JPanel(new BorderLayout());
+        tabbedPane.addTab("工作记录", new ImageIcon(MesClient.class.getResource("/bg/menu_data_preprocess.png")), indexPanelB, null);
 
 
         tabbedPane.addChangeListener(new ChangeListener() {
@@ -903,13 +922,25 @@ public class MesClient extends JFrame {
                 int selectedIndex = tabbedPane.getSelectedIndex();
                 System.out.println("selectedIndex:"+selectedIndex);
 
-                if(selectedIndex == 1){
-
+                if (selectedIndex >= 0 && "工作记录".equals(tabbedPane.getTitleAt(selectedIndex)) && workRecordPanel != null) {
+                    workRecordPanel.refreshData();
                 }
             }
         });
     }
 
+    public static void refreshWorkRecordPanel() {
+        if (indexPanelB == null) {
+            return;
+        }
+        workRecordPanel = new WorkRecordPanel(mes_gw, mes_line_sn);
+        indexPanelB.removeAll();
+        indexPanelB.add(workRecordPanel, BorderLayout.CENTER);
+        indexPanelB.revalidate();
+        indexPanelB.repaint();
+        workRecordPanel.refreshData();
+    }
+
     public static void setMenuStatus(String msg,int error){
         if(error == 0){
             MesClient.status_menu.setForeground(Color.GREEN);

+ 70 - 0
src/com/mes/ui/WorkRecordData.java

@@ -0,0 +1,70 @@
+package com.mes.ui;
+
+/**
+ * 工作记录数据实体类
+ */
+public class WorkRecordData {
+    private String id;
+    private String sn;          // 精追码
+    private String dbjSn;       // 主工件码
+    private String oprno;       // 工位号
+    private String updateBy;    // 作业人员
+    private String updateDate;  // 日期
+    private String content;     // 工位结果
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getSn() {
+        return sn;
+    }
+
+    public void setSn(String sn) {
+        this.sn = sn;
+    }
+
+    public String getDbjSn() {
+        return dbjSn;
+    }
+
+    public void setDbjSn(String dbjSn) {
+        this.dbjSn = dbjSn;
+    }
+
+    public String getOprno() {
+        return oprno;
+    }
+
+    public void setOprno(String oprno) {
+        this.oprno = oprno;
+    }
+
+    public String getUpdateBy() {
+        return updateBy;
+    }
+
+    public void setUpdateBy(String updateBy) {
+        this.updateBy = updateBy;
+    }
+
+    public String getUpdateDate() {
+        return updateDate;
+    }
+
+    public void setUpdateDate(String updateDate) {
+        this.updateDate = updateDate;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+}

+ 411 - 0
src/com/mes/ui/WorkRecordPanel.java

@@ -0,0 +1,411 @@
+package com.mes.ui;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.swing.*;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.DefaultTableModel;
+import java.awt.*;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.util.List;
+
+/**
+ * 工作记录面板组件
+ * 支持分页查询和展示工作记录数据
+ */
+public class WorkRecordPanel extends JPanel {
+
+    private static final Logger log = LoggerFactory.getLogger(WorkRecordPanel.class);
+
+    // 表格列名
+    private static final String[] COLUMN_NAMES = {"精追码", "工位号", "工位结果", "作业人员", "日期"};
+
+    // 分页配置
+    private static final int PAGE_SIZE = 20;
+
+    // 查询条件
+    private String oprno;  // 工位号(null或空则查全部)
+    private String lineSn; // 产线编号
+    private boolean isAllStation; // 是否是"所有工位"模式
+
+    // 分页状态
+    private int pageNo = 1;
+    private long totalCount = 0;
+    private int totalPages = 0;
+
+    // UI组件
+    private JTable table;
+    private DefaultTableModel tableModel;
+    private JLabel pageLabel;
+    private JButton btnPrevious;
+    private JButton btnNext;
+    private JButton btnRefresh;
+    private JButton btnSearch;
+    private JLabel statusLabel;
+    private JTextField snSearchField;      // 工件码搜索框
+    private JTextField oprnoSearchField;   // 工位号搜索框(仅"所有工位"模式)
+    private JLabel snLabel;
+    private JLabel oprnoLabel;
+
+    /**
+     * 构造函数
+     * @param oprno 工位号(如OP150A查询该工位,null或空则查所有工位)
+     * @param lineSn 产线编号
+     */
+    public WorkRecordPanel(String oprno, String lineSn) {
+        this.oprno = oprno;
+        this.lineSn = lineSn;
+        this.isAllStation = (oprno == null || oprno.isEmpty());
+        initUI();
+    }
+
+    /**
+     * 初始化UI
+     */
+    private void initUI() {
+        setLayout(null);
+        setBackground(Color.WHITE);
+
+        // 顶部控制区
+        JPanel topPanel = new JPanel();
+        topPanel.setLayout(null);
+        topPanel.setBounds(0, 0, 1200, 50);
+        topPanel.setBackground(new Color(248, 248, 248));
+        add(topPanel);
+
+        int xPos = 10;
+
+        // 状态标签
+        String displayOprno = isAllStation ? "所有工位" : oprno;
+        statusLabel = new JLabel("工位:" + displayOprno);
+        statusLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+        statusLabel.setBounds(xPos, 10, 160, 30);
+        topPanel.add(statusLabel);
+        xPos += 165;
+
+        // 精追码搜索
+        snLabel = new JLabel("精追码:");
+        snLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+        snLabel.setBounds(xPos, 10, 50, 30);
+        topPanel.add(snLabel);
+        xPos += 52;
+
+        snSearchField = new JTextField();
+        snSearchField.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+        snSearchField.setBounds(xPos, 10, 200, 30);
+        snSearchField.addActionListener(e -> doSearch()); // 回车搜索
+        topPanel.add(snSearchField);
+        xPos += 210;
+
+        // 工位号搜索(仅"所有工位"模式显示)
+        if (isAllStation) {
+            oprnoLabel = new JLabel("工位:");
+            oprnoLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+            oprnoLabel.setBounds(xPos, 10, 40, 30);
+            topPanel.add(oprnoLabel);
+            xPos += 42;
+
+            oprnoSearchField = new JTextField();
+            oprnoSearchField.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+            oprnoSearchField.setBounds(xPos, 10, 100, 30);
+            oprnoSearchField.addActionListener(e -> doSearch()); // 回车搜索
+            topPanel.add(oprnoSearchField);
+            xPos += 110;
+        }
+
+        // 查询按钮
+        btnSearch = new JButton("查询");
+        btnSearch.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+        btnSearch.setBounds(xPos, 10, 70, 30);
+        btnSearch.addActionListener(e -> doSearch());
+        topPanel.add(btnSearch);
+        xPos += 80;
+
+        // 刷新按钮
+        btnRefresh = new JButton("刷新");
+        btnRefresh.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+        btnRefresh.setBounds(xPos, 10, 70, 30);
+        btnRefresh.addActionListener(e -> {
+            clearSearchFields();
+            refreshData();
+        });
+        topPanel.add(btnRefresh);
+        xPos += 80;
+
+        // 上一页按钮
+        btnPrevious = new JButton("上一页");
+        btnPrevious.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+        btnPrevious.setBounds(xPos, 10, 80, 30);
+        btnPrevious.addActionListener(e -> {
+            if (pageNo > 1) {
+                pageNo--;
+                loadData();
+            }
+        });
+        topPanel.add(btnPrevious);
+        xPos += 85;
+
+        // 页码显示
+        pageLabel = new JLabel("0/0");
+        pageLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+        pageLabel.setHorizontalAlignment(SwingConstants.CENTER);
+        pageLabel.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
+        pageLabel.setBounds(xPos, 10, 70, 30);
+        topPanel.add(pageLabel);
+        xPos += 75;
+
+        // 下一页按钮
+        btnNext = new JButton("下一页");
+        btnNext.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+        btnNext.setBounds(xPos, 10, 80, 30);
+        btnNext.addActionListener(e -> {
+            if (pageNo < totalPages) {
+                pageNo++;
+                loadData();
+            }
+        });
+        topPanel.add(btnNext);
+
+        // 表格面板
+        JPanel tablePanel = new JPanel();
+        tablePanel.setBounds(0, 50, 1200, 518);
+        tablePanel.setLayout(new GridLayout(1, 1, 0, 0));
+        add(tablePanel);
+
+        // 创建表格模型
+        tableModel = new DefaultTableModel(COLUMN_NAMES, 0) {
+            @Override
+            public boolean isCellEditable(int row, int column) {
+                return false;
+            }
+        };
+
+        // 创建表格
+        table = new JTable(tableModel);
+        table.setAutoCreateColumnsFromModel(true);
+        table.setRowHeight(32);
+        table.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+        table.getTableHeader().setFont(new Font("微软雅黑", Font.BOLD, 14));
+        table.getTableHeader().setReorderingAllowed(false);
+        applyColumns();
+
+        // 添加滚动面板
+        JScrollPane scrollPane = new JScrollPane(table);
+        tablePanel.add(scrollPane);
+
+        // 添加组件大小变化监听器
+        addComponentListener(new ComponentAdapter() {
+            @Override
+            public void componentResized(ComponentEvent e) {
+                int w = getWidth();
+                int h = getHeight();
+
+                // 调整顶部面板宽度
+                topPanel.setBounds(0, 0, w, 50);
+
+                // 调整表格面板
+                if (h > 50) {
+                    tablePanel.setBounds(0, 50, w, h - 50);
+                }
+
+                topPanel.revalidate();
+                tablePanel.revalidate();
+            }
+        });
+    }
+
+    /**
+     * 设置列宽
+     */
+    private void applyColumns() {
+        tableModel.setColumnIdentifiers(COLUMN_NAMES);
+        for (int i = table.getColumnCount() - 1; i >= 0; i--) {
+            if ("主工件码".equals(table.getColumnName(i))) {
+                table.removeColumn(table.getColumnModel().getColumn(i));
+            }
+        }
+        if (table.getColumnCount() >= 5) {
+            table.getColumnModel().getColumn(0).setPreferredWidth(260);
+            table.getColumnModel().getColumn(1).setPreferredWidth(90);
+            table.getColumnModel().getColumn(2).setPreferredWidth(70);
+            table.getColumnModel().getColumn(3).setPreferredWidth(100);
+            table.getColumnModel().getColumn(4).setPreferredWidth(150);
+        }
+        DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
+        centerRenderer.setHorizontalAlignment(JLabel.CENTER);
+        for (int i = 0; i < table.getColumnCount(); i++) {
+            table.getColumnModel().getColumn(i).setCellRenderer(centerRenderer);
+        }
+    }
+
+    /**
+     * 清空搜索框
+     */
+    private void clearSearchFields() {
+        snSearchField.setText("");
+        if (oprnoSearchField != null) {
+            oprnoSearchField.setText("");
+        }
+    }
+
+    /**
+     * 执行搜索
+     */
+    private void doSearch() {
+        pageNo = 1;
+        loadData();
+    }
+
+    /**
+     * 刷新数据(重置到第一页)
+     */
+    public void refreshData() {
+        pageNo = 1;
+        loadData();
+    }
+
+    /**
+     * 加载数据
+     */
+    public void loadData() {
+        // 获取搜索条件
+        String searchSn = snSearchField.getText().trim();
+        String searchOprno = oprno; // 默认使用构造函数传入的工位号
+        System.out.println("search="+searchOprno);
+
+        // 如果是"所有工位"模式,检查是否有工位号搜索条件
+        if (isAllStation && oprnoSearchField != null) {
+            String inputOprno = oprnoSearchField.getText().trim();
+            if (!inputOprno.isEmpty()) {
+                searchOprno = inputOprno;
+            }
+        }
+
+        final String finalSearchOprno = searchOprno;
+        final String finalSearchSn = searchSn;
+
+        // 在后台线程中执行数据加载
+        SwingWorker<WorkRecordResp, Void> worker = new SwingWorker<WorkRecordResp, Void>() {
+            @Override
+            protected WorkRecordResp doInBackground() throws Exception {
+                // 更新状态
+                SwingUtilities.invokeLater(() -> {
+                    statusLabel.setText("正在加载...");
+                    setButtonsEnabled(false);
+                });
+
+                // 调用接口查询数据
+                return DataUtil.getWorkRecordList(finalSearchOprno, finalSearchSn, pageNo, PAGE_SIZE);
+            }
+
+            @Override
+            protected void done() {
+                try {
+                    WorkRecordResp resp = get();
+                    updateUI(resp, finalSearchOprno);
+                } catch (Exception e) {
+                    log.error("加载工作记录失败: {}", e.getMessage());
+                    updateUIError("加载失败: " + e.getMessage());
+                }
+            }
+        };
+        worker.execute();
+    }
+
+    /**
+     * 设置按钮启用状态
+     */
+    private void setButtonsEnabled(boolean enabled) {
+        btnSearch.setEnabled(enabled);
+        btnRefresh.setEnabled(enabled);
+        btnPrevious.setEnabled(enabled && pageNo > 1);
+        btnNext.setEnabled(enabled && pageNo < totalPages);
+    }
+
+    /**
+     * 更新UI显示数据
+     */
+    private void updateUI(WorkRecordResp resp, String queryOprno) {
+        // 清空表格
+        tableModel.setRowCount(0);
+
+        if (resp == null || !resp.isResult()) {
+            String msg = resp != null ? resp.getMessage() : "请求失败";
+            updateUIError(msg);
+            return;
+        }
+
+        // 更新分页信息
+        totalCount = resp.getCount();
+        totalPages = (int) Math.ceil((double) totalCount / PAGE_SIZE);
+        if (totalPages == 0) totalPages = 1;
+
+        // 更新页码显示
+        pageLabel.setText(pageNo + "/" + totalPages);
+
+        // 更新按钮状态
+        setButtonsEnabled(true);
+
+        // 更新状态标签
+        String oprnoDisplay;
+        if (isAllStation) {
+            oprnoDisplay = (queryOprno == null || queryOprno.isEmpty()) ? "所有工位" : queryOprno;
+        } else {
+            oprnoDisplay = oprno;
+        }
+        statusLabel.setText("工位:" + oprnoDisplay + " 共" + totalCount + "条");
+
+        applyColumns();
+
+        // 填充表格数据
+        List<WorkRecordData> list = resp.getList();
+        if (list != null && !list.isEmpty()) {
+            for (WorkRecordData data : list) {
+                Object[] row = {
+                        data.getSn(),
+                        data.getOprno(),
+                        data.getContent(),
+                        data.getUpdateBy(),
+                        data.getUpdateDate()
+                };
+                tableModel.addRow(row);
+            }
+        }
+
+        // 刷新表格
+        table.repaint();
+    }
+
+    /**
+     * 更新UI显示错误信息
+     */
+    private void updateUIError(String message) {
+        statusLabel.setText("错误: " + message);
+        pageLabel.setText("0/0");
+        setButtonsEnabled(true);
+        btnPrevious.setEnabled(false);
+        btnNext.setEnabled(false);
+        tableModel.setRowCount(0);
+    }
+
+    /**
+     * 获取当前工位号
+     */
+    public String getOprno() {
+        return oprno;
+    }
+
+    /**
+     * 登录或切换工位后更新查询工位
+     */
+    public void setQueryOprno(String oprno) {
+        this.oprno = oprno;
+        this.isAllStation = (oprno == null || oprno.isEmpty());
+        if (statusLabel != null) {
+            String displayOprno = isAllStation ? "所有工位" : oprno;
+            statusLabel.setText("工位:" + displayOprno);
+        }
+    }
+}

+ 63 - 0
src/com/mes/ui/WorkRecordResp.java

@@ -0,0 +1,63 @@
+package com.mes.ui;
+
+import java.util.List;
+
+/**
+ * 工作记录查询响应实体类
+ */
+public class WorkRecordResp {
+    private boolean result;
+    private String message;
+    private int pageNo;
+    private int pageSize;
+    private long count;
+    private List<WorkRecordData> list;
+
+    public boolean isResult() {
+        return result;
+    }
+
+    public void setResult(boolean result) {
+        this.result = result;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public int getPageNo() {
+        return pageNo;
+    }
+
+    public void setPageNo(int pageNo) {
+        this.pageNo = pageNo;
+    }
+
+    public int getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(int pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public long getCount() {
+        return count;
+    }
+
+    public void setCount(long count) {
+        this.count = count;
+    }
+
+    public List<WorkRecordData> getList() {
+        return list;
+    }
+
+    public void setList(List<WorkRecordData> list) {
+        this.list = list;
+    }
+}

Diff do ficheiro suprimidas por serem muito extensas
+ 1091 - 0
src/com/mes/util/ClientUpgrade.java


+ 75 - 0
src/com/mes/util/ClientVersionUtils.java

@@ -0,0 +1,75 @@
+package com.mes.util;
+
+/**
+ * 客户端版本号比较,格式 0.0.0 / 0.0.1。
+ * 没有点的旧数字(如 1)按 0.0.1 处理。
+ */
+public final class ClientVersionUtils {
+
+    private ClientVersionUtils() {
+    }
+
+    public static int compare(String left, String right) {
+        int[] a = parse(left);
+        int[] b = parse(right);
+        int n = Math.max(a.length, b.length);
+        for (int i = 0; i < n; i++) {
+            int va = i < a.length ? a[i] : 0;
+            int vb = i < b.length ? b[i] : 0;
+            if (va != vb) {
+                return va < vb ? -1 : 1;
+            }
+        }
+        return 0;
+    }
+
+    public static boolean isNewer(String latest, String current) {
+        return compare(latest, current) > 0;
+    }
+
+    public static String max(String left, String right) {
+        if (isBlank(left)) {
+            return normalize(right);
+        }
+        if (isBlank(right)) {
+            return normalize(left);
+        }
+        return compare(left, right) >= 0 ? normalize(left) : normalize(right);
+    }
+
+    public static String normalize(String version) {
+        if (isBlank(version)) {
+            return "0.0.0";
+        }
+        int[] parts = parse(version);
+        return parts[0] + "." + parts[1] + "." + parts[2];
+    }
+
+    private static int[] parse(String version) {
+        int[] result = new int[] {0, 0, 0};
+        if (isBlank(version)) {
+            return result;
+        }
+        String[] parts = version.trim().split("\\.");
+        if (parts.length == 1) {
+            result[2] = parseInt(parts[0]);
+            return result;
+        }
+        for (int i = 0; i < parts.length && i < 3; i++) {
+            result[i] = parseInt(parts[i]);
+        }
+        return result;
+    }
+
+    private static int parseInt(String value) {
+        try {
+            return Integer.parseInt(value.trim());
+        } catch (Exception e) {
+            return 0;
+        }
+    }
+
+    private static boolean isBlank(String value) {
+        return value == null || value.trim().length() == 0;
+    }
+}

+ 21 - 0
src/com/mes/util/HttpUtils.java

@@ -1,5 +1,7 @@
 package com.mes.util;
 
+import com.mes.ui.MesClient;
+
 import java.io.*;
 import java.net.HttpURLConnection;
 import java.net.URISyntaxException;
@@ -13,6 +15,7 @@ public class HttpUtils {
 	 */
     //http post请求
     public static String sendRequest(String urlParam) {
+        urlParam = appendSid(urlParam);
 		String requestType = "POST";
         //根据接收内容返回数据结果
     	String ret = "";
@@ -66,6 +69,7 @@ public class HttpUtils {
 
 
     public static String sendPostRequest(String urlParam, String params) {
+        urlParam = appendSid(urlParam);
         String requestType = "POST";
         //根据接收内容返回数据结果
         String ret = "";
@@ -124,6 +128,7 @@ public class HttpUtils {
     }
 
     public static String sendPostRequestJson(String apiUrl, String jsonData) throws IOException {
+        apiUrl = appendSid(apiUrl);
         URL url = new URL(apiUrl);
         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
         conn.setRequestMethod("POST");
@@ -154,4 +159,20 @@ public class HttpUtils {
         return response.toString();
     }
 
+
+    private static String appendSid(String url) {
+        if (url == null || url.contains("__sid=") || url.contains("/login")) {
+            return url;
+        }
+        try {
+            String sid = MesClient.sessionid;
+            if (sid == null || sid.length() == 0) {
+                return url;
+            }
+            return url + (url.contains("?") ? "&" : "?") + "__sid=" + sid;
+        } catch (Exception e) {
+            return url;
+        }
+    }
+
 }

+ 3 - 0
src/resources/config/config.properties

@@ -8,3 +8,6 @@ mes.line_sn=XT
 mes.press_rivet_upload_url=
 #工件码开头格式,扫码后质量查询前校验;多个用逗号分隔
 mes.sn_prefix=30100201543
+# 打包发布时改成和后台「JAR管理」里填的版本号一致(或更大),格式 0.0.0。
+# 每次打包切记去后台查看对应子工位最新版本,打包时版本号+1
+mes.client.version=0.0.0