bonus-ui/src/views/EquipmentRetireApply/audit-detail.vue

735 lines
26 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">
<el-card style="margin-bottom: 20px" shadow="never">
<GoBack :title="'退役审核'" @goBack="goBack"/>
<!-- 基本信息 -->
<el-form ref="baseForm" :model="baseInfo" label-width="120px" inline style="height: 36px">
<el-form-item label="申请单号" style="margin-bottom: 0; height: 36px">
<!-- 申请单号禁用的输入框 -->
<el-input
v-model="baseInfo.code"
placeholder="无"
disabled
:value=" baseInfo.code "
style="width: 240px;"
/>
</el-form-item>
<el-form-item label="任务状态">
<!-- 任务状态:禁用的输入框 + 标签样式 -->
<el-input
:value="getStatusLabel()"
disabled
style="background: transparent; border: none; padding-left: 0; width: 240px;"
>
</el-input>
</el-form-item>
<el-form-item label="申请人">
<!-- 申请人:禁用的输入框 -->
<el-input v-model="baseInfo.createUser" placeholder="无" disabled style="width: 240px;"/>
</el-form-item>
<el-form-item label="申请时间">
<!-- 申请时间:禁用的输入框 -->
<el-input v-model="baseInfo.createTime" placeholder="无" disabled style="width: 240px;"/>
</el-form-item>
</el-form>
</el-card>
<!-- 装备和工具列表 -->
<el-row :gutter="24">
<el-col :span="6">
<el-card shadow="never" style="min-height: 640px;">
<div style="display: flex;align-items: center;padding-bottom: 20px;border-bottom: 1px solid #F0F0F0;">
<span style="font-size: 20px; font-weight: 800;margin-right: 48px;">审批进度</span>
</div>
<div class="approval-progress" v-if="processConfig && processConfig.nodeList && !collapsed">
<el-timeline>
<el-timeline-item
v-for="node in processConfig.nodeList"
:key="node.nodeOrder"
:timestamp="getNodeRecord(node) ? getNodeRecord(node).approveTime : ''"
placement="top"
>
<template #dot>
<img src="../../assets/images/timeIcon.png" class="custom-icon" alt="">
</template>
<div class="timeline-content">
<div class="node-title">
<span class="node-name">节点{{ node.nodeOrder }}{{ node.nodeName }}</span>
<el-tag :type="getNodeTagType(node)">
{{ getNodeStatusLabel(node) }}
</el-tag>
</div>
<div class="node-info">
<div>审批人:{{ node.name }}</div>
<div v-if="getNodeRecord(node)">
<div>审批结果:{{ getNodeRecord(node).approveResult === '1' ? '通过' : '驳回' }}</div>
<div>审批意见:{{ getNodeRecord(node).approveOpinion }}</div>
<div>审批时间:{{ getNodeRecord(node).approveTime }}</div>
</div>
<div v-else-if="node.nodeOrder === approvalInstance.currentNodeOrder">
状态:待审批
</div>
<div v-else>
状态:等待上一节点审批
</div>
</div>
</div>
</el-timeline-item>
</el-timeline>
</div>
<div class="approval-actions" v-if="canApprove && !collapsed">
<div class="section-header">
<span>审批操作</span>
</div>
<el-form ref="approveForm" :model="approveData" label-width="100px">
<el-form-item label="审批意见">
<el-input
v-model="approveData.opinion"
type="textarea"
rows="4"
placeholder="请输入审批意见"
maxlength="500"
show-word-limit
/>
</el-form-item>
<el-form-item>
<el-button type="success" @click="onHandleApprove">通过</el-button>
<el-button type="danger" @click="onHandleReject">驳回</el-button>
<el-button @click="onHandleCancel">取消</el-button>
</el-form-item>
</el-form>
</div>
</el-card>
</el-col>
<el-col :span="18">
<el-card shadow="never">
<div class="mb8" style="display: flex;align-items: center;padding-bottom: 20px;border-bottom: 1px solid #F0F0F0;">
<span style="font-size: 20px; font-weight: 800;margin-right: 48px;">退役审核列表</span>
<span style="font-size: 16px;color: #808080;">共{{detailList.length}}条记录</span>
</div>
<el-table ref="detailTable" :data="detailList" class="my-table" style="width: 100%" border stripe size="small"
fit @selection-change="onSelectionChange"
>
<el-table-column type="selection" width="50"
:selectable="row => !row.reviewStatus || (row.reviewStatus !== '1' && row.reviewStatus !== '2')"
/>
<el-table-column align="center" type="index" label="序号" width="80"/>
<el-table-column align="center" prop="type" label="分类" width="100"/>
<el-table-column align="center" prop="typeName" label="类目" min-width="100"/>
<el-table-column align="center" prop="typeModelName" label="规格型号" width="150"/>
<el-table-column align="center" prop="manageMode" label="管理模式" width="120"/>
<el-table-column align="center" prop="devCode" label="设备编码" min-width="120"/>
<el-table-column align="center" prop="scrapQuantity" label="申请报废数量" width="120"/>
<el-table-column align="center" prop="retireReason" label="退役原因" min-width="120"
show-overflow-tooltip
/>
<el-table-column align="center" label="报废附件" width="120">
<template slot-scope="scope">
<el-button type="text" style="color: #2CBAB2" @click="onHandleViewAttachment(scope.row)">
查看
</el-button>
</template>
</el-table-column>
<el-table-column align="center" prop="reviewStatus" label="审批状态" width="100">
<template slot-scope="scope">
<el-tag :type="getReviewStatusType(scope.row.reviewStatus)">
{{ getReviewStatusLabel(scope.row.reviewStatus) }}
</el-tag>
</template>
</el-table-column>
</el-table>
</el-card>
</el-col>
</el-row>
<!-- <el-card shadow="never">
<div class="detail-section">
<div class="section-header">
<el-row :gutter="20">
<el-col :span="24">
<span style="font-size: 20px; font-weight: 800">退役审核列表</span>
</el-col>
<el-col :span="0" style="display: flex; justify-content: flex-end">
</el-col>
</el-row>
</div>
<el-row :gutter="20">
<el-col :span="collapsed ? 23 : 18">
<el-table ref="detailTable" :data="detailList" class="my-table" style="width: 100%" border stripe size="small"
fit @selection-change="onSelectionChange"
>
<el-table-column type="selection" width="50"
:selectable="row => !row.reviewStatus || (row.reviewStatus !== '1' && row.reviewStatus !== '2')"
/>
<el-table-column align="center" type="index" label="序号" width="80"/>
<el-table-column align="center" prop="type" label="分类" width="100"/>
<el-table-column align="center" prop="typeName" label="类目" min-width="100"/>
<el-table-column align="center" prop="typeModelName" label="规格型号" width="150"/>
<el-table-column align="center" prop="manageMode" label="管理模式" width="120"/>
<el-table-column align="center" prop="devCode" label="设备编码" min-width="120"/>
<el-table-column align="center" prop="scrapQuantity" label="申请报废数量" width="120"/>
<el-table-column align="center" prop="retireReason" label="退役原因" min-width="120"
show-overflow-tooltip
/>
<el-table-column align="center" label="报废附件" width="120">
<template slot-scope="scope">
<el-button type="text" style="color: #2CBAB2" @click="onHandleViewAttachment(scope.row)">
查看
</el-button>
</template>
</el-table-column>
<el-table-column align="center" prop="reviewStatus" label="审批状态" width="100">
<template slot-scope="scope">
<el-tag :type="getReviewStatusType(scope.row.reviewStatus)">
{{ getReviewStatusLabel(scope.row.reviewStatus) }}
</el-tag>
</template>
</el-table-column>
</el-table>
</el-col>
<el-col :span="collapsed ? 1 : 6">
<div class="approval-progress" v-if="processConfig && processConfig.nodeList && !collapsed">
<div class="section-header">
<span>审批进度</span>
</div>
<el-timeline>
<el-timeline-item
v-for="node in processConfig.nodeList"
:key="node.nodeOrder"
:timestamp="getNodeRecord(node) ? getNodeRecord(node).approveTime : ''"
placement="top"
>
<div class="timeline-content">
<div class="node-title">
<span class="node-name">节点{{ node.nodeOrder }}{{ node.nodeName }}</span>
<el-tag :type="getNodeTagType(node)">
{{ getNodeStatusLabel(node) }}
</el-tag>
</div>
<div class="node-info">
<div>审批人:{{ node.name }}</div>
<div v-if="getNodeRecord(node)">
<div>审批结果:{{ getNodeRecord(node).approveResult === '1' ? '通过' : '驳回' }}</div>
<div>审批意见:{{ getNodeRecord(node).approveOpinion }}</div>
<div>审批时间:{{ getNodeRecord(node).approveTime }}</div>
</div>
<div v-else-if="node.nodeOrder === approvalInstance.currentNodeOrder">
状态:待审批
</div>
<div v-else>
状态:等待上一节点审批
</div>
</div>
</div>
</el-timeline-item>
</el-timeline>
</div>
<div class="approval-actions" v-if="canApprove && !collapsed">
<div class="section-header">
<span>审批操作</span>
</div>
<el-form ref="approveForm" :model="approveData" label-width="100px">
<el-form-item label="审批意见">
<el-input
v-model="approveData.opinion"
type="textarea"
rows="4"
placeholder="请输入审批意见"
maxlength="500"
show-word-limit
/>
</el-form-item>
<el-form-item>
<el-button type="success" @click="onHandleApprove">通过</el-button>
<el-button type="danger" @click="onHandleReject">驳回</el-button>
<el-button @click="onHandleCancel">取消</el-button>
</el-form-item>
</el-form>
</div>
<div style="position: absolute; width: 10px; top: 50%; right: 0">
<el-button
size="mini"
@click="collapsed = !collapsed"
:icon="collapsed ? 'el-icon-d-arrow-right' : 'el-icon-d-arrow-left'"
style="padding: 2px"
/>
</div>
</el-col>
</el-row>
</div>
</el-card> -->
</div>
</template>
<script>
import { getRetireApplyDetailAPI } from '@/api/EquipmentRetireApply/index.js'
import { getApprovalDetail, doApprove, getProcessConfig, getApprovalInstanceByBusiness } from '@/api/approval.js'
import { getCurrentUserId } from '@/utils/auth'
import GoBack from '@/components/GoBack'
export default {
components: { GoBack },
data() {
return {
baseInfo: {
id: '',
code: '',
reviewStatus: '1',
createUser: '',
createTime: ''
},
detailList: [],
selectedRows: [],
approvalInstance: {},
approvalProgress: null,
processConfig: null,
canApprove: false,
approveData: {
opinion: ''
},
collapsed: false
}
},
methods: {
goBack() {
this.$router.push({ path: '/all/business/EquipmentRetireApply/EquipmentRetireApply' })
},
async getAuditDetail() {
const id = this.$route.query.id
// 从query参数中获取任务基本信息
const taskInfoStr = this.$route.query.taskInfo
if (taskInfoStr) {
const taskInfo = JSON.parse(taskInfoStr)
this.baseInfo = {
id: taskInfo.id,
code: taskInfo.code,
reviewStatus: taskInfo.reviewStatus,
createUser: taskInfo.createUser,
createTime: taskInfo.createTime
}
}
// 调用API获取明细列表
const res = await getRetireApplyDetailAPI(id)
const data = res.data || []
// 所有数据都是明细项
this.detailList = data.map(item => ({
id: item.id,
keyId: item.keyId,
type: item.type,
typeId: item.typeId,
devType: item.devType,
groupName: item.groupName,
typeName: item.typeName,
typeModelName: item.typeModelName,
manageMode: item.manageMode,
devCode: item.devCode,
code: item.code,
inStockNum: item.inStockNum,
scrapQuantity: item.scrapNum,
retireReason: item.reasonVal,
attachments: item.bmFileInfos || [],
reviewStatus: item.reviewStatus // 审批状态
}))
// 查询审批实例和进度
await this.loadApprovalInfo()
},
async loadApprovalInfo() {
try {
// 第一步根据业务ID查询审批实例获取审批实例ID
console.log('开始查询审批实例业务ID:', this.baseInfo.id)
const instanceRes = await getApprovalInstanceByBusiness('EQUIPMENT_SCRAP', this.baseInfo.id)
console.log('审批实例查询结果:', instanceRes)
if (!instanceRes || !instanceRes.data) {
console.error('未找到审批实例,返回数据:', instanceRes)
this.$message.warning('未找到审批实例')
return
}
const instanceId = instanceRes.data.id
const processId = instanceRes.data.processId
console.log('审批实例ID:', instanceId, '流程ID:', processId)
if (!instanceId || !processId) {
console.error('审批实例ID或流程ID为空')
this.$message.warning('审批实例ID或流程ID为空')
return
}
// 第二步根据审批实例ID查询审批详情包含审批记录
console.log('开始查询审批详情实例ID:', instanceId)
const approvalRes = await getApprovalDetail(instanceId)
console.log('审批详情查询结果:', approvalRes)
if (approvalRes && approvalRes.data) {
this.approvalInstance = approvalRes.data
console.log('审批记录:', this.approvalInstance.recordList)
// 第三步根据流程ID查询审批流程配置获取所有节点信息
console.log('开始查询审批流程配置流程ID:', processId)
const processRes = await getProcessConfig(processId)
console.log('审批流程配置查询结果:', processRes)
if (processRes && processRes.data) {
this.processConfig = processRes.data
console.log('节点列表:', this.processConfig.nodeList)
}
// 第四步:判断当前用户是否可以审批
this.checkCanApprove()
} else {
console.error('审批详情查询失败:', approvalRes)
this.$message.error('审批详情查询失败')
}
} catch (error) {
console.error('加载审批信息失败:', error)
this.$message.error('加载审批信息失败: ' + error.message)
}
},
checkCanApprove() {
// 只有当审批实例状态为"0"(待审批/审批中)时才能审批
if (this.approvalInstance.status !== '0' && this.approvalInstance.status !== '1') {
console.log('审批实例状态不是待审批/审批中:', this.approvalInstance.status)
this.canApprove = false
return
}
// 获取当前用户ID
const currentUserId = getCurrentUserId()
console.log('当前用户ID:', currentUserId)
console.log('当前用户ID类型:', typeof currentUserId)
// 从审批流程配置中获取当前节点的审批人列表
// 根据 currentNodeId 查找当前节点(不是 currentNodeOrder
if (this.processConfig && this.processConfig.nodeList) {
// 先按 ID 查找
let currentNode = this.processConfig.nodeList.find(
node => node.id === this.approvalInstance.currentNodeId
)
// 如果按 ID 找不到,尝试按 nodeOrder 找
if (!currentNode && this.approvalInstance.currentNodeOrder) {
console.log('⚠️ 按 ID 找不到节点,尝试按 nodeOrder 查找...')
currentNode = this.processConfig.nodeList.find(
node => node.nodeOrder === this.approvalInstance.currentNodeOrder
)
}
console.log('当前节点ID:', this.approvalInstance.currentNodeId)
console.log('当前节点顺序:', this.approvalInstance.currentNodeOrder)
console.log('当前节点:', currentNode)
if (currentNode && currentNode.approverIds) {
// 判断当前用户是否在审批人列表中
const approverIds = currentNode.approverIds.split(',').map(id => parseInt(id))
console.log('审批人ID列表:', approverIds)
console.log('审批人ID列表类型:', approverIds.map(id => typeof id))
this.canApprove = approverIds.includes(parseInt(currentUserId))
console.log('是否可以审批:', this.canApprove)
} else {
console.log('当前节点或审批人列表不存在')
}
} else {
console.log('审批流程配置或节点列表不存在')
}
},
onSelectionChange(selection) {
this.selectedRows = selection
},
onHandleViewAttachment(row) {
if (row.attachments && row.attachments.length > 0) {
const file = row.attachments[0]
const parts = file.fileName.split('.')
const extension = parts.pop()
window.open(file.fileUrl, file.fileName)
} else {
this.$message.warning('暂无附件')
}
},
async onHandleApprove() {
if (!this.approveData.opinion) {
this.$message.warning('请输入审批意见')
return
}
this.$confirm('确定通过该审批吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async() => {
try {
await doApprove({
instanceId: this.approvalInstance.id,
approveResult: '1', // 1表示通过
approveOpinion: this.approveData.opinion
})
this.$message.success('审批通过成功')
setTimeout(() => {
this.$router.back()
}, 1000)
} catch (error) {
this.$message.error('审批失败: ' + error.message)
}
}).catch(() => {
})
},
async onHandleReject() {
if (!this.approveData.opinion) {
this.$message.warning('驳回时必须填写审批意见')
return
}
this.$confirm('确定驳回该审批吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async() => {
try {
await doApprove({
instanceId: this.approvalInstance.id,
approveResult: '2', // 2表示驳回
approveOpinion: this.approveData.opinion
})
this.$message.success('审批驳回成功')
setTimeout(() => {
this.$router.back()
}, 1000)
} catch (error) {
this.$message.error('审批失败: ' + error.message)
}
}).catch(() => {
})
},
onHandleCancel() {
this.$router.back()
},
getStatusLabel() {
// 根据明细列表的审批状态计算任务状态
if (this.detailList.length === 0) {
return '待审批'
}
// 统计各状态的数量
const processedCount = this.detailList.filter(item => item.reviewStatus === '1' || item.reviewStatus === '2').length
const totalCount = this.detailList.length
if (processedCount === 0) {
// 一条都没处理
return '待审批'
} else if (processedCount === totalCount) {
// 全部处理完成
return '已审批'
} else {
// 处理了部分
return '审批中'
}
},
getStatusType() {
// 根据任务状态返回标签类型
const label = this.getStatusLabel()
const typeMap = {
'待审批': 'warning',
'审批中': 'info',
'已审批': 'success'
}
return typeMap[label] || 'info'
},
getReviewStatusLabel(status) {
const statusMap = {
'0': '待审批',
'1': '已通过',
'2': '已驳回'
}
return statusMap[status] || '待审批'
},
getReviewStatusType(status) {
const typeMap = {
'0': 'warning',
'1': 'success',
'2': 'danger'
}
return typeMap[status] || 'warning'
},
getNodeRecord(node) {
// 从审批记录中查找该节点的审批记录
// 根据 nodeId 查找(不是 nodeOrder
if (!this.approvalInstance.recordList) {
return null
}
return this.approvalInstance.recordList.find(record => record.nodeId === node.id)
},
getNodeStatusLabel(node) {
// 根据节点状态返回标签文本
const record = this.getNodeRecord(node)
if (record) {
return record.approveResult === '1' ? '已通过' : '已驳回'
}
if (node.id === this.approvalInstance.currentNodeId) {
return '审批中'
}
if (node.nodeOrder < this.approvalInstance.currentNodeOrder) {
return '已通过'
}
return '未开始'
},
getNodeTagType(node) {
// 根据节点状态返回标签类型
const record = this.getNodeRecord(node)
if (record) {
return record.approveResult === '1' ? 'success' : 'danger'
}
if (node.id === this.approvalInstance.currentNodeId) {
return 'warning'
}
return 'info'
},
getNodeStatusLabel_old(status) {
const statusMap = {
'0': '待审批',
'1': '已通过',
'2': '已驳回',
'3': '等待中'
}
return statusMap[status] || '未知'
},
getNodeStatusType(status) {
const typeMap = {
'0': 'warning',
'1': 'success',
'2': 'danger',
'3': 'info'
}
return typeMap[status] || 'info'
}
},
created() {
this.getAuditDetail()
}
}
</script>
<style lang="scss" scoped>
.app-container{
background: #F0F0F0;
}
::v-deep.el-button--primary{
background-color: #2CBAB2;
border-color: #2CBAB2;
}
::v-deep.el-button--danger{
background-color: #FF5129;
border-color: #FF5129;
}
::v-deep.el-tag.el-tag--info {
background-color: #F5F5F5;
border-color: #B3B3B3;
color: #B3B3B3;
}
::v-deep.el-tag.el-tag--warn{
background-color: rgba(255,171,41,0.1);;
border: #FFAB29;
color: #FFAB29;
}
::v-deep.el-tag.el-tag--success {
background-color: rgba(52,226,199,0.1);
border-color: #34E2C7;
color: #34E2C7;
}
.box-card {
margin: 20px;
}
.detail-section,
.approval-progress,
.approval-actions {
margin: 10px 0;
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
font-weight: 600;
font-size: 14px;
}
}
.approval-progress {
::v-deep .el-timeline-item__wrapper {
padding-left: 30px;
}
.timeline-content {
.node-title {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
margin-bottom: 8px;
.node-name {
font-weight: 600;
font-size: 14px;
}
}
.node-info {
font-size: 12px;
background: rgba(52, 226, 199,0.08);
color: #3F3F3F;
padding: 15px;
border-radius:5px;
line-height: 1.8;
}
}
}
.custom-icon{
width: 20px;
height: 20px;
}
.approval-actions {
/* border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 20px;
background-color: #f5f7fa; */
}
.action-buttons {
text-align: center;
margin-top: 30px;
}
</style>