357 lines
11 KiB
Vue
357 lines
11 KiB
Vue
<script setup>
|
||
import { ref,onMounted } from 'vue'
|
||
import router from '@/router'
|
||
import { useRoute } from 'vue-router'
|
||
import { showNotify } from 'vant'
|
||
import { detailsApi,approvalApi,taskCountApi } from '@/api/apply';
|
||
|
||
const route = useRoute()
|
||
const loading = ref(false);
|
||
const params = ref(null)
|
||
const info = ref(null)
|
||
const showReject = ref(false)
|
||
const rejectComment = ref('')
|
||
const taskCount = ref(0);
|
||
// 状态
|
||
const statusList = ref([
|
||
{text:'草稿',value:0},
|
||
{text:'申请中(安全员/技术员)',value:1},
|
||
{text:'申请中(车间主任)',value:2},
|
||
{text:'申请中(车间值班人员)',value:101},
|
||
{text:'各部门会签中',value:3},
|
||
{text:'审批中',value:4},
|
||
{text:'通过',value:5},
|
||
{text:'已关闭',value:97},
|
||
{text:'已撤回',value:98},
|
||
{text:'已驳回',value:99},
|
||
]);
|
||
// 匹配选项
|
||
const matchOptions = (arr,val) => {
|
||
const target = arr.find(item => item.value == val);
|
||
return target ? target.text : '';
|
||
}
|
||
// 路由跳转
|
||
const jumpTo = (val) => {
|
||
// 跳转已审批列表
|
||
if(val===0){
|
||
router.push({
|
||
path: '/applyList',
|
||
})
|
||
}
|
||
// 跳转待审批列表
|
||
if(val===1){
|
||
router.push({
|
||
path: '/waitList',
|
||
})
|
||
}
|
||
// 跳转联锁详情页
|
||
if(val===2){
|
||
router.push({
|
||
path: '/lockDetail',
|
||
query: { id: params.value.id, type: params.value.type,isMyself: params.value.isMyself },
|
||
})
|
||
}
|
||
}
|
||
// 同意
|
||
const agree = async () => {
|
||
loading.value = true;
|
||
try {
|
||
await approvalApi(params.value?.id,{
|
||
action: 'approve',
|
||
comment: '',
|
||
risk_description: info.value?.risk_description,
|
||
safety_measures: info.value?.safety_measures,
|
||
cosign_item_id: info.value?.cosign_item_id,//审核人id
|
||
});
|
||
showNotify({ message: '同意成功', background: '#00b42a' });
|
||
jumpTo(params.value?.type==='wait'?1:0);
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
// 驳回
|
||
const reject = async () => {
|
||
rejectComment.value = '';
|
||
showReject.value = true;
|
||
}
|
||
// 驳回确认
|
||
const rejectConfirm = (action) => new Promise(async (resolve) => {
|
||
// 取消
|
||
if(action==='cancel'){
|
||
showReject.value = false;
|
||
resolve(true);
|
||
}
|
||
// 确认
|
||
if(action==='confirm'){
|
||
if(rejectComment.value==''){
|
||
showNotify({ message: '请输入驳回理由', background: '#ff7d00' });
|
||
resolve(false);
|
||
return;
|
||
}
|
||
try {
|
||
await approvalApi(params.value?.id,{
|
||
action: 'reject',
|
||
comment: rejectComment.value,//驳回理由
|
||
risk_description: info.value?.risk_description,
|
||
safety_measures: info.value?.safety_measures,
|
||
cosign_item_id: info.value?.cosign_item_id,//审核人id
|
||
});
|
||
showNotify({ message: '驳回成功', background: '#00b42a' });
|
||
resolve(true);
|
||
jumpTo(params.value?.type==='wait'?1:0);
|
||
} catch (error) {
|
||
resolve(false);
|
||
}
|
||
}
|
||
});
|
||
// 获取详情
|
||
const getDetail = async () => {
|
||
loading.value = true;
|
||
try {
|
||
const res = await detailsApi(params.value?.id);
|
||
info.value = res.data;
|
||
if(res.data?.status>=1 && res.data?.status<=4){
|
||
let allEmpty = res.data?.draft_apply_steps.every(item => item.draft_cosign_items.length === 0);
|
||
if(res.data?.draft_apply_steps?.length && !allEmpty){
|
||
info.value.steps = res.data?.draft_apply_steps;
|
||
}else{
|
||
info.value.steps = res.data?.apply_steps;
|
||
}
|
||
}else{
|
||
info.value.steps = res.data?.apply_steps;
|
||
}
|
||
console.log(info.value);
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
// 获取待审批数量
|
||
const getTaskCount = async () => {
|
||
try {
|
||
const res = await taskCountApi();
|
||
taskCount.value = res.data?.chain_remove?.count || 0;
|
||
} catch (error) {
|
||
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
getTaskCount();
|
||
if(route.query){
|
||
params.value = route.query;
|
||
getDetail();
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<!-- 全屏加载遮罩层 -->
|
||
<div v-if="loading" class="global-loading-overlay">
|
||
<van-loading size="30px" color="#1989fa" vertical></van-loading>
|
||
</div>
|
||
<div class="main">
|
||
<div class="tab-box">
|
||
<p class="tab" :class="{'active':params?.type==='finish'}" @click="jumpTo(0)">已审批</p>
|
||
<p class="tab" :class="{'active':params?.type==='wait'}" @click="jumpTo(1)">待审批<span v-if="taskCount>0">{{taskCount}}</span></p>
|
||
</div>
|
||
<template v-if="info">
|
||
<div class="detail">
|
||
<div class="row">
|
||
<div v-if="info.template_id==4">
|
||
<p v-if="info.status==1">申请中(车间值班人员)</p>
|
||
<p v-else>{{matchOptions(statusList,info.status)}}</p>
|
||
</div>
|
||
<div v-else>
|
||
<p v-if="info.status==5 || info.status==100">通过</p>
|
||
<p v-else>{{matchOptions(statusList,info.status)}}</p>
|
||
</div>
|
||
<p>申请摘除数量({{info.chain_count}})个</p>
|
||
</div>
|
||
<div class="row">
|
||
<p>岗位:{{info.chain_position}}</p>
|
||
<p>类型:{{info.template_name}}</p>
|
||
</div>
|
||
<div class="row">
|
||
<p>摘除理由:{{info.reasons}}</p>
|
||
</div>
|
||
<div class="row">
|
||
<p>计划摘除时间:{{info.plan_remove_time?.slice(0,16)}}</p>
|
||
</div>
|
||
<div class="row">
|
||
<p>计划恢复时间:{{info.plan_recover_time?.slice(0,16)}}</p>
|
||
</div>
|
||
<div class="row col">
|
||
<p class="blue-color">风险辨识</p>
|
||
<p>{{info.risk_description}}</p>
|
||
</div>
|
||
<div class="row col">
|
||
<p class="blue-color">安全措施</p>
|
||
<p>{{info.safety_measures}}</p>
|
||
</div>
|
||
<div class="row col">
|
||
<div class="timeline">
|
||
<div class="timeline-item" v-if="info.template_id==4">
|
||
<div class="left">
|
||
<p class="timeline-dot"></p>
|
||
<div class="txt">班长:</div>
|
||
<div class="txt">{{info?.apply_user?.realname}}</div>
|
||
</div>
|
||
</div>
|
||
<div class="timeline-item" v-if="info.template_id>=1 && info.template_id<=3">
|
||
<div class="left">
|
||
<p class="timeline-dot"></p>
|
||
<div class="txt">车间工程师:</div>
|
||
<div class="txt">{{info?.apply_user?.realname}}</div>
|
||
</div>
|
||
</div>
|
||
<template v-for="(item, index) in info?.steps || []" :key="index">
|
||
<template v-for="(sItem,sIndex) in (item.cosigns || item.cosign_items || item.draft_cosign_items)" :key="sIndex">
|
||
<div class="timeline-item">
|
||
<div class="left">
|
||
<p class="timeline-dot"></p>
|
||
<div class="txt">{{sItem.dept_name}}:</div>
|
||
<div class="txt">{{sItem.user_name}}</div>
|
||
</div>
|
||
<div class="txt" v-if="sItem.signed_at">{{(sItem.signed_at)?.slice(0,16)}}</div>
|
||
</div>
|
||
</template>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<div class="row" v-if="info?.status==99 && info?.reject">
|
||
<p>驳回:{{info?.reject?.comment}}</p>
|
||
</div>
|
||
</div>
|
||
<div class="btns">
|
||
<p class="btn" @click="jumpTo(params?.type==='finish'?0:1)">返回列表</p>
|
||
<p class="btn" @click="jumpTo(2)">联锁详情</p>
|
||
<div class="op" v-if="
|
||
params?.type==='wait' && info?.status!=99 && params?.isMyself==='pending' &&
|
||
(info?.status==1 || info?.status==2 || info?.status==3 || info?.status==4) &&
|
||
(((info?.template_id==1 || info?.template_id==4) && (info?.current_step>=1 && info?.current_step<=3)) ||
|
||
((info?.template_id==2 || info?.template_id)==3 && (info?.current_step>=1 && info?.current_step<=4))) ||
|
||
info?.template_id==5
|
||
">
|
||
<p class="btn" @click="agree">同意</p>
|
||
<p class="btn" @click="reject">驳回</p>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<template v-else>
|
||
<van-empty description="暂无数据" />
|
||
</template>
|
||
</div>
|
||
<!-- 驳回弹框 -->
|
||
<van-dialog v-model:show="showReject" title="驳回" show-cancel-button :before-close="rejectConfirm" @cancel="rejectConfirm">
|
||
<div style="padding: 10px 0;">
|
||
<van-field v-model="rejectComment" label="驳回理由" type="textarea" rows="1" autosize required clearable placeholder="请输入驳回理由" />
|
||
</div>
|
||
</van-dialog>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.blue-color {
|
||
color: #1989fa;
|
||
}
|
||
.main {
|
||
height: 100vh;
|
||
padding: 12px;
|
||
overflow: hidden;
|
||
font-size: 14px;
|
||
}
|
||
/* tab切换 */
|
||
.tab-box {
|
||
display: flex;
|
||
border: 1px solid #1989fa;
|
||
}
|
||
.tab {
|
||
width: 50%;
|
||
padding: 5px 0;
|
||
text-align: center;
|
||
font-size: 16px;
|
||
color: #1989fa;
|
||
}
|
||
.tab.active {
|
||
background-color: #1989fa;
|
||
color: #fff;
|
||
}
|
||
/* 详情 */
|
||
.detail {
|
||
margin-top: 12px;
|
||
height: calc(100vh - 120px);
|
||
overflow-y: auto;
|
||
border: 1px solid #f1f1f1;
|
||
border-radius: 2px;
|
||
padding: 10px;
|
||
}
|
||
.detail .row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: 10px;
|
||
gap: 10px;
|
||
}
|
||
.detail .col {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 2px;
|
||
}
|
||
.detail .row:first-child {
|
||
margin-top: 0;
|
||
}
|
||
/* 底部按钮 */
|
||
.btns {
|
||
margin-top: 12px;
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
.btns .btn {
|
||
width: 100%;
|
||
text-align: center;
|
||
padding: 8px 0;
|
||
border: 1px solid #eee;
|
||
border-radius: 2px;
|
||
}
|
||
.btns .op {
|
||
width: 100%;
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
/* 时间线 */
|
||
.timeline {
|
||
width: 100%;
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
.timeline:before {
|
||
content: '';
|
||
width: 1px;
|
||
height: calc(100% - 6px);
|
||
background: #1989fa;
|
||
position: absolute;
|
||
top: 3px;
|
||
left: 6px;
|
||
}
|
||
.timeline-item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
position: relative;
|
||
}
|
||
.timeline-item .left {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
/* 圆点 */
|
||
.timeline-dot {
|
||
width: 12px;
|
||
height: 12px;
|
||
background: #1989fa;
|
||
border-radius: 50%;
|
||
margin-right: 10px;
|
||
}
|
||
</style>
|