feat(mes/eqp): 新增设备巡检管理模块及相关组件
本次提交新增了完整的设备巡检管理功能: 1. 新增QRCode组件,支持带文字描述的二维码展示 2. 新增检验部位、设备检验清单、巡检记录的CRUD API 3. 新增检验清单选择器组件 4. 新增巡检部位管理、检验清单管理、巡检记录管理页面 5. 新增设备巡检总览页面,支持拖拽分栏管理部位和检验清单,附带二维码生成打印功能 6. 新增单日设备巡检日报页面
This commit is contained in:
181
klp-ui/src/views/mes/eqp/check/day.vue
Normal file
181
klp-ui/src/views/mes/eqp/check/day.vue
Normal file
@@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<div class="app-container" v-loading="loading">
|
||||
<el-row>
|
||||
<el-form label-width="80px" inline>
|
||||
<el-form-item label="日期">
|
||||
<el-date-picker v-model="queryDate" type="date" value-format="yyyy-MM-dd"
|
||||
placeholder="选择日期" @change="handleQuery" style="width: 200px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="产线">
|
||||
<dict-select v-model="productionLine" dict-type="sys_lines" placeholder="请选择产线"
|
||||
clearable @change="handleQuery" style="width: 150px;" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleQuery">查询</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
|
||||
<el-descriptions title="巡检日报" :column="4" border style="margin-bottom: 16px;">
|
||||
<el-descriptions-item label="巡检部位数">{{ summary.partCount }}</el-descriptions-item>
|
||||
<el-descriptions-item label="巡检项数">{{ summary.checklistCount }}</el-descriptions-item>
|
||||
<el-descriptions-item label="总巡检次数">{{ summary.totalCount }}</el-descriptions-item>
|
||||
<el-descriptions-item label="通过">
|
||||
<el-tag type="success" size="small">{{ summary.passCount }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="不通过">
|
||||
<el-tag type="danger" size="small">{{ summary.failCount }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="通过率">{{ summary.passRate }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-table :data="tableData" border stripe style="width: 100%" :cell-style="cellStyle"
|
||||
@cell-mouse-enter="handleCellEnter" @cell-mouse-leave="handleCellLeave">
|
||||
<el-table-column label="巡检部位" align="center" prop="partName" width="140" />
|
||||
<el-table-column label="巡检内容" align="center" prop="checkContent" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="白班" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
<div v-if="scope.row.dayRecords && scope.row.dayRecords.length" style="display: flex; gap: 8px;">
|
||||
<div v-for="(r, idx) in scope.row.dayRecords" :key="idx" style="margin:2px 0;">
|
||||
<el-tooltip placement="top" popper-class="inspect-tooltip">
|
||||
<div slot="content">
|
||||
<div>{{ r.inspectTime }} | 白班 | {{ r.inspector }} | {{ r.runStatus == 1 ? '通过' : '不通过' }}{{ r.abnormalDesc ? '异常: ' + r.abnormalDesc : '' }}</div>
|
||||
</div>
|
||||
<span v-if="r.runStatus == 1" class="result-icon result-pass">✓</span>
|
||||
<span v-else class="result-icon result-fail">✗</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else class="result-none">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="夜班" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
<div v-if="scope.row.nightRecords && scope.row.nightRecords.length" style="display: flex; gap: 8px;">
|
||||
<div v-for="(r, idx) in scope.row.nightRecords" :key="idx" style="margin:2px 0;">
|
||||
<el-tooltip placement="top" popper-class="inspect-tooltip">
|
||||
<div slot="content">
|
||||
<div>{{ r.inspectTime }} | 夜班 | {{ r.inspector }} | {{ r.runStatus == 1 ? '通过' : '不通过' }} {{ r.abnormalDesc ? '异常: ' + r.abnormalDesc : '' }}</div>
|
||||
</div>
|
||||
<span v-if="r.runStatus == 1" class="result-icon result-pass">✓</span>
|
||||
<span v-else class="result-icon result-fail">✗</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else class="result-none">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listEquipmentPart } from "@/api/mes/eqp/equipmentPart";
|
||||
import { listEquipmentChecklist } from "@/api/mes/eqp/equipmentChecklist";
|
||||
import { listEquipmentInspectionRecord } from "@/api/mes/eqp/equipmentInspectionRecord";
|
||||
|
||||
export default {
|
||||
name: "DailyInspectionReport",
|
||||
dicts: ['sys_lines'],
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
queryDate: this.getToday(),
|
||||
productionLine: '酸轧线',
|
||||
partList: [],
|
||||
checklistList: [],
|
||||
records: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
tableData() {
|
||||
const recordMap = {};
|
||||
this.records.forEach(r => {
|
||||
const key = `${r.checkId}_${r.shift}`;
|
||||
if (!recordMap[key]) recordMap[key] = [];
|
||||
recordMap[key].push(r);
|
||||
});
|
||||
|
||||
return this.checklistList.map(cl => {
|
||||
const dayRecords = recordMap[`${cl.checkId}_1`] || [];
|
||||
const nightRecords = recordMap[`${cl.checkId}_2`] || [];
|
||||
|
||||
return {
|
||||
checkId: cl.checkId,
|
||||
partName: cl.partName,
|
||||
checkContent: cl.checkContent,
|
||||
dayRecords,
|
||||
nightRecords,
|
||||
};
|
||||
});
|
||||
},
|
||||
summary() {
|
||||
const totalCount = this.records.length;
|
||||
const passCount = this.records.filter(r => r.runStatus === 1).length;
|
||||
const failCount = this.records.filter(r => r.runStatus === 2).length;
|
||||
return {
|
||||
partCount: this.partList.length,
|
||||
checklistCount: this.checklistList.length,
|
||||
totalCount,
|
||||
passCount,
|
||||
failCount,
|
||||
passRate: totalCount > 0 ? (passCount / totalCount * 100).toFixed(1) + "%" : "0%",
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getToday() {
|
||||
const d = new Date();
|
||||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
||||
},
|
||||
cellStyle({ columnIndex }) {
|
||||
if (columnIndex === 2 || columnIndex === 3) return { fontSize: "20px", padding: "4px" };
|
||||
},
|
||||
handleCellEnter(row, column) {},
|
||||
handleCellLeave(row, column) {},
|
||||
async handleQuery() {
|
||||
if (!this.queryDate) return;
|
||||
this.loading = true;
|
||||
try {
|
||||
const partParams = {};
|
||||
if (this.productionLine) partParams.productionLine = this.productionLine;
|
||||
const [partRes, checklistRes, recordRes] = await Promise.all([
|
||||
listEquipmentPart(partParams),
|
||||
listEquipmentChecklist(partParams),
|
||||
listEquipmentInspectionRecord({ startTime: this.queryDate, endTime: this.queryDate, pageSize: 9999 }),
|
||||
]);
|
||||
if (partRes.code === 200) this.partList = partRes.rows || [];
|
||||
if (checklistRes.code === 200) this.checklistList = checklistRes.rows || [];
|
||||
if (recordRes.code === 200) this.records = recordRes.rows || [];
|
||||
} catch (e) {
|
||||
console.error("查询失败", e);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.handleQuery();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.result-icon {
|
||||
display: inline-block;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
}
|
||||
.result-pass { color: #67c23a; }
|
||||
.result-fail { color: #f56c6c; }
|
||||
.result-none { color: #c0c4cc; font-size: 16px; }
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.inspect-tooltip {
|
||||
max-width: 400px;
|
||||
line-height: 1.6;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user