|
|
@@ -0,0 +1,329 @@
|
|
|
+<% layout('/layouts/default.html', {title: '工序计件统计', libs: ['dataGrid']}){ %>
|
|
|
+
|
|
|
+<style>
|
|
|
+/* 顶部操作栏 */
|
|
|
+.pw-header {
|
|
|
+ display: flex; align-items: center; justify-content: space-between;
|
|
|
+ background: #fff; border: 1px solid #e8e8e8; border-radius: 6px;
|
|
|
+ padding: 12px 20px; margin-bottom: 14px;
|
|
|
+}
|
|
|
+.pw-header-left { display: flex; align-items: center; gap: 20px; }
|
|
|
+.pw-date-label { font-size: 15px; color: #333; font-weight: 600; }
|
|
|
+.pw-shift-cur { font-size: 13px; color: #888; }
|
|
|
+.pw-shift-cur span { color: #1890ff; font-weight: 600; }
|
|
|
+
|
|
|
+/* 班次 Tab */
|
|
|
+.pw-tabs { display: flex; gap: 6px; align-items: stretch; }
|
|
|
+.pw-tab {
|
|
|
+ min-width: 72px; padding: 0 16px; border-radius: 4px; border: 1px solid #d9d9d9;
|
|
|
+ background: #fafafa; color: #555; cursor: pointer; font-size: 13px;
|
|
|
+ text-align: center; transition: all .15s; user-select: none;
|
|
|
+ display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
|
+ min-height: 48px;
|
|
|
+}
|
|
|
+.pw-tab:hover { border-color: #1890ff; color: #1890ff; }
|
|
|
+.pw-tab.active { background: #1890ff; border-color: #1890ff; color: #fff; font-weight: 600; }
|
|
|
+
|
|
|
+/* 汇总卡片 */
|
|
|
+.pw-cards { display: flex; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
|
|
|
+.pw-card {
|
|
|
+ flex: 1; min-width: 130px; background: #fff; border-radius: 8px;
|
|
|
+ border: 1px solid #e8e8e8; padding: 16px 20px 12px; text-align: center;
|
|
|
+ box-shadow: 0 1px 4px rgba(0,0,0,.05);
|
|
|
+}
|
|
|
+.pw-card-label { font-size: 12px; color: #999; margin-bottom: 8px; }
|
|
|
+.pw-card-num { font-size: 40px; font-weight: 700; line-height: 1; color: #1890ff; }
|
|
|
+.pw-card.c-green .pw-card-num { color: #27ae60; }
|
|
|
+.pw-card.c-orange .pw-card-num { color: #e67e22; }
|
|
|
+
|
|
|
+/* 主表格容器:左右两列并排 */
|
|
|
+.pw-table-wrap {
|
|
|
+ display: flex; gap: 0; background: #fff;
|
|
|
+ border: 1px solid #d0d7de; border-radius: 6px; overflow: hidden;
|
|
|
+}
|
|
|
+/* 左右两个产品区域各占一半 */
|
|
|
+.pw-col { flex: 1; min-width: 0; overflow-x: auto; }
|
|
|
+.pw-col + .pw-col { border-left: 1px solid #d0d7de; }
|
|
|
+
|
|
|
+/* 表格本身 */
|
|
|
+.pw-tbl { width: 100%; border-collapse: collapse; table-layout: fixed; }
|
|
|
+
|
|
|
+/* 产品大标题行 */
|
|
|
+.pw-tbl .th-product {
|
|
|
+ background: #e6f4ff; color: #1890ff; font-size: 14px; font-weight: 700;
|
|
|
+ text-align: center; padding: 10px 0; border-bottom: 1px solid #d0d7de;
|
|
|
+ letter-spacing: 1px;
|
|
|
+}
|
|
|
+.pw-col:nth-child(2) .th-product { background: #fff7e6; color: #e67e22; border-bottom-color: #d0d7de; }
|
|
|
+
|
|
|
+/* 列头 */
|
|
|
+.pw-tbl .th-col {
|
|
|
+ background: #f6f8fa; font-size: 12px; color: #57606a; font-weight: 600;
|
|
|
+ padding: 8px 10px; border-bottom: 1px solid #d0d7de;
|
|
|
+ text-align: center; white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+/* 数据行 */
|
|
|
+.pw-tbl td {
|
|
|
+ padding: 0 10px; border-bottom: 1px solid #eaeef2;
|
|
|
+ font-size: 13px; vertical-align: middle;
|
|
|
+}
|
|
|
+.pw-tbl tr:last-child td { border-bottom: none; }
|
|
|
+
|
|
|
+/* 工序组交替背景色 */
|
|
|
+.pw-tbl tr.group-odd td { background: #fff; }
|
|
|
+.pw-tbl tr.group-even td { background: #f6f8ff; }
|
|
|
+
|
|
|
+/* hover 高亮 */
|
|
|
+.pw-tbl tr.row-hover td { background: #dbeafe !important; }
|
|
|
+
|
|
|
+/* 工位名称列 */
|
|
|
+.td-name {
|
|
|
+ text-align: center; font-weight: 600; color: #24292f; font-size: 11px;
|
|
|
+ border-right: 1px solid #eaeef2; width: 110px; line-height: 1.4;
|
|
|
+ vertical-align: middle; padding: 0 6px;
|
|
|
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
|
+}
|
|
|
+/* 工位号列 */
|
|
|
+.td-oprno { text-align: center; color: #888; font-size: 12px; width: 72px;
|
|
|
+ border-right: 1px solid #eaeef2; height: 38px; line-height: 38px; }
|
|
|
+/* 数量列 */
|
|
|
+.td-cnt { text-align: center; font-size: 16px; font-weight: 700; color: #27ae60; }
|
|
|
+/* 小计行 */
|
|
|
+.td-subtotal-label { text-align: right; font-size: 11px; color: #888;
|
|
|
+ border-right: 1px solid #eaeef2; padding-right: 8px; }
|
|
|
+.td-subtotal-num { text-align: center; font-size: 14px; font-weight: 700; color: #1890ff; }
|
|
|
+.tr-subtotal td { background: #f0f7ff !important; border-top: 1px solid #d0d7de; }
|
|
|
+
|
|
|
+/* 空/loading */
|
|
|
+#loadingTip { padding: 50px 0; text-align: center; color: #aaa; font-size: 14px; }
|
|
|
+#emptyTip { padding: 60px 0; text-align: center; color: #bbb; font-size: 15px;
|
|
|
+ background: #fff; border-radius: 6px; border: 1px solid #eaeef2; }
|
|
|
+#emptyTip i { font-size: 40px; display: block; margin-bottom: 12px; }
|
|
|
+</style>
|
|
|
+
|
|
|
+<div class="main-content">
|
|
|
+ <div class="box box-main">
|
|
|
+ <div class="box-header">
|
|
|
+ <div class="box-title"><i class="fa fa-bar-chart"></i> 工序计件统计</div>
|
|
|
+ </div>
|
|
|
+ <div class="box-body">
|
|
|
+
|
|
|
+ <!-- 顶部操作栏 -->
|
|
|
+ <div class="pw-header">
|
|
|
+ <div class="pw-header-left">
|
|
|
+ <div>
|
|
|
+ <input type="text" id="queryDate" class="form-control laydate"
|
|
|
+ data-type="date" data-format="yyyy-MM-dd"
|
|
|
+ style="display:inline-block;width:130px;cursor:pointer;"/>
|
|
|
+ </div>
|
|
|
+ <div class="pw-shift-cur">当前班次:<span id="curShiftLabel">-</span></div>
|
|
|
+ </div>
|
|
|
+ <div class="pw-tabs" id="shiftTabs">
|
|
|
+ <div class="pw-tab" data-shift-id="" data-shift-name="全天">全天</div>
|
|
|
+ <% for(var s in shiftList){ %>
|
|
|
+ <div class="pw-tab" data-shift-id="${s.id}" data-shift-name="${s.title}">
|
|
|
+ ${s.title}<br/><small style="font-size:11px;opacity:.8;">${s.start}~${s.end}</small>
|
|
|
+ </div>
|
|
|
+ <% } %>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 汇总卡片 -->
|
|
|
+ <div id="summaryCards" class="pw-cards" style="display:none;"></div>
|
|
|
+
|
|
|
+ <!-- loading -->
|
|
|
+ <div id="loadingTip"><i class="fa fa-spinner fa-spin"></i> 加载中...</div>
|
|
|
+
|
|
|
+ <!-- 主表格(JS渲染) -->
|
|
|
+ <div id="tableWrap" style="display:none;">
|
|
|
+ <div class="pw-table-wrap" id="mainTable"></div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 空状态 -->
|
|
|
+ <div id="emptyTip" style="display:none;">
|
|
|
+ <i class="fa fa-inbox"></i>暂无加工数据
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+
|
|
|
+<script>
|
|
|
+$(function(){
|
|
|
+
|
|
|
+ var curShiftId = '${curShiftId}';
|
|
|
+ var activeShiftId = curShiftId;
|
|
|
+
|
|
|
+ // 今天日期
|
|
|
+ var now = new Date();
|
|
|
+ var today = now.getFullYear() + '-'
|
|
|
+ + String(now.getMonth()+1).padStart(2,'0') + '-'
|
|
|
+ + String(now.getDate()).padStart(2,'0');
|
|
|
+ $('#queryDate').val(today);
|
|
|
+
|
|
|
+ if(typeof laydate !== 'undefined'){
|
|
|
+ laydate.render({
|
|
|
+ elem: '#queryDate',
|
|
|
+ type: 'date',
|
|
|
+ format: 'yyyy-MM-dd',
|
|
|
+ done: function(value){
|
|
|
+ doQuery();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ $('#queryDate').on('change', function(){ doQuery(); });
|
|
|
+ }
|
|
|
+ function initTabs(){
|
|
|
+ $('#shiftTabs .pw-tab').each(function(){
|
|
|
+ var sid = String($(this).data('shift-id'));
|
|
|
+ var match = curShiftId ? (sid === String(curShiftId)) : (sid === '');
|
|
|
+ if(match){
|
|
|
+ $(this).addClass('active');
|
|
|
+ $('#curShiftLabel').text($(this).data('shift-name'));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ if($('#shiftTabs .pw-tab.active').length === 0){
|
|
|
+ $('#shiftTabs .pw-tab').first().addClass('active');
|
|
|
+ $('#curShiftLabel').text('全天');
|
|
|
+ activeShiftId = '';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ initTabs();
|
|
|
+
|
|
|
+ // Tab 切换
|
|
|
+ $('#shiftTabs').on('click', '.pw-tab', function(){
|
|
|
+ $('#shiftTabs .pw-tab').removeClass('active');
|
|
|
+ $(this).addClass('active');
|
|
|
+ activeShiftId = String($(this).data('shift-id'));
|
|
|
+ doQuery();
|
|
|
+ });
|
|
|
+
|
|
|
+ doQuery();
|
|
|
+
|
|
|
+ /* ---- 查询 ---- */
|
|
|
+ function doQuery(){
|
|
|
+ var date = $('#queryDate').val() || today;
|
|
|
+ $('#summaryCards').hide();
|
|
|
+ $('#tableWrap').hide();
|
|
|
+ $('#emptyTip').hide();
|
|
|
+ $('#loadingTip').show();
|
|
|
+
|
|
|
+ $.ajax({
|
|
|
+ url: '${ctx}/mes/mesProductRecord/pieceworkData',
|
|
|
+ type: 'GET',
|
|
|
+ data: { date: date, shiftId: activeShiftId, oprno: '' },
|
|
|
+ dataType: 'json',
|
|
|
+ success: function(data){
|
|
|
+ $('#loadingTip').hide();
|
|
|
+ if(data.result != 1){ $('#emptyTip').show(); return; }
|
|
|
+ renderResult(data);
|
|
|
+ },
|
|
|
+ error: function(){ $('#loadingTip').hide(); $('#emptyTip').show(); }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ---- 渲染 ---- */
|
|
|
+ function renderResult(data){
|
|
|
+ var groups = data.groups || [];
|
|
|
+ var prefixes = data.prefixes || [];
|
|
|
+
|
|
|
+ if(groups.length === 0){ $('#emptyTip').show(); return; }
|
|
|
+
|
|
|
+ /* 汇总卡片 */
|
|
|
+ var totalAll = 0;
|
|
|
+ var prefixTotals = {};
|
|
|
+ prefixes.forEach(function(p){ prefixTotals[p] = 0; });
|
|
|
+ groups.forEach(function(g){
|
|
|
+ (g.rows||[]).forEach(function(row){
|
|
|
+ var pm = row.prefixMap || {};
|
|
|
+ prefixes.forEach(function(p){
|
|
|
+ var v = pm[p] || 0;
|
|
|
+ totalAll += v;
|
|
|
+ prefixTotals[p] = (prefixTotals[p]||0) + v;
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+ var cardColors = ['c-green','c-orange','c-purple',''];
|
|
|
+ var cards = '<div class="pw-card"><div class="pw-card-label">当班合计</div>'
|
|
|
+ + '<div class="pw-card-num">' + totalAll + '</div></div>';
|
|
|
+ prefixes.forEach(function(p, i){
|
|
|
+ cards += '<div class="pw-card ' + (cardColors[i]||'') + '">'
|
|
|
+ + '<div class="pw-card-label">' + esc(p) + '</div>'
|
|
|
+ + '<div class="pw-card-num">' + (prefixTotals[p]||0) + '</div></div>';
|
|
|
+ });
|
|
|
+ $('#summaryCards').html(cards).show();
|
|
|
+
|
|
|
+ /* 左右两列表格,每列对应一个产品前缀 */
|
|
|
+ var html = '';
|
|
|
+ prefixes.forEach(function(prefix, pi){
|
|
|
+ html += '<div class="pw-col">';
|
|
|
+ html += '<table class="pw-tbl">';
|
|
|
+ // 产品大标题
|
|
|
+ html += '<thead><tr><th class="th-product" colspan="4">' + esc(prefix) + '</th></tr>';
|
|
|
+ html += '<tr>'
|
|
|
+ + '<th class="th-col" style="width:110px;">工位名称</th>'
|
|
|
+ + '<th class="th-col" style="width:72px;">工位号</th>'
|
|
|
+ + '<th class="th-col">数量</th>'
|
|
|
+ + '<th class="th-col" style="width:60px;">小计</th>'
|
|
|
+ + '</tr></thead>';
|
|
|
+ html += '<tbody>';
|
|
|
+
|
|
|
+ groups.forEach(function(g, gi){
|
|
|
+ var rows = g.rows || [];
|
|
|
+
|
|
|
+ // 过滤:只保留该前缀数量 > 0 的行
|
|
|
+ var visibleRows = rows.filter(function(row){
|
|
|
+ return ((row.prefixMap||{})[prefix] || 0) > 0;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 整组都没数据则跳过
|
|
|
+ if(visibleRows.length === 0) return;
|
|
|
+
|
|
|
+ var rowCnt = visibleRows.length;
|
|
|
+ var groupClass = (gi % 2 === 0) ? 'group-odd' : 'group-even';
|
|
|
+ // 计算该工序组在此前缀下的小计
|
|
|
+ var groupTotal = visibleRows.reduce(function(sum, row){
|
|
|
+ return sum + (((row.prefixMap||{})[prefix]) || 0);
|
|
|
+ }, 0);
|
|
|
+
|
|
|
+ visibleRows.forEach(function(row, ri){
|
|
|
+ html += '<tr class="' + groupClass + '" data-group="' + prefix + '-' + gi + '">';
|
|
|
+ if(ri === 0){
|
|
|
+ html += '<td class="td-name" rowspan="' + rowCnt + '">' + esc(g.title||'') + '</td>';
|
|
|
+ }
|
|
|
+ html += '<td class="td-oprno">' + esc(row.oprno||'') + '</td>';
|
|
|
+ var cnt = (row.prefixMap||{})[prefix] || 0;
|
|
|
+ html += '<td class="td-cnt">' + cnt + '</td>';
|
|
|
+ // 小计列:只在第一行输出,rowspan 跨所有子行
|
|
|
+ if(ri === 0){
|
|
|
+ html += '<td class="td-subtotal-num" rowspan="' + rowCnt + '">' + groupTotal + '</td>';
|
|
|
+ }
|
|
|
+ html += '</tr>';
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ html += '</tbody></table></div>';
|
|
|
+ });
|
|
|
+
|
|
|
+ $('#mainTable').html(html);
|
|
|
+
|
|
|
+ // 绑定整组 hover:同一 data-group 的所有行一起高亮
|
|
|
+ $('#mainTable').on('mouseenter', 'tr[data-group]', function(){
|
|
|
+ var g = $(this).data('group');
|
|
|
+ $('tr[data-group="' + g + '"]').addClass('row-hover');
|
|
|
+ }).on('mouseleave', 'tr[data-group]', function(){
|
|
|
+ var g = $(this).data('group');
|
|
|
+ $('tr[data-group="' + g + '"]').removeClass('row-hover');
|
|
|
+ });
|
|
|
+
|
|
|
+ $('#tableWrap').show();
|
|
|
+ }
|
|
|
+
|
|
|
+ function esc(s){
|
|
|
+ return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
|
+ }
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<% } %>
|