Files
klp-oa/klp-ui/src/views/wms/post/eqp/index.vue

818 lines
38 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="app-container count-container">
<DragResizePanel :initialSize="280" :minSize="280" :maxSize="600">
<template #panelA>
<div class="left-panel">
<div class="panel-header">
<div class="header-title">
<i class="el-icon-s-tools"></i>
<span>检修单</span>
<el-button size="mini" type="text" icon="el-icon-refresh" @click="getList" style="margin-left:4px;" title="刷新列表"></el-button>
</div>
</div>
<div class="search-row">
<el-input v-model="queryParams.planNo" placeholder="搜索计划编号..." clearable prefix-icon="el-icon-search"
size="small" @keyup.enter.native="handleQuery" @clear="handleQuery" />
<el-button type="primary" size="small" @click="handleAdd">
<i class="el-icon-plus"></i>
</el-button>
</div>
<div v-loading="loading" class="list-body">
<div v-for="item in dataList" :key="item.planId" class="list-item"
:class="{ active: currentRow && currentRow.planId === item.planId }" @click="handleRowClick(item)">
<div class="item-main">
<span class="item-title">{{ item.planNo }}</span>
<span class="item-sub">{{ item.planName }}</span>
</div>
<div class="item-actions">
<el-button size="mini" type="text" icon="el-icon-edit" @click.stop="handleUpdate(item)"></el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click.stop="handleDelete(item)"></el-button>
</div>
</div>
<div v-if="dataList.length === 0 && !loading" class="list-empty">
<i class="el-icon-folder-opened"></i>
<span>暂无检修单数据</span>
</div>
</div>
<div class="list-footer">
<pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="getList" />
</div>
</div>
</template>
<template #panelB>
<div class="right-panel">
<div v-if="!currentRow" class="empty-tip">
<i class="el-icon-info"></i>
<span>请在左侧列表中选择一条检修单查看详情</span>
</div>
<div v-else class="right-content">
<!-- 基本信息栏 -->
<div class="info-bar">
<div class="info-bar-left">
<span class="info-label">产线</span>
<span class="info-value">{{ currentRow.productionLine || '未设置' }}</span>
<el-divider direction="vertical" />
<span class="info-label">时间</span>
<span class="info-value">{{ parseTime(currentRow.plannedStartTime, '{y}-{m}-{d}') }} ~ {{ parseTime(currentRow.plannedEndTime, '{y}-{m}-{d}') }}</span>
</div>
<div class="info-bar-right">
<el-button size="mini" icon="el-icon-search" @click="queryInspectionRecords" :disabled="!currentRow.productionLine">查询检修记录</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(currentRow)">编辑</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(currentRow)">删除</el-button>
</div>
</div>
<!-- 上半部分检修记录 -->
<div class="section inspection-section">
<div class="section-header">
<span class="section-title">检修记录 <span class="en-sub">· Inspection Records</span></span>
<span class="section-count" v-if="inspectionRecords.length"> {{ inspectionTotal }} </span>
</div>
<el-table :data="inspectionRecords" border size="small" v-loading="inspectionLoading" height="100%"
@selection-change="handleInspectionSelectionChange" ref="inspectionTable">
<el-table-column type="selection" width="40" />
<el-table-column label="设备部件" align="center" prop="partName" min-width="120" show-overflow-tooltip />
<el-table-column label="班次" align="center" width="70">
<template slot-scope="scope">{{ scope.row.shift == 1 ? '白班' : '夜班' }}</template>
</el-table-column>
<el-table-column label="巡检时间" align="center" width="150">
<template slot-scope="scope">{{ parseTime(scope.row.inspectTime, '{y}-{m}-{d} {h}:{i}') }}</template>
</el-table-column>
<el-table-column label="运行状态" align="center" width="80">
<template slot-scope="scope">
<el-tag v-if="scope.row.runStatus == 1" type="success" size="mini">正常</el-tag>
<el-tag v-else type="danger" size="mini">故障</el-tag>
</template>
</el-table-column>
<el-table-column label="巡检人" align="center" prop="inspector" width="90" />
<el-table-column label="异常描述" align="center" prop="abnormalDesc" min-width="150" show-overflow-tooltip />
<el-table-column label="备注" align="center" prop="remark" min-width="120" show-overflow-tooltip />
</el-table>
<div v-if="!inspectionLoading && inspectionQueried && inspectionRecords.length === 0" class="empty-data">
该产线该时间段内暂无检修记录
</div>
<div v-if="!inspectionQueried" class="empty-data">
点击"查询检修记录"查看该产线时间段内的巡检数据
</div>
</div>
<!-- 下半部分维修明细 -->
<div class="section detail-section">
<div class="section-header">
<span class="section-title">维修明细 <span class="en-sub">· Maintenance Details</span></span>
<div class="section-actions">
<el-button size="mini" type="primary" plain icon="el-icon-plus" @click="handleAddDetail">新增</el-button>
<el-button v-if="selectedInspectionRows.length"
size="mini" type="success" plain icon="el-icon-plus" @click="addDetailFromSelection">
从选中记录添加{{ selectedInspectionRows.length }}
</el-button>
</div>
</div>
<el-table :data="detailList" border size="small" v-loading="detailLoading" height="100%">
<el-table-column label="编号" align="center" width="60">
<template slot-scope="scope">{{ scope.$index + 1 }}</template>
</el-table-column>
<el-table-column label="时间" align="center" width="130">
<template slot-scope="scope">
<el-date-picker
v-if="scope.row._editing"
v-model="scope.row.itemPlanDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择日期"
style="width: 118px;" />
<span v-else>{{ parseTime(scope.row.itemPlanDate, '{y}/{m}/{d}') || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="具体工作内容" align="center" min-width="220">
<template slot-scope="scope">
<MemoInput
v-if="scope.row._editing"
v-model="scope.row.repairContent"
storageKey="maintenanceRepairContent"
matchRule="contain"
triggerMode="focus"
placeholder="请输入具体工作内容" />
<div v-else class="cell-pre-wrap">{{ scope.row.repairContent || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="产线执行人员" align="center" min-width="150">
<template slot-scope="scope">
<MemoInput
v-if="scope.row._editing"
v-model="scope.row.lineExecutor"
storageKey="maintenanceLineExecutor"
matchRule="contain"
triggerMode="focus"
placeholder="请输入产线执行人员" />
<div v-else class="cell-pre-wrap">{{ scope.row.lineExecutor || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="设备执行人员" align="center" min-width="150">
<template slot-scope="scope">
<MemoInput
v-if="scope.row._editing"
v-model="scope.row.equipmentExecutor"
storageKey="maintenanceEquipmentExecutor"
matchRule="contain"
triggerMode="focus"
placeholder="请输入设备执行人员" />
<div v-else class="cell-pre-wrap">{{ scope.row.equipmentExecutor || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="预计开始时间" align="center" min-width="120">
<template slot-scope="scope">
<MemoInput
v-if="scope.row._editing"
v-model="scope.row.plannedStartTime"
storageKey="maintenancePlannedStartTime"
matchRule="contain"
triggerMode="focus"
:inputFormatter="normalizeHm"
placeholder="如 07:00" />
<span v-else>{{ scope.row.plannedStartTime || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="预计结束时间" align="center" min-width="120">
<template slot-scope="scope">
<MemoInput
v-if="scope.row._editing"
v-model="scope.row.plannedCompleteTime"
storageKey="maintenancePlannedCompleteTime"
matchRule="contain"
triggerMode="focus"
:inputFormatter="normalizeHm"
placeholder="如 18:30" />
<span v-else>{{ scope.row.plannedCompleteTime || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="实际开始时间" align="center" min-width="120">
<template slot-scope="scope">
<MemoInput
v-if="scope.row._editing"
v-model="scope.row.actualStartTime"
storageKey="maintenanceActualStartTime"
matchRule="contain"
triggerMode="focus"
:inputFormatter="normalizeHm"
placeholder="如 07:00" />
<span v-else>{{ scope.row.actualStartTime || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="实际结束时间" align="center" min-width="120">
<template slot-scope="scope">
<MemoInput
v-if="scope.row._editing"
v-model="scope.row.actualCompleteTime"
storageKey="maintenanceActualCompleteTime"
matchRule="contain"
triggerMode="focus"
:inputFormatter="normalizeHm"
placeholder="如 18:30" />
<span v-else>{{ scope.row.actualCompleteTime || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="总耗时h" align="center" min-width="110">
<template slot-scope="scope">
<MemoInput
v-if="scope.row._editing"
v-model="scope.row.totalHours"
storageKey="maintenanceTotalHours"
matchRule="contain"
triggerMode="focus"
placeholder="如 9.5小时" />
<span v-else>{{ scope.row.totalHours || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="验收人" align="center" min-width="110">
<template slot-scope="scope">
<MemoInput
v-if="scope.row._editing"
v-model="scope.row.acceptanceUser"
storageKey="maintenanceAcceptanceUser"
matchRule="contain"
triggerMode="focus"
placeholder="请输入验收人" />
<span v-else>{{ scope.row.acceptanceUser || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="是否完成" align="center" width="100">
<template slot-scope="scope">
<el-select
v-if="scope.row._editing"
v-model="scope.row.detailStatus"
placeholder="请选择"
style="width: 80px;">
<el-option label="否" :value="0" />
<el-option label="是" :value="2" />
</el-select>
<span v-else>{{ scope.row.detailStatus === 2 ? '' : '' }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" min-width="180">
<template slot-scope="scope">
<MemoInput
v-if="scope.row._editing"
v-model="scope.row.remark"
storageKey="maintenanceRemark"
matchRule="contain"
triggerMode="focus"
placeholder="请输入备注" />
<div v-else class="cell-pre-wrap">{{ scope.row.remark || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="140" fixed="right">
<template slot-scope="scope">
<template v-if="scope.row._editing">
<el-button size="mini" type="text" icon="el-icon-check" @click="saveDetailRow(scope.row)">保存</el-button>
<el-button size="mini" type="text" icon="el-icon-close" @click="cancelEditDetail(scope.row)">取消</el-button>
</template>
<template v-else>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleEditDetail(scope.row)">编辑</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDeleteDetail(scope.row)">删除</el-button>
</template>
</template>
</el-table-column>
</el-table>
<div v-if="detailList.length === 0 && !detailLoading" class="empty-data">暂无维修明细请手动添加</div>
</div>
</div>
</div>
</template>
</DragResizePanel>
<!-- 新增/编辑计划弹窗 -->
<el-dialog :title="title" :visible.sync="open" width="580px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="110px">
<el-form-item label="计划名称" prop="planName">
<el-input v-model="form.planName" placeholder="请输入计划名称" />
</el-form-item>
<el-form-item label="产线" prop="productionLine">
<el-select v-model="form.productionLine" placeholder="请选择产线" clearable style="width:100%">
<el-option v-for="item in lineList" :key="item.lineId" :label="item.lineName" :value="item.lineName" />
</el-select>
</el-form-item>
<el-form-item label="开始时间" prop="plannedStartTime">
<el-date-picker clearable v-model="form.plannedStartTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择" style="width:100%" />
</el-form-item>
<el-form-item label="结束时间" prop="plannedEndTime">
<el-date-picker clearable v-model="form.plannedEndTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择" style="width:100%" />
</el-form-item>
<el-form-item label="维修类型" prop="repairType">
<el-select v-model="form.repairType" placeholder="请选择" style="width:100%">
<el-option label="定期保养" :value="1" />
<el-option label="安全整改" :value="2" />
<el-option label="专项检修" :value="3" />
<el-option label="故障维修" :value="4" />
</el-select>
</el-form-item>
<el-form-item label="优先级" prop="priorityLevel">
<el-select v-model="form.priorityLevel" placeholder="请选择" style="width:100%">
<el-option label="普通" :value="1" />
<el-option label="重要" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="计划说明" prop="planDescription">
<el-input v-model="form.planDescription" type="textarea" :rows="3" placeholder="请输入计划说明" />
</el-form-item>
<el-form-item label="负责部门" prop="dutyDept">
<el-input v-model="form.dutyDept" placeholder="请输入负责部门" />
</el-form-item>
<el-form-item label="负责人" prop="planOwner">
<el-input v-model="form.planOwner" placeholder="请输入负责人" />
</el-form-item>
<el-form-item label="预算金额(元)" prop="budgetAmount">
<el-input-number v-model="form.budgetAmount" :min="0" :precision="2" style="width:100%" placeholder="请输入预算金额" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listMaintenancePlan, getMaintenancePlan, addMaintenancePlan, updateMaintenancePlan, delMaintenancePlan } from "@/api/flow/maintenancePlan";
import { listMaintenancePlanDetail, addMaintenancePlanDetail, updateMaintenancePlanDetail, delMaintenancePlanDetail } from "@/api/flow/maintenancePlanDetail";
import { listEquipmentInspectionRecord } from "@/api/mes/eqp/equipmentInspectionRecord";
import { listProductionLine } from "@/api/wms/productionLine";
import DragResizePanel from "@/components/DragResizePanel/index.vue";
import MemoInput from "@/components/MemoInput/index.vue";
import { parseTime } from '@/utils/klp'
export default {
name: "EqpMaintenance",
components: { DragResizePanel, MemoInput },
data() {
return {
loading: false,
detailLoading: false,
buttonLoading: false,
inspectionLoading: false,
total: 0,
dataList: [],
currentRow: null,
title: "",
open: false,
form: {},
rules: {
planName: [{ required: true, message: "请输入计划名称", trigger: "blur" }],
productionLine: [{ required: true, message: "请选择产线", trigger: "change" }],
plannedStartTime: [{ required: true, message: "请选择开始时间", trigger: "change" }],
plannedEndTime: [{ required: true, message: "请选择结束时间", trigger: "change" }],
repairType: [{ required: true, message: "请选择维修类型", trigger: "change" }]
},
queryParams: { pageNum: 1, pageSize: 10, planNo: undefined },
lineList: [],
// 检修记录
inspectionRecords: [],
inspectionTotal: 0,
inspectionQueried: false,
selectedInspectionRows: [],
// 维修明细
detailList: []
};
},
created() {
this.loadLineList();
},
methods: {
parseTime,
normalizeHm(value) {
if (value === null || value === undefined) return '';
var s = String(value).trim();
if (!s) return '';
var m = s.match(/^(\d{1,2})(?::(\d{1,2}))?$/);
if (!m) return s;
var hh = parseInt(m[1], 10);
var mm = m[2] === undefined ? 0 : parseInt(m[2], 10);
if (isNaN(hh) || isNaN(mm) || hh < 0 || hh > 23 || mm < 0 || mm > 59) return s;
return String(hh).padStart(2, '0') + ':' + String(mm).padStart(2, '0');
},
async loadLineList() {
try {
const res = await listProductionLine({ pageSize: 999 });
if (res.rows) this.lineList = res.rows;
this.getList();
} catch (e) { console.error('加载产线列表失败', e); }
},
getList() {
this.loading = true;
var self = this;
listMaintenancePlan(this.queryParams).then(function(response) {
self.dataList = response.rows;
self.total = response.total;
self.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
handleRowClick(row) {
this.currentRow = row;
this.inspectionQueried = false;
this.inspectionRecords = [];
this.selectedInspectionRows = [];
this.loadDetail(row.planId);
},
loadDetail(planId) {
this.detailLoading = true;
var self = this;
getMaintenancePlan(planId).then(function(response) {
self.currentRow = response.data;
self.loadDetailList(planId);
self.detailLoading = false;
}).catch(function() { self.detailLoading = false; });
},
loadDetailList(planId) {
var self = this;
listMaintenancePlanDetail({ planId: planId, pageNum: 1, pageSize: 999 }).then(function(r) {
self.detailList = (r.rows || []).map(function(item) {
return self.decorateDetailRow(item);
});
});
},
// ---- 检修记录查询 ----
queryInspectionRecords() {
if (!this.currentRow || !this.currentRow.productionLine) {
this.$modal.msgWarning("请先在编辑中设置产线");
return;
}
this.inspectionLoading = true;
var self = this;
var params = {
pageNum: 1,
pageSize: 9999,
productionLine: this.getProductionLineId(this.currentRow.productionLine),
startTime: this.currentRow.plannedStartTime ? parseTime(this.currentRow.plannedStartTime, '{y}-{m}-{d}') : undefined,
endTime: this.currentRow.plannedEndTime ? parseTime(this.currentRow.plannedEndTime, '{y}-{m}-{d}') : undefined
};
listEquipmentInspectionRecord(params).then(function(response) {
self.inspectionRecords = response.rows || [];
self.inspectionTotal = response.total || 0;
self.inspectionQueried = true;
self.$nextTick(function() {
if (self.$refs.inspectionTable) {
self.$refs.inspectionTable.clearSelection();
}
});
}).finally(function() {
self.inspectionLoading = false;
});
},
getProductionLineId(name) {
var line = this.lineList.find(function(l) { return l.lineName === name; });
return line ? line.lineId : null;
},
handleInspectionSelectionChange(selection) {
this.selectedInspectionRows = selection;
},
addDetailFromSelection() {
if (this.selectedInspectionRows.length === 0) return;
var self = this;
var planId = this.currentRow.planId;
this.$modal.loading("正在添加维修明细...");
var promises = this.selectedInspectionRows.map(function(rec) {
return addMaintenancePlanDetail({
planId: planId,
componentName: rec.partName || '',
productionLine: self.currentRow.productionLine || rec.productionLine || '',
maintenanceCategory: 1,
itemPlanDate: rec.inspectTime ? parseTime(rec.inspectTime, '{y}-{m}-{d}') : parseTime(new Date(), '{y}-{m}-{d}'),
repairContent: rec.abnormalDesc || '',
lineExecutor: rec.inspector || '',
equipmentExecutor: '',
plannedStartTime: '',
plannedCompleteTime: '',
actualStartTime: '',
actualCompleteTime: '',
totalHours: '',
acceptanceUser: '',
repairUser: rec.inspector || '',
detailStatus: 0,
remark: rec.remark || ''
});
});
Promise.all(promises).then(function() {
self.$modal.closeLoading();
self.$modal.msgSuccess("已从检修记录添加 " + self.selectedInspectionRows.length + " 条明细");
self.loadDetailList(planId);
self.$refs.inspectionTable.clearSelection();
self.selectedInspectionRows = [];
}).catch(function() {
self.$modal.closeLoading();
self.$modal.msgError("操作失败");
});
},
// ---- 计划 CRUD ----
handleAdd() {
this.reset();
this.open = true;
this.title = "新增检修单";
},
handleUpdate(row) {
this.reset();
var self = this;
getMaintenancePlan(row.planId).then(function(response) {
self.form = response.data;
self.open = true;
self.title = "修改检修单";
});
},
reset() {
this.form = {
planId: undefined, planNo: undefined, planName: undefined,
productionLine: undefined,
repairType: undefined, priorityLevel: 1,
plannedStartTime: undefined, plannedEndTime: undefined,
dutyDept: undefined, planOwner: undefined,
budgetAmount: undefined, planDescription: undefined,
remark: undefined
};
this.resetForm("form");
},
cancel() {
this.open = false;
this.reset();
},
submitForm() {
var self = this;
this.$refs["form"].validate(function(valid) {
if (valid) {
self.buttonLoading = true;
self.form.planNo = self.form.planNo || self.form.planName;
if (self.form.planId != null) {
updateMaintenancePlan(self.form).then(function() {
self.$modal.msgSuccess("修改成功");
self.open = false;
self.getList();
if (self.currentRow && self.currentRow.planId === self.form.planId) {
self.loadDetail(self.currentRow.planId);
}
}).finally(function() { self.buttonLoading = false; });
} else {
addMaintenancePlan(self.form).then(function(response) {
self.$modal.msgSuccess("新增成功");
self.open = false;
self.getList();
}).finally(function() { self.buttonLoading = false; });
}
}
});
},
handleDelete(row) {
var self = this;
this.$modal.confirm('是否确认删除检修单"' + row.planNo + '"').then(function() {
self.loading = true;
return delMaintenancePlan(row.planId);
}).then(function() {
self.loading = false;
self.getList();
if (self.currentRow && self.currentRow.planId === row.planId) {
self.currentRow = null;
self.detailList = [];
self.inspectionRecords = [];
self.inspectionQueried = false;
}
self.$modal.msgSuccess("删除成功");
}).catch(function() { }).finally(function() { self.loading = false; });
},
// ---- 明细管理 ----
handleAddDetail() {
if (!this.currentRow || !this.currentRow.planId) return;
if (this.getEditingDetailRow()) {
this.$modal.msgWarning("请先保存或取消当前编辑行");
return;
}
var row = this.decorateDetailRow(this.getDefaultDetailRow());
row._isNew = true;
row._editing = true;
row._origin = null;
this.detailList.unshift(row);
},
handleEditDetail(row) {
if (!row) return;
var editing = this.getEditingDetailRow();
if (editing && editing !== row) {
this.$modal.msgWarning("请先保存或取消当前编辑行");
return;
}
this.beginDetailEdit(row);
},
beginDetailEdit(row) {
if (!row) return;
row._origin = JSON.parse(JSON.stringify({
itemPlanDate: row.itemPlanDate,
repairContent: row.repairContent,
lineExecutor: row.lineExecutor,
equipmentExecutor: row.equipmentExecutor,
plannedStartTime: row.plannedStartTime,
plannedCompleteTime: row.plannedCompleteTime,
actualStartTime: row.actualStartTime,
actualCompleteTime: row.actualCompleteTime,
totalHours: row.totalHours,
acceptanceUser: row.acceptanceUser,
detailStatus: row.detailStatus,
remark: row.remark
}));
row._editing = true;
},
cancelEditDetail(row) {
if (!row) return;
if (row._isNew) {
this.detailList = this.detailList.filter(function(r) { return r !== row; });
return;
}
if (row._origin) {
row.itemPlanDate = row._origin.itemPlanDate;
row.repairContent = row._origin.repairContent;
row.lineExecutor = row._origin.lineExecutor;
row.equipmentExecutor = row._origin.equipmentExecutor;
row.plannedStartTime = row._origin.plannedStartTime;
row.plannedCompleteTime = row._origin.plannedCompleteTime;
row.actualStartTime = row._origin.actualStartTime;
row.actualCompleteTime = row._origin.actualCompleteTime;
row.totalHours = row._origin.totalHours;
row.acceptanceUser = row._origin.acceptanceUser;
row.detailStatus = row._origin.detailStatus;
row.remark = row._origin.remark;
}
row._editing = false;
row._origin = null;
},
handleDeleteDetail(row) {
var self = this;
if (!row) return;
if (row._isNew) {
this.detailList = this.detailList.filter(function(r) { return r !== row; });
return;
}
this.$modal.confirm('确认删除该维修明细?').then(function() {
return delMaintenancePlanDetail(row.detailId);
}).then(function() {
self.$modal.msgSuccess("删除成功");
self.loadDetailList(self.currentRow.planId);
}).catch(function() { });
},
getEditingDetailRow() {
return this.detailList.find(function(r) { return r && r._editing; }) || null;
},
decorateDetailRow(row) {
var r = row || {};
if (!r._rowKey) r._rowKey = r.detailId ? ("detail_" + r.detailId) : ("tmp_" + Date.now() + "_" + Math.random().toString(16).slice(2));
if (typeof r._editing !== 'boolean') r._editing = false;
if (typeof r._isNew !== 'boolean') r._isNew = false;
if (!r._origin) r._origin = null;
if (r.detailStatus === undefined || r.detailStatus === null) r.detailStatus = 0;
return r;
},
getDefaultDetailRow() {
return {
planId: this.currentRow.planId,
componentName: '',
productionLine: this.currentRow.productionLine || '',
maintenanceCategory: 1,
itemPlanDate: parseTime(new Date(), '{y}-{m}-{d}'),
repairContent: '',
lineExecutor: '',
equipmentExecutor: '',
plannedStartTime: '',
plannedCompleteTime: '',
actualStartTime: '',
actualCompleteTime: '',
totalHours: '',
acceptanceUser: '',
detailStatus: 0,
remark: ''
};
},
buildDetailPayload(row) {
return {
detailId: row.detailId,
planId: this.currentRow.planId,
componentName: row.componentName,
productionLine: this.currentRow.productionLine || row.productionLine || '',
maintenanceCategory: row.maintenanceCategory === undefined ? 1 : row.maintenanceCategory,
itemPlanDate: row.itemPlanDate,
repairContent: row.repairContent,
lineExecutor: row.lineExecutor,
equipmentExecutor: row.equipmentExecutor,
plannedStartTime: row.plannedStartTime,
plannedCompleteTime: row.plannedCompleteTime,
actualStartTime: row.actualStartTime,
actualCompleteTime: row.actualCompleteTime,
totalHours: row.totalHours,
acceptanceUser: row.acceptanceUser,
detailStatus: row.detailStatus,
remark: row.remark
};
},
saveDetailRow(row) {
if (!row) return;
if (!row.repairContent) {
this.$modal.msgWarning("请输入具体工作内容");
return;
}
var self = this;
var payload = this.buildDetailPayload(row);
var api = row._isNew || !row.detailId ? addMaintenancePlanDetail : updateMaintenancePlanDetail;
api(payload).then(function() {
self.$modal.msgSuccess(row._isNew || !row.detailId ? "添加成功" : "修改成功");
self.loadDetailList(self.currentRow.planId);
});
}
}
};
</script>
<style scoped>
.count-container { height: calc(100vh - 84px); }
/* ========== 左侧面板 ========== */
.left-panel { display: flex; flex-direction: column; height: 100%; background: #f5f7fa; border-right: 1px solid #e4e7ed; }
.panel-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 14px 8px; background: #f5f7fa; }
.header-title { display: flex; align-items: center; gap: 6px; font-size: 14px; font-weight: 600; color: #303133; }
.header-title i { color: #409eff; font-size: 16px; }
.search-row { display: flex; align-items: center; gap: 6px; padding: 0 14px 10px; background: #f5f7fa; }
.search-row .el-input { flex: 1; }
.list-body { flex: 1; overflow-y: auto; padding: 0 6px; }
.list-body { scrollbar-width: thin; scrollbar-color: rgba(144, 147, 153, 0.45) transparent; }
.list-body::-webkit-scrollbar { width: 8px; height: 8px; }
.list-body::-webkit-scrollbar-track { background: transparent; }
.list-body::-webkit-scrollbar-thumb { background: rgba(144, 147, 153, 0.35); border-radius: 6px; }
.list-body::-webkit-scrollbar-thumb:hover { background: rgba(144, 147, 153, 0.55); }
.list-item { display: flex; align-items: center; padding: 10px 12px; margin-bottom: 2px; cursor: pointer; border-radius: 6px; transition: all 0.15s; }
.list-item:hover { background: #ebeef5; }
.list-item.active { background: #d9ecff; }
.list-item.active .item-title { color: #409eff; font-weight: 600; }
.item-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.item-title { font-size: 13px; font-weight: 500; color: #303133; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.item-sub { font-size: 12px; color: #909399; }
.item-actions { flex-shrink: 0; opacity: 0; transition: opacity 0.15s; }
.list-item:hover .item-actions { opacity: 1; }
.list-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 60px 0; color: #c0c4cc; font-size: 13px; gap: 8px; }
.list-empty i { font-size: 32px; }
.list-footer { border-top: 1px solid #e4e7ed; padding: 2px 8px 0; background: #f5f7fa; }
/* ========== 右侧面板 ========== */
.right-panel { height: 100%; overflow: hidden; display: flex; flex-direction: column; background: #faf8f5; }
.empty-tip { display: flex; align-items: center; justify-content: center; height: 100%; color: #909399; font-size: 14px; gap: 8px; }
.right-content { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; padding: 12px 16px; }
/* 基本信息栏 */
.info-bar { display: flex; align-items: center; justify-content: space-between; padding: 8px 16px; background: #fff; border: 1px solid #e4e7ed; border-radius: 4px; margin-bottom: 10px; flex-shrink: 0; }
.info-bar-left { display: flex; align-items: center; gap: 6px; font-size: 13px; }
.info-label { color: #909399; }
.info-value { color: #303133; font-weight: 500; }
.info-bar-right { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
/* 上半部分:检修记录 */
.inspection-section { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; background: #fff; border: 1px solid #e4e7ed; border-radius: 4px; margin-bottom: 10px; }
.section-header { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; border-bottom: 1px solid #ebeef5; flex-shrink: 0; }
.section-title { font-size: 13px; font-weight: 600; color: #303133; }
.section-title .en-sub { font-size: 11px; font-weight: 400; color: #c0c4cc; font-style: italic; }
.section-count { font-size: 12px; color: #909399; }
.section-actions { display: flex; align-items: center; gap: 6px; }
.inspection-section .el-table { flex: 1; overflow-y: auto; }
.inspection-section .el-table { scrollbar-width: thin; scrollbar-color: rgba(144, 147, 153, 0.45) transparent; }
.detail-section .el-table { scrollbar-width: thin; scrollbar-color: rgba(144, 147, 153, 0.45) transparent; }
.inspection-section ::v-deep .el-table,
.detail-section ::v-deep .el-table { display: flex; flex-direction: column; height: 100%; }
.inspection-section ::v-deep .el-table__body-wrapper,
.detail-section ::v-deep .el-table__body-wrapper { flex: 1; min-height: 0; }
.inspection-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar,
.detail-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar { width: 8px; height: 8px; }
.inspection-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar-track,
.detail-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar-track { background: transparent; }
.inspection-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar-thumb,
.detail-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar-thumb { background: rgba(144, 147, 153, 0.35); border-radius: 6px; }
.inspection-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar-thumb:hover,
.detail-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar-thumb:hover { background: rgba(144, 147, 153, 0.55); }
/* 下半部分:维修明细 */
.detail-section { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; background: #fff; border: 1px solid #e4e7ed; border-radius: 4px; }
.detail-section .el-table { flex: 1; overflow-y: auto; }
.cell-pre-wrap { white-space: pre-wrap; word-break: break-all; line-height: 18px; }
.empty-data { padding: 24px 0; text-align: center; color: #c0c4cc; font-size: 13px; }
/* 表格样式 */
.right-panel .el-table { border: none !important; }
.right-panel .el-table th { background: #f5f7fa !important; color: #606266 !important; font-size: 12px !important; padding: 6px 0 !important; }
.right-panel .el-table td { padding: 6px 0 !important; font-size: 12px !important; color: #303133 !important; }
.right-panel .el-divider--vertical { margin: 0 6px; }
</style>