|
|
@@ -40,34 +40,34 @@
|
|
|
.mes4-title {
|
|
|
position: absolute;
|
|
|
left: 50%;
|
|
|
- top: 20px;
|
|
|
+ top: 18px;
|
|
|
transform: translateX(-50%);
|
|
|
- font-size: 38px;
|
|
|
+ font-size: 42px;
|
|
|
font-weight: bold;
|
|
|
color: #FFFFFF;
|
|
|
- letter-spacing: 3px;
|
|
|
+ letter-spacing: 4px;
|
|
|
text-shadow: 0 0 10px rgba(77, 201, 245, 0.6);
|
|
|
white-space: nowrap;
|
|
|
}
|
|
|
.mes4-datetime {
|
|
|
position: absolute;
|
|
|
- top: 32px;
|
|
|
+ top: 30px;
|
|
|
right: 50px;
|
|
|
text-align: right;
|
|
|
}
|
|
|
.mes4-datetime .mes4-date {
|
|
|
display: inline-block;
|
|
|
- font-size: 18px;
|
|
|
+ font-size: 20px;
|
|
|
color: #FFFFFF;
|
|
|
- line-height: 28px;
|
|
|
+ line-height: 30px;
|
|
|
}
|
|
|
.mes4-datetime .mes4-time {
|
|
|
display: inline-block;
|
|
|
margin-left: 14px;
|
|
|
- font-size: 22px;
|
|
|
+ font-size: 26px;
|
|
|
color: #FFFFFF;
|
|
|
font-weight: bold;
|
|
|
- line-height: 28px;
|
|
|
+ line-height: 30px;
|
|
|
}
|
|
|
|
|
|
/* ===== 面板通用 - 使用自定义类名避免与 Bootstrap 的 .panel 冲突 ===== */
|
|
|
@@ -79,107 +79,111 @@
|
|
|
box-shadow: none;
|
|
|
}
|
|
|
.mes4-panel .mes4-ptitle {
|
|
|
- height: 42px;
|
|
|
- line-height: 42px;
|
|
|
+ height: 48px;
|
|
|
+ line-height: 48px;
|
|
|
text-align: center;
|
|
|
- font-size: 18px;
|
|
|
+ font-size: 22px;
|
|
|
font-weight: bold;
|
|
|
color: #4DC9F5;
|
|
|
- letter-spacing: 1px;
|
|
|
+ letter-spacing: 2px;
|
|
|
background: transparent;
|
|
|
}
|
|
|
.mes4-panel .mes4-pbody {
|
|
|
position: absolute;
|
|
|
- top: 42px;
|
|
|
+ top: 48px;
|
|
|
left: 0;
|
|
|
right: 0;
|
|
|
bottom: 0;
|
|
|
- padding: 5px 10px;
|
|
|
+ padding: 8px 14px;
|
|
|
background: transparent;
|
|
|
}
|
|
|
.chart { width: 100%; height: 100%; display: block; }
|
|
|
|
|
|
- /* ===== 位置定义 (1920 × 1080 画布) ===== */
|
|
|
- /* 左列: x=30, 宽=430 */
|
|
|
- .p-alert { left: 40px; top: 130px; width: 430px; height: 290px; }
|
|
|
- .p-quality { left: 40px; top: 455px; width: 430px; height: 265px; }
|
|
|
- .p-product { left: 40px; top: 763px; width: 430px; height: 306px; }
|
|
|
+ /* ===== 位置:左右对齐,中间表按 5 行收短 ===== */
|
|
|
+ .p-alert { left: 36px; top: 118px; width: 440px; height: 430px; }
|
|
|
+ .p-quality { left: 36px; top: 568px; width: 440px; height: 460px; }
|
|
|
+ .p-product { display: none; }
|
|
|
|
|
|
- /* 中列: x=480, 宽=960 */
|
|
|
- .p-table { left: 480px; top: 130px; width: 960px; height: 575px; }
|
|
|
- .p-month { left: 480px; top: 725px; width: 960px; height: 306px; }
|
|
|
+ .p-table { left: 496px; top: 118px; width: 928px; height: 560px; }
|
|
|
+ .p-month { display: none; }
|
|
|
|
|
|
- /* 右列: right=30, 宽=430 */
|
|
|
.p-overview { right: 30px; top: 130px; width: 430px; height: 290px; }
|
|
|
- .p-achieve { right: 40px; top: 455px; width: 430px; height: 265px; }
|
|
|
- .p-hourly { right: 40px; top: 763px; width: 430px; height: 306px; }
|
|
|
+ .p-achieve { display: none; }
|
|
|
+ .p-hourly { right: 36px; top: 568px; width: 440px; height: 460px; }
|
|
|
|
|
|
- /* 阶段报警信息 - 按模板比例调整 */
|
|
|
- .p-alert .mes4-ptitle {
|
|
|
+ .p-alert .mes4-ptitle,
|
|
|
+ .p-overview .mes4-ptitle,
|
|
|
+ .p-quality .mes4-ptitle,
|
|
|
+ .p-hourly .mes4-ptitle {
|
|
|
height: 40px;
|
|
|
line-height: 40px;
|
|
|
font-size: 16px;
|
|
|
- color: #4DC9F5;
|
|
|
- font-weight: bold;
|
|
|
- letter-spacing: 1px;
|
|
|
}
|
|
|
- .p-alert .mes4-pbody { top: 40px; padding: 0; }
|
|
|
- /* 总览标题 */
|
|
|
- .p-overview .mes4-ptitle { height: 40px; line-height: 40px; font-size: 16px; }
|
|
|
- .p-overview .mes4-pbody { top: 40px; }
|
|
|
+ .p-alert .mes4-pbody { top: 48px; padding: 0 6px 8px; }
|
|
|
+ .p-overview .mes4-pbody { top: 40px; padding: 0; }
|
|
|
+ .p-table .mes4-pbody { top: 8px; padding: 8px 28px 12px; }
|
|
|
+ .p-quality .mes4-pbody,
|
|
|
+ .p-hourly .mes4-pbody { top: 72px; }
|
|
|
|
|
|
/* ===== 报警信息 ===== */
|
|
|
.alert-head {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- height: 36px;
|
|
|
- padding: 0 22px;
|
|
|
+ height: 42px;
|
|
|
+ padding: 0 20px;
|
|
|
color: #4DC9F5;
|
|
|
- font-size: 14px;
|
|
|
+ font-size: 16px;
|
|
|
font-weight: bold;
|
|
|
- border-bottom: 1px solid rgba(77, 201, 245, 0.3);
|
|
|
+ border-bottom: 2px solid rgba(77, 201, 245, 0.45);
|
|
|
}
|
|
|
- .alert-head > div:nth-child(1) { width: 70px; }
|
|
|
+ .alert-head > div:nth-child(1) { width: 72px; }
|
|
|
.alert-head > div:nth-child(2) { flex: 1; }
|
|
|
.alert-row {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- height: 28px;
|
|
|
- padding: 0 22px;
|
|
|
+ height: 32px;
|
|
|
+ padding: 0 20px;
|
|
|
color: #ffffff;
|
|
|
- font-size: 13px;
|
|
|
+ font-size: 15px;
|
|
|
+ border-bottom: 1px solid rgba(77, 201, 245, 0.12);
|
|
|
}
|
|
|
- .alert-row > div:nth-child(1) { width: 70px; color: #4DC9F5; font-weight: bold; }
|
|
|
- .alert-row > div:nth-child(2) { flex: 1; }
|
|
|
+ .alert-row > div:nth-child(1) { width: 72px; color: #4DC9F5; font-weight: bold; font-size: 16px; }
|
|
|
+ .alert-row > div:nth-child(2) { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
|
|
|
|
- /* ===== OP 表格 ===== */
|
|
|
+ /* ===== OP 表格(5 行加高、字号加大) ===== */
|
|
|
.op-table {
|
|
|
width: 100%;
|
|
|
+ height: 100%;
|
|
|
border-collapse: collapse;
|
|
|
color: #ffffff;
|
|
|
- font-size: 16px;
|
|
|
+ font-size: 20px;
|
|
|
table-layout: fixed;
|
|
|
}
|
|
|
.op-table thead th {
|
|
|
- height: 46px;
|
|
|
+ height: 58px;
|
|
|
color: #4DC9F5;
|
|
|
- font-weight: normal;
|
|
|
- font-size: 16px;
|
|
|
- background: rgba(0, 100, 180, 0.25);
|
|
|
- border-bottom: 1px solid rgba(77, 201, 245, 0.35);
|
|
|
+ font-weight: bold;
|
|
|
+ font-size: 20px;
|
|
|
+ letter-spacing: 1px;
|
|
|
+ background: rgba(0, 100, 180, 0.28);
|
|
|
+ border-bottom: 2px solid rgba(77, 201, 245, 0.5);
|
|
|
text-align: center;
|
|
|
}
|
|
|
.op-table tbody td {
|
|
|
- height: 46px;
|
|
|
+ height: 88px;
|
|
|
text-align: center;
|
|
|
- border-bottom: 1px dashed rgba(77, 201, 245, 0.18);
|
|
|
+ border-bottom: 1px solid rgba(77, 201, 245, 0.22);
|
|
|
+ vertical-align: middle;
|
|
|
+ }
|
|
|
+ .op-table tbody tr:last-child td {
|
|
|
+ border-bottom: 2px solid rgba(77, 201, 245, 0.35);
|
|
|
}
|
|
|
.op-table tbody tr:hover td { background: rgba(0, 200, 255, 0.06); }
|
|
|
- .op-table td.op-col { color: #00C8FF; font-weight: bold; font-size: 18px; }
|
|
|
- .op-table td.op-name { color: #ffffff; text-align: center; }
|
|
|
- .op-table td.op-val { color: #00F7FF; font-weight: bold; font-size: 18px; }
|
|
|
+ .op-table td.op-col { color: #00C8FF; font-weight: bold; font-size: 26px; }
|
|
|
+ .op-table td.op-name { color: #ffffff; text-align: center; font-size: 22px; }
|
|
|
+ .op-table td.op-val { color: #00F7FF; font-weight: bold; font-size: 28px; }
|
|
|
|
|
|
- /* ===== 产线阶段数据总览 (按模板排布) ===== */
|
|
|
+ /* ===== 产线阶段数据总览(对齐原 UI 四宫格框) ===== */
|
|
|
.overview-wrap {
|
|
|
position: relative;
|
|
|
height: 100%;
|
|
|
@@ -211,11 +215,6 @@
|
|
|
font-weight: bold;
|
|
|
line-height: 1;
|
|
|
}
|
|
|
- .overview-card .oc-value .arrow {
|
|
|
- font-size: 13px;
|
|
|
- color: #00FF88;
|
|
|
- margin-left: 3px;
|
|
|
- }
|
|
|
.overview-gauge-wrap {
|
|
|
position: absolute;
|
|
|
top: 50%;
|
|
|
@@ -250,17 +249,17 @@
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: flex-end;
|
|
|
- padding: 4px 18px 0;
|
|
|
- font-size: 13px;
|
|
|
+ padding: 6px 22px 0;
|
|
|
+ font-size: 15px;
|
|
|
color: #ffffff;
|
|
|
- gap: 18px;
|
|
|
+ gap: 22px;
|
|
|
}
|
|
|
.legend-inline .dot {
|
|
|
display: inline-block;
|
|
|
width: 12px;
|
|
|
height: 12px;
|
|
|
border-radius: 50%;
|
|
|
- margin-right: 5px;
|
|
|
+ margin-right: 6px;
|
|
|
vertical-align: middle;
|
|
|
}
|
|
|
</style>
|
|
|
@@ -279,7 +278,7 @@
|
|
|
|
|
|
<div class="mes4-panel p-alert">
|
|
|
<div class="mes4-ptitle"> 产线阶段报警信息 </div>
|
|
|
- <div class="mes4-pbody" style="padding:0">
|
|
|
+ <div class="mes4-pbody">
|
|
|
<div class="alert-head"><div>序号</div><div>报警类型</div></div>
|
|
|
<div id="alertList"></div>
|
|
|
</div>
|
|
|
@@ -291,7 +290,7 @@
|
|
|
<span><i class="dot" style="background:#00FF88"></i>产出</span>
|
|
|
<span><i class="dot" style="background:#00C8FF"></i>不良</span>
|
|
|
</div>
|
|
|
- <div class="mes4-pbody" style="top:62px"><canvas id="chartQuality" class="chart"></canvas></div>
|
|
|
+ <div class="mes4-pbody"><canvas id="chartQuality" class="chart"></canvas></div>
|
|
|
</div>
|
|
|
|
|
|
<div class="mes4-panel p-product">
|
|
|
@@ -300,16 +299,14 @@
|
|
|
</div>
|
|
|
|
|
|
<div class="mes4-panel p-table">
|
|
|
- <div class="mes4-pbody" style="top:5px;padding:0 20px">
|
|
|
+ <div class="mes4-pbody">
|
|
|
<table class="op-table">
|
|
|
<thead>
|
|
|
<tr>
|
|
|
- <th style="width:110px">工位</th>
|
|
|
- <th style="width:200px">工位名称</th>
|
|
|
- <th>计划产能</th>
|
|
|
- <th>小时产能</th>
|
|
|
+ <th style="width:160px">工位</th>
|
|
|
+ <th style="width:300px">工位名称</th>
|
|
|
<th>实际产能</th>
|
|
|
- <th>当班产能(12小时内)</th>
|
|
|
+ <th>当班产能</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
<tbody id="opTableBody"></tbody>
|
|
|
@@ -329,26 +326,19 @@
|
|
|
<div class="overview-cards">
|
|
|
<div class="overview-card">
|
|
|
<div class="oc-label">生产人数</div>
|
|
|
- <div class="oc-value"><span id="productionPeople">48</span> <span class="arrow">↑</span></div>
|
|
|
+ <div class="oc-value"><span id="productionPeople">48</span></div>
|
|
|
</div>
|
|
|
<div class="overview-card">
|
|
|
<div class="oc-label">设备数量</div>
|
|
|
- <div class="oc-value"><span id="deviceCount">56</span> <span class="arrow">↑</span></div>
|
|
|
+ <div class="oc-value"><span id="deviceCount">56</span></div>
|
|
|
</div>
|
|
|
<div class="overview-card">
|
|
|
<div class="oc-label">计划产量</div>
|
|
|
- <div class="oc-value"><span id="planProduction">221</span> <span class="arrow">↑</span></div>
|
|
|
+ <div class="oc-value"><span id="planProduction">221</span></div>
|
|
|
</div>
|
|
|
<div class="overview-card">
|
|
|
<div class="oc-label">实际产量</div>
|
|
|
- <div class="oc-value"><span id="actualProduction">201</span> <span class="arrow">↑</span></div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="overview-gauge-wrap">
|
|
|
- <canvas id="gaugeRate"></canvas>
|
|
|
- <div class="gauge-txt">
|
|
|
- <div class="val"><span id="gaugeVal">70</span>%</div>
|
|
|
- <div class="lbl">今日达成率</div>
|
|
|
+ <div class="oc-value"><span id="actualProduction">201</span></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -370,7 +360,7 @@
|
|
|
<span><i class="dot" style="background:#00FF88"></i>上线</span>
|
|
|
<span><i class="dot" style="background:#00C8FF"></i>下线</span>
|
|
|
</div>
|
|
|
- <div class="mes4-pbody" style="top:62px"><canvas id="chartHourly" class="chart"></canvas></div>
|
|
|
+ <div class="mes4-pbody"><canvas id="chartHourly" class="chart"></canvas></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -390,10 +380,12 @@
|
|
|
document.getElementById('mes4Time').textContent = pad(h) + ':' + pad(mi) + ':' + pad(s);
|
|
|
}
|
|
|
|
|
|
- // ====== 测试数据 ======
|
|
|
+ // ====== 页面数据 ======
|
|
|
var mockData = {};
|
|
|
- /** 每小时上下线:mesProduct/hourlyUpDownOkData(本页 lineSn=MP,上线 OP120 / 下线 OP190) */
|
|
|
- var HOURLY_API_CFG = { lineSn: 'MP', onlineOprno: 'OP120', offlineOprno: 'OP190' };
|
|
|
+ /** 本屏产线:XT(工位 OP060–OP100,与 mesLineProcess 一致) */
|
|
|
+ var LINE_SN = 'XT';
|
|
|
+ /** 每小时上下线:上线 OP060 / 下线 OP100 */
|
|
|
+ var HOURLY_API_CFG = { lineSn: LINE_SN, onlineOprno: 'OP060', offlineOprno: 'OP100' };
|
|
|
function buildHourLabels24() {
|
|
|
var hourLabels = [];
|
|
|
for (var i = 1; i <= 24; i++) {
|
|
|
@@ -438,24 +430,13 @@
|
|
|
weekLabels.push((d.getMonth() + 1) + '月' + d.getDate() + '日');
|
|
|
}
|
|
|
|
|
|
- // 报警信息由 updateDeviceAlarmAlertsByApi 拉取 mesDeviceAlarm/listData,展示 type
|
|
|
+ // 报警信息由 updateDeviceAlarmAlertsByApi 拉取
|
|
|
mockData.alerts = [];
|
|
|
|
|
|
- // OP 表格
|
|
|
- mockData.opTable = [
|
|
|
- { code: 'OP10', name: '上料+打码', plan: 50, hour: 50, real: 50, shift: 50 },
|
|
|
- { code: 'OP20', name: '底板组件-正面FSW', plan: 50, hour: 50, real: 50, shift: 50 },
|
|
|
- { code: 'OP25', name: '底板组件-打磨', plan: 50, hour: 50, real: 50, shift: 50 },
|
|
|
- { code: 'OP30', name: '底板组件-反面FSW', plan: 50, hour: 50, real: 50, shift: 50 },
|
|
|
- { code: 'OP40', name: '底板组件-CNC', plan: 50, hour: 50, real: 50, shift: 50 },
|
|
|
- { code: 'OP50', name: '干冰清洁', plan: 50, hour: 50, real: 50, shift: 50 },
|
|
|
- { code: 'OP55', name: '模组梁+封板预装', plan: 50, hour: 50, real: 50, shift: 50 },
|
|
|
- { code: 'OP60', name: '封板正面FSW', plan: 50, hour: 50, real: 50, shift: 50 },
|
|
|
- { code: 'OP65', name: '打磨', plan: 50, hour: 50, real: 50, shift: 50 },
|
|
|
- { code: 'OP70', name: '封板反面FSW', plan: 50, hour: 50, real: 50, shift: 50 }
|
|
|
- ];
|
|
|
-
|
|
|
- // 数据卡
|
|
|
+ // 工位表由 mesLineProcess/listData(XT,OP060–OP100)拉取,不使用假数据
|
|
|
+ mockData.opTable = [];
|
|
|
+
|
|
|
+ // 数据卡(总览固定展示用)
|
|
|
mockData.cards = {
|
|
|
productionPeople: 48,
|
|
|
deviceCount: 56,
|
|
|
@@ -521,15 +502,15 @@
|
|
|
}
|
|
|
function drawYAxis(ctx, padL, padT, cw, ch, maxV, minV) {
|
|
|
minV = minV || 0;
|
|
|
- ctx.fillStyle = 'rgba(77,201,245,0.8)';
|
|
|
- ctx.font = '11px Microsoft YaHei';
|
|
|
+ ctx.fillStyle = 'rgba(77,201,245,0.9)';
|
|
|
+ ctx.font = '13px Microsoft YaHei';
|
|
|
ctx.textAlign = 'right';
|
|
|
- ctx.strokeStyle = 'rgba(0,143,253,0.18)';
|
|
|
+ ctx.strokeStyle = 'rgba(0,143,253,0.22)';
|
|
|
ctx.lineWidth = 1;
|
|
|
for (var i = 0; i <= 4; i++) {
|
|
|
var val = Math.round(minV + (maxV - minV) * (4 - i) / 4);
|
|
|
var y = padT + ch * i / 4;
|
|
|
- ctx.fillText(val, padL - 5, y + 4);
|
|
|
+ ctx.fillText(val, padL - 6, y + 4);
|
|
|
ctx.beginPath();
|
|
|
ctx.moveTo(padL, y);
|
|
|
ctx.lineTo(padL + cw, y);
|
|
|
@@ -541,12 +522,12 @@
|
|
|
function drawDoubleBar(id, a, b, labels, maxV) {
|
|
|
var p = prepareCanvas(id); if (!p) return;
|
|
|
var ctx = p.ctx, w = p.w, h = p.h;
|
|
|
- var padL = 38, padR = 10, padT = 12, padB = 22;
|
|
|
+ var padL = 46, padR = 12, padT = 16, padB = 28;
|
|
|
var cw = w - padL - padR, ch = h - padT - padB;
|
|
|
ctx.clearRect(0, 0, w, h);
|
|
|
drawYAxis(ctx, padL, padT, cw, ch, maxV);
|
|
|
if (!a || a.length < 1) return;
|
|
|
- var gw = cw / a.length, bw = gw * 0.3;
|
|
|
+ var gw = cw / a.length, bw = gw * 0.28;
|
|
|
for (var i = 0; i < a.length; i++) {
|
|
|
var cx = padL + i * gw + gw / 2;
|
|
|
var h1 = (a[i] / maxV) * ch;
|
|
|
@@ -559,11 +540,11 @@
|
|
|
g2.addColorStop(0, '#00C8FF'); g2.addColorStop(1, 'rgba(0,200,255,0.15)');
|
|
|
ctx.fillStyle = g2;
|
|
|
ctx.fillRect(cx + 2, padT + ch - h2, bw, h2);
|
|
|
- ctx.fillStyle = '#00FF88'; ctx.font = 'bold 10px Microsoft YaHei';
|
|
|
+ ctx.fillStyle = '#00FF88'; ctx.font = 'bold 12px Microsoft YaHei';
|
|
|
ctx.textAlign = 'center'; ctx.fillText(a[i], cx - bw / 2 - 2, padT + ch - h1 - 4);
|
|
|
ctx.fillStyle = '#00C8FF'; ctx.fillText(b[i], cx + bw / 2 + 2, padT + ch - h2 - 4);
|
|
|
- ctx.fillStyle = 'rgba(77,201,245,0.9)'; ctx.font = '11px Microsoft YaHei';
|
|
|
- ctx.fillText(labels[i], cx, h - 6);
|
|
|
+ ctx.fillStyle = 'rgba(77,201,245,0.95)'; ctx.font = '13px Microsoft YaHei';
|
|
|
+ ctx.fillText(labels[i], cx, h - 8);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -699,7 +680,7 @@
|
|
|
function drawAreaLine(id, up, down, labels, maxV) {
|
|
|
var p = prepareCanvas(id); if (!p) return;
|
|
|
var ctx = p.ctx, w = p.w, h = p.h;
|
|
|
- var padL = 30, padR = 12, padT = 12, padB = 22;
|
|
|
+ var padL = 42, padR = 14, padT = 16, padB = 28;
|
|
|
var cw = w - padL - padR, ch = h - padT - padB;
|
|
|
ctx.clearRect(0, 0, w, h);
|
|
|
drawYAxis(ctx, padL, padT, cw, ch, maxV);
|
|
|
@@ -735,15 +716,15 @@
|
|
|
ctx.bezierCurveTo(cpx2, py2, cpx2, y2, x2, y2);
|
|
|
}
|
|
|
}
|
|
|
- ctx.strokeStyle = color; ctx.lineWidth = 1.6; ctx.stroke();
|
|
|
+ ctx.strokeStyle = color; ctx.lineWidth = 2; ctx.stroke();
|
|
|
}
|
|
|
draw(up, '#00FF88');
|
|
|
draw(down, '#00C8FF');
|
|
|
- ctx.fillStyle = 'rgba(77,201,245,0.9)'; ctx.font = '10px Microsoft YaHei'; ctx.textAlign = 'center';
|
|
|
+ ctx.fillStyle = 'rgba(77,201,245,0.95)'; ctx.font = '12px Microsoft YaHei'; ctx.textAlign = 'center';
|
|
|
for (var i = 0; i < labels.length; i++) {
|
|
|
if (i % 2 === 0) {
|
|
|
var x = padL + i * stepX;
|
|
|
- ctx.fillText(labels[i], x, h - 6);
|
|
|
+ ctx.fillText(labels[i], x, h - 8);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -833,8 +814,6 @@
|
|
|
html += '<tr>';
|
|
|
html += '<td class="op-col">' + r.code + '</td>';
|
|
|
html += '<td class="op-name">' + r.name + '</td>';
|
|
|
- html += '<td class="op-val">' + r.plan + '</td>';
|
|
|
- html += '<td class="op-val">' + r.hour + '</td>';
|
|
|
html += '<td class="op-val">' + r.real + '</td>';
|
|
|
html += '<td class="op-val">' + r.shift + '</td>';
|
|
|
html += '</tr>';
|
|
|
@@ -843,42 +822,24 @@
|
|
|
if (el) el.innerHTML = html;
|
|
|
}
|
|
|
|
|
|
- function applyLatestPlanNumsToTable(planNums) {
|
|
|
- var list = mockData.opTable || [];
|
|
|
- for (var i = 0; i < list.length; i++) {
|
|
|
- list[i].plan = planNums;
|
|
|
- }
|
|
|
- renderTable();
|
|
|
+ function parseOprnoNum(code) {
|
|
|
+ var m = String(code || '').toUpperCase().match(/^OP(\d+)/);
|
|
|
+ return m ? parseInt(m[1], 10) : -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ function isOprnoInRange(code, fromNum, toNum) {
|
|
|
+ var n = parseOprnoNum(code);
|
|
|
+ return n >= fromNum && n <= toNum;
|
|
|
}
|
|
|
|
|
|
+ function applyLatestPlanNumsToTable(planNums) {}
|
|
|
function calcRatePercent(actual, plan) {
|
|
|
actual = Number(actual) || 0;
|
|
|
plan = Number(plan) || 0;
|
|
|
- if (plan <= 0) {
|
|
|
- return 0;
|
|
|
- }
|
|
|
+ if (plan <= 0) return 0;
|
|
|
return Math.round(actual * 100 / plan);
|
|
|
}
|
|
|
-
|
|
|
- function syncAchieveRateWithPlan() {
|
|
|
- mockData.cards = mockData.cards || {};
|
|
|
- mockData.achieveRate = mockData.achieveRate || { labels: [], actual: [] };
|
|
|
- var planNums = Number(mockData.cards.planProduction) || 0;
|
|
|
- var actualProduction = Number(mockData.cards.actualProduction) || 0;
|
|
|
- var labels = mockData.achieveRate.labels || [];
|
|
|
- var actualSeries = mockData.achieveRate.actual || [];
|
|
|
- var targetSeries = [];
|
|
|
- for (var i = 0; i < labels.length; i++) {
|
|
|
- targetSeries.push(planNums);
|
|
|
- }
|
|
|
- var maxVal = Math.max.apply(null, targetSeries.concat(actualSeries).concat([10]));
|
|
|
- mockData.achieveRate.target = targetSeries;
|
|
|
- mockData.achieveRate.max = maxVal <= 0 ? 10 : Math.ceil(maxVal * 1.1);
|
|
|
- mockData.achieveRate.min = 0;
|
|
|
- mockData.rate = calcRatePercent(actualProduction, planNums);
|
|
|
- renderCards();
|
|
|
- drawAllCharts();
|
|
|
- }
|
|
|
+ function syncAchieveRateWithPlan() {}
|
|
|
|
|
|
function updateLineProcessTableByApi() {
|
|
|
$.ajax({
|
|
|
@@ -888,7 +849,7 @@
|
|
|
data: {
|
|
|
pageNo: 1,
|
|
|
pageSize: 5000,
|
|
|
- lineSn: 'MP'
|
|
|
+ lineSn: LINE_SN
|
|
|
},
|
|
|
success: function(resp) {
|
|
|
var rows = extractUserList(resp);
|
|
|
@@ -896,19 +857,21 @@
|
|
|
for (var i = 0; i < rows.length; i++) {
|
|
|
var p = rows[i] || {};
|
|
|
var ls = String(p.lineSn || p.line_sn || '').toUpperCase();
|
|
|
- if (ls !== 'MP') continue;
|
|
|
+ if (ls !== LINE_SN) continue;
|
|
|
+ var code = p.oprno || '';
|
|
|
+ if (!isOprnoInRange(code, 60, 100)) continue;
|
|
|
out.push({
|
|
|
- code: p.oprno || '',
|
|
|
+ code: code,
|
|
|
name: p.title || '',
|
|
|
- plan: (mockData.cards && mockData.cards.planProduction) || 0,
|
|
|
- hour: 0,
|
|
|
real: 0,
|
|
|
- shift: 0
|
|
|
+ shift: 0,
|
|
|
+ _opNum: parseOprnoNum(code)
|
|
|
});
|
|
|
}
|
|
|
+ out.sort(function(a, b) { return a._opNum - b._opNum; });
|
|
|
+ mockData.opTable = out;
|
|
|
+ renderTable();
|
|
|
if (out.length > 0) {
|
|
|
- mockData.opTable = out;
|
|
|
- renderTable();
|
|
|
updateHourlyCapacityByApi();
|
|
|
}
|
|
|
}
|
|
|
@@ -917,77 +880,58 @@
|
|
|
|
|
|
function updateHourlyCapacityByApi() {
|
|
|
var list = mockData.opTable || [];
|
|
|
- if (!list.length) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (!list.length) return;
|
|
|
var oprnos = [];
|
|
|
for (var i = 0; i < list.length; i++) {
|
|
|
- if (list[i] && list[i].code) {
|
|
|
- oprnos.push(list[i].code);
|
|
|
- }
|
|
|
+ if (list[i] && list[i].code) oprnos.push(list[i].code);
|
|
|
}
|
|
|
- if (!oprnos.length) {
|
|
|
- return;
|
|
|
- }
|
|
|
- fetchCapacityInto('hourlyCapacityData', 'MP', oprnos, 'hour');
|
|
|
- fetchCapacityInto('dayCapacityData', 'MP', oprnos, 'real');
|
|
|
- fetchCapacityInto('shiftCapacityData', 'MP', oprnos, 'shift');
|
|
|
+ if (!oprnos.length) return;
|
|
|
+ fetchCapacityInto('dayCapacityData', LINE_SN, oprnos, 'real', {});
|
|
|
+ fetchCapacityInto('shiftCapacityData', LINE_SN, oprnos, 'shift', { fixedTwoShift: 'true' });
|
|
|
}
|
|
|
|
|
|
- // 通用:拉取产能数据并写入表格指定字段(hour=小时产能, real=实际/当天产能, shift=当班产能)
|
|
|
- function fetchCapacityInto(action, lineSn, oprnos, field) {
|
|
|
+ function fetchCapacityInto(action, lineSn, oprnos, field, extraData) {
|
|
|
+ var data = { lineSn: lineSn, oprnos: oprnos.join(',') };
|
|
|
+ if (extraData) {
|
|
|
+ for (var k in extraData) {
|
|
|
+ if (extraData.hasOwnProperty(k)) data[k] = extraData[k];
|
|
|
+ }
|
|
|
+ }
|
|
|
$.ajax({
|
|
|
url: '${ctx}/mes/mesProductRecord/' + action,
|
|
|
type: 'POST',
|
|
|
dataType: 'json',
|
|
|
- data: {
|
|
|
- lineSn: lineSn,
|
|
|
- oprnos: oprnos.join(',')
|
|
|
- },
|
|
|
+ data: data,
|
|
|
success: function(resp) {
|
|
|
- if (!resp || resp.result !== 'true' || !resp.data) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (!resp || resp.result !== 'true' || !resp.data) return;
|
|
|
var map = resp.data || {};
|
|
|
var list = mockData.opTable || [];
|
|
|
for (var i = 0; i < list.length; i++) {
|
|
|
var code = list[i].code || '';
|
|
|
- list[i][field] = Number(map[code]) || 0;
|
|
|
+ var formatted = code.length === 6 ? code.substring(0, 5) : code;
|
|
|
+ list[i][field] = Number(map[code] != null ? map[code] : map[formatted]) || 0;
|
|
|
}
|
|
|
renderTable();
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- function updateLatestPlanProductionByApi() {
|
|
|
- $.ajax({
|
|
|
- url: '${ctx}/mes/mesPlan/latestPlanNums',
|
|
|
- type: 'POST',
|
|
|
- dataType: 'json',
|
|
|
- success: function(resp) {
|
|
|
- if (!resp || resp.result !== 'true' || !resp.data) {
|
|
|
- return;
|
|
|
- }
|
|
|
- var planNums = Number(resp.data.nums) || 0;
|
|
|
- mockData.cards = mockData.cards || {};
|
|
|
- mockData.cards.planProduction = planNums;
|
|
|
- applyLatestPlanNumsToTable(planNums);
|
|
|
- syncAchieveRateWithPlan();
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
+ function updateLatestPlanProductionByApi() {}
|
|
|
|
|
|
- // 近七日产品质检数据分析(产出=半成品入库,不良=返工记录),按产线 MP 统计
|
|
|
+ // 近七日产品质检:产出=OP100 OK,不良=返工 OP070–OP100
|
|
|
function updateWeekQualityByApi() {
|
|
|
$.ajax({
|
|
|
url: '${ctx}/mes/mesProductRecord/weekQualityData',
|
|
|
type: 'POST',
|
|
|
dataType: 'json',
|
|
|
- data: { lineSn: 'MP' },
|
|
|
+ data: {
|
|
|
+ lineSn: LINE_SN,
|
|
|
+ producedOprno: 'OP100',
|
|
|
+ defectOprnoFrom: 'OP070',
|
|
|
+ defectOprnoTo: 'OP100'
|
|
|
+ },
|
|
|
success: function(resp) {
|
|
|
- if (!resp || resp.result !== 'true' || !resp.data) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (!resp || resp.result !== 'true' || !resp.data) return;
|
|
|
var d = resp.data;
|
|
|
var produced = d.produced || [];
|
|
|
var defect = d.defect || [];
|
|
|
@@ -1002,68 +946,16 @@
|
|
|
labels: d.labels || [],
|
|
|
produced: produced,
|
|
|
defect: defect,
|
|
|
- max: Math.ceil(maxV * 1.2)
|
|
|
- };
|
|
|
- // 近七日产量分析(折线图):产量取每日入库数量(半成品入库管理)
|
|
|
- var pMax = 0, pMin = Number.MAX_VALUE;
|
|
|
- for (var k = 0; k < produced.length; k++) {
|
|
|
- var v = Number(produced[k]) || 0;
|
|
|
- if (v > pMax) pMax = v;
|
|
|
- if (v < pMin) pMin = v;
|
|
|
- }
|
|
|
- if (pMin === Number.MAX_VALUE) { pMin = 0; }
|
|
|
- var pad = Math.max(1, Math.round((pMax - pMin) * 0.2));
|
|
|
- mockData.productTrend = {
|
|
|
- labels: d.labels || [],
|
|
|
- data: produced,
|
|
|
- max: pMax + pad,
|
|
|
- min: Math.max(0, pMin - pad)
|
|
|
+ max: Math.ceil(maxV * 1.15) || 10
|
|
|
};
|
|
|
drawAllCharts();
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- // 各月产量统计图(半成品入库管理按月统计),按产线 MP
|
|
|
- function updateMonthlyProductByApi() {
|
|
|
- $.ajax({
|
|
|
- url: '${ctx}/mes/mesProductRecord/monthlyProductData',
|
|
|
- type: 'POST',
|
|
|
- dataType: 'json',
|
|
|
- data: { lineSn: 'MP' },
|
|
|
- success: function(resp) {
|
|
|
- if (!resp || resp.result !== 'true' || !resp.data) {
|
|
|
- return;
|
|
|
- }
|
|
|
- var d = resp.data;
|
|
|
- var data = d.data || [];
|
|
|
- var maxV = 10;
|
|
|
- for (var i = 0; i < data.length; i++) {
|
|
|
- if (Number(data[i]) > maxV) maxV = Number(data[i]);
|
|
|
- }
|
|
|
- mockData.monthly = {
|
|
|
- labels: d.labels || [],
|
|
|
- data: data,
|
|
|
- max: Math.ceil(maxV * 1.2)
|
|
|
- };
|
|
|
- drawAllCharts();
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- function renderCards() {
|
|
|
- var c = mockData.cards || {};
|
|
|
- var m = {
|
|
|
- productionPeople: c.productionPeople,
|
|
|
- deviceCount: c.deviceCount,
|
|
|
- planProduction: c.planProduction,
|
|
|
- actualProduction: c.actualProduction,
|
|
|
- gaugeVal: mockData.rate
|
|
|
- };
|
|
|
- for (var k in m) {
|
|
|
- var el = document.getElementById(k);
|
|
|
- if (el && m[k] != null) el.textContent = m[k];
|
|
|
- }
|
|
|
- }
|
|
|
+ function updateMonthlyProductByApi() {}
|
|
|
+
|
|
|
+ function renderCards() {}
|
|
|
|
|
|
function extractUserList(resp) {
|
|
|
if (!resp) return [];
|
|
|
@@ -1231,9 +1123,9 @@
|
|
|
data: {
|
|
|
pageNo: pageNo,
|
|
|
pageSize: pageSize,
|
|
|
- oprno: 'OP190',
|
|
|
+ oprno: 'OP100',
|
|
|
content: 'OK',
|
|
|
- lineSn: 'MP',
|
|
|
+ lineSn: LINE_SN,
|
|
|
updateDate_gte: dayRange.gte,
|
|
|
updateDate_lte: dayRange.lte
|
|
|
},
|
|
|
@@ -1241,7 +1133,7 @@
|
|
|
var list = extractUserList(resp);
|
|
|
for (var i = 0; i < list.length; i++) {
|
|
|
var r = list[i] || {};
|
|
|
- if (isOpr190Family(r.oprno) && isRecordContentOk(r.content) && isRecordWithinToday(r, dayRange) && String(r.lineSn || r.line_sn || '').toUpperCase() === 'MP') {
|
|
|
+ if (isOprnoInRange(r.oprno, 100, 100) && isRecordContentOk(r.content) && isRecordWithinToday(r, dayRange) && String(r.lineSn || r.line_sn || '').toUpperCase() === LINE_SN) {
|
|
|
totalOk++;
|
|
|
}
|
|
|
}
|
|
|
@@ -1263,31 +1155,18 @@
|
|
|
function drawAllCharts() {
|
|
|
var q = mockData.qualityInspect;
|
|
|
if (q) drawDoubleBar('chartQuality', q.produced, q.defect, q.labels, q.max);
|
|
|
- var t = mockData.productTrend;
|
|
|
- if (t) drawLineChart('chartProduct', t.data, t.labels, t.max, t.min, '#00C8FF');
|
|
|
- var a = mockData.achieveRate;
|
|
|
- if (a) drawDualLine('chartAchieve', a.target, a.actual, a.labels, a.max, a.min);
|
|
|
var hr = mockData.hourly;
|
|
|
if (hr) drawAreaLine('chartHourly', hr.up, hr.down, hr.labels, hr.max);
|
|
|
- var mo = mockData.monthly;
|
|
|
- if (mo) drawMonthBar('chartMonth', mo.data, mo.labels, mo.max);
|
|
|
- drawGauge('gaugeRate', mockData.rate || 0);
|
|
|
}
|
|
|
|
|
|
function refreshData() {
|
|
|
generateMockData();
|
|
|
renderAlerts();
|
|
|
renderTable();
|
|
|
- renderCards();
|
|
|
- updateLatestPlanProductionByApi();
|
|
|
updateLineProcessTableByApi();
|
|
|
- updateProductionPeopleByApi();
|
|
|
- updateDeviceCountByApi();
|
|
|
- updateActualProductionByApi();
|
|
|
updateDeviceAlarmAlertsByApi();
|
|
|
updateHourlyUpDownByApi();
|
|
|
updateWeekQualityByApi();
|
|
|
- updateMonthlyProductByApi();
|
|
|
drawAllCharts();
|
|
|
}
|
|
|
|