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-check" > < / 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" / >
< / 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 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 · Approval < / 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" > Approval / 状态 : < / span >
< el-tag size = "small" > 待审批 < / el-tag >
< / 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.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 >
< el-divider / >
< div class = "section-title" >
< span > 计划说明 < span class = "en-sub" > · Description < / span > < / span >
< / div >
< div class = "remark-content" > { { currentRow . planDescription || '无' } } < / div >
< el-divider / >
< div class = "section-title" >
< span > 审批操作 < span class = "en-sub" > · Approval Actions < / span > < / span >
< / div >
< div class = "flow-actions" >
< el-button type = "danger" size = "small" icon = "el-icon-close" :loading = "rejectLoading" @click ="handleReject" > 驳回 < / el -button >
< el-button type = "primary" size = "small" icon = "el-icon-check" :loading = "approveLoading" @click ="handleApprove" > 审批通过 < / el -button >
< / div >
< el-divider / >
< div class = "section-title" >
< span > 维修明细 < span class = "en-sub" > · Maintenance Details < / 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 >
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" 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 = " 220 " show -overflow -tooltip / >
< el-table-column label = "产线执行人员" align = "center" prop = "lineExecutor" min -width = " 150 " show -overflow -tooltip / >
< el-table-column label = "设备执行人员" align = "center" prop = "equipmentExecutor" min -width = " 150 " show -overflow -tooltip / >
2026-07-08 13:10:43 +08:00
< el-table-column label = "预计开始时间" align = "center" prop = "plannedStartTime" min -width = " 120 " show -overflow -tooltip / >
< el-table-column label = "预计结束时间" align = "center" prop = "plannedCompleteTime" min -width = " 120 " show -overflow -tooltip / >
< el-table-column label = "实际开始时间" align = "center" prop = "actualStartTime" min -width = " 120 " show -overflow -tooltip / >
< el-table-column label = "实际结束时间" align = "center" prop = "actualCompleteTime" min -width = " 120 " show -overflow -tooltip / >
2026-07-08 11:25:13 +08:00
< el-table-column label = "总耗时( h) " align = "center" prop = "totalHours" min -width = " 110 " show -overflow -tooltip / >
< el-table-column label = "验收人" align = "center" prop = "acceptanceUser" min -width = " 110 " show -overflow -tooltip / >
< el-table-column label = "是否完成" align = "center" width = "100" >
< template slot -scope = " scope " > { { scope . row . detailStatus === 2 ? '是' : '否' } } < / template >
< / el-table-column >
< el-table-column label = "备注" align = "center" prop = "remark" min -width = " 180 " show -overflow -tooltip / >
2026-06-25 10:53:27 +08:00
< / 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 >
< / div >
< / template >
< script >
import { listMaintenancePlan , getMaintenancePlan , updateMaintenancePlan } from "@/api/flow/maintenancePlan" ;
import { listMaintenancePlanDetail } from "@/api/flow/maintenancePlanDetail" ;
import DragResizePanel from "@/components/DragResizePanel/index.vue" ;
import { parseTime } from '@/utils/klp'
export default {
name : "EqpApproval" ,
components : { DragResizePanel } ,
data ( ) {
return {
loading : false ,
detailLoading : false ,
approveLoading : false ,
rejectLoading : false ,
total : 0 ,
queryParams : { pageNum : 1 , pageSize : 10 , planNo : undefined , approvalStatus : 1 } ,
dataList : [ ] ,
currentRow : null ,
detailList : [ ]
} ;
} ,
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 ) ;
} ,
handleApprove ( ) {
var self = this ;
this . $modal . confirm ( '确认通过"' + this . currentRow . planNo + '"的审批?' ) . then ( function ( ) {
self . approveLoading = true ;
return updateMaintenancePlan ( { planId : self . currentRow . planId , approvalStatus : 2 , planStatus : 1 } ) ;
} ) . then ( function ( ) {
self . $modal . msgSuccess ( "审批通过,维修计划已进入待维修状态" ) ;
self . approveLoading = false ;
self . currentRow = null ;
self . getList ( ) ;
} ) . catch ( function ( ) { self . approveLoading = false ; } ) ;
} ,
handleReject ( ) {
var self = this ;
this . $prompt ( '请输入驳回原因' , '驳回审批' , {
confirmButtonText : '确定' ,
cancelButtonText : '取消' ,
inputType : 'textarea' ,
inputValidator : function ( val ) { return val ? true : '驳回原因不能为空' ; }
} ) . then ( function ( { value } ) {
self . rejectLoading = true ;
return updateMaintenancePlan ( { planId : self . currentRow . planId , approvalStatus : 3 , remark : value } ) ;
} ) . then ( function ( ) {
self . $modal . msgSuccess ( "已驳回" ) ;
self . rejectLoading = false ;
self . currentRow = null ;
self . getList ( ) ;
} ) . catch ( function ( ) { self . rejectLoading = false ; } ) ;
}
}
} ;
< / 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 ; }
. 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 ; }
. flow - actions { display : flex ; gap : 10 px ; flex - wrap : wrap ; }
. 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 >