2026-06-25 10:53:27 +08:00
< 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-promotion" > < / i >
< span > 维修执行 < / span >
< el-button size = "mini" type = "text" icon = "el-icon-refresh" @click ="getList" style = "margin-left:4px;" title = "刷新列表" > < / el-button >
< / div >
< el-select v-model = "queryParams.planStatus" placeholder="执行状态" clearable size="mini" @change="handleQuery" class="header-filter" >
< el-option label = "待维修" :value = "1" / >
< el-option label = "维修中" :value = "2" / >
< el-option label = "已完成" :value = "3" / >
< / el-select >
< / 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" / >
< / 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-meta" >
< el-tag v-if = "item.planStatus === 1" type="info" size="mini" > 待维修 < / el -tag >
< el-tag v-else-if = "item.planStatus === 2" type="warning" size="mini" > 维修中 < / el -tag >
< el-tag v-else-if = "item.planStatus === 3" type="success" size="mini" > 已完成 < / el -tag >
< / div >
< div class = "item-actions" >
< el-button size = "mini" type = "text" icon = "el-icon-view" @click.stop ="handleRowClick(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 v-loading = "detailLoading" class="detail-content" >
< div class = "doc-header" >
< div class = "doc-header-top" >
< div class = "doc-title-group" >
< div class = "doc-title" > { { currentRow . planNo } } < / div >
< div class = "doc-subtitle" > Maintenance Plan · Execution < / div >
< / div >
< div class = "doc-header-right" >
< el-button size = "mini" type = "text" icon = "el-icon-refresh" @click ="handleRefreshDetail" title = "刷新详情" > 刷新 < / el-button >
< / div >
< / div >
< div class = "doc-status-row" >
< span class = "doc-status-label" > Status / 状态 : < / span >
< el-tag v-if = "currentRow.planStatus === 1" type="info" size="small" > 待维修 < / el -tag >
< el-tag v-else-if = "currentRow.planStatus === 2" type="warning" size="small" > 维修中 < / el -tag >
< el-tag v-else-if = "currentRow.planStatus === 3" type="success" size="small" > 已完成 < / el -tag >
< el-button v-if = "canComplete" size="mini" type="success" plain icon="el-icon-circle-check" style="margin-left:12px;" @click="handleCompletePlan" > 完成计划 < / el -button >
< / div >
< / div >
< div class = "detail-meta" >
< span > < i class = "el-icon-document" > < / i > { { currentRow . planName } } < / span >
< span v-if = "currentRow.repairType === 1" > 定期保养 < / span >
< span v-else-if = "currentRow.repairType === 2" > 安全整改 < / span >
< span v-else-if = "currentRow.repairType === 3" > 专项检修 < / span >
< span v-else-if = "currentRow.repairType === 4" > 故障维修 < / 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 >
< / div >
< el-divider / >
< div class = "section-title" >
< span > 维修明细进度 < span class = "en-sub" > · Detail Progress < / span > < / span >
< / div >
< el-table :data = "detailList" border size = "small" style = "width:100%" >
2026-07-08 11:25:13 +08:00
< 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 " > { { parseTime ( scope . row . itemPlanDate , '{y}/{m}/{d}' ) || '-' } } < / template >
2026-06-25 10:53:27 +08:00
< / el-table-column >
2026-07-08 11:25:13 +08:00
< el-table-column label = "具体工作内容" align = "center" prop = "repairContent" min -width = " 200 " show -overflow -tooltip / >
< el-table-column label = "产线执行人员" align = "center" prop = "lineExecutor" min -width = " 130 " show -overflow -tooltip / >
< el-table-column label = "设备执行人员" align = "center" prop = "equipmentExecutor" min -width = " 130 " show -overflow -tooltip / >
< el-table-column label = "预计完成时间" align = "center" prop = "plannedCompleteTime" min -width = " 120 " show -overflow -tooltip / >
< el-table-column label = "实际完成时间" align = "center" prop = "actualCompleteTime" min -width = " 120 " show -overflow -tooltip / >
< el-table-column label = "总耗时( h) " align = "center" prop = "totalHours" min -width = " 100 " show -overflow -tooltip / >
< el-table-column label = "验收人" align = "center" prop = "acceptanceUser" min -width = " 100 " show -overflow -tooltip / >
2026-06-25 10:53:27 +08:00
< el-table-column label = "进度" align = "center" width = "160" >
< template slot -scope = " scope " >
< el-progress : percentage = "scope.row.progressRate || 0" : status = "scope.row.detailStatus === 2 ? 'success' : ''" :stroke-width = "12" style = "padding: 4px 0;" / >
< / template >
< / el-table-column >
< el-table-column label = "状态" align = "center" width = "80" >
< template slot -scope = " scope " >
< el-tag v-if = "scope.row.detailStatus === 0" type="info" size="mini" > 未开始 < / el -tag >
< el-tag v-else-if = "scope.row.detailStatus === 1" type="warning" size="mini" > 进行中 < / el -tag >
< el-tag v-else-if = "scope.row.detailStatus === 2" type="success" size="mini" > 已完成 < / el -tag >
< / template >
< / el-table-column >
< el-table-column label = "实际成本" align = "center" width = "100" >
< template slot -scope = " scope " > ¥ { { scope . row . actualCost || 0 } } < / template >
< / el-table-column >
< el-table-column label = "操作" align = "center" width = "80" fixed = "right" v-if = "currentRow.planStatus !== 3" >
< template slot -scope = " scope " >
< el-button size = "mini" type = "text" icon = "el-icon-edit" @click ="handleEditProgress(scope.row)" > 进度 < / el -button >
< / template >
< / el-table-column >
< / el-table >
< div v-if = "detailList.length === 0" class="empty-data" style="margin-top:8px;" > 暂无维修明细 < / div >
< div class = "section-gap" / >
< div class = "section-title" > 备注 < span class = "en-sub" > · Remarks < / span > < / div >
< div class = "remark-content" > { { currentRow . remark || '无' } } < / div >
< / div >
< / div >
< / template >
< / DragResizePanel >
<!-- 进度编辑弹窗 -- >
< el-dialog title = "更新维修进度" :visible.sync = "progressDialogVisible" width = "550px" append -to -body >
< el-form ref = "progressForm" :model = "progressForm" label -width = " 100px " >
< el-form-item label = "设备部件" >
< span > { { progressForm . componentName } } < / span >
< / el-form-item >
< el-form-item label = "维修内容" >
< span > { { progressForm . repairContent } } < / span >
< / el-form-item >
< el-form-item label = "完成进度" prop = "progressRate" >
< el-slider v-model = "progressForm.progressRate" :min="0" :max="100" :step="5" show-input style="padding:0 10px;" / >
< / el-form-item >
< el-form-item label = "完成状态" prop = "detailStatus" >
< el-select v-model = "progressForm.detailStatus" placeholder="请选择" style="width:100%" >
< el-option label = "未开始" :value = "0" / >
< el-option label = "进行中" :value = "1" / >
< el-option label = "已完成" :value = "2" / >
< / el-select >
< / el-form-item >
< el-form-item label = "实际成本(元)" prop = "actualCost" >
< el-input-number v-model = "progressForm.actualCost" :min="0" :precision="2" style="width:100%" / >
< / el-form-item >
< el-form-item label = "负责人" prop = "repairUser" >
< el-input v-model = "progressForm.repairUser" placeholder="请输入维修负责人" / >
< / el-form-item >
< el-form-item label = "完成时间" prop = "completeTime" v-if = "progressForm.detailStatus === 2" >
< el-date-picker clearable v-model = "progressForm.completeTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择完成时间" style="width:100%" / >
< / el-form-item >
< / el-form >
< div slot = "footer" class = "dialog-footer" >
< el-button @click ="progressDialogVisible = false" > 取 消 < / el -button >
< el-button type = "primary" :loading = "progressButtonLoading" @click ="submitProgressForm" > 保 存 < / el -button >
< / div >
< / el-dialog >
< / div >
< / template >
< script >
import { listMaintenancePlan , getMaintenancePlan , updateMaintenancePlan } from "@/api/flow/maintenancePlan" ;
import { listMaintenancePlanDetail , updateMaintenancePlanDetail } from "@/api/flow/maintenancePlanDetail" ;
import DragResizePanel from "@/components/DragResizePanel/index.vue" ;
import { parseTime } from '@/utils/klp'
export default {
name : "EqpExecute" ,
components : { DragResizePanel } ,
data ( ) {
return {
loading : false ,
detailLoading : false ,
progressButtonLoading : false ,
total : 0 ,
queryParams : { pageNum : 1 , pageSize : 10 , planNo : undefined , approvalStatus : 2 , planStatus : undefined } ,
dataList : [ ] ,
currentRow : null ,
detailList : [ ] ,
progressDialogVisible : false ,
progressForm : { }
} ;
} ,
computed : {
canComplete ( ) {
if ( ! this . currentRow || this . currentRow . planStatus === 3 ) return false ;
if ( this . detailList . length === 0 ) return false ;
return this . detailList . every ( function ( d ) { return d . detailStatus === 2 ; } ) ;
}
} ,
created ( ) { this . getList ( ) ; } ,
methods : {
parseTime ,
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 . loadDetail ( row . planId ) ;
} ,
loadDetail ( planId ) {
this . detailLoading = true ;
var self = this ;
getMaintenancePlan ( planId ) . then ( function ( response ) {
self . currentRow = response . data ;
self . loadDetailList ( planId ) ;
} ) . finally ( function ( ) { self . detailLoading = false ; } ) ;
} ,
loadDetailList ( planId ) {
var self = this ;
listMaintenancePlanDetail ( { planId : planId , pageNum : 1 , pageSize : 999 } ) . then ( function ( r ) {
self . detailList = r . rows || [ ] ;
} ) ;
} ,
handleRefreshDetail ( ) {
if ( this . currentRow && this . currentRow . planId ) this . loadDetail ( this . currentRow . planId ) ;
} ,
// ---- Progress editing ----
handleEditProgress ( row ) {
this . progressForm = Object . assign ( { } , row , {
completeTime : row . completeTime || undefined
} ) ;
this . progressDialogVisible = true ;
} ,
submitProgressForm ( ) {
var self = this ;
var data = {
detailId : this . progressForm . detailId ,
progressRate : this . progressForm . progressRate ,
detailStatus : this . progressForm . detailStatus ,
actualCost : this . progressForm . actualCost ,
repairUser : this . progressForm . repairUser
} ;
if ( this . progressForm . detailStatus === 2 ) {
data . completeTime = this . progressForm . completeTime || this . parseTime ( new Date ( ) , '{y}-{m}-{d} {h}:{i}:{s}' ) ;
}
this . progressButtonLoading = true ;
updateMaintenancePlanDetail ( data ) . then ( function ( ) {
self . $modal . msgSuccess ( "进度更新成功" ) ;
self . progressDialogVisible = false ;
self . loadDetailList ( self . currentRow . planId ) ;
} ) . finally ( function ( ) { self . progressButtonLoading = false ; } ) ;
} ,
// ---- Complete plan ----
handleCompletePlan ( ) {
var self = this ;
this . $modal . confirm ( '确认完成维修计划"' + this . currentRow . planNo + '"? ' ) . then ( function ( ) {
return updateMaintenancePlan ( {
planId : self . currentRow . planId ,
planStatus : 3 ,
actualCompleteTime : self . parseTime ( new Date ( ) , '{y}-{m}-{d} {h}:{i}:{s}' )
} ) ;
} ) . then ( function ( ) {
self . $modal . msgSuccess ( "维修计划已完成" ) ;
self . loadDetail ( self . currentRow . planId ) ;
self . getList ( ) ;
} ) . catch ( function ( ) { } ) ;
}
}
} ;
< / script >
< style scoped >
. count - container { height : calc ( 100 vh - 84 px ) ; }
. left - panel { display : flex ; flex - direction : column ; height : 100 % ; background : # f5f7fa ; border - right : 1 px solid # e4e7ed ; }
. panel - header { display : flex ; align - items : center ; justify - content : space - between ; padding : 12 px 14 px 8 px ; background : # f5f7fa ; }
. header - title { display : flex ; align - items : center ; gap : 6 px ; font - size : 14 px ; font - weight : 600 ; color : # 303133 ; }
. header - title i { color : # 409 eff ; font - size : 16 px ; }
. header - filter { width : 120 px ; }
. search - row { display : flex ; align - items : center ; gap : 6 px ; padding : 0 14 px 10 px ; background : # f5f7fa ; }
. list - body { flex : 1 ; overflow - y : auto ; padding : 0 6 px ; }
. list - item { display : flex ; align - items : center ; padding : 10 px 12 px ; margin - bottom : 2 px ; cursor : pointer ; border - radius : 6 px ; transition : all 0.15 s ; }
. list - item : hover { background : # ebeef5 ; }
. list - item . active { background : # d9ecff ; }
. list - item . active . item - title { color : # 409 eff ; font - weight : 600 ; }
. item - main { flex : 1 ; min - width : 0 ; display : flex ; flex - direction : column ; gap : 3 px ; }
. item - title { font - size : 13 px ; font - weight : 500 ; color : # 303133 ; white - space : nowrap ; overflow : hidden ; text - overflow : ellipsis ; }
. item - sub { font - size : 12 px ; color : # 909399 ; }
. item - meta { flex - shrink : 0 ; margin : 0 8 px ; }
. item - actions { flex - shrink : 0 ; opacity : 0 ; transition : opacity 0.15 s ; }
. list - item : hover . item - actions { opacity : 1 ; }
. list - empty { display : flex ; flex - direction : column ; align - items : center ; justify - content : center ; padding : 60 px 0 ; color : # c0c4cc ; font - size : 13 px ; gap : 8 px ; }
. list - empty i { font - size : 32 px ; }
. list - footer { border - top : 1 px solid # e4e7ed ; padding : 2 px 8 px 0 ; background : # f5f7fa ; }
. right - panel { height : 100 % ; overflow - y : auto ; padding : 12 px 16 px ; background : # faf8f5 ; }
. right - panel . detail - content { margin : 0 auto ; background : # ffffff ; padding : 28 px 32 px 36 px ; box - shadow : 0 1 px 4 px rgba ( 0 , 0 , 0 , 0.06 ) , 0 2 px 12 px 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 : 14 px ; gap : 8 px ; }
. doc - header { margin - bottom : 18 px ; padding - bottom : 14 px ; border - bottom : 2 px solid # 1 a3c6e ; }
. doc - header - top { display : flex ; align - items : flex - start ; justify - content : space - between ; gap : 16 px ; }
. doc - title - group { flex : 1 ; min - width : 0 ; }
. doc - title { font - family : 'Georgia' , 'Times New Roman' , 'Noto Serif SC' , 'SimSun' , serif ; font - size : 24 px ; font - weight : 700 ; color : # 1 a1a1a ; line - height : 1.3 ; letter - spacing : 0.5 px ; }
. doc - subtitle { font - family : 'Georgia' , 'Times New Roman' , serif ; font - size : 12 px ; font - weight : 400 ; color : # 8 c8c8c ; font - style : italic ; letter - spacing : 0.8 px ; margin - top : 2 px ; }
. doc - header - right { flex - shrink : 0 ; }
. doc - status - row { display : flex ; align - items : center ; gap : 8 px ; margin - top : 10 px ; }
. doc - status - label { font - family : 'Georgia' , 'Times New Roman' , serif ; font - size : 11 px ; color : # 8 c8c8c ; letter - spacing : 0.3 px ; }
. detail - meta { display : flex ; flex - wrap : wrap ; gap : 16 px ; font - size : 12 px ; color : # 909399 ; margin - bottom : 16 px ; padding - bottom : 12 px ; border - bottom : 1 px solid # e0dcd6 ; }
. detail - meta span { display : inline - flex ; align - items : center ; gap : 4 px ; }
. detail - meta i { font - size : 13 px ; }
. section - title { font - family : 'Georgia' , 'Times New Roman' , 'Noto Serif SC' , 'SimSun' , serif ; width : 100 % ; font - size : 15 px ; font - weight : 700 ; color : # 1 a1a1a ; margin : 22 px 0 12 px 0 ; padding : 0 0 10 px 0 ; border - bottom : 1 px solid # d4d0c8 ; display : flex ; align - items : center ; gap : 10 px ; letter - spacing : 0.3 px ; }
. section - title : first - child { margin - top : 0 ; }
. section - title . en - sub { font - size : 11 px ; font - weight : 400 ; color : # 8 c8c8c ; letter - spacing : 0.5 px ; font - family : 'Georgia' , 'Times New Roman' , serif ; font - style : italic ; }
. remark - content { padding : 12 px 16 px ; background : # faf8f5 ; border : 1 px solid # e8e4de ; border - radius : 2 px ; font - size : 13 px ; line - height : 1.8 ; color : # 1 a1a1a ; }
. section - gap { height : 16 px ; }
. empty - data { color : # 8 c8c8c ; font - size : 13 px ; font - style : italic ; }
. right - panel . el - table { border : 1 px solid # e8e4de ! important ; border - radius : 2 px ! important ; font - size : 12 px ! important ; }
. right - panel . el - table thead th { background - color : # 2 c3e50 ! important ; color : # ffffff ! important ; font - weight : 600 ! important ; font - size : 11 px ! important ; letter - spacing : 0.5 px ! important ; border - bottom : none ! important ; font - family : 'Georgia' , 'Times New Roman' , serif ; }
. right - panel . el - table thead th . cell { color : # ffffff ! important ; }
. right - panel . el - table _ _body tr : hover > td { background - color : # f7f5f0 ! important ; }
. right - panel . el - table -- border td { border - right : 1 px solid # f0ece6 ! important ; }
. right - panel . el - table -- border th { border - right : 1 px solid # 3 a5166 ! important ; }
. right - panel . el - table td { padding : 6 px 4 px ! important ; color : # 3 a3a3a ! important ; }
. right - panel . el - divider -- horizontal { margin : 8 px 0 4 px ; background - color : # e0dcd6 ; }
. right - panel . el - tag { border - radius : 2 px ; font - family : 'Georgia' , 'Times New Roman' , serif ; letter - spacing : 0.3 px ; }
< / style >