2025-12-25 10:21:01 +08:00

1057 lines
31 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>
<transition name="approval-progress-fade">
<div v-if="visible" class="approval-progress-detail">
<div class="detail-panel">
<div class="detail-header">
<div class="header-left">
<el-button
type="text"
icon="el-icon-arrow-left"
class="back-button"
@click="handleBack"
>
</el-button>
<div class="header-info">
<div class="title">
审批详情
</div>
</div>
</div>
<div class="header-right">
<el-button
v-if="flowStatus === 'APPROVING'"
@click="handleReject"
>
退回
</el-button>
<el-button
v-if="flowStatus === 'APPROVING'"
type="primary"
@click="handleSubmit"
>
提交
</el-button>
</div>
</div>
<div class="detail-content">
<section class="block-section">
<div class="section-title">
基础信息
</div>
<div class="info-grid">
<div class="info-item">
<span class="label">流程主题</span>
<span class="value">{{ progressDetail.progressTitle || '--' }}</span>
</div>
<div class="info-item">
<span class="label">模板名称</span>
<span class="value">{{ progressDetail.contentName || '--' }}</span>
</div>
<div class="info-item">
<span class="label">版本</span>
<span class="value">{{ progressDetail.templateVersion || '--' }}</span>
</div>
</div>
</section>
<section class="block-section">
<div class="section-title">
审批内容
</div>
<el-table :data="changeTable || []" border class="change-table">
<el-table-column label="序号" type="index" width="60" />
<el-table-column
prop="subsystemName"
label="子系统"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
prop="parameterType"
label="类型"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
prop="partsName"
label="零部件名称"
min-width="140"
show-overflow-tooltip
/>
<el-table-column
prop="parameterName"
label="参数名称"
min-width="140"
show-overflow-tooltip
/>
<el-table-column
prop="unit"
label="单位"
min-width="80"
show-overflow-tooltip
/>
<el-table-column prop="remarks" label="备注" min-width="120" show-overflow-tooltip />
<el-table-column prop="modifyDetails" label="变动说明" min-width="160">
<template slot-scope="scope">
<span class="modify-details-text">
{{ scope.row.modifyDetails || '--' }}
</span>
</template>
</el-table-column>
</el-table>
<el-pagination
v-if="changeTableTotal > 0"
class="pagination-wrapper"
:current-page="changeTablePageNumber"
:page-size="changeTablePageSize"
:total="changeTableTotal"
layout="total, prev, pager, next, jumper"
@current-change="handlePageChange"
/>
<div v-if="progressDetail.description" class="description-block">
<span class="label">说明</span>
<span class="value">{{ detailOption }}</span>
</div>
</section>
<section class="block-section">
<div class="section-title">
审批流程
</div>
<div v-if="timelineNodes && timelineNodes.length > 0" class="approval-timeline">
<el-timeline>
<el-timeline-item
v-for="(node, index) in timelineNodes"
:key="index"
:color="getTimelineColor(node.result || node.status, index, timelineNodes.length)"
>
<div class="timeline-item-content">
<div class="node-title">
{{ node.title }}
</div>
<div class="node-content">
<div class="node-info">
<div class="node-user">
{{ node.userName || '--' }}
<span class="node-time">
{{ formatTime(node.operateTime) }}
</span>
</div>
</div>
<div class="node-status" :class="getStatusClass(node.result || node.status)">
{{ setStatusText(node.result || node.status) }}
</div>
<div v-if="node.opinion" class="node-opinion">
审批意见{{ node.opinion }}
</div>
</div>
</div>
</el-timeline-item>
</el-timeline>
</div>
<div v-else class="empty-progress">
<span class="empty-text">暂无审批流程数据</span>
</div>
</section>
</div>
</div>
<!-- 审批弹窗 -->
<el-dialog
:title="approvalDialogTitle"
:visible.sync="approvalDialogVisible"
width="500px"
:close-on-click-modal="false"
append-to-body
@close="handleApprovalDialogClose"
>
<el-form ref="approvalForm" :model="approvalFormData" :rules="approvalFormRules" label-width="80px">
<el-form-item
:label="approvalActionType === 'reject' ? '退回意见:' : '提交意见:'"
prop="opinion"
>
<el-input
v-model="approvalFormData.opinion"
type="textarea"
:rows="4"
:placeholder="approvalActionType === 'reject' ? '请输入退回意见' : '请输入提交意见'"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="handleApprovalDialogClose">
取消
</el-button>
<el-button type="primary" :loading="approvalSubmitting" @click="handleApprovalSubmit">
确定
</el-button>
</div>
</el-dialog>
</div>
</transition>
</template>
<script>
import api from '@/api'
import { getUserId } from '@/utils'
export default {
name: 'ApprovalProgressDetail',
props: {
detail: {
type: Object,
default: () => ({})
},
visible: {
type: Boolean,
default: true
}
},
data() {
return {
changeTable: [],
progress: [], // 保留用于兼容
flowNodes: [], // 审批流程节点数据
progressDetailData: {}, // 审批流程详情数据
detailOption: '',
flowStatus: '',
changeTableTotal: 0,
changeTablePageNumber: 1,
changeTablePageSize: 10,
approvalDialogVisible: false,
approvalActionType: '', // 'reject' 或 'submit'
approvalDialogTitle: '审核',
approvalFormData: {
result: '',
opinion: ''
},
approvalSubmitting: false,
approvalFormRules: {
opinion: [
{ required: true, message: '请输入意见', trigger: 'blur' }
]
}
}
},
computed: {
progressDetail() {
// 优先使用从接口获取的数据,如果没有则使用默认值
if (this.progressDetailData && Object.keys(this.progressDetailData).length > 0) {
return {
progressTitle: this.progressDetailData.progressTitle || '',
contentName: this.progressDetailData.contentName || '',
templateVersion: this.progressDetailData.templateVersion || '',
explanation: this.progressDetailData.explanation || '',
description: this.progressDetailData.explanation || this.progressDetailData.description || ''
}
}
const defaultDetail = {
approvalNumber: '',
modelName: '',
applicant: '',
department: '',
submitTime: '',
description: '',
changeTable: [],
progress: []
}
return Object.assign(defaultDetail, this.detail || {})
},
// 发起审批节点
initiateNode() {
if (!this.detail || !this.detail.createBy) {
return null
}
return {
userName: this.detail.createBy,
createTime: this.detail.createdTime || this.detail.createTime,
status: 'INITIATE',
result: 'INITIATE'
}
},
// 合并所有节点为时间线数据
timelineNodes() {
const nodes = []
// 添加发起审批节点
if (this.initiateNode) {
nodes.push({
title: '提交申请',
userName: this.detail.username,
operateTime: this.detail.createdTime,
result: 'INITIATE',
status: 'INITIATE',
opinion: this.initiateNode.opinion || this.initiateNode.remark || this.initiateNode.comment || null
})
}
// 使用 flowNodes 数据构建审批流程节点
if (this.flowNodes && this.flowNodes.length > 0) {
// 按照 nodeOrder 排序
const sortedNodes = [...this.flowNodes].sort((a, b) => {
const orderA = a.nodeOrder !== undefined && a.nodeOrder !== null ? Number(a.nodeOrder) : 0
const orderB = b.nodeOrder !== undefined && b.nodeOrder !== null ? Number(b.nodeOrder) : 0
return orderA - orderB
})
// 查找是否有 REJECT 节点
const rejectIndex = sortedNodes.findIndex((node) => node.result === 'REJECT')
let processedNodes = sortedNodes
if (rejectIndex !== -1) {
// 如果有 REJECT 节点,删除该节点后面的所有节点
processedNodes = sortedNodes.slice(0, rejectIndex + 1)
} else {
// 如果没有 REJECT将第一个 result 是"待审批"的节点改为 APPROVING
const stayIndex = processedNodes.findIndex((node) => node.result === '待审批')
if (stayIndex !== -1) {
processedNodes = processedNodes.map((node, index) => {
if (index === stayIndex) {
return {
...node,
result: 'APPROVING'
}
}
return node
})
}
}
// 转换为时间线节点格式
processedNodes.forEach((node) => {
let result = 'APPROVING'
let status = 'APPROVING'
if (node.result) {
if (node.result === 'REJECT') {
result = 'REJECT'
status = 'REJECT'
} else if (node.result === 'APPROVING') {
result = 'APPROVING'
status = 'APPROVING'
} else if (node.result === 'PASS') {
result = 'PASS'
status = 'PASS'
} else if (node.result === '待审批') {
result = 'STAY'
status = 'STAY'
}
}
// 处理审批意见:如果有 opinions 数组,取第一个;否则为空
const opinion = node.opinions && Array.isArray(node.opinions) && node.opinions.length > 0
? node.opinions[0]
: null
// 处理用户名:优先使用 todoUserName已处理人如果没有则使用 undoUserName待处理人
const userName = node.todoUserName || node.undoUserName || '--'
nodes.push({
title: node.node || '审批节点',
userName: userName,
operateTime: node.approvalTime || '',
result: result,
status: status,
opinion: opinion
})
})
} else if (this.progress && this.progress.length > 0) {
// 兼容旧逻辑:如果没有 flowNodes则使用原来的 progress 数据
this.progress.forEach((stage) => {
if (stage.steps && stage.steps.length > 0) {
stage.steps.forEach((step) => {
nodes.push({
title: stage.title || '审批节点',
userName: step.userName,
operateTime: step.approvalTime || step.handleTime || step.createTime,
result: step.result || step.status,
status: step.result || step.status,
opinion: step.opinion || step.remark || step.comment || null
})
})
}
})
}
return nodes
},
// 判断是否可以审批(状态为审批中且当前用户是审批人)
canApprove() {
if (!this.detail || !this.detail.status) {
return false
}
// 状态为审批中
if (this.detail.status === 'APPROVING' || this.detail.status === '审批中') {
// 检查当前用户是否是审批人
if (this.detail.currentApprovers && Array.isArray(this.detail.currentApprovers)) {
const currentUserId = getUserId()
return this.detail.currentApprovers.some(
approver => approver.userId === currentUserId || approver.userNumber === currentUserId
)
}
// 如果没有 currentApprovers 字段,也可以显示按钮(由后端判断)
return true
}
return false
}
},
mounted() {
this.getApprovalChange()
this.getApprovalSpet()
},
methods: {
setStatusText(result) {
const statusMap = {
'PASS': '通过',
'REJECT': '驳回',
'REFUSE': '驳回',
'APPROVE': '批准',
'APPROVING': '审批中',
'PENDING': '待处理',
'WAITING': '待处理',
'INITIATE': '发起审批'
}
return statusMap[result] || result || '待处理'
},
formatTime(time) {
if (!time) return '--'
// 如果是时间戳,转换为日期格式
if (typeof time === 'number') {
const date = new Date(time)
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
}
// 如果已经是字符串格式,直接返回
return time
},
getTimelineColor(result, stepIndex, totalSteps) {
// 根据审批结果设置颜色
if (result === 'PASS' || result === 'APPROVE') {
return '#67C23A' // 绿色 - 通过
} else if (result === 'REJECT' || result === 'REFUSE') {
return '#F56C6C' // 红色 - 驳回
} else if (result === 'APPROVING' || result === 'PENDING' || result === 'WAITING') {
return '#E6A23C' // 橙色 - 审批中
} else if (result === 'INITIATE') {
return '#409EFF' // 蓝色 - 发起审批
}
// 默认颜色
return '#409EFF' // 默认蓝色
},
getStatusClass(result) {
const classMap = {
'PASS': 'status-pass',
'APPROVE': 'status-approve',
'REJECT': 'status-reject',
'REFUSE': 'status-reject',
'APPROVING': 'status-approving',
'PENDING': 'status-pending',
'WAITING': 'status-pending',
'INITIATE': 'status-initiate'
}
return classMap[result] || 'status-default'
},
getApprovalSpet() {
if (!this.detail || !this.detail.flowId) {
return
}
const request = {
...api.GET_PPROVAL_ALL,
params: {
flowId: this.detail.flowId,
templateId: this.detail.templateId
},
disableSuccessMsg: true
}
this.$request(request)
.asyncThen((resp) => {
console.log('审批流程数据:', resp)
const data = resp.data || resp
// 保存流程状态
this.flowStatus = data.flowStatus || ''
// 保存流程详情
this.progressDetailData = {
explanation: data.explanation || '',
flowStatus: data.flowStatus || '',
progressTitle: data.progressTitle || '',
templateVersion: data.templateVersion || '',
contentName: data.contentName || ''
}
// 处理说明信息
this.detailOption = data.explanation || data.description || ''
// 保存流程节点
this.flowNodes = data.flowNodes || []
// 兼容旧逻辑:如果没有 flowNodes尝试使用旧的节点结构
if (!this.flowNodes || this.flowNodes.length === 0) {
let list = []
// 处理校对节点
if (data.proofreadNodes && Array.isArray(data.proofreadNodes) && data.proofreadNodes.length > 0) {
const steps = data.proofreadNodes
list.push({
title: '校对',
steps: this.deduplicateSteps(Array.isArray(steps) ? steps : [steps])
})
}
// 处理审核节点
if (data.reviewNodes && Array.isArray(data.reviewNodes) && data.reviewNodes.length > 0) {
const steps = data.reviewNodes
list.push({
title: '审核',
steps: this.deduplicateSteps(Array.isArray(steps) ? steps : [steps])
})
}
// 处理会签节点
if (data.countersignNodes && Array.isArray(data.countersignNodes) && data.countersignNodes.length > 0) {
const steps = data.countersignNodes
list.push({
title: '会签',
steps: this.deduplicateSteps(Array.isArray(steps) ? steps : [steps])
})
}
// 处理批准节点
if (data.approveNodes && Array.isArray(data.approveNodes) && data.approveNodes.length > 0) {
const steps = data.approveNodes
list.push({
title: '批准',
steps: this.deduplicateSteps(Array.isArray(steps) ? steps : [steps])
})
}
// 如果数据结构是直接返回数组,则直接使用
if (list.length === 0 && data) {
// 尝试直接使用返回的数据结构
if (Array.isArray(data)) {
list = data.map(item => ({
title: item.title || item.nodeType || item.name || '审批节点',
steps: this.deduplicateSteps(Array.isArray(item.steps) ? item.steps : (Array.isArray(item.nodes) ? item.nodes : []))
}))
} else if (data.nodes && Array.isArray(data.nodes)) {
list = data.nodes.map(item => ({
title: item.title || item.nodeType || item.name || '审批节点',
steps: this.deduplicateSteps(Array.isArray(item.steps) ? item.steps : (Array.isArray(item.nodes) ? item.nodes : []))
}))
} else if (data.processNodes && Array.isArray(data.processNodes)) {
list = data.processNodes.map(item => ({
title: item.title || item.nodeType || item.name || '审批节点',
steps: this.deduplicateSteps(Array.isArray(item.steps) ? item.steps : (Array.isArray(item.nodes) ? item.nodes : []))
}))
}
}
this.progress = list
console.log('处理后的流程数据(兼容模式):', this.progress)
}
})
.asyncErrorCatch((err) => {
console.error('获取审批流程数据失败:', err)
this.flowNodes = []
this.progressDetailData = {}
})
},
getApprovalChange() {
const request = {
...api.GET_APPROVAL_CHANGE,
params: {
flowId: this.detail.flowId,
templateId: this.detail.templateId,
pageNumber: this.changeTablePageNumber,
pageSize: this.changeTablePageSize
},
disableSuccessMsg: true
}
this.$request(request).asyncThen((resp) => {
console.log(resp)
this.changeTable = resp.data.result
this.changeTableTotal = resp.data.count
})
},
handleBack() {
this.$emit('close')
},
handlePageChange(page) {
this.changeTablePageNumber = page
this.getApprovalChange()
},
changeTypeClass(type) {
if (type === 'create') return 'change-create'
if (type === 'delete') return 'change-delete'
if (type === 'update') return 'change-update'
return 'change-default'
},
// 对steps数组进行去重优先保留有approvalTime的记录
deduplicateSteps(steps) {
if (!Array.isArray(steps) || steps.length === 0) {
return []
}
// 使用Map来存储已见过的唯一标识和对应的记录
const seen = new Map()
const result = []
steps.forEach(step => {
// 生成唯一标识优先使用id如果没有id则使用多个字段组合
let uniqueKey = null
if (step.id) {
// 如果有id使用id作为唯一标识
uniqueKey = `id_${step.id}`
} else {
// 如果没有id使用多个字段组合作为唯一标识
const userName = step.userName || step.operator || step.username || step.handlerName || step.handler || ''
const approvalTime = step.approvalTime || step.handleTime || step.createTime || ''
const stepResult = step.result || step.status || ''
uniqueKey = `combo_${userName}_${approvalTime}_${stepResult}`
}
if (!uniqueKey) {
return // 跳过无法生成唯一标识的记录
}
// 检查是否已存在相同唯一标识的记录
if (seen.has(uniqueKey)) {
// 如果已存在比较两条记录优先保留有approvalTime的记录
const existingIndex = seen.get(uniqueKey)
const existingStep = result[existingIndex]
const currentHasTime = !!(step.approvalTime || step.handleTime || step.createTime)
const existingHasTime = !!(existingStep.approvalTime || existingStep.handleTime || existingStep.createTime)
// 如果当前记录有approvalTime而已存在的没有则替换
if (currentHasTime && !existingHasTime) {
result[existingIndex] = step
}
// 如果两条都有或都没有,保留已存在的(第一条)
} else {
// 如果这个唯一标识还没有出现过,添加到结果中
seen.set(uniqueKey, result.length)
result.push(step)
}
})
// 对所有记录的approvalTime进行格式化
return result.map(step => {
const formattedStep = { ...step }
if (formattedStep.approvalTime) {
formattedStep.approvalTime = this.formatTime(formattedStep.approvalTime)
}
if (formattedStep.handleTime) {
formattedStep.handleTime = this.formatTime(formattedStep.handleTime)
}
if (formattedStep.createTime) {
formattedStep.createTime = this.formatTime(formattedStep.createTime)
}
return formattedStep
})
},
handleReject() {
this.approvalActionType = 'reject'
this.approvalDialogTitle = '退回'
this.approvalFormData = {
result: 'REJECT',
opinion: ''
}
this.approvalDialogVisible = true
// 清除表单验证
this.$nextTick(() => {
if (this.$refs.approvalForm) {
this.$refs.approvalForm.clearValidate()
}
})
},
handleSubmit() {
this.approvalActionType = 'submit'
this.approvalDialogTitle = '提交'
this.approvalFormData = {
result: 'PASS',
opinion: ''
}
this.approvalDialogVisible = true
// 清除表单验证
this.$nextTick(() => {
if (this.$refs.approvalForm) {
this.$refs.approvalForm.clearValidate()
}
})
},
handleApprovalDialogClose() {
this.approvalDialogVisible = false
this.approvalActionType = ''
this.approvalDialogTitle = '审核'
this.approvalFormData = {
result: '',
opinion: ''
}
// 清除表单验证
if (this.$refs.approvalForm) {
this.$refs.approvalForm.clearValidate()
}
},
handleApprovalSubmit() {
// 表单验证
this.$refs.approvalForm.validate((valid) => {
if (!valid) {
return false
}
if (!this.detail || !this.detail.flowId) {
this.$message.error('缺少审批信息')
return
}
// 构建提交数据
const submitData = {
flowId: this.detail.flowId,
userId: getUserId(),
result: this.approvalFormData.result, // 'PASS' 或 'REJECT'
opinion: this.approvalFormData.opinion || ''
}
this.approvalSubmitting = true
const request = {
...api.HANDLE_APPROVAL,
params: submitData
}
this.$request(request)
.asyncThen((resp) => {
this.handleApprovalDialogClose()
// 重新加载审批流程和变更数据
this.getApprovalSpet()
this.getApprovalChange()
// 通知父组件刷新数据
this.$emit('refresh')
this.approvalSubmitting = false
})
.asyncErrorCatch((err) => {
console.error('审批提交失败:', err)
this.$message.error('审批提交失败,请重试')
this.approvalSubmitting = false
})
.finally(() => {
this.approvalSubmitting = false
})
})
}
}
}
</script>
<style lang="scss" scoped>
.approval-progress-detail {
width: 100%;
height: 100%;
background: #fff;
display: flex;
justify-content: flex-start;
align-items: flex-start;
padding: 0;
box-sizing: border-box;
overflow-y: auto;
.detail-panel {
width: 100%;
max-width: 1200px;
background: #ffffff;
border-radius: 12px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.detail-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24px 32px 16px 32px;
border-bottom: 1px solid #ebeef5;
.header-left {
display: flex;
align-items: center;
gap: 16px;
}
.header-right {
display: flex;
align-items: center;
gap: 8px;
}
.back-button {
display: flex;
align-items: center;
font-size: 14px;
color: #606266;
::v-deep .el-icon-arrow-left {
font-size: 16px;
}
}
.header-info {
display: flex;
flex-direction: column;
gap: 4px;
.title {
font-size: 20px;
font-weight: 600;
color: #303133;
}
.subtitle {
font-size: 14px;
color: #909399;
}
}
}
.detail-content {
padding: 24px 32px 40px 32px;
display: flex;
flex-direction: column;
height: 100%;
overflow: auto;
gap: 24px;
}
.block-section {
border-radius: 8px;
padding: 24px 24px 16px 24px;
.section-title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin-bottom: 16px;
}
}
.info-grid {
display: grid;
grid-template-columns: repeat(3, minmax(200px, 1fr));
gap: 16px 24px;
.info-item {
display: flex;
flex-direction: column;
gap: 6px;
.label {
font-size: 13px;
color: #909399;
}
.value {
font-size: 15px;
color: #303133;
font-weight: 500;
}
}
}
.change-table {
width: 100%;
margin-bottom: 16px;
::v-deep .el-table__header-wrapper th {
background: #f0f2f5;
color: #606266;
font-weight: 500;
}
}
.pagination-wrapper {
display: flex;
justify-content: flex-end;
margin-top: 16px;
margin-bottom: 16px;
}
.modify-details-text {
white-space: pre-line;
word-break: break-word;
}
.change-tag {
display: inline-block;
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
line-height: 1;
background: #f4f4f5;
color: #909399;
}
.change-create {
background: #f0f9eb;
color: #67c23a;
}
.change-update {
background: #ecf5ff;
color: #409eff;
}
.change-delete {
background: #fef0f0;
color: #f56c6c;
}
.description-block {
display: flex;
gap: 8px;
font-size: 14px;
color: #606266;
.label {
color: #909399;
}
}
.approval-timeline {
padding: 20px 0;
::v-deep .el-timeline {
padding-left: 0;
.el-timeline-item {
padding-bottom: 24px;
&:last-child {
padding-bottom: 0;
}
.el-timeline-item__node {
width: 12px;
height: 12px;
left: 0;
top: 4px;
border: 2px solid #fff;
box-shadow: 0 0 0 2px #409eff;
}
.el-timeline-item__wrapper {
top: 0;
padding-left: 28px;
.el-timeline-item__content {
padding-top: 0;
}
}
}
}
}
.timeline-item-content {
display: flex;
flex-direction: column;
gap: 0;
.node-title {
font-size: 14px;
font-weight: 600;
color: #303133;
margin-bottom: 8px;
}
.node-content {
background: #f4f7fc;
border-radius: 4px;
padding: 12px;
display: flex;
flex-direction: column;
gap: 8px;
}
.node-info {
display: flex;
flex-direction: column;
gap: 4px;
font-size: 13px;
color: #606266;
}
.node-user {
color: #303133;
font-weight: 500;
}
.node-time {
color: #909399;
font-size: 12px;
margin-left: 8px;
}
.node-status {
display: inline-block;
font-size: 13px;
font-weight: 500;
border-radius: 2px;
width: fit-content;
&.status-initiate,
&.status-pass,
&.status-approve {
color: #67c23a;
background: transparent;
}
&.status-reject {
color: #f56c6c;
background: transparent;
}
&.status-approving,
&.status-pending {
color: #e6a23c;
background: transparent;
}
&.status-default {
color: #909399;
background: transparent;
}
}
.node-opinion {
font-size: 13px;
color: #000;
line-height: 1.6;
}
}
.empty-progress {
text-align: center;
padding: 40px 20px;
color: #909399;
font-size: 14px;
.empty-text {
display: inline-block;
}
}
}
.approval-progress-fade-enter-active,
.approval-progress-fade-leave-active {
transition: opacity 0.2s ease;
}
.approval-progress-fade-enter,
.approval-progress-fade-leave-to {
opacity: 0;
}
::v-deep .el-timeline-item__tail{
left: 7px;
top: 3px;
}
</style>