You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ocr-web/src/views/task/content/Content.vue

760 lines
19 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<script lang="ts" setup>
import { getAllfieldList, getfieldList } from '@/api/home/filter'
import { audit } from '@/api/task/task'
import { getTaskDetailInfo, getTaskDetailPictureList } from '@/api/work/work'
import { useTask } from '@/store/modules/task'
import { useUser } from '@/store/modules/user'
import { isEmpty } from '@/utils'
import { hideDownload } from '@/utils/image'
import emitter from '@/utils/mitt'
import _ from 'lodash'
import { clone } from 'lodash-es'
import { useDialog, useMessage } from 'naive-ui'
import { computed, onUnmounted, reactive, ref, unref, watch } from 'vue'
import BatchModal from '../modal/BatchModal.vue'
import ConfrimModal from '../modal/ConfrimModal.vue'
import CustomSettingModal from '../modal/CustomSettingModal.vue'
import PictureTable from './PictureTable.vue'
import TaskTable from './TaskTable.vue'
import type { ApprovalParam, PictureSortParam } from '/#/api'
const batch = ref(false)
const selectItems = ref<any[]>([])
const message = useMessage()
const dialog = useDialog()
const confrimModalRef = ref(null)
const batchModalRef = ref(null)
const totalCount = ref(0)
const CustomSettingModalRef = ref(null)
const taskTableData = ref<any[]>([])
const sortBy: PictureSortParam = {
orderbyname: "asc",
orderbyvalue: "fromuptime",
};
function setBatch(value: boolean) {
if (totalCount.value === 0) return;
batch.value = value;
if (value === false) {
selectItems.value.forEach((item) => (item.checked = false));
selectItems.value.length = 0;
}
}
function onCheckChange(checked: any, item: any) {
const index = selectItems.value.indexOf(item);
item.checked = checked;
if (index === -1 && checked) selectItems.value.push(item);
else selectItems.value.splice(index, 1);
}
const showActions = computed(() => {
return selectItems.value.length > 0 && batch;
});
const taskpagination = reactive({
pageNo: 1,
pageSize: 10,
});
const taskStore = useTask();
const overTask = ref<any>(null);
const taskDetailInfo = ref<any>({});
const taskDetailPictureList = ref<any[]>([]);
const userStore = useUser();
const imageRef = ref<ComponentElRef | null>();
let processItems: any[] = [];
// states:1未提交2待审批3通过4不通过
function validate(items: any[]) {
if (items.length === 0) return "至少选中一个任务";
// const useInfo = userStore.getUserInfo
// const username = useInfo.loginname
// for (const item of items) {
// const { iztrueorfalse, states, assignee } = item
// if (iztrueorfalse === null)
// return '未判别真假'
// else if (states !== 2)
// return '审批状态不合法'
// else if (assignee !== username)
// return '审批人不一致'
// }
return null;
}
function approvalHandler(items?: any) {
let cloneItem: any;
if (batch.value) {
processItems = selectItems.value;
} else if (overTask.value) {
cloneItem = clone(overTask.value);
processItems = [cloneItem];
}
if (items !== undefined && !(items instanceof PointerEvent))
processItems = items;
const msg = validate(processItems);
if (msg !== null) {
message.error(msg);
return;
}
dialog.info({
title: "确认提示",
content: "确认给该任务审批为【通过】吗?",
positiveText: "确定",
negativeText: "取消",
onPositiveClick: () => {
approval();
},
onNegativeClick: () => {},
});
}
function rejectHandler(items?: any) {
let cloneItem: any;
if (batch.value) {
processItems = selectItems.value;
} else if (overTask.value) {
cloneItem = clone(overTask.value);
processItems = [cloneItem];
}
if (items !== undefined && !(items instanceof PointerEvent))
processItems = items;
const msg = validate(processItems);
if (msg !== null) {
message.error(msg);
return;
}
const modal = unref(confrimModalRef)! as any;
modal.showModal();
}
function approval() {
const formIds: string[] = processItems.map((item) => item.id);
const taskIds: string[] = processItems.map((item) => item.taskId);
const tasknames: string[] = processItems.map((item) => item.taskname);
const param: ApprovalParam = {
formid: formIds,
taskId: taskIds,
approvd: true,
taskComment: "approval",
taskname: tasknames,
};
doAudit(param);
}
function doAudit(param: any) {
audit(param).then((res) => {
const { code } = res;
processItems.length = 0;
if (code === "OK") emitter.emit("refresh");
else message.error(res.message);
});
}
function reject(idOrDesc: string, backId: string, isOther: boolean) {
const formIds: string[] = processItems.map((item) => item.id);
const taskIds: string[] = processItems.map((item) => item.taskId);
const tasknames: string[] = processItems.map((item) => item.taskname);
const param: ApprovalParam = {
formid: formIds,
taskId: taskIds,
approvd: false,
taskComment: idOrDesc,
taskname: isOther ? tasknames : ["其他"],
};
doAudit(param);
}
function showModal(modalRef: any) {
const modal = unref(modalRef)! as any;
modal.showModal();
}
function forwardHandler() {
taskStore.forward();
}
function backHandler() {
taskStore.back();
}
async function getTableData(){
const useInfo = userStore.getUserInfo;
let listData = [];
const reviewType = 3; //类型
let res = await getAllfieldList(reviewType);
let fieldList = (res as any)?.data;
res = await getfieldList(reviewType, useInfo.id);
let userFieldList = (res as any)?.data.userFieldFixed;
console.log(userFieldList,'userFieldList')
fieldList.map((v) => {
if (userFieldList.indexOf(v.name) > -1) {
let item = {
label: v.fieldDesc,
value: taskDetailInfo.value.ocrPicture[v.name],
key: v.name,
};
listData.push(item);
}
});
taskTableData.value = _.chunk(listData, 2);
}
watch(
() => [taskStore.activeId],
async (newValue, oldValue) => {
const packageid = taskStore.getPackageid
const taskId = taskStore.getActiveId
if (isEmpty(taskId)) return;
taskDetailInfo.value = await getTaskDetailInfo(taskId, packageid);
getTableData();
// TODO mock备份 确认逻辑无误后删除
// let showFieldData = taskDetailInfo.value.ocrPicture;
// let showFieldKeys = Object.keys(showFieldData);
// for (let i in showFieldKeys) {
// let key = showFieldKeys[i];
// let hasKey = userFieldList.indexOf(key) > -1;
// if (hasKey) {
// let currentData = fieldList.find((v) => v.name == key);
// if (currentData) {
// console.log(fieldList, key, "currentData");
// console.log(currentData, "currentData222");
// let item = {
// label: currentData.fieldDesc,
// value: taskDetailInfo.value.ocrPicture[key],
// key: currentData.name,
// };
// listData.push(item);
// }
// }
// }
if (!isEmpty(packageid)) {
const { data, total } = await getTaskDetailPictureList(
packageid,
taskId,
{ ...taskpagination, ...sortBy },
)
taskDetailPictureList.value = data
totalCount.value = total
}
}
);
const currentTaskId = computed(() => {
const index = taskStore.getCurrentIndex;
return taskStore.getApprovalList[index]?.formid || "";
});
function overTaskHandle() {
const item = taskDetailInfo.value;
if (validate([item]) == null && batch.value === false) overTask.value = item;
}
function leaveTaskHandler() {
overTask.value = null;
}
function showActionsModal() {
const modal = unref(CustomSettingModalRef)! as any;
modal.showModal();
}
onUnmounted(() => {
taskStore.reset();
});
const mark = computed(() => {
return taskDetailInfo.value.iztrueorfalse === null ? "未标记" : "已标记";
});
function immersionHandler() {
taskStore.updateImmersion();
}
function previewHandler(event: MouseEvent) {
event.stopImmediatePropagation();
event.stopPropagation();
if (imageRef.value && (imageRef.value as any).src)
(imageRef.value as any).mergedOnClick()
}
function getPercent(pictureid: string) {
const { ocpictureid, pictureresult } = taskDetailInfo.value;
const index = ocpictureid.split(",").indexOf(String(pictureid));
const results = pictureresult.split(",");
const percent = results[index] || "0";
const val = Number.parseFloat(percent);
return `${val}%`;
}
</script>
<template>
<div class="wrapper">
<div class="wrapper-header">
<div class="left">
<span class="font">任务ID{{ currentTaskId }}</span>
<SvgIcon
size="22"
class="forward"
name="arrow-left"
@click="backHandler"
/>
<SvgIcon
size="22"
class="back"
name="arrow-right"
@click="forwardHandler"
/>
</div>
<div class="right">
<div
v-show="!showActions"
style="display: flex; align-items: center"
@click="setBatch(true)"
>
<div class="btn">
<SvgIcon style="margin-right: 6px" size="14" name="tf" />
批量审批
</div>
<SvgIcon
style="cursor: pointer"
size="20"
name="immersion-model"
@click="immersionHandler"
/>
</div>
<div v-show="showActions" class="batch">
<n-button text @click="setBatch(false)">
<template #icon>
<SvgIcon name="revoke" />
</template>
返回
</n-button>
<div
style="cursor: pointer; margin-left: 16px"
@click.stop="rejectHandler"
>
<SvgIcon width="64" height="28" name="a1" />
</div>
<SvgIcon size="24" name="vs" />
<div style="cursor: pointer" @click.stop="approvalHandler">
<SvgIcon width="64" height="28" name="a2" />
</div>
</div>
</div>
</div>
<div class="wrapper-detail">
<div
class="left"
:style="{
'background-image': `url(${taskDetailInfo?.ocrPicture?.imgurl})`,
}"
@mouseover="overTaskHandle"
@mouseleave="leaveTaskHandler"
@click="previewHandler"
>
<div v-show="overTask" class="action">
<SvgIcon
style="cursor: pointer"
width="168"
height="48"
name="r6"
@click.stop="approvalHandler"
/>
<SvgIcon
style="cursor: pointer; margin-left: 30px"
width="168"
height="48"
name="r7"
@click.stop="rejectHandler"
/>
</div>
<div class="mark">
<SvgIcon
v-show="taskDetailInfo?.iztrueorfalse === 0"
size="128"
name="jia"
/>
</div>
<div class="mark">
<SvgIcon
v-show="taskDetailInfo?.iztrueorfalse === 1"
size="128"
name="zhen"
/>
</div>
<div class="info">
<n-grid x-gap="12" y-gap="10" :cols="12">
<n-gi span="4" class="gi1">
<span>
<SvgIcon size="40" name="m1" />
</span>
</n-gi>
<n-gi span="8" class="gi2">
<span style="font-size: bold; font-size: 18px">{{ mark }}</span>
<span>审批状态</span>
</n-gi>
<n-gi span="4" class="gi1">
<span>
<SvgIcon size="40" name="m2" />
</span>
</n-gi>
<n-gi span="8" class="gi2">
<span style="font-size: bold; font-size: 18px">{{
totalCount
}}</span>
<span>相似匹配</span>
</n-gi>
</n-grid>
</div>
<div class="time">
<SvgIcon color="#FFF" size="16" name="time" />
<span>2023-09-17 13:09:10</span>
</div>
<div style="display: none">
<n-image
ref="imageRef"
:img-props="{ onClick: hideDownload }"
:src="taskDetailInfo?.ocrPicture?.imgurl"
/>
</div>
</div>
<div class="right">
<n-scrollbar v-if="totalCount > 0" style="max-height: 100%">
<div class="header">
<span>相似图片({{ totalCount }})</span>
<SvgIcon
style="margin-right: 20px; cursor: pointer"
name="max"
size="24"
@click="showModal(batchModalRef)"
/>
</div>
<div class="list">
<div
v-for="(item, index) in taskDetailPictureList"
:key="index"
class="item"
>
<div
class="img-wrapper"
:style="{ 'background-image': `url(${item.thumburl})` }"
/>
<div class="check">
<n-checkbox
v-show="batch"
v-model:checked="item.checked"
@click.stop
@update:checked="onCheckChange($event, item)"
/>
</div>
<div class="percent">
{{ getPercent(item.pictureid) }}
</div>
</div>
</div>
</n-scrollbar>
<n-empty v-else description="暂无数据" />
</div>
</div>
<div class="info-header">
<div class="left_box">
<div class="title">填报信息</div>
<SvgIcon size="12" name="collapse" />
</div>
<div class="right_box" @click="showActionsModal">
自定义设置
<SvgIcon size="16" name="pencil" />
</div>
</div>
<n-tabs type="line" animated>
<n-tab-pane name="task-info" tab="任务信息">
<TaskTable :data="taskDetailInfo" :taskTableData="taskTableData" />
</n-tab-pane>
<n-tab-pane name="picture-info" tab="图片信息">
<PictureTable :data="taskDetailInfo" />
</n-tab-pane>
<n-tab-pane name="history" tab="历史审查" />
</n-tabs>
<ConfrimModal ref="confrimModalRef" @commit="reject" />
<BatchModal
ref="batchModalRef"
@reject="rejectHandler"
@approval="approvalHandler"
/>
<CustomSettingModal ref="CustomSettingModalRef" :review-type="3" @onOk="getTableData"/>
</div>
</template>
<style lang="less" scoped>
.wrapper {
display: flex;
flex: 1;
flex-direction: column;
box-sizing: border-box;
margin-left: 16px;
padding: 16px 16px 0px 16px;
background: #fff;
border-radius: 3px;
border: 1px solid rgb(239, 239, 245);
height: calc(100vh - 88px);
overflow-y: scroll;
&-header {
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
box-sizing: border-box;
border-radius: 3px;
margin-bottom: 16px;
width: 100%;
height: 36px;
.left {
display: flex;
align-items: center;
.forward {
cursor: pointer;
margin-left: 16px;
}
.back {
cursor: pointer;
margin-left: 6px;
}
}
.right {
display: flex;
align-items: center;
.btn {
width: 118px;
height: 36px;
background: linear-gradient(135deg, #5b85f8, #3c6cf0);
border-radius: 17px;
box-shadow: 0px 2px 6px 0px rgba(116, 153, 253, 0.3);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
margin-right: 6px;
cursor: pointer;
}
.batch {
display: flex;
align-items: center;
}
}
.font {
font-size: 18px;
font-weight: bold;
color: #333333;
line-height: 25px;
margin-left: 12px;
}
}
&-detail {
display: flex;
height: calc((100vh - 88px - 72px) / 2);
.left {
flex: 0.6;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border-radius: 8px;
position: relative;
.mark {
position: absolute;
z-index: 1;
right: 15%;
top: 0px;
width: 128px;
height: 80px;
overflow: hidden;
overflow: hidden;
svg {
position: absolute;
top: -48px;
}
}
.action {
position: absolute;
z-index: 3;
width: 100%;
height: 100%;
display: flex;
border-radius: 8px;
align-items: center;
justify-content: center;
background-color: rgba(102, 102, 102, 0.5);
}
.info {
position: absolute;
z-index: 3;
right: 2%;
bottom: 2%;
width: 136px;
height: 119px;
background: rgba(216, 216, 216, 0.4);
border-radius: 7px;
padding: 10px;
color: #fff;
}
.time {
position: absolute;
z-index: 3;
left: 2%;
bottom: 2%;
color: #fff;
display: flex;
align-items: center;
}
.gi1 {
display: flex;
align-items: center;
}
.gi2 {
display: flex;
align-items: left;
flex-direction: column;
justify-content: center;
}
}
.right {
flex: 0.5;
background: #fafafa;
border-radius: 8px;
margin-left: 20px;
padding: 24px;
.header {
display: flex;
justify-content: space-between;
font-size: 17px;
font-weight: bold;
color: #333333;
margin-bottom: 16px;
}
.list {
display: flex;
flex-wrap: wrap;
flex-direction: row;
justify-content: flex-start;
flex-shrink: 0;
}
.item {
flex-basis: calc((100% - 48px) / 3);
box-sizing: border-box;
border-radius: 8px;
position: relative;
overflow: hidden;
margin: 0px 16px 27px 0px;
.img-wrapper {
width: 230px;
height: 130px;
overflow: hidden;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border-radius: 8px;
}
.check {
position: absolute;
z-index: 5;
left: 6px;
top: 4px;
}
}
.percent {
position: absolute;
text-align: center;
width: 35px;
height: 18px;
opacity: 0.9;
background: #6f92fd;
border-radius: 6px 0px 6px 0px;
z-index: 5;
right: 12px;
top: 2px;
color: #fff;
}
}
}
.info-header {
display: flex;
padding: 16px 0px;
align-items: center;
justify-content: space-between;
.left_box {
display: flex;
align-items: center;
.title {
display: flex;
align-items: center;
margin-right: 8px;
&:before {
content: "";
width: 4px;
height: 18px;
background: #507afd;
border-radius: 3px;
display: inline-block;
margin-right: 10px;
}
}
}
.right_box {
display: flex;
align-items: center;
cursor: pointer;
}
}
}
</style>