You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

256 lines
8.3 KiB

<template>
<div class="hazard-audit-page">
<el-page-header content="隐患审核" class="page-header" @back="goBack" />
<el-alert
v-if="routeError"
:title="routeError"
type="warning"
:closable="false"
show-icon
class="route-alert"
/>
<div v-else v-loading="loading" class="audit-content">
<el-alert
v-if="loadError"
:title="loadError"
type="error"
:closable="false"
show-icon
class="route-alert"
>
<el-button slot="default" type="text" @click="loadPage">重新加载</el-button>
</el-alert>
<template v-else-if="loaded">
<el-alert
v-if="!taskId"
title="当前未获取到任务 ID,仅可查看详情,无法执行审批。"
type="warning"
:closable="false"
show-icon
class="route-alert"
style="grid-column: 1 / -1;"
/>
<hazard-report-panel :report="report" :attachments="attachments" />
<audit-action-form
:report="report"
:report-id="reportId"
:attachments="attachments"
:task-id="taskId"
:submitting="submitting"
@upload="handleAttachmentUpload"
@submit="handleAuditSubmit"
@switch-level="handleSwitchLevel"
@estimated-points="estimatedPoints = $event"
/>
<flow-sync-panel :report="report" :estimated-points="estimatedPoints" />
</template>
</div>
</div>
</template>
<script>
import HazardReportPanel from './components/HazardReportPanel'
import AuditActionForm from './components/AuditActionForm'
import FlowSyncPanel from './components/FlowSyncPanel'
import {
completeHazardAudit,
getHazardAuditDetail,
getHazardProcessDetail,
listHazardAttachments,
switchHazardLevel,
updateHazardReport,
uploadHazardAttachment
} from '@/api/hazard/audit'
export default {
name: 'HazardAudit',
components: { HazardReportPanel, AuditActionForm, FlowSyncPanel },
data() {
return {
loading: false,
submitting: false,
loaded: false,
loadError: '',
report: {},
taskId: '',
attachments: [],
estimatedPoints: null
}
},
computed: {
reportId() {
return String(this.$route.query.id || '')
},
routeError() {
if (!this.reportId) return '缺少隐患 ID,无法加载审核详情。'
return ''
}
},
watch: {
'$route.query': {
immediate: true,
deep: true,
handler() {
if (this.routeError) {
this.resetPage()
return
}
this.loadPage()
}
}
},
methods: {
resetPage() {
this.loading = false
this.loaded = false
this.loadError = ''
this.report = {}
this.taskId = ''
this.attachments = []
this.estimatedPoints = null
},
async loadPage() {
this.loading = true
this.loaded = false
this.loadError = ''
try {
const results = await Promise.all([
getHazardAuditDetail(this.reportId),
listHazardAttachments(this.reportId)
])
this.report = results[0].data || {}
this.attachments = Array.isArray(results[1].data) ? results[1].data : []
const routeFlowInstanceId = String(this.$route.query.flowInstanceId || '')
if (routeFlowInstanceId && !this.report.flowInstanceId) this.$set(this.report, 'flowInstanceId', routeFlowInstanceId)
if (this.$route.query.canApprove === '1' && this.report.canApprove !== true && String(this.report.canApprove) !== 'true') {
this.$set(this.report, 'canApprove', true)
}
await this.resolveCurrentTask()
this.loaded = true
} catch (error) {
this.loadError = '隐患详情或附件加载失败,请稍后重试。'
} finally {
this.loading = false
}
},
async resolveCurrentTask() {
this.taskId = String(this.$route.query.taskId || '')
const canApprove = this.report.canApprove === true || String(this.report.canApprove) === 'true'
if (this.taskId || !canApprove || !this.report.flowInstanceId) return
try {
const response = await getHazardProcessDetail(this.report.flowInstanceId)
const currentTask = this.findCurrentTask(response.data)
this.taskId = currentTask ? String(currentTask.taskId || '') : ''
} catch (error) {
this.taskId = ''
}
},
findCurrentTask(data) {
const tasks = this.normalizeTaskList(data)
if (!tasks.length) return null
return tasks.find(task => task.taskId && !task.endTime && !task.finishTime) ||
tasks.find(task => task.taskId) ||
null
},
normalizeTaskList(data) {
const tasks = []
const visit = value => {
if (Array.isArray(value)) {
value.forEach(visit)
return
}
if (!value || typeof value !== 'object') return
if (Object.prototype.hasOwnProperty.call(value, 'taskId')) tasks.push(value)
Object.keys(value).forEach(key => {
if (key !== 'taskId') visit(value[key])
})
}
visit(data)
return tasks
},
handleAttachmentUpload(payload) {
uploadHazardAttachment(payload.file, payload.attachmentType).then(response => {
const data = response.data
const items = Array.isArray(data) ? data : (data ? [data] : [])
if (!items.length) throw new Error('上传接口未返回附件信息')
payload.done(items.map(item => ({
...item,
reportId: item.reportId || this.reportId,
attachmentType: item.attachmentType || payload.attachmentType
})))
this.$modal.msgSuccess('附件上传成功,审批时将保存关联')
}).catch(() => {
payload.fail()
})
},
async handleAuditSubmit(payload) {
if (!this.taskId) {
this.$modal.msgWarning('当前未获取到任务 ID,无法执行审批;请从待办任务入口进入。')
return
}
this.submitting = true
try {
await updateHazardReport(payload.saveData)
await completeHazardAudit(payload.completeData)
this.$modal.msgSuccess(payload.completeData.result === '1' ? '审核已通过' : '上报已驳回')
this.goBack()
} catch (error) {
this.$modal.msgError('操作未完成:补录保存或流程审批失败')
} finally {
this.submitting = false
}
},
handleSwitchLevel(data) {
this.$confirm('切换等级后旧流程将作废,并按新等级重新发起审批。是否继续?', '重新评估等级', {
confirmButtonText: '确认切换',
cancelButtonText: '取消',
type: 'warning'
}).then(async() => {
this.submitting = true
try {
await switchHazardLevel({
reportId: this.reportId,
newHazardLevel: data.newHazardLevel,
switchReason: data.switchReason
})
this.$modal.msgSuccess('隐患等级已切换,新流程已启动')
this.goBack()
} catch (error) {
this.$modal.msgError('隐患等级切换失败')
} finally {
this.submitting = false
}
}).catch(() => {})
},
goBack() {
if (window.history.length > 1) {
this.$tab.closePage().then(() => {})
return
}
this.$router.push('/')
}
}
}
</script>
<style lang="scss" scoped>
.hazard-audit-page { min-height: calc(100vh - 52px); padding: 16px 20px 40px; background: #f0f2f5; }
.page-header { margin-bottom: 14px; padding: 14px 18px; border-radius: 8px; background: #fff; }
.route-alert { margin-bottom: 14px; }
.audit-content { display: grid; grid-template-columns: minmax(330px, 1.05fr) minmax(540px, 1.9fr) minmax(280px, .9fr); align-items: start; gap: 14px; min-height: 280px; }
::v-deep .audit-card { margin: 0; border: 0; border-radius: 8px; }
::v-deep .audit-card > .el-card__header { padding: 16px; border-bottom-color: #ebeef5; }
::v-deep .audit-card > .el-card__body { padding: 16px; }
@media (max-width: 1450px) {
.audit-content { grid-template-columns: minmax(300px, 1fr) minmax(500px, 1.6fr); }
.audit-content > :last-child { grid-column: 1 / -1; }
}
@media (max-width: 980px) {
.audit-content { grid-template-columns: 1fr; }
.audit-content > :last-child { grid-column: auto; }
}
</style>