approvalMobile/src/views/applyList/detail.vue

357 lines
11 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.

<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>