|
|
@@ -1,75 +1,176 @@
|
|
|
<% 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> ${text('通用客户端版本管理')}
|
|
|
- </div>
|
|
|
- <div class="box-tools pull-right">
|
|
|
- <a href="#" class="btn btn-default" id="btnSearch" title="${text('查询')}"><i class="fa fa-filter"></i> ${text('查询')}</a>
|
|
|
- <% if(hasPermi('mes:clientVersion:edit')){ %>
|
|
|
- <a href="${ctx}/mes/clientVersion/form" class="btn btn-default btnTool" title="${text('新增通用客户端版本')}"><i class="fa fa-plus"></i> ${text('新增')}</a>
|
|
|
- <% } %>
|
|
|
- <a href="#" class="btn btn-default" id="btnSetting" title="${text('设置')}"><i class="fa fa-navicon"></i></a>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="box-body">
|
|
|
- <#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}">
|
|
|
- <div class="form-group">
|
|
|
- <label class="control-label">${text('版本号')}:</label>
|
|
|
- <div class="control-inline">
|
|
|
- <#form:input path="ver" maxlength="9" class="form-control width-120"/>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="form-group">
|
|
|
- <label class="control-label">${text('更新内容')}:</label>
|
|
|
- <div class="control-inline">
|
|
|
- <#form:input path="content" class="form-control width-120"/>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="form-group">
|
|
|
- <label class="control-label">${text('状态')}:</label>
|
|
|
- <div class="control-inline width-120">
|
|
|
- <#form:select path="state" dictType="mes_app_status" blankOption="true" class="form-control"/>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="form-group">
|
|
|
- <button type="submit" class="btn btn-primary btn-sm">${text('查询')}</button>
|
|
|
- <button type="reset" class="btn btn-default btn-sm">${text('重置')}</button>
|
|
|
- </div>
|
|
|
- </#form:form>
|
|
|
- <table id="dataGrid"></table>
|
|
|
- <div id="dataGridPage"></div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+<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>
|
|
|
-// 初始化DataGrid对象
|
|
|
+var CLIENT_TYPE_NAME = {MANUAL:'手工位通用', QIMI:'气密通用', RIVETING:'拉铆通用'};
|
|
|
+function clientTypeName(v){ return CLIENT_TYPE_NAME[v] || v || '-'; }
|
|
|
+
|
|
|
+// —— 版本发布 grid ——
|
|
|
$('#dataGrid').dataGrid({
|
|
|
- searchForm: $("#searchForm"),
|
|
|
- columnModel: [
|
|
|
- {header:'${text("版本号")}', name:'ver', index:'a.ver', width:150, align:"center", frozen:true, formatter: function(val, obj, row, act){
|
|
|
- return '<a href="${ctx}/mes/clientVersion/form?id='+row.id+'" class="btnList" data-title="${text("编辑通用客户端版本")}">'+(val||row.id)+'</a>';
|
|
|
- }},
|
|
|
- {header:'${text("更新内容")}', name:'content', index:'a.content', width:250, align:"center"},
|
|
|
- {header:'${text("状态")}', name:'state', index:'a.state', width:150, align:"center", formatter: function(val, obj, row, act){
|
|
|
- return js.getDictLabel(${@DictUtils.getDictListJson('mes_app_status')}, val, '${text("未知")}', true);
|
|
|
- }},
|
|
|
- {header:'${text("更新时间")}', name:'updateDate', index:'a.update_date', width:150, align:"center"},
|
|
|
- {header:'${text("操作")}', name:'actions', width:120, formatter: function(val, obj, row, act){
|
|
|
- var actions = [];
|
|
|
- //<% if(hasPermi('mes:clientVersion:edit')){ %>
|
|
|
- actions.push('<a href="${ctx}/mes/clientVersion/form?id='+row.id+'" class="btnList" title="${text("编辑通用客户端版本")}"><i class="fa fa-pencil"></i></a> ');
|
|
|
- actions.push('<a href="${ctx}/mes/clientVersion/delete?id='+row.id+'" class="btnList" title="${text("删除通用客户端版本")}" data-confirm="${text("确认要删除该通用客户端版本吗?")}"><i class="fa fa-trash-o"></i></a> ');
|
|
|
- //<% } %>
|
|
|
- return actions.join('');
|
|
|
- }}
|
|
|
- ],
|
|
|
- // 加载成功后执行事件
|
|
|
- ajaxSuccess: function(data){
|
|
|
-
|
|
|
- }
|
|
|
+ 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');
|
|
|
});
|
|
|
-</script>
|
|
|
+// 页面进入时恢复
|
|
|
+$(function(){ if(location.hash) activateTab(location.hash); });
|
|
|
+</script>
|