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/final/content/Content.vue

1000 lines
24 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 type { DataTableColumns, DataTableRowKey, PaginationProps } from 'naive-ui'
import { NButton, NDataTable, useDialog, useMessage } from 'naive-ui'
import {
computed,
defineEmits,
h,
nextTick,
onBeforeMount,
onMounted,
onUnmounted,
reactive,
ref,
unref,
watch,
} from 'vue'
import { rowPropKeys } from 'naive-ui/es/legacy-grid/src/Row'
import { useRoute, useRouter } from 'vue-router'
import {
Action,
CustomTabelModal,
ImportExcelModal,
ListAction,
RepeatModal,
RepeatTaskTableModal,
StatusItem,
} from '../comp'
import { getFinalList } from '@/api/final'
import { audit } from '@/api/task/task'
import SvgIcon from '@/components/Icon/SvgIcon.vue'
import type { RowData } from '@/config/final'
import { findKey, headRules } from '@/config/final'
import { useWindowSizeFn } from '@/hooks/event/useWindowSizeFn'
import { useUser } from '@/store/modules/user'
import { useFinal } from '@/store/modules/final'
import { getViewportOffset } from '@/utils/domUtils'
import { isBoolean } from '@/utils/is'
import { useDictionary } from '@/store/modules/dictonary'
import ConfrimModal from '@/views/task/modal/ConfrimModal.vue'
import type { ApprovalParam } from '/#/api'
import emitter from '@/utils/mitt'
import { formatToDateHMS } from '@/utils/dateUtil'
import { getAllfieldList, getfieldList, savefield } from '@/api/home/filter'
const emit = defineEmits(['changeShow'])
function changeContent() {
emit('changeShow')
}
const dicStore = useDictionary()
const izstatusList = ref([])
const router = useRouter()
const route = useRoute()
const reviewType = 0
const actionsColumns = {
title: '操作',
key: 'actions',
minWidth: 200,
fixed: 'right',
render(row) {
return h(ListAction, {
id: row.id,
status: row.states,
trigger: (action) => {
actionHandler(action, row)
},
})
},
}
const columnsRef = ref<DataTableColumns<RowData>>([])
// const columns: DataTableColumns<RowData> = [
// {
// type: "selection",
// fixed: "left",
// width: 50,
// },
// {
// title: "任务Id",
// key: "id",
// fixed: "left",
// width: 190,
// },
// {
// title: "任务名称",
// key: "fromtaskname",
// fixed: "left",
// width: 200,
// ellipsis: {
// tooltip: true,
// },
// },
// {
// title: "审批节点",
// key: "taskname",
// width: 100,
// },
// {
// title: "审批状态",
// key: "states",
// width: 100,
// sorter: "default",
// renderSorterIcon: ({ order }) => {
// if (order === false) return h(SvgIcon, { name: "sort-2" });
// if (order === "ascend") return h(SvgIcon, { name: "sort-1" });
// if (order === "descend") return h(SvgIcon, { name: "sort-3" });
// },
// render(row: any) {
// const item: any = izstatusList.value.find(
// (item: any) => item.value == row.states
// );
// return item ? item.label : "";
// },
// },
// {
// title: "图片相似度",
// key: "similarityscore",
// width: 150,
// sorter: "default",
// renderSorterIcon: ({ order }) => {
// if (order === false) return h(SvgIcon, { name: "sort-2" });
// if (order === "ascend") return h(SvgIcon, { name: "sort-1" });
// if (order === "descend") return h(SvgIcon, { name: "sort-3" });
// },
// render(row: any) {
// return row.similarityscore ? `${row.similarityscore}%` : "";
// },
// },
// {
// title: "提报时间",
// key: "createdate",
// width: 200,
// sorter: "default",
// renderSorterIcon: ({ order }) => {
// if (order === false) return h(SvgIcon, { name: "sort-2" });
// if (order === "ascend") return h(SvgIcon, { name: "sort-1" });
// if (order === "descend") return h(SvgIcon, { name: "sort-3" });
// },
// render(row: any) {
// return formatToDateHMS(row.createdate || 0);
// },
// },
// {
// title: "更新时间",
// key: "updatetime",
// width: 200,
// render(row: any) {
// return row.updatetime ? formatToDateHMS(row.updatetime) : "";
// },
// },
// {
// title: "操作",
// key: "actions",
// width: 200,
// fixed: "right",
// render(row) {
// return h(Action, {
// id: row.id,
// status: row.states,
// trigger: (action) => {
// actionHandler(action, row);
// },
// });
// },
// },
// ];
async function getColumns() {
columnsRef.value = [
{
type: 'selection',
fixed: 'left',
width: 50,
},
]
const userStore = useUser()
const userInfo = userStore.getUserInfo
let res
res = await getAllfieldList(reviewType) // 所有筛选项目
const allList = res.data
res = await getfieldList(reviewType, userInfo.id) // 当前用户选择的项目
const useList = res.data
const userFieldFixed = useList.userFieldFixed?.split(',')
const userFieldUnFixed = useList.userFieldUnFixed?.split(',')
console.log(userFieldFixed, userFieldUnFixed, 'userFieldUnFixed')
console.log(allList, 'allList')
allList?.map((v) => {
if (!userFieldFixed?.length && !userFieldUnFixed?.length) {
if (v.isrequired == 2) {
columnsRef.value.push({
title: v.fieldDesc,
fixed: 'left',
key: v.name,
width: 120,
})
}
}
if (userFieldFixed?.find(v2 => v2 == v.name)) {
columnsRef.value.push({
title: v.fieldDesc,
key: v.name,
fixed: 'left',
width: 120,
})
}
if (userFieldUnFixed?.find(v2 => v2 == v.name)) {
columnsRef.value.push({
title: v.fieldDesc,
key: v.name,
width: 120,
})
}
})
columnsRef.value.push(actionsColumns as any)
formatColumns()
console.log(columnsRef.value, 'columnsRef')
}
async function formatColumns() {
// TODO处理特殊字段
let index
index = columnsRef.value.findIndex(v => v.title == '任务ID')
if (index > -1) {
columnsRef.value[index] = {
title: '任务ID',
key: columnsRef.value[index].key, // "id"
fixed: columnsRef.value[index].fixed || undefined,
width: 200,
render(row) {
const item: any = izstatusList.value.find(
(item: any) => item.value == row.states,
)
return h(
NButton,
{
'strong': true,
'tertiary': true,
'text': true,
'size': 'small',
'text-color': '#507AFD',
'onClick': () => goDetail(row),
},
{ default: () => row.id },
)
},
}
}
index = columnsRef.value.findIndex(v => v.title == '任务名称')
if (index > -1) {
columnsRef.value[index] = {
title: '任务名称',
key: columnsRef.value[index].key, // "fromtaskname"
fixed: columnsRef.value[index].fixed || undefined,
width: 200,
ellipsis: {
tooltip: true,
},
}
}
index = columnsRef.value.findIndex(v => v.title == '审批状态')
if (index > -1) {
columnsRef.value[index] = {
title: '审批状态',
key: columnsRef.value[index].key,
fixed: columnsRef.value[index].fixed || undefined,
width: 120,
sorter: 'default',
renderSorterIcon: ({ order }) => {
if (order === false)
return h(SvgIcon, { name: 'sort-2' })
if (order === 'ascend')
return h(SvgIcon, { name: 'sort-1' })
if (order === 'descend')
return h(SvgIcon, { name: 'sort-3' })
},
render(row) {
const item: any = izstatusList.value.find(
(item: any) => item.value == row.states,
)
return h(StatusItem, {
id: row.id,
status: row.states,
label: item ? item.label : '',
})
},
}
}
index = columnsRef.value.findIndex(v => v.title == '图片相似度')
if (index > -1) {
columnsRef.value[index] = {
title: '图片相似度',
key: columnsRef.value[index].key,
fixed: columnsRef.value[index].fixed || undefined,
width: 150,
sorter: 'default',
renderSorterIcon: ({ order }) => {
if (order === false)
return h(SvgIcon, { name: 'sort-2' })
if (order === 'ascend')
return h(SvgIcon, { name: 'sort-1' })
if (order === 'descend')
return h(SvgIcon, { name: 'sort-3' })
},
render(row: any) {
return row.similarityscore ? `${row.similarityscore}%` : ''
},
}
}
index = columnsRef.value.findIndex(v => v.title == '提报时间')
if (index > -1) {
columnsRef.value[index] = {
title: '提报时间',
key: columnsRef.value[index].key,
fixed: columnsRef.value[index].fixed || undefined,
width: 200,
sorter: 'default',
renderSorterIcon: ({ order }) => {
if (order === false)
return h(SvgIcon, { name: 'sort-2' })
if (order === 'ascend')
return h(SvgIcon, { name: 'sort-1' })
if (order === 'descend')
return h(SvgIcon, { name: 'sort-3' })
},
render(row: any) {
return formatToDateHMS(row.createdate || 0)
},
}
}
index = columnsRef.value.findIndex(v => v.title == '更新时间')
if (index > -1) {
columnsRef.value[index] = {
title: '更新时间',
key: columnsRef.value[index].key,
fixed: columnsRef.value[index].fixed || undefined,
width: 200,
render(row: any) {
return row.updatetime ? formatToDateHMS(row.updatetime) : ''
},
}
}
}
const deviceHeight = ref(600)
onMounted(() => {
emitter.on('filter-final', refreshHandler)
getColumns()
nextTick(() => {
computeListHeight()
})
})
onBeforeMount(() => {
dicStore.fetchizstatusListt()
})
watch(
() => dicStore.izstatusList,
(newval) => {
izstatusList.value = newval
},
)
onUnmounted(() => {
emitter.off('filter-final', refreshHandler)
})
const tableRef = ref<InstanceType<typeof NDataTable>>()
const rowKey = (row: RowData) => row.id
const loading = ref(true)
const total = ref(0);
const pagination = reactive({
page: 1,
pageCount: 1,
pageSize: 10,
showSizePicker: true,
pageSizes: [
{
label: '10 每页',
value: 10,
},
{
label: '15 每页',
value: 15,
},
{
label: '30 每页',
value: 30,
},
{
label: '50 每页',
value: 50,
},
],
showQuickJumper: true,
prefix:()=>`${total.value} 条数据`
})
const tableData = ref<Array<RowData>>([])
const selectionIds = ref<DataTableRowKey[]>([])
const dialog = useDialog()
const message = useMessage()
const finalStore = useFinal()
async function query(page: number, pageSize: number, filterId?: any) {
const asideParmas = unref(finalStore.getAsideValue)
// 有过滤配置的时候优先使用过滤配置,不要使用左侧参数
const params = filterId ? { userSearchId: filterId } : asideParmas
const result = await getFinalList({
sortorder: 'asc',
pageSize,
currPage: page,
sortname: '',
...params,
})
const { data, pageCount } = result
tableData.value = data
total.value = data.totalCount;
pagination.page = page
pagination.pageCount = pageCount
loading.value = false
}
async function handlePageChange(currentPage) {
if (loading.value)
return
const { pageSize } = pagination
await query(currentPage, pageSize)
}
async function handlePageSizeChange(currentPageSize) {
if (loading.value)
return
const { page } = pagination
pagination.pageSize = currentPageSize
await query(page, currentPageSize)
}
function handleCheck(rowKeys: DataTableRowKey[]) {
selectionIds.value = rowKeys
}
async function computeListHeight() {
const table = unref(tableRef)
if (!table)
return
const tableEl: any = table?.$el
const headEl = tableEl.querySelector('.n-data-table-thead ')
const { bottomIncludeBody } = getViewportOffset(headEl)
const headerH = 64
let paginationH = 2
const marginH = 60
if (!isBoolean(unref(pagination))) {
const paginationEl = tableEl.querySelector(
'.n-data-table__pagination',
) as HTMLElement
if (paginationEl) {
const offsetHeight = paginationEl.offsetHeight
paginationH += offsetHeight || 0
}
else {
paginationH += 28
}
}
let height = bottomIncludeBody - (headerH + paginationH + marginH)
const maxHeight = 800
height = maxHeight && maxHeight < height ? maxHeight : height
deviceHeight.value = height
}
useWindowSizeFn(computeListHeight)
const maxHeight = computed(() => {
return tableData.value.length ? `${unref(deviceHeight)}px` : 'auto'
})
query(pagination.page, pagination.pageSize)
const customTabelRef = ref(null)
const importExcelRef = ref(null)
const rejectModalRef = ref(null)
const repeatModalRef = ref(null)
const repeatTaskTableModalRef = ref(null)
function showModal(modalRef: any) {
const modal = unref(modalRef)! as any
modal.showModal()
}
const popover = ref<ComponentRef | null>(null)
function importHandler() {
(popover.value as any).setShow(false)
showModal(importExcelRef)
}
function exportHandler() {
loading.value = true
import('@/utils/exportExcel').then((excel) => {
const tHeader = [
'任务Id',
'任务名称',
'审批节点',
'审批状态',
'图片相似度',
'提报时间',
'更新时间',
]
const filterVal = [
'id',
'name',
'approvalnode',
'approvalstatus',
'similarity',
'uptime',
'updatetime',
]
const list = tableData.value
const data = formatJson(filterVal, list)
excel.export_json_to_excel({
header: tHeader,
data,
filename: 'data',
autoWidth: true,
bookType: 'xlsx',
})
loading.value = false
})
}
function formatJson(filterVal, jsonDataList) {
return jsonDataList.map(v =>
filterVal.map((j) => {
return v[j]
}),
)
}
function sucessHandler(excelData) {
const data = excelData.content.map((item) => {
const obj = {}
Object.keys(item).forEach((key) => {
const k = findKey(columns, key)
obj[k] = item[key]
})
return obj
})
tableData.value = data
}
function commitHandler(columns) {
columnsRef.value = [
...columns,
actionsColumns,
]
formatColumns()
}
function actionHandler(action: any, row: any) {
const { key } = action
switch (key) {
case 'view':
goDetail(row)
break
case 'reset':
resetHandler()
break
case 'approval':
approvalHandler(row)
break
case 'reject':
rejectHandler(row)
break
default:
break
}
}
// 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 goDetail(row) {
router.push({ name: 'final-detail', query: { id: row.id, packageid: row.packageid } })
}
function resetHandler() {
dialog.info({
title: '确认提示',
content: '确认重置当前选中的任务的审批吗?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
// TODO需要支持重置
// const result = await resetApproval()
},
onNegativeClick: () => {},
})
}
function getSelectItems() {
return tableData.value.filter(item => selectionIds.value.includes(item.id))
}
function approvalHandler(row) {
// const items = getSelectItems()
const items = [row]
const msg = validate(items)
if (msg !== null) {
message.error(msg)
return
}
dialog.info({
title: '确认提示',
content: '确认给该任务审批为【通过】吗?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
approval(items)
},
onNegativeClick: () => {},
})
}
function batchApproval() {
const items = getSelectItems()
const msg = validate(items)
if (msg !== null) {
message.error(msg)
return
}
dialog.info({
title: '确认提示',
content: '确认给该任务审批为【通过】吗?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
approval(items)
},
onNegativeClick: () => {},
})
}
function approval(items) {
const formIds: string[] = items.map(item => item.id)
const taskIds: string[] = items.map(item => item.taskId)
const tasknames: string[] = items.map(item => item.taskname)
const param: ApprovalParam = {
formid: formIds,
taskId: taskIds,
approvd: true,
taskComment: 'approval',
taskname: tasknames,
}
doAudit(param)
}
function rejectHandler(row) {
// const items = getSelectItems()
const items = [row]
const msg = validate(items)
if (msg !== null) {
message.error(msg)
return
}
const modal = unref(rejectModalRef)! as any
modal.showModal()
}
function reject(idOrDesc: string, backId: string, isOther: boolean) {
const items = getSelectItems()
const formIds: string[] = items.map(item => item.id)
const taskIds: string[] = items.map(item => item.fromtaskid)
const tasknames: string[] = items.map(item => item.fromtaskname)
const param: ApprovalParam = {
formid: formIds,
taskId: taskIds,
approvd: false,
taskComment: idOrDesc,
taskname: isOther ? tasknames : ['其他'],
}
doAudit(param)
}
function doAudit(param: any) {
audit(param).then((res) => {
const { code } = res
if (code === 'OK')
reload()
else message.error(res.message)
})
}
function reload() {
const { page, pageSize } = unref(tableRef.value?.pagination) as PaginationProps
query(page!, pageSize!)
}
watch(
() => finalStore.asideValue,
(newVal, oldVal) => {
refreshHandler()
},
{ deep: true },
)
function reset() {
pagination.page = 1
pagination.pageCount = 1
pagination.pageSize = 1
}
async function refreshHandler(searchId?: any) {
reset()
query(pagination.page, pagination.pageSize, searchId)
}
</script>
<template>
<div class="wrapper">
<div class="wrapper-header">
<div class="wrapper-header-left">
<span class="wrapper-header-font">任务管理列表</span>
<SvgIcon size="32" name="magnifying" @click="changeContent" />
</div>
<div>
<NButton class="xjcc" text @click="showModal(repeatModalRef)">
</NButton>
<div class="btn" @click="batchApproval">
<SvgIcon style="margin-right: 6px" size="14" name="tf" />
批量审批
</div>
<n-popover
ref="popover"
:style="{ padding: '0px' }"
style="width: 148px"
:show-arrow="false"
placement="bottom-start"
trigger="click"
>
<template #trigger>
<SvgIcon style="cursor: pointer" size="20" name="more-ver" />
</template>
<ul class="wrapper-header-action">
<li @click="importHandler">
<SvgIcon size="20" name="download" /><span style="margin-left: 5px">批量导入数据</span>
</li>
<li @click="exportHandler">
<SvgIcon size="20" name="download" /><span style="margin-left: 5px">导出待审数据</span>
</li>
<li>
<SvgIcon size="20" name="download" /><span style="margin-left: 5px">导出全部数据</span>
</li>
</ul>
</n-popover>
</div>
</div>
<!-- <div class="wrapper-statistic">
<div v-for="i in 7" :key="i" class="item">
<div class="icon" />
<div class="content">
<span class="num">6399</span>
<span class="label">任务总数</span>
</div>
<div v-if="i === 7" style="display: flex;align-items: center;">
<div class="divider" />
<SvgIcon size="18" name="setting" />
</div>
</div>
</div> -->
<div class="wrapper-settings">
<SvgIcon
style="cursor: pointer"
size="18"
name="setting"
@click="showModal(customTabelRef)"
/>
</div>
<div class="wrapper-content">
<NDataTable
ref="tableRef"
remote
:columns="columnsRef"
:scroll-x="1250"
:max-height="maxHeight"
:data="tableData"
:loading="loading"
:pagination="pagination"
:row-key="rowKey"
@update:page="handlePageChange"
@update-page-size="handlePageSizeChange"
@update:checked-row-keys="handleCheck"
/>
</div>
<CustomTabelModal ref="customTabelRef" @commit="commitHandler" />
<ImportExcelModal
ref="importExcelRef"
:on-success="sucessHandler"
:header-config="headRules"
/>
<ConfrimModal ref="rejectModalRef" @commit="reject" />
<RepeatModal
ref="repeatModalRef"
@reject="showModal(rejectModalRef)"
@viewrepeat="showModal(repeatTaskTableModalRef)"
/>
<RepeatTaskTableModal ref="repeatTaskTableModalRef" />
</div>
</template>
<style lang="less" scoped>
.wrapper {
display: flex;
flex: 1;
overflow: hidden;
flex-direction: column;
box-sizing: border-box;
margin-left: 16px;
width: 100%;
background: #fff;
padding: 0px 24px 24px 24px;
&-header {
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
height: 64px;
width: 100%;
div {
display: flex;
align-items: center;
}
.xjcc {
font-weight: bold;
color: #6a80fc;
margin-right: 20px;
}
.title {
color: #507afd;
font-weight: bold;
font-size: 14px;
margin-left: 8px;
}
.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;
}
.more {
width: 30px;
height: 30px;
line-height: 30px;
opacity: 0.1;
background: linear-gradient(144deg, #73a7f9 0%, #3e6ef1 96%);
border-radius: 8px;
}
&-font {
font-size: 18px;
font-weight: bold;
color: #333333;
line-height: 25px;
margin-right: 8px;
}
&-action {
padding: 8px;
li {
height: 32px;
line-height: 32px;
cursor: pointer;
&:hover {
background-color: #f3f8ff;
}
}
}
}
&-settings {
display: flex;
align-items: center;
justify-content: flex-end;
padding: 12px 0px;
}
&-statistic {
width: 100%;
height: 76px;
background: #f9fafb;
border-radius: 4px;
display: flex;
padding: 0px 10px;
justify-content: space-between;
.item {
display: flex;
align-items: center;
padding: 20px 0;
.icon {
background-color: #ecf2fe;
width: 50px;
height: 50px;
border-radius: 50px;
}
.content {
text-align: center;
margin-left: 12px;
.num {
font-weight: bold;
color: #202020;
font-size: 18px;
}
.label {
color: #202020;
opacity: 0.6;
}
span {
display: block;
}
}
.divider {
background-color: #e8e8e8;
margin: 0px 10px;
width: 3px;
height: 20px;
}
}
}
&-content {
flex: auto;
background: #fff;
}
}
:deep(.n-data-table .n-data-table-td.n-data-table-td--fixed-right){
z-index: 100 !important;
}
</style>