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

601 lines
14 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 { computed, h, nextTick, onMounted, reactive, ref, unref } from 'vue'
import { NDataTable, useDialog, useMessage } from 'naive-ui'
import type { DataTableColumns, DataTableRowKey, PaginationProps } from 'naive-ui'
import { Action, CustomTabelModal, ImportExcelModal, RepeatModal, RepeatTaskTableModal } from '../comp'
import ConfrimModal from '@/views/task/modal/ConfrimModal.vue'
import { useWindowSizeFn } from '@/hooks/event/useWindowSizeFn'
import { getViewportOffset } from '@/utils/domUtils'
import type { RowData } from '@/config/final'
import { findKey, headRules } from '@/config/final'
import { getFinalList } from '@/api/final'
import { isBoolean } from '@/utils/is'
import { useUser } from '@/store/modules/user'
import type { ApprovalParam } from '/#/api'
import { audit } from '@/api/task/task'
import SvgIcon from '@/components/Icon/SvgIcon.vue'
const columns: DataTableColumns<RowData> = [
{
type: 'selection',
fixed: 'left',
width: 50,
},
{
title: '任务Id',
key: 'id',
fixed: 'left',
width: 100,
},
{
title: '任务名称',
key: 'fromtaskname',
fixed: 'left',
width: 150,
},
{
title: '审批节点',
key: 'approvalnode',
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' })
},
},
{
title: '图片相似度',
key: 'similarity',
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' })
},
},
{
title: '提报时间',
key: 'fromuptime',
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' })
},
},
{
title: '更新时间',
key: 'updatetime',
width: 200,
},
{
title: '操作',
key: 'actions',
width: 200,
fixed: 'right',
render(row) {
return h(
Action,
{
id: row.id,
status: row.states,
trigger: actionHandler,
},
)
},
},
]
const deviceHeight = ref(600)
onMounted(() => {
nextTick(() => {
computeListHeight()
})
})
const columnsRef = ref(columns)
const tableRef = ref<InstanceType<typeof NDataTable>>()
const rowKey = (row: RowData) => row.id
const loading = ref(true)
const pagination = reactive({
page: 1,
pageCount: 1,
pageSize: 10,
})
const tableData = ref<Array<RowData>>([])
const selectionIds = ref<DataTableRowKey[]>([])
const userStore = useUser()
const dialog = useDialog()
const message = useMessage()
async function query(page: number, pageSize: number) {
const result = await getFinalList({ sortorder: 'asc', pageSize, currPage: page, sortname: '' })
const { data, pageCount } = result
tableData.value = data
pagination.page = page
pagination.pageCount = pageCount
loading.value = false
}
async function handlePageChange(currentPage) {
if (loading.value)
return
const { pageSize } = pagination
await query(currentPage, pageSize)
}
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
}
function actionHandler(action: any) {
const { key } = action
switch (key) {
case 'view':
break
case 'reset':
resetHandler()
break
case 'approval':
approvalHandler()
break
case 'reject':
rejectHandler()
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 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() {
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() {
const items = getSelectItems()
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!)
}
</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" />
</div>
<div>
<n-button class="xjcc" text @click="showModal(repeatModalRef)">
</n-button>
<div class="btn">
<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: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.30);
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;
}
}
</style>