feat(equipment-maintenance): 新增产线字段并完善查询条件与日期格式化

1.  在维修计划实体、BO和VO类中新增产线字段,添加excel导出注解
2.  维修计划列表查询增加产线过滤条件
3.  为维修计划明细的执行日期添加日期格式化注解
This commit is contained in:
2026-07-03 16:56:44 +08:00
parent 00f5abc0db
commit 338d205cc6
6 changed files with 237 additions and 305 deletions

View File

@@ -51,6 +51,10 @@ public class EqpMaintenancePlan extends BaseEntity {
* 审批状态0=草稿 1=待审批 2=已审批 3=已驳回 * 审批状态0=草稿 1=待审批 2=已审批 3=已驳回
*/ */
private Long approvalStatus; private Long approvalStatus;
/**
* 产线
*/
private String productionLine;
/** /**
* 计划开始时间 * 计划开始时间
*/ */

View File

@@ -36,6 +36,11 @@ public class EqpMaintenancePlanBo extends BaseEntity {
*/ */
private String planName; private String planName;
/**
* 产线
*/
private String productionLine;
/** /**
* 维修类型1=定期保养 2=安全整改 3=专项检修 4=故障维修 * 维修类型1=定期保养 2=安全整改 3=专项检修 4=故障维修
*/ */

View File

@@ -8,6 +8,7 @@ import javax.validation.constraints.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
/** /**
* 维修计划明细业务对象 eqp_maintenance_plan_detail * 维修计划明细业务对象 eqp_maintenance_plan_detail
@@ -43,6 +44,8 @@ public class EqpMaintenancePlanDetailBo extends BaseEntity {
/** /**
* 单条维修项计划执行日期 * 单条维修项计划执行日期
*/ */
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date itemPlanDate; private Date itemPlanDate;
/** /**

View File

@@ -40,6 +40,12 @@ public class EqpMaintenancePlanVo {
@ExcelProperty(value = "计划名称") @ExcelProperty(value = "计划名称")
private String planName; private String planName;
/**
* 产线
*/
@ExcelProperty(value = "产线")
private String productionLine;
/** /**
* 维修类型1=定期保养 2=安全整改 3=专项检修 4=故障维修 * 维修类型1=定期保养 2=安全整改 3=专项检修 4=故障维修
*/ */

View File

@@ -63,6 +63,7 @@ public class EqpMaintenancePlanServiceImpl implements IEqpMaintenancePlanService
LambdaQueryWrapper<EqpMaintenancePlan> lqw = Wrappers.lambdaQuery(); LambdaQueryWrapper<EqpMaintenancePlan> lqw = Wrappers.lambdaQuery();
lqw.eq(StringUtils.isNotBlank(bo.getPlanNo()), EqpMaintenancePlan::getPlanNo, bo.getPlanNo()); lqw.eq(StringUtils.isNotBlank(bo.getPlanNo()), EqpMaintenancePlan::getPlanNo, bo.getPlanNo());
lqw.like(StringUtils.isNotBlank(bo.getPlanName()), EqpMaintenancePlan::getPlanName, bo.getPlanName()); lqw.like(StringUtils.isNotBlank(bo.getPlanName()), EqpMaintenancePlan::getPlanName, bo.getPlanName());
lqw.eq(StringUtils.isNotBlank(bo.getProductionLine()), EqpMaintenancePlan::getProductionLine, bo.getProductionLine());
lqw.eq(bo.getRepairType() != null, EqpMaintenancePlan::getRepairType, bo.getRepairType()); lqw.eq(bo.getRepairType() != null, EqpMaintenancePlan::getRepairType, bo.getRepairType());
lqw.eq(bo.getPriorityLevel() != null, EqpMaintenancePlan::getPriorityLevel, bo.getPriorityLevel()); lqw.eq(bo.getPriorityLevel() != null, EqpMaintenancePlan::getPriorityLevel, bo.getPriorityLevel());
lqw.eq(bo.getPlanStatus() != null, EqpMaintenancePlan::getPlanStatus, bo.getPlanStatus()); lqw.eq(bo.getPlanStatus() != null, EqpMaintenancePlan::getPlanStatus, bo.getPlanStatus());

View File

@@ -6,13 +6,9 @@
<div class="panel-header"> <div class="panel-header">
<div class="header-title"> <div class="header-title">
<i class="el-icon-s-tools"></i> <i class="el-icon-s-tools"></i>
<span>维修计划</span> <span>检修单</span>
<el-button size="mini" type="text" icon="el-icon-refresh" @click="getList" style="margin-left:4px;" title="刷新列表"></el-button> <el-button size="mini" type="text" icon="el-icon-refresh" @click="getList" style="margin-left:4px;" title="刷新列表"></el-button>
</div> </div>
<el-select v-model="queryParams.approvalStatus" placeholder="审批状态" clearable size="mini" @change="handleQuery" class="header-filter">
<el-option label="草稿" :value="0" />
<el-option label="已驳回" :value="3" />
</el-select>
</div> </div>
<div class="search-row"> <div class="search-row">
@@ -30,10 +26,6 @@
<span class="item-title">{{ item.planNo }}</span> <span class="item-title">{{ item.planNo }}</span>
<span class="item-sub">{{ item.planName }}</span> <span class="item-sub">{{ item.planName }}</span>
</div> </div>
<div class="item-meta">
<el-tag v-if="item.approvalStatus === 0" type="info" size="mini">草稿</el-tag>
<el-tag v-else-if="item.approvalStatus === 3" type="danger" size="mini">已驳回</el-tag>
</div>
<div class="item-actions"> <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-edit" @click.stop="handleUpdate(item)"></el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click.stop="handleDelete(item)"></el-button> <el-button size="mini" type="text" icon="el-icon-delete" @click.stop="handleDelete(item)"></el-button>
@@ -41,7 +33,7 @@
</div> </div>
<div v-if="dataList.length === 0 && !loading" class="list-empty"> <div v-if="dataList.length === 0 && !loading" class="list-empty">
<i class="el-icon-folder-opened"></i> <i class="el-icon-folder-opened"></i>
<span>暂无维修计划数据</span> <span>暂无检修单数据</span>
</div> </div>
</div> </div>
@@ -56,84 +48,73 @@
<div class="right-panel"> <div class="right-panel">
<div v-if="!currentRow" class="empty-tip"> <div v-if="!currentRow" class="empty-tip">
<i class="el-icon-info"></i> <i class="el-icon-info"></i>
<span>请在左侧列表中选择一条维修计划查看详情</span> <span>请在左侧列表中选择一条检修单查看详情</span>
</div> </div>
<div v-else v-loading="detailLoading" class="detail-content"> <div v-else class="right-content">
<div class="doc-header"> <!-- 基本信息栏 -->
<div class="doc-header-top"> <div class="info-bar">
<div class="doc-title-group"> <div class="info-bar-left">
<div class="doc-title">{{ currentRow.planNo }}</div> <span class="info-label">产线</span>
<div class="doc-subtitle">Maintenance Plan</div> <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>
<div class="doc-header-right"> <div class="info-bar-right">
<el-button size="mini" type="text" icon="el-icon-refresh" @click="handleRefreshDetail" title="刷新详情">刷新</el-button> <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)" v-if="currentRow.approvalStatus !== 2">编辑</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)" v-if="currentRow.approvalStatus !== 2">删除</el-button> <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(currentRow)">删除</el-button>
</div>
</div>
<div class="doc-status-row">
<span class="doc-status-label">Approval / 状态</span>
<el-tag v-if="currentRow.approvalStatus === 0" type="info" size="small">草稿</el-tag>
<el-tag v-else-if="currentRow.approvalStatus === 1" size="small">待审批</el-tag>
<el-tag v-else-if="currentRow.approvalStatus === 2" type="success" size="small">已审批</el-tag>
<el-tag v-else-if="currentRow.approvalStatus === 3" type="danger" size="small">已驳回</el-tag>
</div> </div>
</div> </div>
<div class="detail-meta"> <!-- 上半部分检修记录 -->
<span><i class="el-icon-document"></i>{{ currentRow.planName }}</span> <div class="section inspection-section">
<span v-if="currentRow.repairType === 1">定期保养</span> <div class="section-header">
<span v-else-if="currentRow.repairType === 2">安全整改</span> <span class="section-title">检修记录 <span class="en-sub">· Inspection Records</span></span>
<span v-else-if="currentRow.repairType === 3">专项检修</span> <span class="section-count" v-if="inspectionRecords.length"> {{ inspectionTotal }} </span>
<span v-else-if="currentRow.repairType === 4">故障维修</span>
<span v-if="currentRow.priorityLevel === 1"><el-tag size="mini" type="info">普通</el-tag></span>
<span v-if="currentRow.priorityLevel === 2"><el-tag size="mini" type="danger">重要</el-tag></span>
<span v-if="currentRow.plannedStartTime"><i class="el-icon-time"></i>开始: {{ parseTime(currentRow.plannedStartTime, '{y}-{m}-{d}') }}</span>
<span v-if="currentRow.plannedEndTime"><i class="el-icon-time"></i>结束: {{ parseTime(currentRow.plannedEndTime, '{y}-{m}-{d}') }}</span>
<span v-if="currentRow.dutyDept"><i class="el-icon-s-home"></i>{{ currentRow.dutyDept }}</span>
<span v-if="currentRow.planOwner"><i class="el-icon-user-solid"></i>{{ currentRow.planOwner }}</span>
<span v-if="currentRow.budgetAmount"><i class="el-icon-money"></i>预算: ¥{{ currentRow.budgetAmount }}</span>
</div> </div>
<el-table :data="inspectionRecords" border size="small" v-loading="inspectionLoading" max-height="300"
<el-divider /> @selection-change="handleInspectionSelectionChange" ref="inspectionTable">
<el-table-column type="selection" width="40" />
<div class="section-title"> <el-table-column label="设备部件" align="center" prop="partName" min-width="120" show-overflow-tooltip />
<span>计划说明 <span class="en-sub">· Description</span></span> <el-table-column label="班次" align="center" width="70">
</div>
<div class="remark-content">{{ currentRow.planDescription || '无' }}</div>
<el-divider />
<div class="section-title">
<span>关联异常记录 <span class="en-sub">· Related Abnormal Records</span></span>
<el-button v-if="currentRow.approvalStatus === 0 || currentRow.approvalStatus === 3" size="mini" type="primary" plain icon="el-icon-plus" style="margin-left:8px;" @click="handleSelectAbnormalRecords">选择记录</el-button>
</div>
<el-table :data="abnormalList" border size="small" style="width:100%" v-loading="abnormalLoading">
<el-table-column label="设备部件" align="center" prop="partName" width="140" />
<el-table-column label="产线" align="center" prop="productionLine" width="120" />
<el-table-column label="班次" align="center" width="80">
<template slot-scope="scope">{{ scope.row.shift == 1 ? '白班' : '夜班' }}</template> <template slot-scope="scope">{{ scope.row.shift == 1 ? '白班' : '夜班' }}</template>
</el-table-column> </el-table-column>
<el-table-column label="巡检时间" align="center" width="150"> <el-table-column label="巡检时间" align="center" width="150">
<template slot-scope="scope">{{ parseTime(scope.row.inspectTime, '{y}-{m}-{d} {h}:{i}') }}</template> <template slot-scope="scope">{{ parseTime(scope.row.inspectTime, '{y}-{m}-{d} {h}:{i}') }}</template>
</el-table-column> </el-table-column>
<el-table-column label="异常描述" align="center" prop="abnormalDesc" min-width="160" show-overflow-tooltip /> <el-table-column label="运行状态" align="center" width="80">
<el-table-column label="操作" align="center" width="70" v-if="currentRow.approvalStatus === 0 || currentRow.approvalStatus === 3">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleRemoveAbnormal(scope.row)"></el-button> <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> </template>
</el-table-column> </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> </el-table>
<div v-if="abnormalList.length === 0 && !abnormalLoading" class="empty-data" style="margin-top:8px;">暂无关联异常记录</div> <div v-if="!inspectionLoading && inspectionQueried && inspectionRecords.length === 0" class="empty-data">
该产线该时间段内暂无检修记录
<div class="section-gap" />
<div class="section-title">
<span>维修明细 <span class="en-sub">· Maintenance Details</span></span>
<el-button v-if="currentRow.approvalStatus === 0 || currentRow.approvalStatus === 3" size="mini" type="primary" plain icon="el-icon-plus" style="margin-left:8px;" @click="handleAddDetail">添加</el-button>
</div> </div>
<el-table :data="detailList" border size="small" style="width:100%" v-loading="detailLoading"> <div v-if="!inspectionQueried" class="empty-data">
<el-table-column label="设备部件" align="center" prop="componentName" width="130" /> 点击"查询检修记录"查看该产线时间段内的巡检数据
</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" max-height="300">
<el-table-column label="设备部件" align="center" prop="componentName" min-width="120" show-overflow-tooltip />
<el-table-column label="产线" align="center" prop="productionLine" width="110" /> <el-table-column label="产线" align="center" prop="productionLine" width="110" />
<el-table-column label="类型" align="center" width="70"> <el-table-column label="类型" align="center" width="70">
<template slot-scope="scope"> <template slot-scope="scope">
@@ -147,31 +128,38 @@
<template slot-scope="scope">{{ parseTime(scope.row.itemPlanDate, '{y}-{m}-{d}') }}</template> <template slot-scope="scope">{{ parseTime(scope.row.itemPlanDate, '{y}-{m}-{d}') }}</template>
</el-table-column> </el-table-column>
<el-table-column label="目标厂家" align="center" prop="targetManufacturer" width="110" /> <el-table-column label="目标厂家" align="center" prop="targetManufacturer" width="110" />
<el-table-column label="操作" align="center" width="100" fixed="right" v-if="currentRow.approvalStatus === 0 || currentRow.approvalStatus === 3"> <el-table-column label="操作" align="center" width="100" fixed="right">
<template slot-scope="scope"> <template slot-scope="scope">
<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-edit" @click="handleEditDetail(scope.row)"></el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDeleteDetail(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-column>
</el-table> </el-table>
<div v-if="detailList.length === 0 && !detailLoading" class="empty-data" style="margin-top:8px;">暂无维修明细关联异常记录后自动生成或手动添加</div> <div v-if="detailList.length === 0 && !detailLoading" class="empty-data">暂无维修明细请手动添加</div>
<div class="section-gap" />
<div v-if="currentRow.approvalStatus === 0 || currentRow.approvalStatus === 3" class="form-actions">
<el-button type="primary" :loading="submitLoading" icon="el-icon-s-promotion" @click="handleSubmitApproval">提交审批</el-button>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
</DragResizePanel> </DragResizePanel>
<!-- 新增/编辑计划弹窗 --> <!-- 新增/编辑计划弹窗 -->
<el-dialog :title="title" :visible.sync="open" width="650px" append-to-body> <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 ref="form" :model="form" :rules="rules" label-width="110px">
<el-form-item label="计划名称" prop="planName"> <el-form-item label="计划名称" prop="planName">
<el-input v-model="form.planName" placeholder="请输入计划名称" /> <el-input v-model="form.planName" placeholder="请输入计划名称" />
</el-form-item> </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-form-item label="维修类型" prop="repairType">
<el-select v-model="form.repairType" placeholder="请选择" style="width:100%"> <el-select v-model="form.repairType" placeholder="请选择" style="width:100%">
<el-option label="定期保养" :value="1" /> <el-option label="定期保养" :value="1" />
@@ -186,24 +174,18 @@
<el-option label="重要" :value="2" /> <el-option label="重要" :value="2" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="计划开始时间" prop="plannedStartTime"> <el-form-item label="计划说明" prop="planDescription">
<el-date-picker clearable v-model="form.plannedStartTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择" style="width:100%" /> <el-input v-model="form.planDescription" type="textarea" :rows="3" placeholder="请输入计划说明" />
</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>
<el-form-item label="负责部门" prop="dutyDept"> <el-form-item label="负责部门" prop="dutyDept">
<el-input v-model="form.dutyDept" placeholder="请输入负责部门" /> <el-input v-model="form.dutyDept" placeholder="请输入负责部门" />
</el-form-item> </el-form-item>
<el-form-item label="计划负责人" prop="planOwner"> <el-form-item label="负责人" prop="planOwner">
<el-input v-model="form.planOwner" placeholder="请输入负责人" /> <el-input v-model="form.planOwner" placeholder="请输入负责人" />
</el-form-item> </el-form-item>
<el-form-item label="预算金额(元)" prop="budgetAmount"> <el-form-item label="预算金额(元)" prop="budgetAmount">
<el-input-number v-model="form.budgetAmount" :min="0" :precision="2" style="width:100%" placeholder="请输入预算金额" /> <el-input-number v-model="form.budgetAmount" :min="0" :precision="2" style="width:100%" placeholder="请输入预算金额" />
</el-form-item> </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="remark"> <el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" /> <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
</el-form-item> </el-form-item>
@@ -214,39 +196,6 @@
</div> </div>
</el-dialog> </el-dialog>
<!-- 选择异常巡检记录弹窗 -->
<el-dialog title="选择异常巡检记录" :visible.sync="abnormalSelectorVisible" width="800px" append-to-body>
<el-form :model="abnormalQuery" size="small" :inline="true" style="margin-bottom:12px;">
<el-form-item label="产线">
<el-select v-model="abnormalQuery.productionLine" placeholder="请选择" clearable @change="loadAbnormalRecords" style="width:140px;">
<el-option v-for="item in lineList" :key="item.lineId" :label="item.lineName" :value="item.lineId" />
</el-select>
</el-form-item>
<el-form-item label="巡检人">
<el-input v-model="abnormalQuery.inspector" placeholder="搜索" clearable style="width:120px;" @keyup.enter.native="loadAbnormalRecords" />
</el-form-item>
<el-form-item>
<el-button type="primary" size="mini" @click="loadAbnormalRecords">搜索</el-button>
</el-form-item>
</el-form>
<el-table ref="abnormalTable" :data="abnormalRecordList" border size="small" @selection-change="handleAbnormalSelectionChange" max-height="400">
<el-table-column type="selection" width="45" />
<el-table-column label="设备部件" align="center" prop="partName" width="130" />
<el-table-column label="产线" align="center" prop="productionLine" width="110" />
<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" prop="inspectTime" 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" prop="abnormalDesc" min-width="160" show-overflow-tooltip />
</el-table>
<div slot="footer" class="dialog-footer">
<el-button @click="abnormalSelectorVisible = false"> </el-button>
<el-button type="primary" @click="confirmAbnormalSelection"> 已选 {{ abnormalSelectedRecords.length }} </el-button>
</div>
</el-dialog>
<!-- 明细编辑弹窗 --> <!-- 明细编辑弹窗 -->
<el-dialog :title="detailDialogTitle" :visible.sync="detailDialogVisible" width="550px" append-to-body> <el-dialog :title="detailDialogTitle" :visible.sync="detailDialogVisible" width="550px" append-to-body>
<el-form ref="detailForm" :model="detailForm" label-width="110px"> <el-form ref="detailForm" :model="detailForm" label-width="110px">
@@ -288,7 +237,6 @@
<script> <script>
import { listMaintenancePlan, getMaintenancePlan, addMaintenancePlan, updateMaintenancePlan, delMaintenancePlan } from "@/api/flow/maintenancePlan"; import { listMaintenancePlan, getMaintenancePlan, addMaintenancePlan, updateMaintenancePlan, delMaintenancePlan } from "@/api/flow/maintenancePlan";
import { listMaintenancePlanDetail, addMaintenancePlanDetail, updateMaintenancePlanDetail, delMaintenancePlanDetail } from "@/api/flow/maintenancePlanDetail"; import { listMaintenancePlanDetail, addMaintenancePlanDetail, updateMaintenancePlanDetail, delMaintenancePlanDetail } from "@/api/flow/maintenancePlanDetail";
import { listMaintenancePlanAbnormal, addMaintenancePlanAbnormal, delMaintenancePlanAbnormal } from "@/api/flow/maintenancePlanAbnormal";
import { listEquipmentInspectionRecord } from "@/api/mes/eqp/equipmentInspectionRecord"; import { listEquipmentInspectionRecord } from "@/api/mes/eqp/equipmentInspectionRecord";
import { listProductionLine } from "@/api/wms/productionLine"; import { listProductionLine } from "@/api/wms/productionLine";
import DragResizePanel from "@/components/DragResizePanel/index.vue"; import DragResizePanel from "@/components/DragResizePanel/index.vue";
@@ -302,8 +250,7 @@ export default {
loading: false, loading: false,
detailLoading: false, detailLoading: false,
buttonLoading: false, buttonLoading: false,
submitLoading: false, inspectionLoading: false,
abnormalLoading: false,
detailButtonLoading: false, detailButtonLoading: false,
total: 0, total: 0,
dataList: [], dataList: [],
@@ -313,17 +260,19 @@ export default {
form: {}, form: {},
rules: { rules: {
planName: [{ required: true, message: "请输入计划名称", trigger: "blur" }], 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" }] repairType: [{ required: true, message: "请选择维修类型", trigger: "change" }]
}, },
queryParams: { pageNum: 1, pageSize: 10, planNo: undefined, approvalStatus: undefined }, queryParams: { pageNum: 1, pageSize: 10, planNo: undefined },
// Abnormal records
abnormalList: [],
abnormalSelectorVisible: false,
abnormalQuery: { productionLine: undefined, inspector: undefined },
abnormalRecordList: [],
abnormalSelectedRecords: [],
lineList: [], lineList: [],
// Details // 检修记录
inspectionRecords: [],
inspectionTotal: 0,
inspectionQueried: false,
selectedInspectionRows: [],
// 维修明细
detailList: [], detailList: [],
detailDialogVisible: false, detailDialogVisible: false,
detailDialogTitle: "", detailDialogTitle: "",
@@ -358,6 +307,9 @@ export default {
}, },
handleRowClick(row) { handleRowClick(row) {
this.currentRow = row; this.currentRow = row;
this.inspectionQueried = false;
this.inspectionRecords = [];
this.selectedInspectionRows = [];
this.loadDetail(row.planId); this.loadDetail(row.planId);
}, },
loadDetail(planId) { loadDetail(planId) {
@@ -365,19 +317,9 @@ export default {
var self = this; var self = this;
getMaintenancePlan(planId).then(function(response) { getMaintenancePlan(planId).then(function(response) {
self.currentRow = response.data; self.currentRow = response.data;
self.loadAbnormalList(planId);
self.loadDetailList(planId); self.loadDetailList(planId);
}).finally(function() { self.detailLoading = false; }); self.detailLoading = false;
}, }).catch(function() { self.detailLoading = false; });
loadAbnormalList(planId) {
this.abnormalLoading = true;
var self = this;
listMaintenancePlanAbnormal({ planId: planId, pageNum: 1, pageSize: 999 }).then(function(r) {
var rows = r.rows || [];
// Enrich with inspection record info — query each record
self.abnormalList = rows;
self.abnormalLoading = false;
}).catch(function() { self.abnormalLoading = false; });
}, },
loadDetailList(planId) { loadDetailList(planId) {
var self = this; var self = this;
@@ -385,16 +327,73 @@ export default {
self.detailList = r.rows || []; self.detailList = r.rows || [];
}); });
}, },
handleRefreshDetail() { // ---- 检修记录查询 ----
if (this.currentRow && this.currentRow.planId) { queryInspectionRecords() {
this.loadDetail(this.currentRow.planId); 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;
});
}, },
// ---- Plan CRUD ---- 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,
repairContent: rec.abnormalDesc || '',
repairUser: rec.inspector || '',
detailStatus: 0
});
});
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() { handleAdd() {
this.reset(); this.reset();
this.open = true; this.open = true;
this.title = "新增维修计划"; this.title = "新增检修单";
}, },
handleUpdate(row) { handleUpdate(row) {
this.reset(); this.reset();
@@ -402,17 +401,18 @@ export default {
getMaintenancePlan(row.planId).then(function(response) { getMaintenancePlan(row.planId).then(function(response) {
self.form = response.data; self.form = response.data;
self.open = true; self.open = true;
self.title = "修改维修计划"; self.title = "修改检修单";
}); });
}, },
reset() { reset() {
this.form = { this.form = {
planId: undefined, planNo: undefined, planName: undefined, planId: undefined, planNo: undefined, planName: undefined,
productionLine: undefined,
repairType: undefined, priorityLevel: 1, repairType: undefined, priorityLevel: 1,
plannedStartTime: undefined, plannedEndTime: undefined, plannedStartTime: undefined, plannedEndTime: undefined,
dutyDept: undefined, planOwner: undefined, dutyDept: undefined, planOwner: undefined,
budgetAmount: undefined, planDescription: undefined, budgetAmount: undefined, planDescription: undefined,
approvalStatus: 0, remark: undefined remark: undefined
}; };
this.resetForm("form"); this.resetForm("form");
}, },
@@ -447,7 +447,7 @@ export default {
}, },
handleDelete(row) { handleDelete(row) {
var self = this; var self = this;
this.$modal.confirm('是否确认删除维修计划"' + row.planNo + '"').then(function() { this.$modal.confirm('是否确认删除检修单"' + row.planNo + '"').then(function() {
self.loading = true; self.loading = true;
return delMaintenancePlan(row.planId); return delMaintenancePlan(row.planId);
}).then(function() { }).then(function() {
@@ -455,103 +455,25 @@ export default {
self.getList(); self.getList();
if (self.currentRow && self.currentRow.planId === row.planId) { if (self.currentRow && self.currentRow.planId === row.planId) {
self.currentRow = null; self.currentRow = null;
self.abnormalList = [];
self.detailList = []; self.detailList = [];
self.inspectionRecords = [];
self.inspectionQueried = false;
} }
self.$modal.msgSuccess("删除成功"); self.$modal.msgSuccess("删除成功");
}).catch(function() { }).finally(function() { self.loading = false; }); }).catch(function() { }).finally(function() { self.loading = false; });
}, },
// ---- Submit for approval ---- // ---- 明细管理 ----
handleSubmitApproval() {
var self = this;
if (this.detailList.length === 0) {
this.$modal.msgWarning("请至少添加一条维修明细");
return;
}
this.$modal.confirm('确认提交维修计划"' + this.currentRow.planNo + '"进行审批?').then(function() {
self.submitLoading = true;
return updateMaintenancePlan({ planId: self.currentRow.planId, approvalStatus: 1 });
}).then(function() {
self.$modal.msgSuccess("提交审批成功");
self.submitLoading = false;
self.loadDetail(self.currentRow.planId);
self.getList();
}).catch(function() { self.submitLoading = false; });
},
// ---- Abnormal records selection ----
handleSelectAbnormalRecords() {
this.abnormalSelectedRecords = [];
this.abnormalQuery = { productionLine: undefined, inspector: undefined };
this.loadAbnormalRecords();
this.abnormalSelectorVisible = true;
},
loadAbnormalRecords() {
var self = this;
var params = {
pageNum: 1, pageSize: 999,
productionLine: this.abnormalQuery.productionLine,
inspector: this.abnormalQuery.inspector,
runStatus: 0 // 异常记录
};
listEquipmentInspectionRecord(params).then(function(response) {
self.abnormalRecordList = response.rows || [];
});
},
handleAbnormalSelectionChange(selection) {
this.abnormalSelectedRecords = selection;
},
confirmAbnormalSelection() {
if (this.abnormalSelectedRecords.length === 0) {
this.$modal.msgWarning("请至少选择一条异常记录");
return;
}
var self = this;
var planId = this.currentRow.planId;
var addPromises = this.abnormalSelectedRecords.map(function(rec) {
return addMaintenancePlanAbnormal({ planId: planId });
});
// Also auto-create detail rows from selected records
this.$modal.loading("正在关联异常记录并生成维修明细...");
Promise.all(addPromises).then(function() {
// Create detail rows
var detailPromises = self.abnormalSelectedRecords.map(function(rec) {
return addMaintenancePlanDetail({
planId: planId,
componentName: rec.partName || '',
productionLine: rec.productionLine || '',
maintenanceCategory: 1,
detailStatus: 0
});
});
return Promise.all(detailPromises);
}).then(function() {
self.$modal.closeLoading();
self.$modal.msgSuccess("关联成功,已生成维修明细");
self.abnormalSelectorVisible = false;
self.loadAbnormalList(planId);
self.loadDetailList(planId);
}).catch(function() {
self.$modal.closeLoading();
self.$modal.msgError("操作失败");
});
},
handleRemoveAbnormal(row) {
var self = this;
this.$modal.confirm('确认移除该异常记录关联?').then(function() {
return delMaintenancePlanAbnormal(row.relId);
}).then(function() {
self.$modal.msgSuccess("移除成功");
self.loadAbnormalList(self.currentRow.planId);
}).catch(function() { });
},
// ---- Detail management ----
handleAddDetail() { handleAddDetail() {
this.detailForm = { this.detailForm = {
planId: this.currentRow.planId, planId: this.currentRow.planId,
componentName: '', productionLine: '', componentName: '',
maintenanceCategory: 1, repairContent: '', productionLine: this.currentRow.productionLine || '',
repairUser: '', itemPlanDate: undefined, maintenanceCategory: 1,
targetManufacturer: '', detailStatus: 0 repairContent: '',
repairUser: '',
itemPlanDate: undefined,
targetManufacturer: '',
detailStatus: 0
}; };
this.editingDetailId = null; this.editingDetailId = null;
this.detailDialogTitle = "添加维修明细"; this.detailDialogTitle = "添加维修明细";
@@ -605,8 +527,8 @@ export default {
.panel-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 14px 8px; background: #f5f7fa; } .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 { display: flex; align-items: center; gap: 6px; font-size: 14px; font-weight: 600; color: #303133; }
.header-title i { color: #409eff; font-size: 16px; } .header-title i { color: #409eff; font-size: 16px; }
.header-filter { width: 120px; }
.search-row { display: flex; align-items: center; gap: 6px; padding: 0 14px 10px; background: #f5f7fa; } .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 { flex: 1; overflow-y: auto; padding: 0 6px; }
.list-item { display: flex; align-items: center; padding: 10px 12px; margin-bottom: 2px; cursor: pointer; border-radius: 6px; transition: all 0.15s; } .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:hover { background: #ebeef5; }
@@ -615,51 +537,42 @@ export default {
.item-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; } .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-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-sub { font-size: 12px; color: #909399; }
.item-meta { flex-shrink: 0; margin: 0 8px; }
.item-actions { flex-shrink: 0; opacity: 0; transition: opacity 0.15s; } .item-actions { flex-shrink: 0; opacity: 0; transition: opacity 0.15s; }
.list-item:hover .item-actions { opacity: 1; } .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 { 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-empty i { font-size: 32px; }
.list-footer { border-top: 1px solid #e4e7ed; padding: 2px 8px 0; background: #f5f7fa; } .list-footer { border-top: 1px solid #e4e7ed; padding: 2px 8px 0; background: #f5f7fa; }
/* ========== 右侧面板 — Word 文档风格 ========== */ /* ========== 右侧面板 ========== */
.right-panel { height: 100%; overflow-y: auto; padding: 12px 16px; background: #faf8f5; } .right-panel { height: 100%; overflow: hidden; display: flex; flex-direction: column; background: #faf8f5; }
.right-panel .detail-content { margin: 0 auto; background: #ffffff; padding: 28px 32px 36px; box-shadow: 0 1px 4px rgba(0,0,0,0.06), 0 2px 12px rgba(0,0,0,0.04); min-height: 100%; }
.empty-tip { display: flex; align-items: center; justify-content: center; height: 100%; color: #909399; font-size: 14px; gap: 8px; } .empty-tip { display: flex; align-items: center; justify-content: center; height: 100%; color: #909399; font-size: 14px; gap: 8px; }
.right-content { flex: 1; display: flex; flex-direction: column; overflow: hidden; padding: 12px 16px; }
.doc-header { margin-bottom: 18px; padding-bottom: 14px; border-bottom: 2px solid #1a3c6e; } /* 基本信息栏 */
.doc-header-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 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; }
.doc-title-group { flex: 1; min-width: 0; } .info-bar-left { display: flex; align-items: center; gap: 6px; font-size: 13px; }
.doc-title { font-family: 'Georgia', 'Times New Roman', 'Noto Serif SC', 'SimSun', serif; font-size: 24px; font-weight: 700; color: #1a1a1a; line-height: 1.3; letter-spacing: 0.5px; } .info-label { color: #909399; }
.doc-subtitle { font-family: 'Georgia', 'Times New Roman', serif; font-size: 12px; font-weight: 400; color: #8c8c8c; font-style: italic; letter-spacing: 0.8px; margin-top: 2px; } .info-value { color: #303133; font-weight: 500; }
.doc-header-right { flex-shrink: 0; } .info-bar-right { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.doc-status-row { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.doc-status-label { font-family: 'Georgia', 'Times New Roman', serif; font-size: 11px; color: #8c8c8c; letter-spacing: 0.3px; }
.detail-meta { display: flex; flex-wrap: wrap; gap: 16px; font-size: 12px; color: #909399; margin-bottom: 16px; padding-bottom: 12px; border-bottom: 1px solid #e0dcd6; } /* 上半部分:检修记录 */
.detail-meta span { display: inline-flex; align-items: center; gap: 4px; } .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; }
.detail-meta i { font-size: 13px; } .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; }
.section-title { font-family: 'Georgia', 'Times New Roman', 'Noto Serif SC', 'SimSun', serif; width: 100%; font-size: 15px; font-weight: 700; color: #1a1a1a; margin: 22px 0 12px 0; padding: 0 0 10px 0; border-bottom: 1px solid #d4d0c8; display: flex; align-items: center; gap: 10px; letter-spacing: 0.3px; } /* 下半部分:维修明细 */
.section-title:first-child { margin-top: 0; } .detail-section { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; background: #fff; border: 1px solid #e4e7ed; border-radius: 4px; }
.section-title .en-sub { font-size: 11px; font-weight: 400; color: #8c8c8c; letter-spacing: 0.5px; font-family: 'Georgia', 'Times New Roman', serif; font-style: italic; } .detail-section .el-table { flex: 1; overflow-y: auto; }
.section-title i { font-size: 16px; color: #1a3c6e; }
.remark-content { padding: 12px 16px; background: #faf8f5; border: 1px solid #e8e4de; border-radius: 2px; font-size: 13px; line-height: 1.8; color: #1a1a1a; } .empty-data { padding: 24px 0; text-align: center; color: #c0c4cc; font-size: 13px; }
.section-gap { height: 16px; }
.empty-data { color: #8c8c8c; font-size: 13px; font-style: italic; }
.form-actions { display: flex; justify-content: flex-end; gap: 10px; padding: 16px 0; border-top: 1px solid #e0dcd6; margin-top: 8px; } /* 表格样式 */
.right-panel .el-table { border: none !important; }
.right-panel .el-table { border: 1px solid #e8e4de !important; border-radius: 2px !important; font-size: 12px !important; } .right-panel .el-table th { background: #f5f7fa !important; color: #606266 !important; font-size: 12px !important; padding: 6px 0 !important; }
.right-panel .el-table thead th { background-color: #2c3e50 !important; color: #ffffff !important; font-weight: 600 !important; font-size: 11px !important; letter-spacing: 0.5px !important; border-bottom: none !important; font-family: 'Georgia', 'Times New Roman', serif; } .right-panel .el-table td { padding: 6px 0 !important; font-size: 12px !important; color: #303133 !important; }
.right-panel .el-table thead th .cell { color: #ffffff !important; } .right-panel .el-divider--vertical { margin: 0 6px; }
.right-panel .el-table__body tr:hover > td { background-color: #f7f5f0 !important; }
.right-panel .el-table--border td { border-right: 1px solid #f0ece6 !important; }
.right-panel .el-table--border th { border-right: 1px solid #3a5166 !important; }
.right-panel .el-table td { padding: 6px 4px !important; color: #3a3a3a !important; }
.right-panel .el-divider--horizontal { margin: 8px 0 4px; background-color: #e0dcd6; }
.right-panel .el-tag { border-radius: 2px; font-family: 'Georgia', 'Times New Roman', serif; letter-spacing: 0.3px; }
.right-panel .el-tag--mini { padding: 0 6px; line-height: 20px; height: 20px; }
.right-panel .el-tag--small { padding: 0 8px; }
</style> </style>