cola-web/src/views/Inspection/index.vue

703 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="inspection-container">
<div class="header">
<div class="header_left">
<!-- 日期输入框 -->
<IxDateInput label="日期" name="date" :value="currentDate" class="input-spacing" @dateChange="handleDateChange">
</IxDateInput>
<!-- 班次选择框 -->
<IxSelect Outline id="triggerId" v-model="shift" label="班次" hideListHeader="true" class="input-spacing" @valueChange="handleShiftChange">
<IxSelectItem icon="sun" label="白班" value="0"></IxSelectItem>
<IxSelectItem icon="moon" label="晚班" value="1"></IxSelectItem>
</IxSelect>
<!-- 状态选择框 -->
<IxSelect Outline id="statusSelect" v-model="status" label="状态" hideListHeader="true" class="input-spacing">
<IxSelectItem icon="sun" label="全部" value="0"></IxSelectItem>
<IxSelectItem icon="sun" label="正常" value="1"></IxSelectItem>
<IxSelectItem label="报警" value="2"></IxSelectItem>
</IxSelect>
<!-- 查询按钮 -->
<IxButton @click="handleSearch" class="input-spacing btn-style"> 刷新 </IxButton>
<!-- 导出按钮 -->
<IxButton class="input-spacing btn-style" @click="handleExport"> 导出全部 </IxButton>
</div>
<div class="header_right">
<!-- 设备列表按钮 -->
<span v-for="(item, index) in deviceList" :key="item.id">
<IxButton :outline="selectedDeviceTypeId !== item.id" class="btn-style" @click="handleDeviceListChange(item.id)">
{{ item.name }}
</IxButton>
</span>
</div>
</div>
<div class="inspection-table">
<div class="table-container">
<!-- 表头部分 -->
<div class="table-row fixed-row">
<span class="fixed-width title-width"></span>
<span class="fixed-width"></span>
<span class="fixed-width"></span>
<span class="fixed-width"></span>
<span class="fixed-width" v-for="(hour, index) in hours" :key="hour">
<IxButton :disabled="confirmedHours.includes(hour) || hourCheckStatus[hour] !== null" class="custom-button" @click="handleInspection(hour, index)">
{{ hourCheckStatus[hour] !== null ? hourCheckTime[hour] : '确认' }}
</IxButton>
</span>
</div>
<div class="table-row-1 fixed-row">
<span class="header-row title-width">点检项目</span>
<span class="header-row">单位</span>
<span class="header-row">参考值</span>
<span class="header-row">当前值</span>
<span class="header-row" v-for="hour in hours" :key="hour"
:style="{ backgroundColor: getDeviceNameById(selectedDeviceTypeId) === '灌注机' ? '#00FFB9' : '#00E4FF', color: '#000028' }">
{{ hour }}
</span>
</div>
<!-- 点检项目列表 -->
<IxEventList>
<IxEventListItem v-for="(item, index) in inspectionItems" :key="index" color="color-success" class="testbg" :class="{
'alarm-cell-title': Object.values(item.data).some(data => data.checkStatus === 0),
'highlight-cell-title': Object.values(item.data).some(data => data.checkStatus === 1 && !Object.values(item.data).some(data => data.checkStatus === 0))
}">
<div class="table-row">
<span class="fixed-width title-width">{{ item.label }}</span>
<span class="fixed-width">{{ item.unit || '--' }}</span>
<span class="fixed-width">{{ item.reference || '--' }}</span>
<span class="fixed-width">{{ item.current }}</span> <!-- 确保这里绑定的是 item.current -->
<span class="fixed-width" v-for="hour in hours" :key="hour"
:class="{
'highlight-cell': item.data[hour]?.checkStatus === 1,
'alarm-cell': item.data[hour]?.checkStatus === 0
}"
@click="item.data[hour]?.checkStatus === 0 || item.data[hour]?.checkStatus === 1 ? handleAlarmInspection(hour, index, item.data[hour]?.checkStatus) : null">
{{ formatNumberWithCommas(item.data[hour]?.value) || '--' }}
</span>
</div>
</IxEventListItem>
</IxEventList>
</div>
</div>
<!-- 点检表单 -->
<InspectionForm v-if="showForm" :itemName="selectedItemName" :itemTime="selectedItemTime" :timezone="timezone" :currentAlarmInfo="currentAlarmInfo" @close="showForm = false" @submit="(currentTime, reason) => handleFormSubmit(selectedItemIndex, reason)" />
</div>
</template>
<script setup>
// 引入必要的库和组件
import moment from 'moment';
import momentTimezone from 'moment-timezone';
import { ref, computed, getCurrentInstance, onMounted, defineEmits, onUnmounted } from 'vue';
import { IxDatePicker, IxButton, IxDropdownItem, IxEventList, IxEventListItem, IxSelect, IxSelectItem, IxDateInput } from '@siemens/ix-vue';
import InspectionForm from './InspectionForm.vue';
import { getInspectionCurrent, getInspectionData, getCheckParas, getDeviceList, exportExcel, sharpConfirm, alarmReasonConfirm } from '@/api/inspection';
const emit = defineEmits(['send-data']);
// 设置为您期望的时区,比如 "Asia/Shanghai"
const timezone = 'Asia/Shanghai';
// 全局响应式变量
const globalTime = ref(moment().tz(timezone).format('YYYY-MM-DD HH:mm'));
const isAutoUpdate = ref(true); // 是否自动更新
const sendDataToParent = () => {
emit('send-data', 'SIEMENS MIS 1.0 无菌产线点检界面', 'other-app');
};
// 获取当前时间的小时数
const currentHour = new Date().getHours();
// 根据当前时间确定默认班次
const defaultShift = currentHour >= 7 && currentHour < 19 ? '0' : '1'; // 早班为0晚班为1
// 定义响应式变量
const shift = ref(defaultShift); // 设置默认班次
const status = ref('0'); // 确保 status 变量已定义
// 设备列表
const deviceList = ref([]);
const selectedDeviceTypeId = ref(null);
const showForm = ref(false);
const selectedItemName = ref('');
const selectedItemTime = ref('');
const selectedItemIndex = ref(null);
const currentDeviceId = ref(null);
const selectedDate = ref(null);
const formatDate = (date) => {
const d = new Date(date);
const year = d.getFullYear();
const month = (d.getMonth() + 1).toString().padStart(2, '0');
const day = d.getDate().toString().padStart(2, '0');
return `${year}/${month}/${day}`;
};
const formatNumberWithCommas = (number) => {
if (number === null || number === undefined) return '--';
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
};
const currentAlarmInfo = ref({
currentStatus: '',
currentAlarmId: '',
currentCheckParamId: '',
});
const currentDate = formatDate(new Date());
// 计算当前班次的时间段
const hours = computed(() => {
const shiftValue = shift.value;
const hours = [];
if (shiftValue === '0') {
for (let i = 7; i <= 18; i++) {
hours.push(`${i < 10 ? '0' : ''}${i}:00`); // 白班 07:00-18:00
}
} else {
for (let i = 19; i <= 23;i++) {
hours.push(`${i}:00`); // 晚班 19:00-23:00
}
for (let i = 0; i <= 6; i++) {
hours.push(`${i < 10 ? '0' : ''}${i}:00`); // 晚班 00:00-06:00
}
}
return hours;
});
// JSON 数据
const data = ref({});
const inspectionItems = ref([]);
const confirmedHours = ref([]);
const confirmedTimes = ref({});
// 点检状态
const hourCheckStatus = ref({});
const hourCheckTime = ref({});
const alarmId = ref({}); // 改为双层结构:{ [deviceId: string]: { [recordTime: string]: number } }
const hourCheckValid = ref({});
const formatTime = (time) => {
const date = new Date(time);
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
};
// 点检按钮点击事件
const handleInspection = (hour, index) => {
selectedItemName.value = inspectionItems.value[index].label;
selectedItemTime.value = hour;
selectedItemIndex.value = index; // 保存当前索引
// 获取当前时间点所有设备报警ID
const currentAlarmIds = [];
// 校验是否已经确认异常数据
if (hourCheckValid.value[hour] !== 1) {
showWarningMessage('存在设备异常,无法执行点检操作!');
return;
}
// 校验这个时刻这一列有咩有报警未处理的数据也就是checkStatus为0的数据
if (inspectionItems.value.some(item => item.data[hour]?.checkStatus === 0)) {
showWarningMessage('存在报警数据未处理,无法执行点检操作!');
return;
}
// 遍历所有设备收集报警ID和状态
Object.keys(alarmId.value).forEach(deviceId => {
if (alarmId.value[deviceId]?.[hour]) {
currentAlarmIds.push(alarmId.value[deviceId][hour]);
}
});
console.log("🚀 ~ handleInspection ~ currentAlarmIds:", currentAlarmIds)
showConfirmMessage('确认要执行点检操作吗?', async () => {
await sharpConfirm(currentAlarmIds, 'admin').then(() => {
confirmedHours.value.push(selectedItemTime.value);
confirmedTimes.value[selectedItemTime.value] = moment().tz(timezone).format('HH:mm');
hourCheckStatus.value[hour] = 1; // 更新点检状态
hourCheckTime.value[hour] = confirmedTimes.value[selectedItemTime.value]; // 更新点检时间
});
}, () => {
showWarningMessage('取消点检操作');
});
};
// 点检按钮点击事件
const handleAlarmInspection = (hour, index, status) => {
selectedItemName.value = inspectionItems.value[index].label;
selectedItemTime.value = hour;
selectedItemIndex.value = index; // 确保在这里设置 selectedItemIndex
// 从数据项中获取实际设备ID
currentDeviceId.value = inspectionItems.value[index].data[hour]?.deviceId;
currentAlarmInfo.value = {
currentStatus : status === 0 ? '未处理' : '已处理',
currentAlarmId: alarmId.value[currentDeviceId.value]?.[selectedItemTime.value],
currentCheckParamId: inspectionItems.value[index].data[hour]?.checkParamId
}
showForm.value = true;
};
// 点检表单提交事件
const handleFormSubmit = async (index, reason) => {
await alarmReasonConfirm({
alarmId: currentAlarmInfo.value.currentAlarmId, // alarmId.value[selectedItemTime.value],
checkParamId: currentAlarmInfo.value.currentCheckParamId,//inspectionItems.value[index].data[selectedItemTime.value]checkParamId,
alarmReason: reason,
checkUser: 'admin'
}).then(() => {
inspectionItems.value[index].data[selectedItemTime.value].checkStatus = 1; // 更新点检项目状态
inspectionItems.value[index].data[selectedItemTime.value].checkUser = 'admin'; // 更新点检人员
inspectionItems.value[index].data[selectedItemTime.value].checkText = '点检正常'; // 更新点检结果
});
showForm.value = false;
};
const { proxy } = getCurrentInstance();
const showInfoMessage = (message) => {
proxy.$message.info(message);
};
const showInfoMessageWithAction = (message, action) => {
proxy.$message.$confirm(message, action);
};
const showDangerMessage = (message) => {
proxy.$message.danger(message);
};
const showWarningMessage = (message) => {
proxy.$message.warning(message);
};
const showConfirmMessage = (message, onConfirm, onCancel) => {
proxy.$message.confirm(message, onConfirm, onCancel);
};
// 日期变化处理函数
const handleDateChange = (event) => {
selectedDate.value = event.target.value;
fetchInspectionData();
fetchCurrentValues();
};
// 班次变化处理函数
const handleShiftChange = (event) => {
shift.value = event.detail;
fetchInspectionData();
fetchCurrentValues();
};
// 切换设备
const handleDeviceListChange = (id) => {
selectedDeviceTypeId.value = id;
fetchInspectionData();
fetchCurrentValues();
};
// 获取设备列表
const fetchDeviceList = async () => {
try {
const response = await getDeviceList();
if (response.code === 200) {
deviceList.value = response.data;
selectedDeviceTypeId.value = deviceList.value[0].id; // 默认选中第一个设备
} else {
showWarningMessage('获取设备列表失败!');
}
} catch (error) {
console.error('Error fetching device list:', error);
}
};
// 获取当前值、参考值、单位等信息
const fetchCurrentValues = async () => {
try {
const deviceId = selectedDeviceTypeId.value; // 根据实际情况设置设备ID
const dateValue = selectedDate.value || currentDate;
const response = await getInspectionCurrent(deviceId, dateValue);
const params = await getCheckParas(deviceId, dateValue);
if (response.data) {
const currentValueList = response.data;
console.log("🚀 ~ fetchCurrentValues ~ currentValueList:", currentValueList)
const currentValues = {};
currentValueList.forEach(item => {
// 解析设备实时数据并过滤掉值为 null 的项
for (const [key, value] of Object.entries(item.data)) {
if (value !== null) {
currentValues[key] = value;
}
}
});
console.log("🚀 ~ fetchCurrentValues ~ currentValues:", currentValues)
inspectionItems.value.forEach(item => {
item.current = formatNumberWithCommas(currentValues?.[item.name]) || '--'; // 添加对 currentValues 的检查
});
} else {
showWarningMessage('获取当前设备当前值失败!');
}
if (params.data) {
const paramData = params.data;
inspectionItems.value.forEach(item => {
const param = paramData.find(param => {
return param.keyname === item.name;
});
if (param) {
item.label = param.projectDescription;
item.reference = param.referenceValue;
item.unit = param.unit;
}
});
} else {
showWarningMessage('获取设备具体信息失败!');
}
} catch (error) {
console.error('Error fetching current values:', error);
}
};
// 获取点检数据
const fetchInspectionData = async () => {
try {
const deviceTypeId = selectedDeviceTypeId.value; // 根据实际情况设置设备ID
const shiftValue = shift.value;
const dateValue = selectedDate.value || currentDate;
const response = await getInspectionData(deviceTypeId, dateValue, shiftValue);
if (response.data) {
const inspectionData = response.data;
const itemsMap = {};
inspectionData.forEach(record => {
const recordTime = record.recordTime;
const data = record.data;
const deviceId = record.deviceId;
hourCheckStatus.value[recordTime] = record.hourCheckStatus;
hourCheckTime.value[recordTime] = formatTime(record.hourCheckTime);
if (!alarmId.value[deviceId]) {
alarmId.value[deviceId] = {} // 初始化设备ID对应的存储空间
}
alarmId.value[deviceId][recordTime] = record.alarmId;
hourCheckValid.value[recordTime] = record.hourCheckValid;
for (const [name, valueObj] of Object.entries(data)) {
if (valueObj === null || valueObj.valule === null) continue; // 过滤掉值为 null 的属性
if (!itemsMap[name]) {
itemsMap[name] = {
name,
label: name, // 可以根据需要调整label的值
reference: 0,
current: 0,
unit: '', // 添加单位字段
data: {}
};
}
itemsMap[name].data[recordTime] = {
value: valueObj.valule,
checkStatus: valueObj.checkStatus,
checkText: valueObj.checkText,
checkParamId: valueObj.checkParamId,
checkUser: valueObj.checkUser,
deviceId: record.deviceId,
// hourCheckStatus: valueObj.hourCheckStatus,
// hourCheckTime: valueObj.hourCheckTime
};
}
});
inspectionItems.value = Object.values(itemsMap);
} else {
showWarningMessage('获取点检数据失败!');
}
} catch (error) {
console.error('Error fetching inspection data:', error);
}
};
// 查询接口数据
const handleSearch = async () => {
try {
await Promise.all([fetchInspectionData(), fetchCurrentValues()]);
// showInfoMessage('数据刷新成功');
} catch (error) {
showWarningMessage('数据刷新失败');
console.error('Error refreshing data:', error);
}
};
const handleExport = () => {
// 增加一个判断判断当前的数据中inspectionItems.value中是否有报警数据如果有则提示不允许导出
if (inspectionItems.value.some(item => Object.values(item.data).some(data => data.checkStatus === 0))) {
showWarningMessage('请先处理报警数据,再进行导出!');
return;
}
showInfoMessage('导出全部');
exportExcel(selectedDeviceTypeId.value, selectedDate.value || currentDate, shift.value)
.then((response) => {
if (response) {
// 有数据的处理
const url = window.URL.createObjectURL(new Blob([response]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'inspection_data.xlsx');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showInfoMessage('导出成功');
} else {
// 没有数据的处理
showWarningMessage('没有可导出的数据');
}
})
.catch((error) => {
console.error('导出 Excel 失败:', error);
showDangerMessage('导出 Excel 失败,请稍后重试');
});
};
// 通过设备ID获取设备名称
const getDeviceNameById = (id) => {
const device = deviceList.value.find(device => device.id === id);
return device ? device.name : '';
};
// let autoUpdateInterval = null;
onMounted(async () => {
await fetchDeviceList();
await handleSearch();
sendDataToParent();
// const setupAutoUpdate = () => {
// if (isAutoUpdate.value) {
// autoUpdateInterval = setInterval(() => {
// fetchCurrentValues();
// fetchInspectionData();
// }, 60000); // 每分钟刷新一次接口
// } else if (autoUpdateInterval) {
// clearInterval(autoUpdateInterval);
// autoUpdateInterval = null;
// }
// };
// setupAutoUpdate();
// 监听 update-history 事件
// window.addEventListener('update-history', (event) => {
// isAutoUpdate.value = false;
// selectedDate.value = event.detail;
// fetchCurrentValues();
// fetchInspectionData();
// setupAutoUpdate();
// });
// 监听 reset 事件
// window.addEventListener('reset', (event) => {
// isAutoUpdate.value = true;
// selectedDate.value = moment().tz(timezone).format('YYYY-MM-DD HH:mm');
// fetchCurrentValues();
// fetchInspectionData();
// setupAutoUpdate();
// });
});
// onUnmounted(() => {
// if (autoUpdateInterval) {
// clearInterval(autoUpdateInterval);
// }
// });
</script>
<style scoped>
/* 样式定义 */
.inspection-container {
width: calc(100% + 1.5rem);
margin: 0 -1.5rem 0 0 !important;
height: 100vh;
/* 修改为100vh使其高度铺满整个视口 */
background-color: #000028;
color: white;
font-size: 1.2rem;
/* 调大全局字体大小 */
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
height: 10%;
}
.header_left {
display: flex;
align-items: center;
width: 70%;
/* 设置宽度,确保三个搜索框都能正常显示 */
font-size: 1rem;
/* 调大左侧头部字体大小 */
}
.header_right {
display: flex;
align-items: center;
gap: 10px;
/* 添加间距 */
font-size: 1rem;
/* 调大右侧头部字体大小 */
}
.inspection-table {
width: 100%;
height: 90%;
/* 修改为90%,使其高度铺满剩余部分 */
overflow-x: visible;
/* 添加水平滚动条 */
}
.table-container {
display: flex;
flex-direction: column;
min-width: 100%;
overflow-x: visible;
/* 添加水平滚动条 */
position: relative; /* 添加此行以确保表头和数据在同一容器内滚动 */
}
.table-row, .table-row-1 {
display: flex;
justify-content: space-between;
align-items: center;
flex: 1;
min-width: max-content; /* 添加此行以确保表格内容不会被压缩 */
}
.table-row-1 {
height: 100%;
display: flex;
justify-content: space-around;
align-items: center;
flex: 1;
position: sticky;
top: 0;
z-index: 2;
background-color: #000028; /* 添加此行以确保表头背景色一致 */
}
.table-row-1.fixed-row {
top: 40px;
z-index: 2;
}
.fixed-width {
width: 6.5rem;
/* 设置固定宽度 */
height: 2.5rem;
text-align: center;
/* 居中对齐 */
font-size: 1rem;
/* 调大固定宽度元素字体大小 */
display: flex;
align-items: center;
justify-content: center;
}
.header-row {
display: flex;
width: 6.5rem;
height: 2.5rem;
background-color: #23233C;
justify-content: center;
align-items: center;
font-size: 1rem;
margin: 0 0 0.5rem 0;
}
.title-width {
width: 15rem;
}
.btn-style {
margin: 1.8rem 0.5rem 0 0;
}
.testbg {
/* 这里是因为它tm组件里的还用的好像是webcomponent但是tmd不对外提供修改的东西好在有个css颜色变量可以修改 */
--theme-event-item--background: #23233C;
/* --theme-event-item--background--hover: blue; */
/* --theme-color-success */
}
.testbg1 {
--theme-event-item--background: #000028;
}
/**如果要改按钮的颜色,那么上面就不要用 :style 改成用 :class 来设置这个总没问题吧然后再f12看按钮有哪些颜色变量 */
/* 定义按钮颜色的 CSS 类 */
.custom-button {
--theme-btn-primary--background: #FFA849;
--theme-btn-primary--background--hover: #FFA849;
}
.drop-down {
width: calc(25% - 1.25rem);
background-color: #23233C;
}
:deep(ix-date-input) {
--theme-menu--background: #23233C;
padding-left: 1rem;
}
.search-label {
margin-right: 10px;
margin-left: 20px;
color: white;
font-size: 1rem;
}
.input-spacing {
margin-right: 1rem;
--theme-color-2: #23233C;
}
.fixed-row {
position: sticky;
top: 0;
background-color: #000028;
z-index: 2;
margin-left: 1rem;
}
.highlight-cell {
background-color: orange;
cursor: pointer;
}
.alarm-cell {
background-color: red;
cursor: pointer;
}
.highlight-cell-title {
--theme-color-success: orange;
}
.alarm-cell-title {
--theme-color-success: red;
}
</style>