|
|
|
@ -1,122 +1,326 @@ |
|
|
|
<template> |
|
|
|
<div> |
|
|
|
<!-- 审核标题 --> |
|
|
|
<div class="flex justify-between items-center" @click="collapsed = !collapsed"> |
|
|
|
<div class="flex justify-between items-center"> |
|
|
|
<div>{{ deliverData ? deliverData.deliverName : '' }}审核状态</div> |
|
|
|
<DownOutlined v-if="!collapsed" /> |
|
|
|
<UpOutlined v-else /> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 审核结果 --> |
|
|
|
<div v-show="collapsed"> |
|
|
|
<div> |
|
|
|
<!-- 提交人和时间 --> |
|
|
|
<div class="my-2 flex justify-between items-center"> |
|
|
|
<div class="text-gray-400 text-xs"> |
|
|
|
<span class="mr-4" v-if="deliverData.submitMemberName">{{ deliverData.submitMemberName }}</span> |
|
|
|
<span v-if="deliverData.submitTime"> {{ dayjs(+deliverData.submitTime).format('MM-DD HH:mm') }}</span> |
|
|
|
</div> |
|
|
|
|
|
|
|
<span class="text-blue-400 text-xs text-right" @click="openDeliverHistory">历史交付物</span> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div @click="openLink" class="break-all text-blue-400 text-xs my-1" v-if="deliverData.details && deliverData.details[0]"> |
|
|
|
<div |
|
|
|
@click="openLink" |
|
|
|
class="break-all text-blue-400 text-xs my-1 cursor-pointer" |
|
|
|
v-if="deliverData.details && deliverData.details[0]" |
|
|
|
> |
|
|
|
{{ deliverData.details[0] }} |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 审核人 标题 --> |
|
|
|
<div class="text-gray-400 flex justify-between mt-3"> |
|
|
|
<span>审核</span> |
|
|
|
<span class="text-blue-400 text-xs" @click="openMoreRecords">更多审核记录</span> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 审核人 列表 --> |
|
|
|
<div v-if="deliverData.checkerList"> |
|
|
|
<div class="mt-2 text-sm flex justify-between" v-for="(item, index) in deliverData.checkerList" :key="index"> |
|
|
|
<div v-for="(item, index) in deliverData.checkerList" :key="index"> |
|
|
|
<template v-if="item.isMine === 1"> |
|
|
|
<div class="mt-2 text-sm flex justify-between" v-show="item.status > 0 && isRepeatCheck === 0"> |
|
|
|
<div> |
|
|
|
<div class="font-semibold">{{ item.checkerName }}</div> |
|
|
|
<div class="text-xs text-gray-400">{{ item.remark }}</div> |
|
|
|
<div class="text-xs text-gray-400" v-if="+item.checkTime > 0">{{ dayjs(+item.checkTime).format('MM-DD HH:mm') }}</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 不是自己 显示审核状态 --> |
|
|
|
<div v-show="item.isMine !== 1" class="text-xs"> |
|
|
|
<span v-if="item.status === 1" class="text-green-600"> 已通过 </span> |
|
|
|
<span v-else-if="item.status === 2" class="text-red-600"> 已驳回 </span> |
|
|
|
<span v-else class="text-gray-400"> 待审核 </span> |
|
|
|
<div class="time-box" v-if="item.checkDuration"> |
|
|
|
<div class="relative"> |
|
|
|
<div class="initial-duration bg-yellow-400" :style="{ width: item.initialPercent + '%' }"></div> |
|
|
|
<div class="absolute duration-value">默认值:{{ deliverData.initialDuration / 3600000 }}小时</div> |
|
|
|
</div> |
|
|
|
<div class="relative"> |
|
|
|
<div class="duration bg-blue-400" :style="{ width: item.currPercent + '%' }"></div> |
|
|
|
<div class="absolute duration-value">工作量时长:{{ deliverData.duration / 3600000 }}小时</div> |
|
|
|
</div> |
|
|
|
<div class="relative"> |
|
|
|
<div class="check-duration bg-green-400" :style="{ width: item.checkPercent + '%' }"></div> |
|
|
|
<div class="absolute duration-value">确认工作:{{ item.checkDuration / 3600000 }}小时</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 自己是当前审核人 且未审核状态 --> |
|
|
|
<div v-show="item.isMine === 1 && (item.status === null || item.status === 0)"> |
|
|
|
<a-button size="small" shape="round" class="mr-4 h-1-4 leading-1-4" type="primary" @click="checkModal.mode = 'RESOLVE'"> |
|
|
|
通过 |
|
|
|
<!-- 自己是审核人 且审核过 当前审核人的审核状态并展示得分情况 --> |
|
|
|
<div class="text-xs"> |
|
|
|
<div class="mb-1"> |
|
|
|
<div v-if="item.status === 1" class="text-green-600">已通过</div> |
|
|
|
<div v-else-if="item.status === 2" class="text-red-600">已驳回</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="text-yellow-500 font-medium text-base">{{ item.score }}</div> |
|
|
|
|
|
|
|
<!-- <view class="text-blue-500" @click="repeatCheck(1)">重新审核</view> --> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div v-if="item.status === null || item.status === 0 || isRepeatCheck === 1"> |
|
|
|
<!-- <view v-if="isRepeatCheck === 1" class="text-blue-500 text-right" @click="repeatCheck(0)">取消重新审核</view> --> |
|
|
|
|
|
|
|
<div class="mt-3 pb-2" style="border-bottom: 1px solid #d1d5db"> |
|
|
|
<div class="flex justify-between"> |
|
|
|
<div class="mr-1 text-sm flex items-center"> |
|
|
|
<div class="mr-2">确认工作</div> |
|
|
|
|
|
|
|
<div class="flex flex-wrap"> |
|
|
|
<a-button |
|
|
|
:type="checkedIndex === 0 ? 'primary' : 'default'" |
|
|
|
size="small" |
|
|
|
class="my-1 ml-0 mr-2" |
|
|
|
@click="handleSelectTime(0)" |
|
|
|
> |
|
|
|
半小时 |
|
|
|
</a-button> |
|
|
|
<a-button |
|
|
|
:type="checkedIndex === 1 ? 'primary' : 'default'" |
|
|
|
size="small" |
|
|
|
class="my-1 ml-0 mr-2" |
|
|
|
@click="handleSelectTime(1)" |
|
|
|
> |
|
|
|
1小时 |
|
|
|
</a-button> |
|
|
|
<a-button size="small" shape="round" class="h-1-4 leading-1-4" type="primary" danger @click="checkModal.mode = 'REJECT'"> |
|
|
|
驳回 |
|
|
|
<a-button |
|
|
|
:type="checkedIndex === 2 ? 'primary' : 'default'" |
|
|
|
size="small" |
|
|
|
class="my-1 ml-0 mr-2" |
|
|
|
@click="handleSelectTime(2)" |
|
|
|
> |
|
|
|
2小时 |
|
|
|
</a-button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 自己是审核人 且审核过 当前审核人的审核状态并展示得分情况 --> |
|
|
|
<div v-show="item.isMine === 1 && item.status > 0" class="text-xs"> |
|
|
|
<div class="mb-1"> |
|
|
|
<span v-if="item.status === 1" class="text-green-600"> 已通过 </span> |
|
|
|
<span v-else-if="item.status === 2" class="text-red-600"> 已驳回 </span> |
|
|
|
<!-- 时长 --> |
|
|
|
<div class="flex items-center justify-end flex-1 text-sm"> |
|
|
|
<a-input |
|
|
|
v-model="checkDuration" |
|
|
|
type="text" |
|
|
|
placeholder="工作量时长" |
|
|
|
class="input" |
|
|
|
style="text-align: right; width: 120px" |
|
|
|
></a-input> |
|
|
|
小时 |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="mt-3 flex justify-between items-center"> |
|
|
|
<div>交付物质量</div> |
|
|
|
|
|
|
|
<a-progress v-if="item.score" type="circle" :percent="item.score" strokeColor="#FA8C16" :width="40" :strokeWidth="10"> |
|
|
|
<template #format="percent"> |
|
|
|
<span |
|
|
|
class="inline-block text-center text-white text-sm rounded-full" |
|
|
|
style="background: #fa8c16; width: 24px; height: 24px; line-height: 24px" |
|
|
|
<div class="flex justify-end items-center"> |
|
|
|
<a-input-number v-model:value="score" :max="100" :min="0" :step="1"> </a-input-number> |
|
|
|
<div class="w-64 ml-3"> |
|
|
|
<a-progress :percent="score" /> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="mt-3 relative"> |
|
|
|
<a-textarea style="height: 80px" v-model:value="commit" placeholder="鼓励一下小伙伴" /> |
|
|
|
|
|
|
|
<div |
|
|
|
class="absolute border border-solid border-gray-300 rounded-md text-center text-base cursor-pointer word-btn" |
|
|
|
@click="showWords = !showWords" |
|
|
|
> |
|
|
|
{{ percent }} |
|
|
|
</span> |
|
|
|
常 |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="common-list" v-if="showWords"> |
|
|
|
<div v-for="(item, index) in words" :key="index" class="px-2 leading-12 word-item" @click="commit = item"> |
|
|
|
{{ item }} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="mt-4 flex justify-center items-center"> |
|
|
|
<a-button class="mx-4" type="primary" @click="handleSubmit(1)"> 通过 </a-button> |
|
|
|
<a-button class="mx-4" type="primary" danger @click="handleSubmit(2)"> 驳回 </a-button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
</a-progress> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div v-for="(item, index) in deliverData.checkerList" :key="index"> |
|
|
|
<!-- 不是我 --> |
|
|
|
<template v-if="item.isMine !== 1"> |
|
|
|
<div class="mt-2 text-sm flex justify-between"> |
|
|
|
<div> |
|
|
|
<div class="font-semibold">{{ item.checkerName }}</div> |
|
|
|
<div class="text-xs text-gray-400">{{ item.remark }}</div> |
|
|
|
<div class="text-xs text-gray-400" v-if="+item.checkTime > 0">{{ dayjs(+item.checkTime).format('MM-DD HH:mm') }}</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="time-box" v-if="item.checkDuration"> |
|
|
|
<div class="relative"> |
|
|
|
<div class="initial-duration bg-yellow-400" :style="{ width: item.initialPercent + '%' }"></div> |
|
|
|
<div class="absolute duration-value">默认值:{{ deliverData.initialDuration / 3600000 }}小时</div> |
|
|
|
</div> |
|
|
|
<div class="relative"> |
|
|
|
<div class="duration bg-blue-400" :style="{ width: item.currPercent + '%' }"></div> |
|
|
|
<span class="absolute duration-value">工作量时长:{{ deliverData.duration / 3600000 }}小时</span> |
|
|
|
</div> |
|
|
|
<div class="relative"> |
|
|
|
<div class="check-duration bg-green-400" :style="{ width: item.checkPercent + '%' }"></div> |
|
|
|
<span class="absolute duration-value">确认工作:{{ item.checkDuration / 3600000 }}小时</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- <checkFormModal :data="checkModal" @hide="checkModal.mode = 'HIDE'" @submit-end="$emit('check-success')" /> --> |
|
|
|
<!-- 不是自己 显示审核状态 --> |
|
|
|
<div class="text-xs"> |
|
|
|
<span v-if="item.status === 1" class="text-green-600"> 已通过 </span> |
|
|
|
<span v-else-if="item.status === 2" class="text-red-600"> 已驳回 </span> |
|
|
|
<span v-else class="text-gray-400"> 待审核 </span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<script setup> |
|
|
|
import { ref, reactive, inject, defineEmits } from 'vue'; |
|
|
|
import { ref, computed, watch, defineProps, defineEmits } from 'vue'; |
|
|
|
import { useStore } from 'vuex'; |
|
|
|
import dayjs from 'dayjs'; |
|
|
|
import { message } from 'ant-design-vue'; |
|
|
|
import { checkDeliver } from 'apis'; |
|
|
|
import { DownOutlined, UpOutlined } from '@ant-design/icons-vue'; |
|
|
|
// import checkFormModal from './check-form-modal.vue'; |
|
|
|
import { quickWords } from '@/utils/deliver'; |
|
|
|
|
|
|
|
const props = defineProps({ |
|
|
|
deliverData: { type: Object, default: () => {} }, |
|
|
|
task: { type: Object, default: () => {} }, |
|
|
|
}); |
|
|
|
|
|
|
|
const store = useStore(); |
|
|
|
const deliverData = inject('deliver'); |
|
|
|
const collapsed = ref(false); // 展开/关闭 |
|
|
|
defineEmits(['check-success']); |
|
|
|
const deliverData = computed(() => props.deliverData); |
|
|
|
const projectId = computed(() => store.getters['project/projectId']); |
|
|
|
const sessionProjectId = sessionStorage.getItem('projectId'); |
|
|
|
const roleId = computed(() => store.state.role.roleId); |
|
|
|
|
|
|
|
const isRepeatCheck = ref(0); // 是否重新审核 |
|
|
|
const words = computed(() => quickWords.RESOLVE); // 快捷用语 |
|
|
|
|
|
|
|
const checkDuration = ref(2); // 工作量时长 |
|
|
|
const checkedIndex = ref(2); // 默认选中 |
|
|
|
const score = ref(100); // 评分 |
|
|
|
const commit = ref(''); // 评价信息 |
|
|
|
const showWords = ref(false); // 是否显示常用语 |
|
|
|
|
|
|
|
const maxDuration = ref(null); |
|
|
|
|
|
|
|
const emits = defineEmits(['check-success']); |
|
|
|
|
|
|
|
if (Object.keys(props.deliverData).length) { |
|
|
|
checkDuration.value = Number(props.deliverData.duration) / 3600000; // 工作量时长 |
|
|
|
checkedIndex.value = checkDuration.value === 0.5 ? 0 : checkDuration.value === 1 ? 1 : checkDuration.value === 2 ? 2 : -1; |
|
|
|
|
|
|
|
handleDataRender(props.deliverData); |
|
|
|
} |
|
|
|
|
|
|
|
watch(deliverData, () => { |
|
|
|
checkDuration.value = Number(deliverData.value.duration) / 3600000; // 工作量时长 |
|
|
|
checkedIndex.value = checkDuration.value === 0.5 ? 0 : checkDuration.value === 1 ? 1 : checkDuration.value === 2 ? 2 : -1; |
|
|
|
|
|
|
|
handleDataRender(deliverData.value); |
|
|
|
}); |
|
|
|
|
|
|
|
// 处理审核记录数据 |
|
|
|
async function handleDataRender(data) { |
|
|
|
maxDuration.value = |
|
|
|
deliverData.value.initialDuration > deliverData.value.duration ? deliverData.value.initialDuration : deliverData.value.duration; |
|
|
|
|
|
|
|
data.checkerList.forEach(item => { |
|
|
|
if (item.checkDuration) { |
|
|
|
maxDuration.value = maxDuration.value > item.checkDuration ? maxDuration.value : item.checkDuration; |
|
|
|
|
|
|
|
if (maxDuration.value === deliverData.value.initialDuration) { |
|
|
|
item.initialPercent = 100; |
|
|
|
item.currPercent = Math.floor((deliverData.value.duration / deliverData.value.initialDuration) * 100); |
|
|
|
item.checkPercent = Math.floor((item.checkDuration / deliverData.value.initialDuration) * 100); |
|
|
|
} else if (maxDuration.value === deliverData.value.duration) { |
|
|
|
item.currPercent = 100; |
|
|
|
item.initialPercent = Math.floor((deliverData.value.initialDuration / deliverData.value.duration) * 100); |
|
|
|
item.checkPercent = Math.floor((item.checkDuration / deliverData.value.duration) * 100); |
|
|
|
} else if (maxDuration.value === item.checkDuration) { |
|
|
|
item.checkPercent = 100; |
|
|
|
item.initialPercent = Math.floor((deliverData.value.initialDuration / item.checkDuration) * 100); |
|
|
|
item.currPercent = Math.floor((deliverData.value.duration / item.checkDuration) * 100); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
return data; |
|
|
|
} |
|
|
|
|
|
|
|
// 交付物链接 |
|
|
|
function openLink() { |
|
|
|
window.open(deliverData.details[0], '_blank'); |
|
|
|
window.open(props.deliverData.details[0], '_blank'); |
|
|
|
} |
|
|
|
|
|
|
|
const checkModal = reactive({ |
|
|
|
mode: 'HIDE', // HIDE->隐藏 RESOLVE->通过 REJECT->驳回 |
|
|
|
deliverRecordId: () => (deliverData.value ? deliverData.value.deliverRecordId : ''), // 交付物记录id |
|
|
|
}); |
|
|
|
// 选择工作量时长 |
|
|
|
function handleSelectTime(data) { |
|
|
|
checkedIndex.value = data; |
|
|
|
checkDuration.value = data === 0 ? 0.5 : data === 1 ? 1 : 2; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 提交评审信息 |
|
|
|
* 提交成功后隐藏modal 重置表单控件 |
|
|
|
* 给父组件信息 更新值 |
|
|
|
* @param {string} mode 'RESOLVE'|'REJECT' |
|
|
|
*/ |
|
|
|
async function handleSubmit(mode) { |
|
|
|
try { |
|
|
|
const { url } = store.state.projects.project; |
|
|
|
const deliverRecordId = props.deliverData ? props.deliverData.deliverRecordId : ''; |
|
|
|
const param = { |
|
|
|
param: { |
|
|
|
projectId: projectId.value || sessionProjectId, |
|
|
|
roleId: roleId.value, |
|
|
|
deliverRecordId, |
|
|
|
type: mode === 'RESOLVE' ? 1 : 2, |
|
|
|
remark: commit.value, |
|
|
|
score: score.value, |
|
|
|
checkDuration: checkDuration.value * 3600000, |
|
|
|
msgId: props.task.msgId, |
|
|
|
}, |
|
|
|
}; |
|
|
|
|
|
|
|
await checkDeliver(param, url); |
|
|
|
handleHide(); // 隐藏 + 重置 |
|
|
|
message.info('审核信息提交成功'); |
|
|
|
// 通知父组件评审成功 更新信息 |
|
|
|
emits('submit-end', param); |
|
|
|
} catch (error) { |
|
|
|
console.error('error: ', error); |
|
|
|
message.info('审核信息提交失败, 请稍后重试'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 历史交付物 |
|
|
|
function openDeliverHistory() { |
|
|
|
const { deliverId } = deliverData.value; |
|
|
|
store.commit('task/setDeliverId', deliverId); // 设置交付物id |
|
|
|
store.commit('task/setTaskDetailUrl', ''); // 设置详情页链接 |
|
|
|
store.commit('task/setTaskDetailShow', 'deliverHistory'); // 设置内置组件关键字(根据关键字判断显示的详情页) |
|
|
|
// 重置 |
|
|
|
function handleHide() { |
|
|
|
score.value = 100; |
|
|
|
commit.value = ''; |
|
|
|
} |
|
|
|
|
|
|
|
// 审核记录 |
|
|
|
function openMoreRecords() { |
|
|
|
const { deliverRecordId } = deliverData.value; |
|
|
|
store.commit('task/setDeliverRecordId', deliverRecordId); // 设置交付物记录id |
|
|
|
store.commit('task/setTaskDetailUrl', ''); // 设置详情页链接 |
|
|
|
store.commit('task/setTaskDetailShow', 'auditRecords'); // 设置内置组件关键字(根据关键字判断显示的详情页) |
|
|
|
// 重新审核 |
|
|
|
function repeatCheck(data) { |
|
|
|
isRepeatCheck.value = data; |
|
|
|
} |
|
|
|
</script> |
|
|
|
|
|
|
|
@ -125,4 +329,34 @@ function openMoreRecords() { |
|
|
|
width: 40px !important; |
|
|
|
height: 40px !important; |
|
|
|
} |
|
|
|
|
|
|
|
.word-btn { |
|
|
|
right: 10px; |
|
|
|
bottom: 10px; |
|
|
|
width: 30px; |
|
|
|
height: 30px; |
|
|
|
line-height: 28px; |
|
|
|
} |
|
|
|
|
|
|
|
.word-item { |
|
|
|
border-bottom: 1px solid #e5e7eb; |
|
|
|
} |
|
|
|
|
|
|
|
.time-box { |
|
|
|
width: 120px; |
|
|
|
} |
|
|
|
|
|
|
|
.time-box view { |
|
|
|
height: 15px; |
|
|
|
border-radius: 2px; |
|
|
|
margin: 2px 0; |
|
|
|
} |
|
|
|
|
|
|
|
.time-box .duration-value { |
|
|
|
height: 15px; |
|
|
|
line-height: 15px; |
|
|
|
font-size: 12px; |
|
|
|
top: 0; |
|
|
|
left: 0; |
|
|
|
} |
|
|
|
</style> |
|
|
|
|