Browse Source

feat: 交付物2 提交、审核人

text-draggable
xuesinan 4 years ago
parent
commit
5ce927be9e
  1. 1
      .eslintrc.js
  2. 117
      src/components/tall/Reviewer/ReviewerSecond.vue
  3. 3
      src/components/tall/plugin/Plugin.vue
  4. 128
      src/plugins/p-deliver-second/p-deliver-check-second.vue
  5. 43
      src/plugins/p-deliver-second/p-deliver-second.vue
  6. 202
      src/plugins/p-deliver-second/p-deliver-upload-second.vue

1
.eslintrc.js

@ -11,6 +11,7 @@ module.exports = {
parser: "vue-eslint-parser", // 添加这一句
plugins: ['vue'],
rules: {
'no-nested-ternary': 'off',
'no-unused-vars': 'off',
'no-loop-func': 'off',
'import/no-unresolved': 0,

117
src/components/tall/Reviewer/ReviewerSecond.vue

@ -0,0 +1,117 @@
<template>
<div class="border border-solid rounded-sm mt-5 p-2 pt-4 pl-1" style="border-color: #d9d9d9" @click="collapsed = !collapsed">
<div class="relative flex justify-between">
<div class="absolute text-sm bg-white reviewer-title">审核人</div>
<div class="flex flex-wrap">
<div v-for="(item, index) in checkers" :key="index">
<a-button
v-if="index < 4"
:type="checkedCheckers.find(checker => checker.memberId === item.memberId) ? 'primary' : 'default'"
size="small"
class="m-1"
@click.stop="handleSelectChecker(item)"
>
{{ item.name }}
</a-button>
</div>
</div>
<DownOutlined style="line-height: 32px" v-if="!collapsed" />
<UpOutlined style="line-height: 32px" v-else />
</div>
<!-- 隐藏的审核人选项 -->
<div v-show="!collapsed" class="mt-2 flex flex-wrap">
<div v-for="(item, index) in checkers" :key="index">
<a-button
v-if="index >= 4"
:type="checkedCheckers.find(checker => checker.memberId === item.memberId) ? 'primary' : 'default'"
size="small"
class="m-1"
@click.stop="handleSelectChecker(item)"
>
{{ item.name }}
</a-button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, defineExpose, defineProps } from 'vue';
import { useStore } from 'vuex';
import { DownOutlined, UpOutlined } from '@ant-design/icons-vue';
const props = defineProps({
dataCheckers: {
type: Array,
default: () => [],
},
});
const store = useStore();
//
const collapsed = ref(true);
// store
//
const checkers = computed(() => store.state.role.members);
//
const checkedCheckers = ref([]);
if (props.dataCheckers && checkers.value.length > 0) {
props.dataCheckers.forEach(item => {
item.name = item.checkerName;
item.memberId = item.checkerId;
});
checkedCheckers.value = props.dataCheckers;
const arr = [];
checkers.value.forEach(item => {
const data = props.dataCheckers.find(checker => checker.memberId === item.memberId);
if (data) {
arr.push(item);
}
});
checkers.value.forEach(item => {
const data = props.dataCheckers.find(checker => checker.memberId === item.memberId);
if (!data) {
arr.push(item);
}
});
store.commit('role/setMembers', arr);
}
// 3...
// const showCheckers = computed(() => (checkedCheckers.value.length > 3 ? checkedCheckers.value.slice(0, 3) : checkedCheckers.value));
defineExpose({ checkedCheckers, collapsed });
/**
* 点击成员 切换检查人的选中状态
* @param {object} member 成员对象
*/
function handleSelectChecker(member) {
const target = checkedCheckers.value.find(item => item.memberId === member.memberId);
if (target) {
//
checkedCheckers.value = checkedCheckers.value.filter(item => item.memberId !== member.memberId);
} else {
checkedCheckers.value.push(member);
}
}
</script>
<style scoped>
.reviewer-title {
width: 60px;
height: 20px;
line-height: 20px;
text-align: center;
top: -25px;
left: 10px;
}
</style>

3
src/components/tall/plugin/Plugin.vue

@ -4,7 +4,7 @@
<!-- 交付物插件 -->
<p-deliver v-else-if="pluginId === '15'" />
<!-- 交付物插件2 -->
<!-- <p-deliver-second v-else-if="pluginId === '25'" /> -->
<!-- <p-deliver-second v-else-if="pluginId === '15'" /> -->
<!-- 资源管理 -->
<!-- <p-source-manage v-else-if="pluginId === '16'" class="p-2" /> -->
@ -43,6 +43,7 @@ import { provide, defineProps } from 'vue';
// import { useStore } from 'vuex';
import pTaskTitle from '@/plugins/p-task-title.vue';
import pDeliver from '@/plugins/p-deliver/p-deliver.vue';
import pDeliverSecond from '@/plugins/p-deliver-second/p-deliver-second.vue';
import pDailyAccount from '@/plugins/p-daily-account/p-daily-account.vue';
const props = defineProps({

128
src/plugins/p-deliver-second/p-deliver-check-second.vue

@ -0,0 +1,128 @@
<template>
<div>
<!-- 审核标题 -->
<div class="flex justify-between items-center" @click="collapsed = !collapsed">
<div>{{ deliverData ? deliverData.deliverName : '' }}审核状态</div>
<DownOutlined v-if="!collapsed" />
<UpOutlined v-else />
</div>
<!-- 审核结果 -->
<div v-show="collapsed">
<!-- 提交人和时间 -->
<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]">
{{ 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>
<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>
<!-- 自己是当前审核人 且未审核状态 -->
<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'">
通过
</a-button>
<a-button size="small" shape="round" class="h-1-4 leading-1-4" type="primary" danger @click="checkModal.mode = 'REJECT'">
驳回
</a-button>
</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>
<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"
>
{{ percent }}
</span>
</template>
</a-progress>
</div>
</div>
</div>
</div>
<!-- <checkFormModal :data="checkModal" @hide="checkModal.mode = 'HIDE'" @submit-end="$emit('check-success')" /> -->
</div>
</template>
<script setup>
import { ref, reactive, inject, defineEmits } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { DownOutlined, UpOutlined } from '@ant-design/icons-vue';
// import checkFormModal from './check-form-modal.vue';
const store = useStore();
const deliverData = inject('deliver');
const collapsed = ref(false); // /
defineEmits(['check-success']);
//
function openLink() {
window.open(deliverData.details[0], '_blank');
}
const checkModal = reactive({
mode: 'HIDE', // HIDE-> RESOLVE-> REJECT->
deliverRecordId: () => (deliverData.value ? deliverData.value.deliverRecordId : ''), // id
});
//
function openDeliverHistory() {
const { deliverId } = deliverData.value;
store.commit('task/setDeliverId', deliverId); // id
store.commit('task/setTaskDetailUrl', ''); //
store.commit('task/setTaskDetailShow', 'deliverHistory'); //
}
//
function openMoreRecords() {
const { deliverRecordId } = deliverData.value;
store.commit('task/setDeliverRecordId', deliverRecordId); // id
store.commit('task/setTaskDetailUrl', ''); //
store.commit('task/setTaskDetailShow', 'auditRecords'); //
}
</script>
<style scoped>
.ant-progress .ant-progress-inner {
width: 40px !important;
height: 40px !important;
}
</style>

43
src/plugins/p-deliver-second/p-deliver-second.vue

@ -0,0 +1,43 @@
<template>
<!-- 任务名插件 -->
<div>
<p-deliver-upload-second v-if="deliver" @upload-success="getDeliverData"></p-deliver-upload-second>
<p-deliver-check-second
class="mt-3"
v-if="deliver && deliver.details && deliver.details.length"
@check-success="getDeliverData"
></p-deliver-check-second>
</div>
</template>
<script setup>
import { useStore } from 'vuex';
import { ref, inject, provide } from 'vue';
import { getDeliverByTaskId } from 'apis';
import { message } from 'ant-design-vue';
import pDeliverUploadSecond from '@/plugins/p-deliver-second/p-deliver-upload-second.vue';
import pDeliverCheckSecond from '@/plugins/p-deliver-second/p-deliver-check-second.vue';
const store = useStore();
const task = inject('task');
const pluginInfo = inject('pluginInfo');
const deliver = ref(null); //
deliver.value = pluginInfo && pluginInfo.data ? JSON.parse(pluginInfo.data) : null;
provide('deliver', deliver);
// id
async function getDeliverData() {
try {
const { url } = store.state.projects.project;
const { id: taskId } = task;
if (!taskId) return;
const param = { param: { taskId } };
const data = await getDeliverByTaskId(param, url);
deliver.value = data;
} catch (error) {
message.info(error);
}
}
</script>

202
src/plugins/p-deliver-second/p-deliver-upload-second.vue

@ -0,0 +1,202 @@
<template>
<div @longpress.prevent="showMask = true">
<!-- 插件名称和提交按钮 -->
<div class="flex item-center justify-between">
<div class="flex-1">
<div v-if="deliver.deliverName" class="relative inline-block">
{{ deliver.deliverName }}
</div>
</div>
<!-- 提交 -->
<a-button type="primary" size="small" @click="submit" :disabled="submitState" :loading="submitBtnLoading"> 提交 </a-button>
</div>
<!-- 插件上传方式 -->
<div class="mt-3">
<div class="link-box">
<a-input v-model:value="linkValue" placeholder="请输入交付物地址/链接" />
</div>
<div class="mt-3">
<!-- <a-button type="primary" size="small" class="mr-3" @click="paste">粘贴</a-button> -->
<div class="inline-block">
<a-upload name="param" :action="action" :headers="headers" :showUploadList="false" :max-count="1" @change="handleChange">
<a-button type="primary" size="small" class="mr-3">文件</a-button>
</a-upload>
</div>
<!-- <a-button type="primary" size="small" class="mr-3" @click="uploadPhoto">拍照</a-button> -->
</div>
</div>
<div class="border border-solid rounded-sm mt-5 p-2 pt-4 pl-1" style="border-color: #d9d9d9">
<div class="relative flex justify-between">
<div class="absolute bg-white text-sm duration-title">工作量时长</div>
<div class="mr-3 flex flex-wrap items-center">
<a-button
:type="checkedIndex === 0 ? 'primary' : 'default'"
size="small"
class="m-1"
style="padding: 0 5px"
@click="handleSelectTime(0)"
>
半小时
</a-button>
<a-button
:type="checkedIndex === 1 ? 'primary' : 'default'"
size="small"
class="m-1"
style="padding: 0 5px"
@click="handleSelectTime(1)"
>
1小时
</a-button>
<a-button
:type="checkedIndex === 2 ? 'primary' : 'default'"
size="small"
class="m-1"
style="padding: 0 5px"
@click="handleSelectTime(2)"
>
2小时
</a-button>
</div>
<!-- 时长 -->
<div class="flex items-center justify-end flex-1 text-sm">
<a-input-number id="inputNumber" v-model:value="duration" :min="0" placeholder="工作量时长" />
<span class="flex-shrink-0">小时</span>
</div>
</div>
</div>
<!-- 插件审核人员选择 -->
<ReviewerSecond class="mt-3" ref="reviewerRef" :dataCheckers="deliver.checkerList ? deliver.checkerList : []" />
</div>
</template>
<script setup>
import { useStore } from 'vuex';
import { ref, inject, computed, defineEmits } from 'vue';
import { message } from 'ant-design-vue';
import { submitDeliverInfo, uploadImg } from 'apis';
import ReviewerSecond from '@/components/tall/Reviewer/ReviewerSecond.vue';
const store = useStore();
const projectId = computed(() => store.getters['project/projectId']);
const sessionProjectId = sessionStorage.getItem('projectId');
const roleId = computed(() => store.state.role.roleId);
const deliver = inject('deliver'); //
const task = inject('task'); //
const linkValue = ref(''); //
const duration = ref(2); //
const checkedIndex = ref(2); //
//
const action = uploadImg;
const token = computed(() => store.state.user.token);
const headers = { Authorization: `Bearer ${token.value}` };
//
const submitState = computed(() => !linkValue.value); //
const submitBtnLoading = ref(false); //
const reviewerRef = ref(null); //
const showMask = ref(false); //
const emits = defineEmits(['upload-success']);
function handleChange(info) {
console.log('info', info);
}
//
function handleSelectTime(data) {
checkedIndex.value = data;
duration.value = data === 0 ? 0.5 : data === 1 ? 1 : 2;
}
//
async function submit() {
console.log(reviewerRef.value);
const { checkedCheckers } = reviewerRef.value; //
//
if (!validateDeliverForm(checkedCheckers)) return;
submitBtnLoading.value = true; // loading
try {
const checkerList = [];
checkedCheckers.forEach(item => {
checkerList.push(item.memberId);
});
const { url } = store.state.projects.project;
const params = {
param: {
projectId: projectId.value || sessionProjectId,
roleId: roleId.value,
deliverId: deliver.value.deliverId,
fileList: [linkValue.value],
checkerList,
msgId: task.msgId,
},
};
const data = await submitDeliverInfo(params, url);
message.info('提交交付物信息成功');
resetControlState(); //
emits('upload-success');
} catch (error) {
message.info('提交交付物信息失败');
submitBtnLoading.value = false; // loading
throw new Error(error);
}
}
//
// function paste() {}
//
// function uploadPhoto() {}
//
function validateDeliverForm(checkedCheckers) {
const reg = /[a-zA-z]+:\/\/[^\s]*/;
if (!reg.test(linkValue.value)) {
// toast
message.info('请输入正确的链接');
return false;
}
//
if (!checkedCheckers || !checkedCheckers.length) {
message.info('请选择检查人');
return false;
}
return true;
}
//
function resetControlState() {
submitBtnLoading.value = false; // loading
linkValue.value = ''; //
reviewerRef.value.collapsed = true; //
}
</script>
<style scoped>
.duration-title {
width: 85px;
height: 20px;
line-height: 20px;
text-align: center;
top: -25px;
left: 10px;
}
</style>
Loading…
Cancel
Save