| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- <% layout('/layouts/default.html', {title: '通用客户端版本管理', libs: ['dataGrid']}){ %>
- <div class="main-content"><div class="box box-main">
- <div class="box-header"><div class="box-title"><i class="fa icon-notebook"></i> 通用客户端版本管理</div><div class="box-tools pull-right">
- <% if(hasPermi('mes:clientVersion:edit')){ %><a href="javascript:openVersionForm()" class="btn btn-default btnTool"><i class="fa fa-plus"></i> 新增版本</a><% } %>
- <a href="#" class="btn btn-default" id="btnSetting"><i class="fa fa-navicon"></i></a></div></div>
- <div class="box-body">
- <ul class="nav nav-tabs" role="tablist">
- <li class="active"><a href="#versionPanel" data-toggle="tab">版本发布</a></li>
- <li><a href="#runtimePanel" data-toggle="tab">客户端监控</a></li>
- </ul>
- <div class="tab-content" style="padding-top:15px">
- <div id="versionPanel" class="tab-pane active">
- <#form:form id="searchForm" model="${mesClientVersion}" action="${ctx}/mes/clientVersion/listData" method="post" class="form-inline hide" data-page-no="${parameter.pageNo}" data-page-size="${parameter.pageSize}" data-order-by="${parameter.orderBy}">
- <#form:input path="ver" class="form-control width-120" placeholder="版本号"/>
- <#form:input path="content" class="form-control width-180" placeholder="更新内容"/>
- <select name="clientType" class="form-control"><option value="">全部客户端</option><option value="MANUAL" ${mesClientVersion.clientType == 'MANUAL' ? 'selected' : ''}>手工位通用</option><option value="QIMI" ${mesClientVersion.clientType == 'QIMI' ? 'selected' : ''}>气密通用</option><option value="RIVETING" ${mesClientVersion.clientType == 'RIVETING' ? 'selected' : ''}>拉铆通用</option></select>
- <#form:select path="state" dictType="mes_app_status" blankOption="true" class="form-control"/>
- <button type="submit" class="btn btn-primary btn-sm">查询</button>
- </#form:form>
- <table id="dataGrid"></table><div id="dataGridPage"></div>
- </div>
- <div id="runtimePanel" class="tab-pane">
- <table id="runtimeGrid"></table><div id="runtimeGridPage"></div>
- </div>
- </div></div></div></div>
- <% } %>
- <script>
- var CLIENT_TYPE_NAME = {MANUAL:'手工位通用', QIMI:'气密通用', RIVETING:'拉铆通用'};
- function clientTypeName(v){ return CLIENT_TYPE_NAME[v] || v || '-'; }
- // —— 版本发布 grid ——
- $('#dataGrid').dataGrid({
- searchForm: $('#searchForm'),
- columnModel: [
- {header:'客户端类型', name:'clientType', width:120, formatter:function(v){return clientTypeName(v);}},
- {header:'版本号', name:'ver', width:90, formatter:function(v,o,row){return '<a href="javascript:openVersionForm(\''+row.id+'\')" class="btnList">'+(v||row.id)+'</a>';}},
- {header:'更新内容', name:'content', width:250},
- {header:'状态', name:'state', width:80, formatter:function(v){return js.getDictLabel(${@DictUtils.getDictListJson('mes_app_status')},v,'未知',true);}},
- {header:'更新时间', name:'updateDate', width:150},
- {header:'操作', name:'actions', width:100, formatter:function(v,o,row){return '<a href="javascript:openVersionForm(\''+row.id+'\')" class="btnList"><i class="fa fa-pencil"></i> 编辑</a>';}}
- ]
- });
- // 用 layer 弹窗打开版本表单,保存后自动刷新 grid(替代原 tab 打开)
- function openVersionForm(id){
- var url = '${ctx}/mes/clientVersion/form' + (id ? '?id='+id : '');
- top.layer.open({
- type: 2, title: id ? '编辑版本' : '新增版本',
- area: ['680px', '560px'], content: url,
- end: function(){ $('#dataGrid').trigger('reloadGrid'); }
- });
- }
- // —— 客户端监控 grid ——
- function heartbeatCell(v){
- if(!v) return '<span class="label label-default">未知</span>';
- var t = new Date(v.replace(' ','T')).getTime();
- if(isNaN(t)) return '<span class="label label-default">'+v+'</span>';
- var gap = (Date.now()-t)/1000, lab;
- if(gap<60) lab='<span class="label label-success">在线</span>';
- else if(gap<180) lab='<span class="label label-warning">疑似离线</span>';
- else lab='<span class="label label-default">离线</span>';
- return lab + ' <span style="color:#888">'+v+'</span>';
- }
- $('#runtimeGrid').dataGrid({
- url: '${ctx}/mes/clientRuntime/listData',
- pager: '#runtimeGridPage',
- columnModel: [
- {header:'类型', name:'clientType', width:110, formatter:function(v){return clientTypeName(v);}},
- {header:'工位', name:'stationCode', width:100},
- {header:'工艺', name:'stationTitle', width:170, formatter:function(v){return v||'<span style="color:#bbb">-</span>';}},
- {header:'IP', name:'hostIp', width:140},
- {header:'JAR版本', name:'jarVersion', width:90},
- {header:'配置版本', name:'configVersion', width:90},
- {header:'心跳/在线', name:'lastHeartbeat', width:260, formatter:function(v){return heartbeatCell(v);}},
- {header:'目标JAR', name:'desiredJarVersion', width:90},
- {header:'目标配置', name:'desiredConfigVersion', width:90},
- {header:'操作', name:'actions', width:130, formatter:function(v,o,row){return '<a href="javascript:openRuntimeDialog(\''+row.id+'\')" class="btn btn-xs btn-primary"><i class="fa fa-cog"></i> 配置/升级</a>';}}
- ]
- });
- // —— 配置/升级弹窗 ——
- function openRuntimeDialog(id){
- $.getJSON('${ctx}/mes/clientRuntime/get', {id:id}, function(resp){
- if(!resp || resp.result !== 'true' || !resp.data){ top.layer.msg('未找到该客户端'); return; }
- renderRuntimeDialog(id, resp.data);
- });
- }
- function renderRuntimeDialog(id, row){
- // 优先展示客户端本地当前配置(心跳上报的),拿不到才用模板兜底
- var cfgStr = row.currentConfigJson || '';
- var pretty = '';
- var isTemplate = false;
- if(cfgStr){
- try{ pretty = JSON.stringify(JSON.parse(cfgStr), null, 2); }catch(e){ pretty = cfgStr; }
- } else {
- isTemplate = true;
- pretty = JSON.stringify({
- "mes.server_ip":"192.168.114.99",
- "mes.gw": row.stationCode || "OP130A",
- "mes.line_sn": row.lineSn || "XT",
- "mes.tcp_port":"3000",
- "mes.heart_beat_cycle":"60"
- }, null, 2);
- }
- var cfgHint = isTemplate
- ? '客户端尚未上报本地配置(首次心跳会带上)。这是一份模板,可以直接下发。'
- : '来自客户端最近一次心跳上报的本地 config.properties。修改后下发会覆盖同名 key(其他 key 保留),并触发重启。';
- var html = ''
- + '<div style="padding:18px 24px">'
- + '<div class="form-group"><label>客户端:</label> <b>'+clientTypeName(row.clientType)+' / '+(row.stationCode||'')+(row.stationTitle?'('+row.stationTitle+')':'')+' / '+(row.hostIp||'')+'</b>'
- + ' <span style="color:#888">(当前 JAR v'+(row.jarVersion||0)+',配置 v'+(row.configVersion||0)+')</span></div>'
- + '<hr style="margin:10px 0">'
- + '<div class="form-group"><label>目标 JAR 版本</label>'
- + '<select id="dlgJarVer" class="form-control"><option value="">— 不升级 —</option></select>'
- + '<small style="color:#888">下拉列出该类型下已启用的版本;改动后客户端将自动下载并重启</small>'
- + '</div>'
- + '<div class="form-group"><label>本地配置(留空 = 不下发配置)</label>'
- + '<textarea id="dlgCfgJson" class="form-control" rows="10" style="font-family:Consolas,monospace;font-size:12px">'+pretty.replace(/</g,'<')+'</textarea>'
- + '<small style="color:#888">'+cfgHint+'</small>'
- + '</div>'
- + '</div>';
- top.layer.open({
- type: 1, title: '配置 / 升级客户端',
- area: ['620px', '520px'],
- content: html,
- btn: ['下发', '关闭'],
- success: function(layero, index){
- // 加载该 type 已启用的版本列表填下拉
- var $sel = $(layero).find('#dlgJarVer');
- $.post('${ctx}/mes/clientVersion/listData',
- {'clientType': row.clientType, 'state': '0', 'pageSize': 200, 'pageNo': 1},
- function(page){
- var list = (page && page.list) || [];
- list.forEach(function(v){
- var sel = (row.desiredJarVersion && String(v.ver) === String(row.desiredJarVersion)) ? ' selected' : '';
- $sel.append('<option value="'+v.ver+'"'+sel+'>v'+v.ver+' — '+(v.content||'').replace(/</g,'<')+'</option>');
- });
- }, 'json');
- },
- yes: function(index, layero){
- var jar = $(layero).find('#dlgJarVer').val();
- var cfg = $.trim($(layero).find('#dlgCfgJson').val());
- var cv = null;
- if(cfg){
- try{ JSON.parse(cfg); }catch(e){ top.layer.msg('配置 JSON 格式错误'); return; }
- cv = (row.desiredConfigVersion || row.configVersion || 0) + 1;
- }
- $.post('${ctx}/mes/clientRuntime/command',
- {id:id, desiredJarVersion:jar||null, desiredConfigVersion:cv, desiredConfigJson:cfg||null},
- function(d){
- top.layer.msg(d.message||'已下发');
- top.layer.close(index);
- $('#runtimeGrid').trigger('reloadGrid');
- }, 'json');
- }
- });
- }
- // —— tab 切换:记录到 hash,刷新页面时恢复;每次切换刷新对应 grid ——
- function activateTab(h){
- var target = (h === '#runtime') ? '#runtimePanel' : '#versionPanel';
- $('a[data-toggle="tab"][href="'+target+'"]').tab('show');
- }
- $('a[data-toggle="tab"]').on('shown.bs.tab', function(e){
- var href = $(e.target).attr('href');
- location.hash = (href === '#runtimePanel') ? 'runtime' : 'version';
- if(href === '#runtimePanel') $('#runtimeGrid').trigger('reloadGrid');
- else $('#dataGrid').trigger('reloadGrid');
- });
- // 页面进入时恢复
- $(function(){ if(location.hash) activateTab(location.hash); });
- </script>
|