|
|
@@ -672,7 +672,7 @@
|
|
|
<div class="card-value" id="actualProduction">225</div>
|
|
|
</div>
|
|
|
<div class="mes3-data-card">
|
|
|
- <div class="card-label">近修数量</div>
|
|
|
+ <div class="card-label">返修数量</div>
|
|
|
<div class="card-value" id="repairCount">11</div>
|
|
|
</div>
|
|
|
<div class="mes3-data-card">
|
|
|
@@ -755,7 +755,7 @@
|
|
|
|
|
|
<!-- 右侧面板3: 报警信息 -->
|
|
|
<div class="mes3-panel-right3">
|
|
|
- <div class="panel-header">报警信息</div>
|
|
|
+ <div class="panel-header">设备报警(类型)</div>
|
|
|
<div class="panel-body" style="padding: 0 15px;">
|
|
|
<div id="alertList" class="mes3-alert-list"></div>
|
|
|
</div>
|
|
|
@@ -842,13 +842,8 @@
|
|
|
max: 120
|
|
|
};
|
|
|
|
|
|
- // 6. 报警信息 (右3)
|
|
|
- mockData.alerts = [
|
|
|
- { no: 1, text: '产线不良率超过3%' },
|
|
|
- { no: 2, text: '产线不良率超过5%' },
|
|
|
- { no: 3, text: '产线不良率超过3%' },
|
|
|
- { no: 4, text: '产线不良率超过7%' }
|
|
|
- ];
|
|
|
+ // 6. 报警信息由 updateDeviceAlarmAlertsByApi 拉取 mesDeviceAlarm/listData,展示 type
|
|
|
+ mockData.alerts = [];
|
|
|
|
|
|
// 7. 设备故障统计分析 - 故障类型排名
|
|
|
mockData.faultRank = [
|
|
|
@@ -1339,14 +1334,23 @@
|
|
|
if (el) el.innerHTML = html;
|
|
|
}
|
|
|
|
|
|
- // 渲染报警信息列表
|
|
|
+ function escapeHtml(s) {
|
|
|
+ if (s == null || s === '') return '';
|
|
|
+ return String(s)
|
|
|
+ .replace(/&/g, '&')
|
|
|
+ .replace(/</g, '<')
|
|
|
+ .replace(/>/g, '>')
|
|
|
+ .replace(/"/g, '"');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 渲染报警信息列表(text 为设备报警记录的 type 字段)
|
|
|
function renderAlerts() {
|
|
|
var list = mockData.alerts || [];
|
|
|
var html = '';
|
|
|
for (var i = 0; i < list.length; i++) {
|
|
|
html += '<div class="mes3-alert-item">';
|
|
|
html += '<div class="alert-no">' + list[i].no + '</div>';
|
|
|
- html += '<div class="alert-text">' + list[i].text + '</div>';
|
|
|
+ html += '<div class="alert-text">' + escapeHtml(list[i].text) + '</div>';
|
|
|
html += '</div>';
|
|
|
}
|
|
|
var el = document.getElementById('alertList');
|
|
|
@@ -1372,10 +1376,284 @@
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ function extractList(resp) {
|
|
|
+ if (!resp) return [];
|
|
|
+ if (Object.prototype.toString.call(resp.list) === '[object Array]') return resp.list;
|
|
|
+ if (Object.prototype.toString.call(resp.rows) === '[object Array]') return resp.rows;
|
|
|
+ if (resp.data) {
|
|
|
+ if (Object.prototype.toString.call(resp.data.list) === '[object Array]') return resp.data.list;
|
|
|
+ if (Object.prototype.toString.call(resp.data.rows) === '[object Array]') return resp.data.rows;
|
|
|
+ }
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ function updateDeviceAlarmAlertsByApi() {
|
|
|
+ $.ajax({
|
|
|
+ url: '${ctx}/mes/mesDeviceAlarm/listData',
|
|
|
+ type: 'POST',
|
|
|
+ dataType: 'json',
|
|
|
+ data: {
|
|
|
+ pageNo: 1,
|
|
|
+ pageSize: 50
|
|
|
+ },
|
|
|
+ success: function(resp) {
|
|
|
+ var rows = extractList(resp);
|
|
|
+ var alerts = [];
|
|
|
+ for (var i = 0; i < rows.length; i++) {
|
|
|
+ var r = rows[i] || {};
|
|
|
+ var typ = (r.type != null && r.type !== undefined) ? String(r.type) : '';
|
|
|
+ alerts.push({ no: i + 1, text: typ });
|
|
|
+ }
|
|
|
+ mockData.alerts = alerts;
|
|
|
+ renderAlerts();
|
|
|
+ },
|
|
|
+ error: function() {
|
|
|
+ mockData.alerts = [];
|
|
|
+ renderAlerts();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function updateProductionPeopleByApi() {
|
|
|
+ $.ajax({
|
|
|
+ url: '${ctx}/sys/empUser/listData',
|
|
|
+ type: 'POST',
|
|
|
+ dataType: 'json',
|
|
|
+ data: {
|
|
|
+ pageNo: 1,
|
|
|
+ pageSize: 5000,
|
|
|
+ 'employee.office.officeName': '生产部'
|
|
|
+ },
|
|
|
+ success: function(resp) {
|
|
|
+ var peopleCount = 0;
|
|
|
+ var users = extractList(resp);
|
|
|
+ for (var i = 0; i < users.length; i++) {
|
|
|
+ var u = users[i] || {};
|
|
|
+ var office = (u.employee && u.employee.office) ? u.employee.office : {};
|
|
|
+ var officeName = office.officeName || '';
|
|
|
+ var normalizedOfficeName = (officeName + '').replace(/\s/g, '');
|
|
|
+ if (normalizedOfficeName === '生产部' || /[/\\>]+生产部$/.test(normalizedOfficeName)) {
|
|
|
+ peopleCount++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ mockData.cards = mockData.cards || {};
|
|
|
+ mockData.cards.productionPeople = peopleCount;
|
|
|
+ var target = document.getElementById('productionPeople');
|
|
|
+ if (target) target.textContent = peopleCount;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function updateDeviceCountByApi() {
|
|
|
+ $.ajax({
|
|
|
+ url: '${ctx}/mes/mesDevice/listData',
|
|
|
+ type: 'POST',
|
|
|
+ dataType: 'json',
|
|
|
+ data: {
|
|
|
+ pageNo: 1,
|
|
|
+ pageSize: 1
|
|
|
+ },
|
|
|
+ success: function(resp) {
|
|
|
+ var deviceCount = 0;
|
|
|
+ if (resp && typeof resp.count === 'number') {
|
|
|
+ deviceCount = resp.count;
|
|
|
+ } else {
|
|
|
+ deviceCount = extractList(resp).length;
|
|
|
+ }
|
|
|
+ mockData.cards = mockData.cards || {};
|
|
|
+ mockData.cards.deviceCount = deviceCount;
|
|
|
+ var target = document.getElementById('deviceCount');
|
|
|
+ if (target) target.textContent = deviceCount;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function isOpr240Family(oprno) {
|
|
|
+ var op = (oprno || '').replace(/\s/g, '').toUpperCase();
|
|
|
+ if (op.length < 5 || op.indexOf('OP240') !== 0) return false;
|
|
|
+ if (op.length === 5) return op === 'OP240';
|
|
|
+ return /^OP240[A-Z0-9_-]+$/.test(op);
|
|
|
+ }
|
|
|
+
|
|
|
+ function isRecordContentOk(content) {
|
|
|
+ var c = (content || '').replace(/\s/g, '').toUpperCase();
|
|
|
+ return c === 'OK';
|
|
|
+ }
|
|
|
+
|
|
|
+ function getCalendarDayUpdateDateRange() {
|
|
|
+ var now = new Date();
|
|
|
+ var p = function(n) { return n < 10 ? '0' + n : '' + n; };
|
|
|
+ var dateStr = now.getFullYear() + '-' + p(now.getMonth() + 1) + '-' + p(now.getDate());
|
|
|
+ return {
|
|
|
+ gte: dateStr + ' 00:00:00',
|
|
|
+ lte: dateStr + ' 23:59:59',
|
|
|
+ startMs: new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0).getTime(),
|
|
|
+ endMs: new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999).getTime()
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ function parseRecordDateMs(val) {
|
|
|
+ if (val == null || val === '') return NaN;
|
|
|
+ if (typeof val === 'number' && !isNaN(val)) return val;
|
|
|
+ var s = String(val).trim().replace(/-/g, '/');
|
|
|
+ var t = Date.parse(s);
|
|
|
+ return isNaN(t) ? NaN : t;
|
|
|
+ }
|
|
|
+
|
|
|
+ function isRecordWithinToday(r, day) {
|
|
|
+ var t = parseRecordDateMs(r.updateDate || r.update_date);
|
|
|
+ if (isNaN(t)) return false;
|
|
|
+ return t >= day.startMs && t <= day.endMs;
|
|
|
+ }
|
|
|
+
|
|
|
+ function updateActualProductionByApi() {
|
|
|
+ var dayRange = getCalendarDayUpdateDateRange();
|
|
|
+ var pageSize = 3000;
|
|
|
+ var maxPages = 200;
|
|
|
+ var pageNo = 1;
|
|
|
+ var totalOk = 0;
|
|
|
+ function fetchPage() {
|
|
|
+ $.ajax({
|
|
|
+ url: '${ctx}/mes/mesProductRecord/listData',
|
|
|
+ type: 'POST',
|
|
|
+ dataType: 'json',
|
|
|
+ data: {
|
|
|
+ pageNo: pageNo,
|
|
|
+ pageSize: pageSize,
|
|
|
+ oprno: 'OP240',
|
|
|
+ content: 'OK',
|
|
|
+ lineSn: 'XT',
|
|
|
+ updateDate_gte: dayRange.gte,
|
|
|
+ updateDate_lte: dayRange.lte
|
|
|
+ },
|
|
|
+ success: function(resp) {
|
|
|
+ var list = extractList(resp);
|
|
|
+ for (var i = 0; i < list.length; i++) {
|
|
|
+ var r = list[i] || {};
|
|
|
+ if (isOpr240Family(r.oprno) && isRecordContentOk(r.content) && isRecordWithinToday(r, dayRange) && String(r.lineSn || r.line_sn || '').toUpperCase() === 'XT') {
|
|
|
+ totalOk++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (list.length === pageSize && pageNo < maxPages) {
|
|
|
+ pageNo++;
|
|
|
+ fetchPage();
|
|
|
+ } else {
|
|
|
+ mockData.cards = mockData.cards || {};
|
|
|
+ mockData.cards.actualProduction = totalOk;
|
|
|
+ var target = document.getElementById('actualProduction');
|
|
|
+ if (target) target.textContent = totalOk;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ error: function() {
|
|
|
+ mockData.cards = mockData.cards || {};
|
|
|
+ mockData.cards.actualProduction = totalOk;
|
|
|
+ var target = document.getElementById('actualProduction');
|
|
|
+ if (target) target.textContent = totalOk;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ fetchPage();
|
|
|
+ }
|
|
|
+
|
|
|
+ function matchesXtOp240OkRecord(r) {
|
|
|
+ var t = parseRecordDateMs(r.updateDate || r.update_date);
|
|
|
+ if (isNaN(t)) return false;
|
|
|
+ if (!isOpr240Family(r.oprno) || !isRecordContentOk(r.content)) return false;
|
|
|
+ if (String(r.lineSn || r.line_sn || '').toUpperCase() !== 'XT') return false;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ function dateKeyFromMs(ms, padFn) {
|
|
|
+ var d = new Date(ms);
|
|
|
+ return d.getFullYear() + '-' + padFn(d.getMonth() + 1) + '-' + padFn(d.getDate());
|
|
|
+ }
|
|
|
+
|
|
|
+ function updateProductTrend7DaysByApi() {
|
|
|
+ var now = new Date();
|
|
|
+ var padFn = function(n) { return n < 10 ? '0' + n : '' + n; };
|
|
|
+ var labels = [];
|
|
|
+ var dayKeys = [];
|
|
|
+ var i;
|
|
|
+ for (i = 6; i >= 0; i--) {
|
|
|
+ var d = new Date(now.getFullYear(), now.getMonth(), now.getDate() - i);
|
|
|
+ labels.push((d.getMonth() + 1) + '月' + d.getDate() + '日');
|
|
|
+ dayKeys.push(d.getFullYear() + '-' + padFn(d.getMonth() + 1) + '-' + padFn(d.getDate()));
|
|
|
+ }
|
|
|
+ var oldest = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 6);
|
|
|
+ var gte = oldest.getFullYear() + '-' + padFn(oldest.getMonth() + 1) + '-' + padFn(oldest.getDate()) + ' 00:00:00';
|
|
|
+ var lte = now.getFullYear() + '-' + padFn(now.getMonth() + 1) + '-' + padFn(now.getDate()) + ' 23:59:59';
|
|
|
+
|
|
|
+ var counts = {};
|
|
|
+ for (i = 0; i < dayKeys.length; i++) {
|
|
|
+ counts[dayKeys[i]] = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ var pageSize = 3000;
|
|
|
+ var maxPages = 500;
|
|
|
+ var pageNo = 1;
|
|
|
+
|
|
|
+ function fetchPage() {
|
|
|
+ $.ajax({
|
|
|
+ url: '${ctx}/mes/mesProductRecord/listData',
|
|
|
+ type: 'POST',
|
|
|
+ dataType: 'json',
|
|
|
+ data: {
|
|
|
+ pageNo: pageNo,
|
|
|
+ pageSize: pageSize,
|
|
|
+ oprno: 'OP240',
|
|
|
+ content: 'OK',
|
|
|
+ lineSn: 'XT',
|
|
|
+ updateDate_gte: gte,
|
|
|
+ updateDate_lte: lte
|
|
|
+ },
|
|
|
+ success: function(resp) {
|
|
|
+ var list = extractList(resp);
|
|
|
+ var j;
|
|
|
+ for (j = 0; j < list.length; j++) {
|
|
|
+ var r = list[j] || {};
|
|
|
+ if (!matchesXtOp240OkRecord(r)) continue;
|
|
|
+ var t = parseRecordDateMs(r.updateDate || r.update_date);
|
|
|
+ var key = dateKeyFromMs(t, padFn);
|
|
|
+ if (Object.prototype.hasOwnProperty.call(counts, key)) {
|
|
|
+ counts[key]++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (list.length === pageSize && pageNo < maxPages) {
|
|
|
+ pageNo++;
|
|
|
+ fetchPage();
|
|
|
+ } else {
|
|
|
+ var data = [];
|
|
|
+ for (j = 0; j < dayKeys.length; j++) {
|
|
|
+ data.push(counts[dayKeys[j]] || 0);
|
|
|
+ }
|
|
|
+ var mx = Math.max.apply(null, data.concat([0]));
|
|
|
+ var maxV = mx <= 0 ? 10 : Math.ceil(mx * 1.15);
|
|
|
+ mockData.productTrend = {
|
|
|
+ labels: labels,
|
|
|
+ data: data,
|
|
|
+ max: maxV,
|
|
|
+ min: 0
|
|
|
+ };
|
|
|
+ drawAllCharts();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ error: function() {
|
|
|
+ drawAllCharts();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ fetchPage();
|
|
|
+ }
|
|
|
+
|
|
|
function refreshData() {
|
|
|
// 暂未修改后台接口,直接使用 mock 数据
|
|
|
generateMockData();
|
|
|
renderTextData();
|
|
|
+ updateProductionPeopleByApi();
|
|
|
+ updateDeviceCountByApi();
|
|
|
+ updateActualProductionByApi();
|
|
|
+ updateProductTrend7DaysByApi();
|
|
|
+ updateDeviceAlarmAlertsByApi();
|
|
|
renderFaultRank();
|
|
|
renderAlerts();
|
|
|
drawAllCharts();
|