|
|
<template>
|
|
|
<el-drawer
|
|
|
:visible.sync="drawerVisible"
|
|
|
:size="drawerSize"
|
|
|
:with-header="true"
|
|
|
:modal="false"
|
|
|
:show-close="showCloseButton"
|
|
|
:before-close="handleClose"
|
|
|
direction="rtl"
|
|
|
custom-class="contract-detail-drawer"
|
|
|
:title="detail?detail.name:'合同详情'"
|
|
|
>
|
|
|
<div class="main-container-over">
|
|
|
<div class="content-area">
|
|
|
|
|
|
<!-- 顶部完整流程概览 -->
|
|
|
<div class="overview-container">
|
|
|
<div class="overview-title">
|
|
|
<i class="fas fa-sitemap" />
|
|
|
流程概览
|
|
|
</div>
|
|
|
<div class="overview-section">
|
|
|
<div class="overview-flow">
|
|
|
<div id="overview-1" :class="['overview-step', getOverviewStepClassForSh()]">
|
|
|
<div class="overview-icon"><i class="fas fa-handshake" /></div>
|
|
|
<div>资金使用上会</div>
|
|
|
<div class="overview-number">1</div>
|
|
|
</div>
|
|
|
<div class="overview-arrow"><i class="fas fa-arrow-right" /></div>
|
|
|
|
|
|
<div id="overview-2" :class="['overview-step', getOverviewStepClassForProcurement()]">
|
|
|
<div class="overview-icon"><i class="fas fa-shopping-cart" /></div>
|
|
|
<div>采购/事前流程</div>
|
|
|
<div class="overview-number">2</div>
|
|
|
</div>
|
|
|
<div class="overview-arrow"><i class="fas fa-arrow-right" /></div>
|
|
|
|
|
|
<div id="overview-3" :class="['overview-step', getOverviewStepClassForTender()]">
|
|
|
<div class="overview-icon"><i class="fas fa-gavel" /></div>
|
|
|
<div>招标审查</div>
|
|
|
<div class="overview-number">3</div>
|
|
|
</div>
|
|
|
<div class="overview-arrow"><i class="fas fa-arrow-right" /></div>
|
|
|
|
|
|
<div id="overview-4" :class="['overview-step', getOverviewStepClassForContractSign()]">
|
|
|
<div class="overview-icon"><i class="fas fa-file-contract" /></div>
|
|
|
<div>合同审批签订</div>
|
|
|
<div class="overview-number">4</div>
|
|
|
</div>
|
|
|
<div class="overview-arrow"><i class="fas fa-arrow-right" /></div>
|
|
|
|
|
|
<div class="payment-group-container">
|
|
|
<div id="overview-5" class="overview-step payment">
|
|
|
<div class="overview-icon"><i class="fas fa-credit-card" /></div>
|
|
|
<div>支付审批上会</div>
|
|
|
<div class="overview-number">5</div>
|
|
|
</div>
|
|
|
|
|
|
<div id="overview-6" class="overview-step payment">
|
|
|
<div class="overview-icon"><i class="fas fa-paper-plane" /></div>
|
|
|
<div>创建付款记录</div>
|
|
|
<div class="overview-number">6</div>
|
|
|
</div>
|
|
|
|
|
|
<div id="overview-7" class="overview-step payment">
|
|
|
<div class="overview-icon"><i class="fas fa-tasks" /></div>
|
|
|
<div>支付审批流转</div>
|
|
|
<div class="overview-number">7</div>
|
|
|
</div>
|
|
|
|
|
|
<div id="overview-8" class="overview-step payment">
|
|
|
<div class="overview-icon"><i class="fas fa-check-circle" /></div>
|
|
|
<div>财务付款确认</div>
|
|
|
<div class="overview-number">8</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="overview-rounds-info">
|
|
|
<span class="badge bg-secondary">{{ (fundLogs && fundLogs.length > 0) ? ('已发起' + fundLogs.length + '轮') : '未发起' }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 列支信息 -->
|
|
|
<div class="expense-info-container">
|
|
|
<div class="expense-info-title">
|
|
|
<i class="fas fa-receipt" />
|
|
|
列支信息
|
|
|
</div>
|
|
|
<div class="expense-info-section">
|
|
|
<div class="expense-info-content">
|
|
|
<div class="expense-info-item">
|
|
|
<span class="expense-info-label">项目名称</span>
|
|
|
<span class="expense-info-value">{{ detail && detail.name ? detail.name : '-' }}</span>
|
|
|
</div>
|
|
|
<div class="expense-info-item">
|
|
|
<span class="expense-info-label">所属科室</span>
|
|
|
<span class="expense-info-value">{{ detail && detail.department ? detail.department.name : '-' }}</span>
|
|
|
</div>
|
|
|
<div class="expense-info-item">
|
|
|
<span class="expense-info-label">经办人</span>
|
|
|
<span class="expense-info-value">{{ detail && detail.admin ? detail.admin.name : '-' }}</span>
|
|
|
</div>
|
|
|
<div class="expense-info-item">
|
|
|
<span class="expense-info-label">预算计划</span>
|
|
|
<span class="expense-info-value">
|
|
|
<div v-if="detail && detail.plans && detail.plans.length > 0">
|
|
|
<div
|
|
|
v-for="item in detail.plans"
|
|
|
:key="item.id"
|
|
|
class="plan-item"
|
|
|
>
|
|
|
[{{ item.year || '-' }}] {{ item.pid_info ? item.pid_info.name : '' }} - {{ item.name || '-' }}
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-else>-</div>
|
|
|
</span>
|
|
|
</div>
|
|
|
<div class="expense-info-item">
|
|
|
<span class="expense-info-label">项目金额(元)</span>
|
|
|
<span class="expense-info-value">{{ detail && detail.total_money ? moneyFormat(detail.total_money) : '0.00' }}</span>
|
|
|
</div>
|
|
|
<div class="expense-info-item">
|
|
|
<span class="expense-info-label">本年预算金额(元)</span>
|
|
|
<span class="expense-info-value">{{ detail && detail.plan_price ? moneyFormat(detail.plan_price) : '0.00' }}</span>
|
|
|
</div>
|
|
|
<div class="expense-info-item">
|
|
|
<span class="expense-info-label">次年预算金额(元)</span>
|
|
|
<span class="expense-info-value">{{ detail && detail.next_year_ ? moneyFormat(detail.plan_price) : '0.00' }}</span>
|
|
|
</div>
|
|
|
<div class="expense-info-item">
|
|
|
<span class="expense-info-label">已确认金额</span>
|
|
|
<span class="expense-info-value">{{confirmedAmount}}</span>
|
|
|
</div>
|
|
|
<!-- <div class="expense-info-item">
|
|
|
<span class="expense-info-label">当前状态</span>
|
|
|
<span class="expense-info-value">
|
|
|
<span class="expense-status">已完成</span>
|
|
|
</span>
|
|
|
</div> -->
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 环节详情展开 -->
|
|
|
<div class="detail-section">
|
|
|
<div class="detail-title">
|
|
|
<i class="fas fa-list-ul" />
|
|
|
信息链详情
|
|
|
</div>
|
|
|
|
|
|
<!-- 环节1:资金使用上会 -->
|
|
|
<div id="detail-1" :class="['step-detail', getOverviewStepClassForSh()]">
|
|
|
<div class="step-header">
|
|
|
<div class="step-title">
|
|
|
<div class="step-icon">
|
|
|
<i class="fas fa-handshake" />
|
|
|
</div>
|
|
|
<div>资金使用上会</div>
|
|
|
</div>
|
|
|
<div v-if="shOaView" :class="['step-status', getOverviewStepClassForSh()]">{{ shOaView? shOaView.status_text : '-' }}</div>
|
|
|
</div>
|
|
|
<template v-if="shOaView">
|
|
|
<div class="step-content">
|
|
|
<div class="step-info">
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">工作名称</span>
|
|
|
<span class="info-value">{{ shOaView.title? shOaView.title : '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">申请人</span>
|
|
|
<span class="info-value">{{ shOaView.creator? shOaView.creator.name : '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">申请部门</span>
|
|
|
<span class="info-value">{{ shOaView.creator_department? shOaView.creator_department.name : '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">申请时间</span>
|
|
|
<span class="info-value">{{ shOaView.create_at }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">审批状态</span>
|
|
|
<span class="info-value">{{ shOaView.status_text? shOaView.status_text : '-' }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
<template v-else>
|
|
|
<div class="step-content">
|
|
|
暂无资金上会流程
|
|
|
</div>
|
|
|
</template>
|
|
|
</div>
|
|
|
|
|
|
<!-- 环节2:采购/事前流程 -->
|
|
|
<div id="detail-2" :class="['step-detail', getStepClassForProcurement()]">
|
|
|
<div class="step-header">
|
|
|
<div class="step-title">
|
|
|
<div class="step-icon">
|
|
|
<i class="fas fa-shopping-cart" />
|
|
|
</div>
|
|
|
<div>采购/事前流程</div>
|
|
|
</div>
|
|
|
<div v-if="procurementStatus.label" :class="['step-status', procurementStatus.class]">{{ procurementStatus.label }}</div>
|
|
|
</div>
|
|
|
<div class="step-content">
|
|
|
<template v-if="procurementFlows && procurementFlows.length > 0">
|
|
|
<div class="flow-block" v-for="(flow, idx) in procurementFlows" :key="flow.id || idx">
|
|
|
<div class="flow-title">{{ flow.custom_model_name || '-' }}</div>
|
|
|
<div class="step-info">
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">流程名称</span>
|
|
|
<span class="info-value">{{ flow.flow_title || '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">发起日期</span>
|
|
|
<span class="info-value">{{ flow.created_at || '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">状态</span>
|
|
|
<span class="info-value">
|
|
|
<span :class="['step-status', getStatusClass(flow.flow_status)]">{{ getStatusLabel(flow.flow_status) }}</span>
|
|
|
</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
<template v-else>
|
|
|
<div class="step-content">暂无采购/事前流程</div>
|
|
|
</template>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 环节3:招标审查 -->
|
|
|
<div id="detail-3-tender" :class="['step-detail', getStepClassForTender()]">
|
|
|
<div class="step-header">
|
|
|
<div class="step-title">
|
|
|
<div class="step-icon">
|
|
|
<i class="fas fa-gavel" />
|
|
|
</div>
|
|
|
<div>招标审查</div>
|
|
|
</div>
|
|
|
<div v-if="tenderStatusText" :class="['step-status', getStepClassForTender()]">{{ tenderStatusText }}</div>
|
|
|
</div>
|
|
|
<div class="step-content">
|
|
|
<template v-if="detail && detail.is_tender_audit === 0">
|
|
|
<div class="step-content">
|
|
|
无需招标审查
|
|
|
</div>
|
|
|
</template>
|
|
|
<template v-else-if="detail && detail.tender && detail.tender.length > 0">
|
|
|
<div class="step-info">
|
|
|
<div class="info-item" v-for="(file, index) in detail.tender" :key="index">
|
|
|
<span class="info-label">文件</span>
|
|
|
<span class="info-value">
|
|
|
<a :href="file.url" target="_blank" rel="noopener">{{ file.original_name || '附件' }}</a>
|
|
|
<span class="action-link" @click.stop="open(file.url)">预览</span>
|
|
|
<span class="action-link" @click.stop="download(file.id)">下载</span>
|
|
|
</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
<template v-else>
|
|
|
<div class="step-content">
|
|
|
暂无招标审查文件
|
|
|
</div>
|
|
|
</template>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 环节4:合同签订 -->
|
|
|
<div id="detail-3" :class="['step-detail', getStepClassForContractSign()]">
|
|
|
<div class="step-header">
|
|
|
<div class="step-title">
|
|
|
<div class="step-icon">
|
|
|
<i class="fas fa-file-contract" />
|
|
|
</div>
|
|
|
<div>合同审批签订</div>
|
|
|
</div>
|
|
|
<div v-if="contractSignStatus.label" :class="['step-status', contractSignStatus.class]">{{ contractSignStatus.label }}</div>
|
|
|
</div>
|
|
|
<div class="step-content">
|
|
|
<div class="step-info">
|
|
|
<template v-if="contractApprovalFlows && contractApprovalFlows.length">
|
|
|
<div class="subsection-title">合同审批流程</div>
|
|
|
<div class="flow-block compact" v-for="(flow, idx) in contractApprovalFlows" :key="'c-'+(flow.id || idx)">
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">流程名称</span>
|
|
|
<span class="info-value">{{ flow.flow_title || '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">发起日期</span>
|
|
|
<span class="info-value">{{ flow.created_at || '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">状态</span>
|
|
|
<span class="info-value">
|
|
|
<span :class="['step-status', getStatusClass(flow.flow_status)]">{{ getStatusLabel(flow.flow_status) }}</span>
|
|
|
</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
<template v-else-if="detail && detail.is_contract">
|
|
|
<div class="subsection-title-with-status">
|
|
|
<div class="subsection-title">合同审批流程</div>
|
|
|
<div class="step-status pending">待申请</div>
|
|
|
</div>
|
|
|
<div class="flow-empty">暂无合同审批流程</div>
|
|
|
</template>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">合同编号</span>
|
|
|
<span class="info-value">{{ detail && detail.number ? detail.number : '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">供应商</span>
|
|
|
<span class="info-value">{{ detail && detail.supply ? detail.supply: '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">合同金额</span>
|
|
|
<span class="info-value">{{ detail && detail.money ? moneyFormat(detail.money) : '0.00' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">签订时间</span>
|
|
|
<span class="info-value">{{ detail && detail.date ? detail.date : '-' }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 付款轮次详情(v-for 渲染) -->
|
|
|
<div v-for="(round, roundIndex) in fundLogs" :key="round.id || roundIndex" :id="`detail-round-${roundIndex+1}`" class="step-detail" :class="getRoundClass(round)">
|
|
|
<div class="step-header">
|
|
|
<div class="step-title">
|
|
|
<div class="step-icon">
|
|
|
<i class="fas fa-credit-card" />
|
|
|
</div>
|
|
|
<div>付款轮次 {{ roundIndex + 1 }} <span v-if="round && round.is_end === 1" class="badge bg-danger ms-2">最后一轮</span></div>
|
|
|
</div>
|
|
|
<div class="step-status" :class="getRoundClass(round)">{{ getRoundStatusText(round) }}</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 环节5:支付审批上会 -->
|
|
|
<div class="round-step-detail-section">
|
|
|
<div class="round-step-header">
|
|
|
<div class="round-step-title">
|
|
|
<i class="fas fa-credit-card me-2" />
|
|
|
支付审批上会
|
|
|
</div>
|
|
|
<div v-if="paymentMeetingFlow(roundIndex)" :class="['round-step-status', getStatusClass(paymentMeetingFlow(roundIndex).status)]">{{ getStatusLabel(paymentMeetingFlow(roundIndex).status) }}</div>
|
|
|
</div>
|
|
|
<div class="step-content">
|
|
|
<template v-if="paymentMeetingFlow(roundIndex)">
|
|
|
<div class="step-info">
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">流程名称</span>
|
|
|
<span class="info-value">{{ paymentMeetingFlow(roundIndex).title || '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">发起日期</span>
|
|
|
<span class="info-value">{{ paymentMeetingFlow(roundIndex).created_at || '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">发起人</span>
|
|
|
<span class="info-value">{{ paymentMeetingFlow(roundIndex).creator && paymentMeetingFlow(roundIndex).creator.name ? paymentMeetingFlow(roundIndex).creator.name : '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">发起部门</span>
|
|
|
<span class="info-value">{{ paymentMeetingFlow(roundIndex).creator_department && paymentMeetingFlow(roundIndex).creator_department.name ? paymentMeetingFlow(roundIndex).creator_department.name : '-' }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
<template v-else>
|
|
|
<div class="step-info">暂无支付审批上会流程</div>
|
|
|
</template>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 环节6:创建付款记录 -->
|
|
|
<div class="round-step-detail-section">
|
|
|
<div class="round-step-header">
|
|
|
<div class="round-step-title">
|
|
|
<i class="fas fa-paper-plane me-2" />
|
|
|
创建付款记录
|
|
|
</div>
|
|
|
<div class="round-step-status completed">已完成</div>
|
|
|
</div>
|
|
|
<div class="step-content">
|
|
|
<div class="step-info">
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">申请付款金额</span>
|
|
|
<span class="info-value">{{ round && round.apply_money ? moneyFormat(round.apply_money) : '0.00' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">款项类型</span>
|
|
|
<span class="info-value">{{ round && round.type ? round.type : '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">申请时间</span>
|
|
|
<span class="info-value">{{ round && round.created_at ? round.created_at : '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">申请人</span>
|
|
|
<span class="info-value">{{ round && round.admin && round.admin.name ? round.admin.name : '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">申请部门</span>
|
|
|
<span class="info-value">{{ round && round.department && round.department.name ? round.department.name : '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">备注</span>
|
|
|
<span class="info-value">{{ round && round.remark ? round.remark : '-' }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 环节7:付款审批流转 -->
|
|
|
<div class="round-step-detail-section">
|
|
|
<div class="round-step-header">
|
|
|
<div class="round-step-title">
|
|
|
<i class="fas fa-tasks me-2" />
|
|
|
支付审批流转
|
|
|
</div>
|
|
|
<div :class="['round-step-status', flowLinksHeaderStatus(roundIndex).class]">{{ flowLinksHeaderStatus(roundIndex).label }}</div>
|
|
|
</div>
|
|
|
<div class="step-content">
|
|
|
<template v-if="flowLinksOfRound(roundIndex).length">
|
|
|
<div class="step-info" v-for="(flow, idx2) in flowLinksOfRound(roundIndex)" :key="flow.id || idx2">
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">流程名称</span>
|
|
|
<span class="info-value">{{ flow.flow_title || '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">发起时间</span>
|
|
|
<span class="info-value">{{ flow.created_at || '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">状态</span>
|
|
|
<span class="info-value">
|
|
|
<span :class="['step-status', getStatusClass(flow.flow_status)]">{{ getStatusLabel(flow.flow_status) }}</span>
|
|
|
</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
<template v-else>
|
|
|
<div class="step-info">暂无付款审批流转流程</div>
|
|
|
</template>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 环节8:财务付款确认 -->
|
|
|
<div class="round-step-detail-section">
|
|
|
<div class="round-step-header">
|
|
|
<div class="round-step-title">
|
|
|
<i class="fas fa-check-circle me-2" />
|
|
|
财务付款确认
|
|
|
</div>
|
|
|
<div :class="['round-step-status', (round && round.status === 1) ? 'completed' : 'pending']">{{ (round && round.status === 1) ? '已审核' : '待审核' }}</div>
|
|
|
</div>
|
|
|
<div class="step-content">
|
|
|
<template v-if="round && round.status === 1">
|
|
|
<div class="step-info">
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">审核时间</span>
|
|
|
<span class="info-value">{{ round.updated_at || '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">审核人</span>
|
|
|
<span class="info-value">{{ round.audit_admin && round.audit_admin.name ? round.audit_admin.name : '-' }}</span>
|
|
|
</div>
|
|
|
<div class="info-item">
|
|
|
<span class="info-label">审核人部门</span>
|
|
|
<span class="info-value">{{ round.audit_admin && round.audit_admin.department && round.audit_admin.department.name ? round.audit_admin.department.name : '-' }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
<template v-else>
|
|
|
<div class="step-info">
|
|
|
{{ flowLinksHeaderStatus(roundIndex).label === '待申请' ? '暂未发起支付审批' : '支付审批流程流转中' }}
|
|
|
</div>
|
|
|
</template>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<Modal :z-index="9999999" v-model="showModal" :width="76" transfer :footer-hide="true" title="预览">
|
|
|
<template>
|
|
|
<iframe style="width: 100%; height: 57vh" :src="codeUri" border="0" />
|
|
|
</template>
|
|
|
</Modal>
|
|
|
</el-drawer>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import { detailContract } from '@/api/contract/contract'
|
|
|
import { getContractSign } from '@/api/contractSign/contractSign'
|
|
|
import { moneyFormatter } from '@/utils'
|
|
|
import { getToken } from '@/utils/auth'
|
|
|
import { getOaView } from '@/api/oatoken'
|
|
|
import {
|
|
|
getFundLog
|
|
|
} from '@/api/paymentRegistration/fundLog'
|
|
|
|
|
|
export default {
|
|
|
name: 'ContractDetailDrawer',
|
|
|
props: {
|
|
|
visible: {
|
|
|
type: Boolean,
|
|
|
default: false
|
|
|
},
|
|
|
contractData: {
|
|
|
type: Object,
|
|
|
default: null
|
|
|
}
|
|
|
},
|
|
|
data() {
|
|
|
return {
|
|
|
// 合同详情
|
|
|
id: '',
|
|
|
detail: null,
|
|
|
shOaView: null,
|
|
|
signPlan: [],
|
|
|
loading: false,
|
|
|
showModal: false,
|
|
|
codeUri: '',
|
|
|
fundLogs: [],
|
|
|
// 合同流程分类结果
|
|
|
procurementFlows: [],
|
|
|
contractApprovalFlows: [],
|
|
|
paymentApprovalFlows: [],
|
|
|
paymentSteps: [
|
|
|
{ id: 5, name: '支付审批上会', icon: 'fas fa-credit-card', desc: '5万以上支付审批流程' },
|
|
|
{ id: 6, name: '创建付款记录', icon: 'fas fa-paper-plane', desc: '创建付款申请单' },
|
|
|
{ id: 7, name: '付款审批流转', icon: 'fas fa-tasks', desc: '付款申请的审批和处理流程' },
|
|
|
{ id: 8, name: '财务付款确认', icon: 'fas fa-check-circle', desc: '财务部门最终确认付款' }
|
|
|
],
|
|
|
// 状态列表:-1 退回,0 办理中,1 已办结
|
|
|
statusList: [
|
|
|
{ value: -1, label: '退回', class: 'rejected' },
|
|
|
{ value: 0, label: '办理中', class: 'active' },
|
|
|
{ value: 1, label: '已办结', class: 'completed' }
|
|
|
]
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
// 路由模式下(含 out_contract_id)强制显示抽屉;组件模式下走父级的 visible
|
|
|
drawerVisible: {
|
|
|
get() {
|
|
|
return !!this.$route.query.out_contract_id || this.visible
|
|
|
},
|
|
|
set(val) {
|
|
|
// 路由模式不响应关闭(已隐藏关闭按钮)
|
|
|
if (this.$route.query.out_contract_id) return
|
|
|
this.$emit('update:visible', val)
|
|
|
}
|
|
|
},
|
|
|
// 合同签订头部状态(简化口径):
|
|
|
// - 不需要签订合同:显示“无需合同签订”
|
|
|
// - 需要签订且未签订:显示“合同待签订”
|
|
|
// - 已签订:显示“已签订”
|
|
|
contractSignStatus() {
|
|
|
const d = this.detail || {}
|
|
|
if (!d.is_contract) return { label: '无需合同签订', class: 'pending' }
|
|
|
if (d.status === 2) return { label: '已签订', class: 'completed' }
|
|
|
return { label: '合同待签订', class: 'pending' }
|
|
|
},
|
|
|
// 招标审查文案:is_tender_audit === 0 -> 空(不显示徽标);否则根据附件判断
|
|
|
tenderStatusText() {
|
|
|
if (this.detail && this.detail.is_tender_audit === 0) return ''
|
|
|
const files = this.detail && this.detail.tender
|
|
|
if (files && files.length > 0) return '已上传'
|
|
|
return '待上传'
|
|
|
},
|
|
|
// 已确认金额:fundLogs中status=1的act_money之和
|
|
|
confirmedAmount() {
|
|
|
if (!this.fundLogs || !this.fundLogs.length) return 0
|
|
|
const total = this.fundLogs
|
|
|
.filter(item => item.status === 1)
|
|
|
.reduce((sum, item) => sum + (parseFloat(item.act_money) || 0), 0)
|
|
|
return total.toFixed(2)
|
|
|
},
|
|
|
// 采购/事前流程头部状态计算(与列表页按钮口径一致):
|
|
|
// - 无任何流程数据且需要走采购流程 => 显示“待申请”
|
|
|
// 需要采购的判定:detail.is_purchase 为真,且
|
|
|
// a) 一般采购 is_common_purchase 为真;或
|
|
|
// b) 非一般采购,且非简易 is_simple !== 1,且非代建 !is_substitute
|
|
|
// - 有流程数据:全部 flow_status===1 => 已办结;否则 办理中
|
|
|
procurementStatus() {
|
|
|
const d = this.detail || {}
|
|
|
const flows = Array.isArray(this.procurementFlows) ? this.procurementFlows : []
|
|
|
if (!flows.length) {
|
|
|
if (d.is_purchase) {
|
|
|
const isGeneral = !!d.is_common_purchase
|
|
|
const needFormal = !d.is_common_purchase && d.is_simple !== 1 && !d.is_substitute
|
|
|
if (isGeneral || needFormal) {
|
|
|
return { label: '待申请', class: 'pending' }
|
|
|
}
|
|
|
}
|
|
|
return { label: '', class: '' }
|
|
|
}
|
|
|
const allDone = flows.every(f => f && f.flow_status === 1)
|
|
|
return allDone ? { label: '已办结', class: 'completed' } : { label: '办理中', class: 'active' }
|
|
|
},
|
|
|
// 根据URL参数决定drawer大小
|
|
|
drawerSize() {
|
|
|
return this.$route.query.out_contract_id ? '100%' : '60%'
|
|
|
},
|
|
|
// 根据URL参数决定是否显示关闭按钮
|
|
|
showCloseButton() {
|
|
|
return !this.$route.query.out_contract_id
|
|
|
}
|
|
|
},
|
|
|
watch: {
|
|
|
showModal(val) {
|
|
|
if (!val) this.codeUri = ''
|
|
|
},
|
|
|
contractData: {
|
|
|
handler(newVal) {
|
|
|
if (newVal && this.visible) {
|
|
|
this.detail = newVal
|
|
|
}
|
|
|
},
|
|
|
immediate: true
|
|
|
},
|
|
|
// 监听路由变化
|
|
|
'$route.query.out_contract_id': {
|
|
|
handler(newVal, oldVal) {
|
|
|
if (newVal && newVal !== oldVal) {
|
|
|
console.log('路由参数变化,检测到out_contract_id:', newVal)
|
|
|
// 重置组件状态并重新加载数据
|
|
|
this.resetAndLoadFromUrl()
|
|
|
}
|
|
|
},
|
|
|
immediate: true
|
|
|
}
|
|
|
},
|
|
|
methods: {
|
|
|
// 付款轮次:获取“支付审批上会”的流程数据(按轮次索引)
|
|
|
paymentMeetingFlow(roundIndex) {
|
|
|
const list = Array.isArray(this.fundLogs) ? this.fundLogs : []
|
|
|
const item = list[roundIndex]
|
|
|
if (!item || !item.meeting_flow) return null
|
|
|
return item.meeting_flow
|
|
|
},
|
|
|
// 付款审批流转:取当前轮次的 flow_links 数组
|
|
|
flowLinksOfRound(roundIndex) {
|
|
|
const list = Array.isArray(this.fundLogs) ? this.fundLogs : []
|
|
|
const item = list[roundIndex]
|
|
|
const links = item && Array.isArray(item.fund_log_flow_links) ? item.fund_log_flow_links : []
|
|
|
return links
|
|
|
},
|
|
|
// 付款审批流转:头部状态
|
|
|
flowLinksHeaderStatus(roundIndex) {
|
|
|
const links = this.flowLinksOfRound(roundIndex)
|
|
|
if (!links.length) {
|
|
|
return { label: '待申请', class: 'pending' }
|
|
|
}
|
|
|
const allDone = links.every(l => l && l.flow_status === 1)
|
|
|
return allDone ? { label: '已办结', class: 'completed' } : { label: '办理中', class: 'active' }
|
|
|
},
|
|
|
handleClose() {
|
|
|
this.$emit('update:visible', false)
|
|
|
// 重置组件状态
|
|
|
this.resetComponent()
|
|
|
},
|
|
|
|
|
|
// 重置组件状态
|
|
|
resetComponent() {
|
|
|
this.id = ''
|
|
|
this.detail = null
|
|
|
this.signPlan = []
|
|
|
this.loading = false
|
|
|
},
|
|
|
|
|
|
// 根据URL参数重置并加载数据
|
|
|
resetAndLoadFromUrl() {
|
|
|
this.resetComponent()
|
|
|
this.checkUrlAndLoadData()
|
|
|
},
|
|
|
|
|
|
// 重新加载数据
|
|
|
retryLoad() {
|
|
|
if (this.id) {
|
|
|
this.getDetail(this.id)
|
|
|
} else if (this.contractData) {
|
|
|
this.detail = this.contractData
|
|
|
}
|
|
|
},
|
|
|
|
|
|
// 检查URL参数并加载数据
|
|
|
checkUrlAndLoadData() {
|
|
|
const outContractId = this.$route.query.out_contract_id
|
|
|
console.log('检查URL参数 - out_contract_id:', outContractId, '当前detail:', this.detail)
|
|
|
if (outContractId && !this.detail) {
|
|
|
console.log('检测到out_contract_id参数,开始获取合同详情:', outContractId)
|
|
|
// 如果有out_contract_id且没有数据,直接获取合同详情
|
|
|
this.getDetail(outContractId)
|
|
|
} else if (outContractId && this.detail) {
|
|
|
console.log('已有合同数据,无需重复获取')
|
|
|
} else {
|
|
|
console.log('未检测到out_contract_id参数')
|
|
|
}
|
|
|
},
|
|
|
// 获取合同详情
|
|
|
async getDetail(id) {
|
|
|
try {
|
|
|
// 防止重复请求:相同ID且正在加载时直接返回
|
|
|
if (this.loading && id === this.id) {
|
|
|
console.log('忽略重复 getDetail 调用: ', id)
|
|
|
return
|
|
|
}
|
|
|
this.loading = true
|
|
|
this.id = id
|
|
|
const res = await detailContract({
|
|
|
id: id
|
|
|
})
|
|
|
this.detail = res
|
|
|
// 注释掉的代码已移除,直接使用res.plans
|
|
|
console.log('合同详情:', this.detail)
|
|
|
// 获取上会详情
|
|
|
if (this.detail.meeting_flow_mod_id) {
|
|
|
this.getShOaView(this.detail.sh_id)
|
|
|
}
|
|
|
// 获取合同签订计划
|
|
|
// const plan = await getContractSign({
|
|
|
// contract_id: id
|
|
|
// })
|
|
|
// this.signPlan = plan.data
|
|
|
|
|
|
// 根据合同流程进行分类
|
|
|
this.categorizeContractFlows(this.detail && this.detail.contract_flow_links)
|
|
|
|
|
|
// 获取付款记录
|
|
|
await this.getFundLogs(id)
|
|
|
} catch (error) {
|
|
|
console.error('获取合同详情失败:', error)
|
|
|
this.$message.error('获取合同详情失败')
|
|
|
} finally {
|
|
|
this.loading = false
|
|
|
}
|
|
|
},
|
|
|
// 将合同的流程链接按业务分类
|
|
|
categorizeContractFlows(flowLinks) {
|
|
|
const flows = Array.isArray(flowLinks) ? flowLinks : []
|
|
|
const procurement = []
|
|
|
const contractApprove = []
|
|
|
const paymentApprove = []
|
|
|
flows.forEach((flow) => {
|
|
|
const modelId = flow && flow.custom_model_id
|
|
|
if (modelId === 72) {
|
|
|
contractApprove.push(flow)
|
|
|
} else if (modelId === 75) {
|
|
|
paymentApprove.push(flow)
|
|
|
} else {
|
|
|
procurement.push(flow)
|
|
|
}
|
|
|
})
|
|
|
this.procurementFlows = procurement
|
|
|
this.contractApprovalFlows = contractApprove
|
|
|
this.paymentApprovalFlows = paymentApprove
|
|
|
console.log('流程分类: 采购/事前', this.procurementFlows, '合同审批', this.contractApprovalFlows, '支付审批', this.paymentApprovalFlows)
|
|
|
},
|
|
|
// 获取付款记录
|
|
|
async getFundLogs(contractId) {
|
|
|
try {
|
|
|
const res = await getFundLog({
|
|
|
page: 1,
|
|
|
page_size: 999,
|
|
|
contract_id: contractId,
|
|
|
sort_type: 'asc',
|
|
|
sort_name: 'is_end'
|
|
|
})
|
|
|
// 兼容不同返回结构
|
|
|
console.log("123r",res)
|
|
|
this.fundLogs = res.data
|
|
|
console.log('付款记录:', this.fundLogs)
|
|
|
} catch (e) {
|
|
|
console.error('获取付款记录失败:', e)
|
|
|
}
|
|
|
},
|
|
|
async getShOaView(id) {
|
|
|
const res = await getOaView({
|
|
|
id: id
|
|
|
})
|
|
|
this.shOaView = res.data.flow
|
|
|
},
|
|
|
// 概览/详情-合同签订状态类名(结合是否需要合同签订与审批完成)
|
|
|
getOverviewStepClassForContractSign() {
|
|
|
const s = this.contractSignStatus
|
|
|
return s && s.class ? s.class : 'pending'
|
|
|
},
|
|
|
getStepClassForContractSign() {
|
|
|
const s = this.contractSignStatus
|
|
|
return s && s.class ? s.class : 'pending'
|
|
|
},
|
|
|
// 概览-招标审查状态:is_tender_audit === 0 -> pending(无需审查,不高亮);否则根据附件
|
|
|
getOverviewStepClassForTender() {
|
|
|
if (this.detail && this.detail.is_tender_audit === 0) return 'pending'
|
|
|
const files = this.detail && this.detail.tender
|
|
|
if (files && files.length > 0) return 'completed'
|
|
|
return 'pending'
|
|
|
},
|
|
|
// 详情块-招标审查状态:is_tender_audit === 0 -> pending;否则根据附件
|
|
|
getStepClassForTender() {
|
|
|
if (this.detail && this.detail.is_tender_audit === 0) return 'pending'
|
|
|
const files = this.detail && this.detail.tender
|
|
|
if (files && files.length > 0) return 'completed'
|
|
|
return 'pending'
|
|
|
},
|
|
|
getOverviewStepClassForSh() {
|
|
|
if (!this.shOaView) return 'pending'
|
|
|
const status = this.shOaView.status
|
|
|
if (status === -1) return 'rejected'
|
|
|
if (status === 0) return 'active'
|
|
|
if (status === 1) return 'completed'
|
|
|
return 'pending'
|
|
|
},
|
|
|
// 概览-采购/事前流程状态类名
|
|
|
getOverviewStepClassForProcurement() {
|
|
|
const s = this.procurementStatus
|
|
|
return s && s.class ? s.class : 'pending'
|
|
|
},
|
|
|
// 详情-采购/事前流程状态类名
|
|
|
getStepClassForProcurement() {
|
|
|
const s = this.procurementStatus
|
|
|
return s && s.class ? s.class : 'pending'
|
|
|
},
|
|
|
// 状态文本
|
|
|
getStatusLabel(status) {
|
|
|
const item = this.statusList.find(s => s.value === status)
|
|
|
return item ? item.label : '办理中'
|
|
|
},
|
|
|
// 状态类名
|
|
|
getStatusClass(status) {
|
|
|
const item = this.statusList.find(s => s.value === status)
|
|
|
return item ? item.class : 'active'
|
|
|
},
|
|
|
// 概览-招标审查状态:有文件 completed,否则 pending
|
|
|
getOverviewStepClassForTender() {
|
|
|
const files = this.detail && this.detail.tender
|
|
|
if (files && files.length > 0) return 'completed'
|
|
|
return 'pending'
|
|
|
},
|
|
|
// 详情块-招标审查状态:有文件 completed,否则 pending
|
|
|
getStepClassForTender() {
|
|
|
const files = this.detail && this.detail.tender
|
|
|
if (files && files.length > 0) return 'completed'
|
|
|
return 'pending'
|
|
|
},
|
|
|
showPaymentStepInfo(roundId, stepId) {
|
|
|
const step = this.paymentSteps.find(s => s.id === stepId)
|
|
|
console.log(`轮次${roundId} - ${step.name}:${step.desc}`)
|
|
|
},
|
|
|
|
|
|
// 类型格式化
|
|
|
typeFormatter(type) {
|
|
|
switch (type) {
|
|
|
case 1:
|
|
|
return '服务'
|
|
|
case 2:
|
|
|
return '货物'
|
|
|
case 3:
|
|
|
return '工程'
|
|
|
case 4:
|
|
|
return '其他'
|
|
|
default:
|
|
|
return '未知'
|
|
|
}
|
|
|
},
|
|
|
// 资金渠道格式化
|
|
|
moneyWayFormatter(arr) {
|
|
|
if (!arr || !Array.isArray(arr)) return ''
|
|
|
const res = arr.map((item) => {
|
|
|
return item && item.value ? item.value : ''
|
|
|
}).filter(Boolean)
|
|
|
return res.join('、') || '暂无'
|
|
|
},
|
|
|
// 金额格式化
|
|
|
moneyFormat(money) {
|
|
|
return moneyFormatter(money)
|
|
|
},
|
|
|
// 轮次总体状态:依据后端 status(0 待处理,1 已办结)
|
|
|
getRoundClass(round) {
|
|
|
if (!round) return 'pending'
|
|
|
return round.status === 1 ? 'completed' : 'pending'
|
|
|
},
|
|
|
getRoundStatusText(round) {
|
|
|
if (!round) return '待处理'
|
|
|
return round.status === 1 ? '已办结' : '待处理'
|
|
|
},
|
|
|
// 预览附件(参考 biddingUpload.vue 的实现)
|
|
|
open(url) {
|
|
|
try {
|
|
|
const toBase64 = (u) => {
|
|
|
if (typeof btoa === 'function') {
|
|
|
return btoa(unescape(encodeURIComponent(u)))
|
|
|
}
|
|
|
if (window.Buffer) {
|
|
|
return window.Buffer.from(u).toString('base64')
|
|
|
}
|
|
|
return u
|
|
|
}
|
|
|
const encoded = toBase64(url)
|
|
|
this.codeUri = `${process.env.VUE_APP_BASE_API}/${process.env.VUE_APP_MODULE_NAME}/#/preview?url=${encodeURIComponent(encoded)}`
|
|
|
this.showModal = true
|
|
|
} catch (e) {
|
|
|
console.error('预览失败:', e)
|
|
|
this.$message.error('预览失败')
|
|
|
}
|
|
|
},
|
|
|
// 下载附件(参考 biddingUpload.vue 的实现)
|
|
|
download(id) {
|
|
|
try {
|
|
|
const url = `${window.location.origin}/api/download-file?id=${id}&token=${window.encodeURIComponent(getToken())}`
|
|
|
window.open(url, '_blank')
|
|
|
} catch (e) {
|
|
|
console.error('下载失败:', e)
|
|
|
this.$message.error('下载失败')
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
mounted() {
|
|
|
console.log('合同详情抽屉组件加载完成')
|
|
|
// 进入页面时不再主动拉取,改由路由侦听器(immediate)或父组件触发
|
|
|
},
|
|
|
|
|
|
beforeDestroy() {
|
|
|
// 组件销毁前重置状态
|
|
|
this.resetComponent()
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
/* 引入Font Awesome图标 */
|
|
|
@import url('https://cdn.bootcdn.net/ajax/libs/font-awesome/6.4.0/css/all.min.css');
|
|
|
|
|
|
|
|
|
::v-deep .el-drawer__body{
|
|
|
overflow: scroll !important;
|
|
|
}
|
|
|
.main-container-over {
|
|
|
background: white;
|
|
|
height: 100%;
|
|
|
overflow: scroll; /* 防止滚动 */
|
|
|
}
|
|
|
|
|
|
.content-area {
|
|
|
padding: 20px;
|
|
|
height: 100%;
|
|
|
overflow-y: auto; /* 内容区域可以滚动 */
|
|
|
}
|
|
|
|
|
|
/* 顶部完整流程展示 */
|
|
|
.overview-container {
|
|
|
margin-bottom: 30px;
|
|
|
}
|
|
|
|
|
|
.overview-title {
|
|
|
font-size: 1.2rem;
|
|
|
font-weight: 600;
|
|
|
color: #333;
|
|
|
margin-bottom: 15px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 10px;
|
|
|
}
|
|
|
|
|
|
.overview-section {
|
|
|
background: #f8f9fa;
|
|
|
border-radius: 10px;
|
|
|
padding: 20px;
|
|
|
}
|
|
|
|
|
|
.overview-flow {
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 8px;
|
|
|
justify-content: center;
|
|
|
align-items: center;
|
|
|
}
|
|
|
|
|
|
.overview-step {
|
|
|
width: 100px;
|
|
|
height: 60px;
|
|
|
background: white;
|
|
|
border: 2px solid #dee2e6;
|
|
|
border-radius: 6px;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
text-align: center;
|
|
|
position: relative;
|
|
|
font-size: 0.75rem;
|
|
|
color: #6c757d;
|
|
|
}
|
|
|
|
|
|
/* 1-4环节的状态样式 */
|
|
|
.overview-step.pending {
|
|
|
border-color: #6c757d;
|
|
|
color: #6c757d;
|
|
|
background: #f8f9fa;
|
|
|
}
|
|
|
|
|
|
.overview-step.active {
|
|
|
border-color: #007bff;
|
|
|
color: #007bff;
|
|
|
background: #e3f2fd;
|
|
|
}
|
|
|
|
|
|
.overview-step.completed {
|
|
|
border-color: #28a745;
|
|
|
color: #28a745;
|
|
|
background: #e8f5e8;
|
|
|
}
|
|
|
|
|
|
/* 5-8环节的组合样式 */
|
|
|
.overview-step.payment {
|
|
|
width: 80px;
|
|
|
height: 50px;
|
|
|
border: 2px dashed #adb5bd;
|
|
|
color: #6c757d;
|
|
|
background: #f8f9fa;
|
|
|
font-size: 0.7rem;
|
|
|
}
|
|
|
|
|
|
.overview-icon {
|
|
|
font-size: 1rem;
|
|
|
margin-bottom: 2px;
|
|
|
}
|
|
|
|
|
|
.overview-number {
|
|
|
position: absolute;
|
|
|
top: -6px;
|
|
|
right: -6px;
|
|
|
width: 18px;
|
|
|
height: 18px;
|
|
|
background: #6c757d;
|
|
|
color: white;
|
|
|
border-radius: 50%;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
font-size: 0.7rem;
|
|
|
font-weight: bold;
|
|
|
}
|
|
|
|
|
|
.overview-step.active .overview-number {
|
|
|
background: #007bff;
|
|
|
}
|
|
|
|
|
|
.overview-step.completed .overview-number {
|
|
|
background: #28a745;
|
|
|
}
|
|
|
|
|
|
.overview-step.pending .overview-number {
|
|
|
background: #6c757d;
|
|
|
}
|
|
|
|
|
|
.overview-step.payment .overview-number {
|
|
|
background: #adb5bd;
|
|
|
}
|
|
|
|
|
|
.overview-arrow {
|
|
|
color: #6c757d;
|
|
|
font-size: 1.2rem;
|
|
|
margin: 0 5px;
|
|
|
}
|
|
|
|
|
|
.overview-rounds-info {
|
|
|
margin-top: 10px;
|
|
|
text-align: center;
|
|
|
}
|
|
|
|
|
|
/* 付款环节组合框 */
|
|
|
.payment-group-container {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 8px;
|
|
|
border: 2px dashed #adb5bd;
|
|
|
border-radius: 8px;
|
|
|
padding: 8px;
|
|
|
background: #f8f9fa;
|
|
|
margin: 0 5px;
|
|
|
}
|
|
|
|
|
|
/* 环节详情区域 */
|
|
|
.detail-section {
|
|
|
margin-bottom: 30px;
|
|
|
}
|
|
|
|
|
|
.detail-title {
|
|
|
font-size: 1.1rem;
|
|
|
font-weight: 600;
|
|
|
color: #333;
|
|
|
margin-bottom: 15px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 10px;
|
|
|
}
|
|
|
|
|
|
.step-detail {
|
|
|
background: white;
|
|
|
border: 2px solid #dee2e6;
|
|
|
border-radius: 10px;
|
|
|
padding: 20px;
|
|
|
margin-bottom: 20px;
|
|
|
transition: all 0.3s ease;
|
|
|
}
|
|
|
|
|
|
.step-detail.active {
|
|
|
border-color: #007bff;
|
|
|
box-shadow: 0 4px 8px rgba(0,123,255,0.1);
|
|
|
}
|
|
|
|
|
|
.step-detail.completed {
|
|
|
border-color: #28a745;
|
|
|
background: #f8fff9;
|
|
|
}
|
|
|
|
|
|
.step-header {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
margin-bottom: 15px;
|
|
|
padding-bottom: 10px;
|
|
|
border-bottom: 1px solid #eee;
|
|
|
}
|
|
|
|
|
|
.step-title {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 10px;
|
|
|
font-size: 1.1rem;
|
|
|
font-weight: 600;
|
|
|
color: #333;
|
|
|
}
|
|
|
|
|
|
.step-icon {
|
|
|
width: 40px;
|
|
|
height: 40px;
|
|
|
background: #f8f9fa;
|
|
|
border-radius: 50%;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
font-size: 1.2rem;
|
|
|
color: #6c757d;
|
|
|
}
|
|
|
|
|
|
.step-detail.active .step-icon {
|
|
|
background: #007bff;
|
|
|
color: white;
|
|
|
}
|
|
|
|
|
|
.step-detail.completed .step-icon {
|
|
|
background: #28a745;
|
|
|
color: white;
|
|
|
}
|
|
|
|
|
|
.step-status {
|
|
|
padding: 4px 12px;
|
|
|
border-radius: 12px;
|
|
|
font-size: 0.8rem;
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
|
|
|
.step-status.pending {
|
|
|
background: #f8f9fa;
|
|
|
color: #6c757d;
|
|
|
}
|
|
|
|
|
|
.step-status.active {
|
|
|
background: #e3f2fd;
|
|
|
color: #1976d2;
|
|
|
}
|
|
|
|
|
|
.step-status.completed {
|
|
|
background: #e8f5e8;
|
|
|
color: #2e7d32;
|
|
|
}
|
|
|
|
|
|
/* 退回状态 */
|
|
|
.step-status.rejected {
|
|
|
background: #fef0f0;
|
|
|
color: #f56c6c;
|
|
|
}
|
|
|
|
|
|
.plan-item {
|
|
|
margin-bottom: 5px;
|
|
|
font-size: 0.9rem;
|
|
|
line-height: 1.4;
|
|
|
}
|
|
|
|
|
|
.plan-item:last-child {
|
|
|
margin-bottom: 0;
|
|
|
}
|
|
|
|
|
|
.step-content {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 15px;
|
|
|
}
|
|
|
|
|
|
.step-info {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 8px;
|
|
|
}
|
|
|
|
|
|
.flow-block {
|
|
|
border: 1px dashed #dcdfe6;
|
|
|
border-radius: 6px;
|
|
|
padding: 12px;
|
|
|
margin-bottom: 12px;
|
|
|
background: #fafafa;
|
|
|
}
|
|
|
|
|
|
.flow-block.compact {
|
|
|
background: #ffffff;
|
|
|
}
|
|
|
|
|
|
.flow-title {
|
|
|
font-weight: 600;
|
|
|
color: #333;
|
|
|
margin-bottom: 8px;
|
|
|
}
|
|
|
|
|
|
.subsection-title {
|
|
|
font-size: 1rem;
|
|
|
font-weight: 600;
|
|
|
color: #333;
|
|
|
margin-bottom: 8px;
|
|
|
}
|
|
|
|
|
|
.subsection-title-with-status {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
margin-bottom: 8px;
|
|
|
}
|
|
|
|
|
|
.flow-empty {
|
|
|
background: #fafafa;
|
|
|
border: 1px dashed #eaeaea;
|
|
|
padding: 12px;
|
|
|
border-radius: 6px;
|
|
|
color: #666;
|
|
|
}
|
|
|
|
|
|
.info-item {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
padding: 8px 12px;
|
|
|
background: #f8f9fa;
|
|
|
border-radius: 4px;
|
|
|
border-left: 3px solid #007bff;
|
|
|
}
|
|
|
|
|
|
.step-detail.completed .info-item {
|
|
|
border-left-color: #28a745;
|
|
|
}
|
|
|
|
|
|
.step-detail.active .info-item {
|
|
|
border-left-color: #ffc107;
|
|
|
}
|
|
|
|
|
|
.step-detail.pending .info-item {
|
|
|
border-left-color: #6c757d;
|
|
|
}
|
|
|
|
|
|
.info-label {
|
|
|
font-weight: 500;
|
|
|
color: #666;
|
|
|
}
|
|
|
|
|
|
.info-value {
|
|
|
color: #333;
|
|
|
}
|
|
|
|
|
|
.action-link {
|
|
|
cursor: pointer;
|
|
|
margin: 0 6px;
|
|
|
color: #409eff;
|
|
|
}
|
|
|
|
|
|
/* 列支信息样式 */
|
|
|
.expense-info-container {
|
|
|
margin-bottom: 20px;
|
|
|
}
|
|
|
|
|
|
.expense-info-title {
|
|
|
font-size: 1.1rem;
|
|
|
font-weight: 600;
|
|
|
color: #333;
|
|
|
margin-bottom: 10px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 10px;
|
|
|
}
|
|
|
|
|
|
.expense-info-section {
|
|
|
background: white;
|
|
|
border: 1px solid #dee2e6;
|
|
|
border-radius: 8px;
|
|
|
padding: 15px;
|
|
|
transition: all 0.3s ease;
|
|
|
}
|
|
|
|
|
|
.expense-info-content {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 8px;
|
|
|
}
|
|
|
|
|
|
.expense-info-item {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
padding: 8px 12px;
|
|
|
background: #f8f9fa;
|
|
|
border-radius: 4px;
|
|
|
border-left: 3px solid #007bff;
|
|
|
font-size: 0.9rem;
|
|
|
}
|
|
|
|
|
|
.expense-info-label {
|
|
|
font-weight: 500;
|
|
|
color: #666;
|
|
|
min-width: 100px;
|
|
|
}
|
|
|
|
|
|
.expense-info-value {
|
|
|
color: #333;
|
|
|
text-align: right;
|
|
|
flex: 1;
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
|
|
|
.expense-status {
|
|
|
display: inline-block;
|
|
|
padding: 4px 12px;
|
|
|
border-radius: 12px;
|
|
|
font-size: 0.8rem;
|
|
|
font-weight: 500;
|
|
|
background: #e8f5e8;
|
|
|
color: #2e7d32;
|
|
|
}
|
|
|
|
|
|
/* 轮次内步骤详情样式 */
|
|
|
.round-step-detail-section {
|
|
|
background: #f8f9fa;
|
|
|
border: 1px solid #dee2e6;
|
|
|
border-radius: 8px;
|
|
|
padding: 15px;
|
|
|
margin-bottom: 15px;
|
|
|
transition: all 0.3s ease;
|
|
|
}
|
|
|
|
|
|
.round-step-detail-section .info-item {
|
|
|
border-left-color: #007bff;
|
|
|
}
|
|
|
|
|
|
.round-step-detail-section .round-step-status.completed ~ .step-content .info-item {
|
|
|
border-left-color: #28a745;
|
|
|
}
|
|
|
|
|
|
.round-step-detail-section .round-step-status.active ~ .step-content .info-item {
|
|
|
border-left-color: #ffc107;
|
|
|
}
|
|
|
|
|
|
.round-step-detail-section .round-step-status.pending ~ .step-content .info-item {
|
|
|
border-left-color: #6c757d;
|
|
|
}
|
|
|
|
|
|
/* 轮次内子环节的布局调整 */
|
|
|
.round-step-detail-section .step-content {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 10px;
|
|
|
}
|
|
|
|
|
|
.round-step-detail-section .step-info {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 6px;
|
|
|
}
|
|
|
|
|
|
.round-step-detail-section:hover {
|
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
|
}
|
|
|
|
|
|
.round-step-header {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
margin-bottom: 15px;
|
|
|
padding-bottom: 8px;
|
|
|
border-bottom: 1px solid #eee;
|
|
|
}
|
|
|
|
|
|
.round-step-title {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
font-size: 1rem;
|
|
|
font-weight: 600;
|
|
|
color: #333;
|
|
|
}
|
|
|
|
|
|
.round-step-status {
|
|
|
padding: 4px 12px;
|
|
|
border-radius: 12px;
|
|
|
font-size: 0.8rem;
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
|
|
|
.round-step-status.pending {
|
|
|
background: #f8f9fa;
|
|
|
color: #6c757d;
|
|
|
}
|
|
|
|
|
|
.round-step-status.active {
|
|
|
background: #e3f2fd;
|
|
|
color: #1976d2;
|
|
|
}
|
|
|
|
|
|
.round-step-status.completed {
|
|
|
background: #e8f5e8;
|
|
|
color: #2e7d32;
|
|
|
}
|
|
|
|
|
|
/* 环节详情样式 */
|
|
|
.detail-section {
|
|
|
background: white;
|
|
|
border: 1px solid #dee2e6;
|
|
|
border-radius: 10px;
|
|
|
padding: 20px;
|
|
|
margin-bottom: 20px;
|
|
|
}
|
|
|
|
|
|
.detail-title {
|
|
|
font-size: 1.1rem;
|
|
|
font-weight: 600;
|
|
|
color: #333;
|
|
|
margin-bottom: 20px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 10px;
|
|
|
}
|
|
|
|
|
|
/* 响应式设计 */
|
|
|
@media (max-width: 768px) {
|
|
|
.step-content {
|
|
|
grid-template-columns: 1fr;
|
|
|
}
|
|
|
|
|
|
.expense-info-content {
|
|
|
grid-template-columns: 1fr;
|
|
|
}
|
|
|
|
|
|
.expense-info-item {
|
|
|
flex-direction: column;
|
|
|
align-items: flex-start;
|
|
|
gap: 5px;
|
|
|
}
|
|
|
|
|
|
.expense-info-value {
|
|
|
text-align: left;
|
|
|
}
|
|
|
|
|
|
.overview-step {
|
|
|
width: 80px;
|
|
|
height: 50px;
|
|
|
font-size: 0.7rem;
|
|
|
}
|
|
|
|
|
|
.overview-step.payment {
|
|
|
width: 60px;
|
|
|
height: 40px;
|
|
|
font-size: 0.6rem;
|
|
|
}
|
|
|
|
|
|
.payment-group-container {
|
|
|
padding: 5px;
|
|
|
gap: 5px;
|
|
|
}
|
|
|
|
|
|
.round-step-detail {
|
|
|
width: 120px;
|
|
|
height: 70px;
|
|
|
}
|
|
|
|
|
|
.round-step-title-detail {
|
|
|
font-size: 0.7rem;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* 自定义drawer样式 */
|
|
|
.contract-detail-drawer .el-drawer__body {
|
|
|
padding: 0;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
/* Bootstrap样式兼容 */
|
|
|
.badge {
|
|
|
display: inline-block;
|
|
|
padding: 0.25em 0.4em;
|
|
|
font-size: 75%;
|
|
|
font-weight: 700;
|
|
|
line-height: 1;
|
|
|
text-align: center;
|
|
|
white-space: nowrap;
|
|
|
vertical-align: baseline;
|
|
|
border-radius: 0.25rem;
|
|
|
}
|
|
|
|
|
|
.bg-secondary {
|
|
|
background-color: #6c757d !important;
|
|
|
color: white;
|
|
|
}
|
|
|
|
|
|
.bg-danger {
|
|
|
background-color: #dc3545 !important;
|
|
|
color: white;
|
|
|
}
|
|
|
|
|
|
.ms-2 {
|
|
|
margin-left: 0.5rem !important;
|
|
|
}
|
|
|
|
|
|
.me-2 {
|
|
|
margin-right: 0.5rem !important;
|
|
|
}
|
|
|
</style>
|