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

1415 lines
38 KiB

<script lang="ts" setup>
import { EllipsisHorizontal, EyeOutline as EyeOutlineIcon } from '@vicons/ionicons5'
import { Download as DownloadIcon, Upload as UploadIcon } from '@vicons/tabler'
import { Icon } from '@vicons/utils'
import { useInfiniteScroll } from '@vueuse/core'
import dayjs from 'dayjs'
// import imagesloaded from 'imagesloaded'
import { cloneDeep, debounce } from 'lodash-es'
// import Masonry from 'masonry-layout'
import { NIcon, useMessage } from 'naive-ui'
import type { Component } from 'vue'
import {
computed,
h,
nextTick,
onBeforeMount,
onMounted,
onUnmounted,
onUpdated,
reactive,
ref,
unref,
watch,
} from 'vue'
import CheckingTaskModal from './modal/CheckingTaskModal.vue'
import FinishPackageModal from './modal/FinishPackageModal.vue'
import GeneratePackageModal from './modal/GeneratePackageModal.vue'
import LoginSuccessModal from './modal/LoginSuccessModal.vue'
import PackageSettingsModal from './modal/PackageSettingsModal.vue'
import QueryRepeatedTasksModal from './modal/QueryRepeatedTasksModal.vue'
import type { PictureSortParam } from '/#/api'
import defaultAvatar from '@/assets/icons/avatar.svg'
import baseImg from '@/assets/images/baseImg.png'
import { getImgUrl } from '@/utils/urlUtils'
import emitter from '@/utils/mitt'
import { hideDownload } from '@/utils/image'
import { getViewportOffset } from '@/utils/domUtils'
import { useConfig } from '@/store/modules/asideConfig'
import { useWindowSizeFn } from '@/hooks/event/useWindowSizeFn'
import { timeOptions, viewOptions } from '@/config/home'
import avatar from '@/assets/images/avatar.jpg'
import {
createPackage,
getCheckDuplicateStatus,
getCheckDuplicateStatusx,
getLastCheckNo,
getLastCheckNox,
getPictureList,
oneClickCheckTaskPackage,
queryPageListByCheckNo
, removeCheckDuplicate,
} from '@/api/home/main'
import bgLoading from '@/assets/images/bg-loading.png'
const listData = ref<any[]>([])
const timer = ref()
const showClose = ref(false)
const deviceHeight = ref(600)
// let _masonry: null | Masonry = null
// let _imagesload: any
// const masonryRef = ref<ComponentRef>(null)
const el = ref<HTMLDivElement | null>(null)
const waterfallRef = ref(null)
const viewMode = ref('masonry')
const pagination = reactive({
pageNo: 0,
pageSize: 30,
})
const configStore = useConfig()
const packageModalRef = ref(null)
const generateModalRef = ref(null)
const queryRepeatedTasksModalRef = ref(null)
const LoginSuccessModalRef = ref(null)
const checkingTaskModalRef = ref(null)
const finishPackageModal = ref(null)
const loading = ref(false)
const message = useMessage()
const totalCount = ref(0)
const loadref = ref(true)
const sortBy: PictureSortParam = {
orderbyname: 'desc',
orderbyvalue: 'pictureResult',
}
const imageRef = ref<ComponentElRef | null>()
const checkDuplicateNo = ref('')
const checkTaskStatus = ref(null) // 1.执行中 2.执行完毕 3.执行失败
const isRefresh = ref(true) // 生成任务包前,点击刷新数据,并将【一键查重】切换按钮为【生成任务包】
const bgLoadingImg = ref(bgLoading)
const cols = ref(7)
let canloadMore = true
let filterId = null
async function computeListHeight() {
const headEl = document.querySelector('.wrapper-content')!
const { bottomIncludeBody } = getViewportOffset(headEl)
const height = bottomIncludeBody
deviceHeight.value = height - 40 - 16 - 24
}
useWindowSizeFn(computeListHeight)
const listStyle = computed(() => {
return {
height: `${deviceHeight.value}px`,
}
})
async function inittask() {
const result = await getLastCheckNox()
console.log(result.data)
if (result.data) {
const res = await getCheckDuplicateStatusx({ checkDuplicateNo: result.data })
if (res.data.status == 1) {
const checkingTaskModal = checkingTaskModalRef.value as any
checkingTaskModal.showModal()
loadref.value = false
}
checkTaskStatus.value = res.data.status // 1.执行中 2.执行完毕
}
}
// const layout = debounce(() => {
// if (masonryRef.value == null || el.value == null)
// return
// if (_masonry !== null)
// (_masonry as any).addItems()
// _masonry = new Masonry(masonryRef.value as any, {
// itemSelector: '.grid-item',
// gutter: 17,
// columnWidth: 182,
// percentPosition: true,
// stagger: 10,
// })
// loading.value = false
// // 暂时注释,请忽略
// _imagesload = imagesloaded('.grid-item')
// _imagesload.on('done', (instance) => {
// (_masonry as any).layout()
// if (!el.value)
// return
// loading.value = false
// })
// _imagesload.on('fail', (instance) => {
// message.error('图片错误')
// loading.value = false
// })
// }, 300)
useInfiniteScroll(
el as any,
() => {
loadMore()
},
{ distance: 10, canLoadMore: () => canloadMore },
)
onBeforeMount(async () => {
const screenWidth = window.screen.width
if (screenWidth > 1920 && screenWidth <= 2560)
cols.value = 11
else if (screenWidth <= 1920 && screenWidth >= 1792)
cols.value = 7
else if (screenWidth < 1792 && screenWidth > 1440)
cols.value = 6
else if (screenWidth <= 1440)
cols.value = 5
})
const timeRange = ref('')
const timeLabel = computed(() => {
const item = timeOptions.find((option) => {
return option.value === timeRange.value
})
return item?.label
})
const isAllowDownload = ref(true)
const calNum = ref(0)
const searchValue = ref('')
const isInitSeaerch = ref(false) // 是否初始化搜索
const collapse = ref(1)
nextTick(() => {
configStore.$subscribe(() => {
// console.log('subscribe', 'configStore')
isAllowDownload.value = configStore.isAllowDownload
calNum.value = configStore.getTimeNum
// console.log("calNum.value----------", calNum.value);
searchValue.value = configStore.getSearchValue
// console.log(configStore.getSearchValue, 'getSearchValue')
collapse.value = configStore.getCollapse ? 1 : 0
// console.log('collapse.value的这个肚饿的点点滴滴的点点滴滴', collapse.value)
})
})
watch(() => collapse.value, (newVal, oldVal) => {
// let val = newVal ? 1 : 0;
if (newVal == 1) {
const screenWidth = window.screen.width
if (screenWidth > 1920 && screenWidth <= 2560)
cols.value = 12
else if (screenWidth <= 1920 && screenWidth >= 1792)
cols.value = 8
else if (screenWidth < 1792 && screenWidth > 1440)
cols.value = 7
else if (screenWidth <= 1440)
cols.value = 6
}
else if (newVal == 0) {
const screenWidth = window.screen.width
if (screenWidth > 1920 && screenWidth <= 2560)
cols.value = 11
else if (screenWidth <= 1920 && screenWidth >= 1792)
cols.value = 7
else if (screenWidth < 1792 && screenWidth > 1440)
cols.value = 6
else if (screenWidth <= 1440)
cols.value = 5
}
})
const asideLoadMore = debounce(() => {
console.log('加载了这里首页的数据--------------------')
loadMore()
}, 400)
watch(
() => searchValue.value,
async (newVal, oldVal) => {
// console.log('首页搜索框的值', newVal, oldVal)
if (newVal) {
isInitSeaerch.value = true
// pagination.pageNo = 0
// const more = await featchList()
// listData.value = more
// isInitSeaerch.value = false
// // configStore.setSearchValue("");
reset()
// loadMore()
asideLoadMore()
isInitSeaerch.value = false
}
// else {
// isInitSeaerch.value = true
// // pagination.pageNo = 0
// // const more = await featchList()
// // listData.value = more
// // isInitSeaerch.value = false
// reset()
// loadMore()
// isInitSeaerch.value = false
// }
},
)
watch(
() => configStore.getAsideValue,
async (newVal, oldVal) => {
// console.log('configStore.getAsideValue首页变化了啊', newVal, oldVal, Object.entries(newVal).toString() === Object.entries(oldVal).toString(), pagination.pageNo)
if (pagination.pageNo > 1 && newVal && !(Object.entries(newVal).toString() === Object.entries(oldVal).toString())) {
reset()
asideLoadMore()
}
},
)
async function featchList(userSearchId?: string) {
loading.value = true
try {
const contentParams = {
search_month: timeRange.value,
search_history: 0,
userSearchId,
}
pagination.pageNo += 1
const searchValue = configStore.getSearchValue // rao
const asideParams = unref(configStore.getAsideValue)
const params = filterId ? { userSearchId: filterId } : cloneDeep(asideParams)
let result = {
pageCount: 0,
data: [],
total: 0,
current: 1,
}
const sortObj: any = {} // rao start
if (sortBy.orderbyvalue == 'pictureResult')
sortObj.ordertype = sortBy.orderbyname
else if (sortBy.orderbyvalue == 'fromuptime')
sortObj.orderByTime = sortBy.orderbyname
if (params.izsimilarity && typeof params.izsimilarity != 'string')
params.izsimilarity = params.izsimilarity.join('-')
// rao end
if (checkTaskStatus.value === 2 && isRefresh) {
result = await queryPageListByCheckNo({
...pagination,
...contentParams,
...params,
...sortObj,
checkDuplicateNo: checkDuplicateNo.value,
upUserName: searchValue,
})
}
else {
result = await getPictureList({
...pagination,
...contentParams,
...params,
...sortObj,
upUserName: searchValue,
})
}
const { data, pageCount, total, current } = result
pagination.pageNo = current || 1
totalCount.value = total
canloadMore = pageCount >= pagination.pageNo && pageCount > 0
const list = data.map((item) => {
return {
id: item.id,
calHeight: 0,
imgUrl: item.imgurl,
thumburl: item.serverThumbnailUrl,
upname: item.upname,
ocrPictureclass: item.ocrPictureclass,
uphead: item.uphead,
similar: item.similarityscore || -1,
imgName: item.imgname,
states: item.states,
isRepeatHis: item.isRepeatHis,
wide: item.wide,
high: item.high,
loadOver: false,
}
})
loading.value = false
return list
}
catch (error) {
canloadMore = false
loading.value = false
return []
}
}
async function loadMore() {
console.log('loadMore加兹安pagination.pageNo----------------', pagination.pageNo)
if (loading.value || el.value == null)
return
canloadMore = false
const more = await featchList()
more.forEach((item) => {
item.calHeight = 182 * item.high / item.wide
})
console.log('loadMore加载后的pagination.pageNo----------------', pagination.pageNo)
// listData.value.push(...more)
if (pagination.pageNo <= 1) {
listData.value = []
listData.value = listData.value.concat(more)
console.log('listData.value首页出来了1111', listData.value)
waterfallRef.value?.resize()
if (listData.value.length == 0)
waterfallRef.value?.clear()
}
else {
listData.value = listData.value.concat(more)
console.log('listData.value首页出来了2222', listData.value)
waterfallRef.value?.mix()
}
}
const gridHeight = computed(() => {
let height = 0
if (viewMode.value === 'masonry')
height = 0
else if (viewMode.value === 'horizontalVersion')
height = 145
else if (viewMode.value === 'verticalVersion')
height = 300
else if (viewMode.value === '3:4')
height = 240
return height
})
watch(() => viewMode.value, (newVal, oldVal) => {
reset()
loadMore()
})
const gridMinHeight = computed(() => {
let height = ''
if (viewMode.value === 'masonry' && loading.value)
height = '145px'
else
height = ''
return height
})
async function oneCheck() {
loadref.value = false
const asideVal = cloneDeep(configStore.getAsideValue)
asideVal.upUserName = searchValue.value
if (asideVal.izyear && asideVal.izyear.length == 2) {
asideVal.izyear = `${dayjs(asideVal.izyear[0]).format('YYYY/MM/DD')}-${dayjs(
asideVal.izyear[1],
).format('YYYY/MM/DD')}`
}
if (asideVal.izsimilarity && typeof asideVal.izsimilarity != 'string')
asideVal.izsimilarity = asideVal.izsimilarity.join('-')
const tasksLoadingModal = queryRepeatedTasksModalRef.value as any
if (calNum.value == 0 && isRefresh.value) {
if (timer.value)
clearInterval(timer.value)
timer.value = setInterval(() => {
if (checkDuplicateNo.value) {
getCheckDuplicateStatus(checkDuplicateNo.value).then((res) => {
if (res.code === 'OK') {
checkTaskStatus.value = res.data.status
if (calNum.value < 90)
calNum.value = calNum.value + 10
configStore.setTimeNum(calNum.value)
if (checkTaskStatus.value === 2 || checkTaskStatus.value === 3) {
if (checkTaskStatus.value === 2) {
message.success('任务执行完毕,正在刷新数据...')
loadref.value = true
}
else {
message.error('查询异常')
loadref.value = true
}
reset()
loadMore()
tasksLoadingModal.closeOnlyModal()
configStore.setTimeNum(100)
if (timer.value)
clearInterval(timer.value)
setTimeout(() => {
configStore.setTimeNum(0)
}, 1000)
// reset()
// loadMore()
}
// waterfallRef.value?.resize()
}
else {
if (timer.value)
clearInterval(timer.value)
}
})
}
}, 1000)
}
// 查重任务编号,状态不为空,或者状态执行中..
if (checkDuplicateNo.value && checkTaskStatus.value && checkTaskStatus.value === 1) {
// 暂时rao
tasksLoadingModal.showModal()
return
}
// 调用查重任务开启流程
oneClickCheckTaskPackage(asideVal).then((res) => {
if (res.code === 'OK') {
checkDuplicateNo.value = res.data.checkDuplicateNo
checkTaskStatus.value = res.data.status
tasksLoadingModal.showModal() // 暂时rao
}
else {
message.error(res.message || '查重失败')
}
})
}
/**
* 显示添加任务包
*/
async function showAddPackage() {
const modal = packageModalRef.value as any
modal.showModal()
}
// 查重任务加载提示框,关闭回调
async function tasksLoadingCloseCallback() {
const checkingTaskModal = checkingTaskModalRef.value as any
checkingTaskModal.showModal()
if (isRefresh.value)
refresh(true)
}
async function showLoginSuccessModal() {
const modal = LoginSuccessModalRef.value as any
modal.showModal()
}
async function closeLoginSuccessModal() {
const modal = LoginSuccessModalRef.value as any
modal.closeModal()
}
// 包id
const packageIdRef = ref('')
async function commitHandler(settingParam) {
const params = {
name: settingParam.packagename,
checkDuplicateNo: checkDuplicateNo.value,
}
const modal = generateModalRef.value as any
const finishModal = finishPackageModal.value as any
modal.showModal()
createPackage(params).then((res) => {
if (res.code === 'OK') {
message.success(res.message)
packageIdRef.value = res.data.id
modal.closeModal()
finishModal.showModal()
// 清空查重任务状态和编号
checkDuplicateNo.value = ''
checkTaskStatus.value = null
}
})
const asideVal = configStore.getAsideValue
const finalParam = { ...asideVal }
finalParam.buessinessno = settingParam.packagename
finalParam.search_history = settingParam.comparehistory ? 1 : 0
waterfallRef.value?.resize()
}
onMounted(() => {
inittask()
emitter.on('filter', refreshHandler)
// emitter.on("filter", (searchId)=>{
// console.log("emitter on filter" + searchId)
// reset()
// featchList(searchId)
// })
// 一件键重——获取任务编号
getLastCheckNo().then((res) => {
if (res.code === 'OK')
checkDuplicateNo.value = res.data
})
nextTick(() => {
computeListHeight()
// 登录后展示成功
// showLoginSuccessModal()
})
})
onUnmounted(() => {
emitter.off('filter', refreshHandler)
clearInterval(timer.value)
})
watch(timeRange, () => {
refreshHandler()
})
watch(
() => configStore.asideValue,
(newVal, oldVal) => {
refreshHandler()
},
{ deep: true },
)
function reset(val?) {
pagination.pageNo = 0
pagination.pageSize = 30
listData.value = []
listData.value.length = 0
loading.value = false
canloadMore = true
filterId = null
// layout()
}
async function refreshHandler(filtersearchId?: any) {
reset(true)
if (filtersearchId)
filterId = filtersearchId
// console.log("执行了这里首页的数据");
// loadMore()
asideLoadMore()
// nextTick(() => {
// setTimeout(() => {
// useInfiniteScroll(
// el as any,
// () => {
// loadMore()
// },
// { distance: 10, canLoadMore: () => canloadMore },
// )
// }, 300)
// })
}
function getAvatar(url: string): string {
return url ? getImgUrl(url) : avatar
}
function sortHandler(orderby: 'pictureResult' | 'fromuptime') {
sortBy.orderbyvalue = orderby
sortBy.orderbyname = sortBy.orderbyname === 'asc' ? 'desc' : 'asc'
refreshHandler()
}
async function downloadImage(item) {
if (!item.imgUrl) {
message.error('请输入有效的图片链接地址')
return
}
try {
// 使用 fetch 发送 GET 请求获取图片
const response = await fetch(item.imgUrl, {
method: 'GET',
mode: 'cors', // 确保跨域请求被正确处理
cache: 'default',
})
// 检查响应状态码,确保请求成功
if (!response.ok)
throw new Error(`HTTP error! Status: ${response.status}`)
// 读取 Blob 数据
const blob = await response.blob()
// 创建一个新的a标签
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = item.imgName // 设置下载的文件名
link.style.display = 'none' // 隐藏链接
// 将链接添加到 DOM 中
document.body.appendChild(link)
// 触发下载
link.click()
// 清理
URL.revokeObjectURL(link.href)
document.body.removeChild(link)
}
catch (error) {
console.error('下载图片时发生错误:', error)
}
}
function previewHandler(index: number, event: MouseEvent, thumburl, imgUrl) {
event.stopImmediatePropagation()
event.stopPropagation()
if (imageRef.value?.[index] && (imageRef.value[index] as any).src)
// (imageRef.value?.[index] as any).mergedOnClick()
previewImageUrl(thumburl, imgUrl);
(imageRef.value?.[index] as any).click()
}
/**
* 检查查重状态
*/
function refresh(val?: any) {
// delete asideVal.izsimilarity
const checkingTaskModal = checkingTaskModalRef.value as any
if (checkDuplicateNo.value) {
getCheckDuplicateStatus(checkDuplicateNo.value).then((res) => {
if (res.code === 'OK') {
checkTaskStatus.value = res.data.status // 1.执行中 2.执行完毕
// if (isRefresh.value === false) {
// const modal = packageModalRef.value as any
// modal.showModal()
// return
// }
if (
(checkTaskStatus.value === 2 || checkTaskStatus.value === 3)
&& isRefresh.value
) {
configStore.setTimeNum(100)
checkingTaskModal.closeModal()
isRefresh.value = false
if (checkTaskStatus.value === 2) {
message.success('任务执行完毕,正在刷新数据...')
loadref.value = true
}
else {
message.error('查询异常')
loadref.value = true
}
reset()
loadMore()
if (timer.value)
clearInterval(timer.value)
// reset()
// loadMore()
configStore.setTimeNum(0)
}
// waterfallRef.value?.resize()
// else if (checkTaskStatus.value === 1) {
// return;
// }
}
})
}
}
/**
* 取消查重任务
*/
function cancel() {
if (checkTaskStatus.value === 1) {
removeCheckDuplicate(checkDuplicateNo.value).then((res) => {
if (res.code === 'OK') {
checkDuplicateNo.value = ''
checkTaskStatus.value = null
message.success('查重任务取消成功')
loadref.value = true
}
})
}
}
function renderIcon(icon: Component) {
return () => {
return h(NIcon, null, {
default: () => h(icon),
})
}
}
const dropdownOptions = ref([
{
label: '导入任务数据',
key: 'profile',
icon: renderIcon(UploadIcon),
},
{
label: '导出任务数据',
key: 'editProfile',
icon: renderIcon(DownloadIcon),
},
{
label: '查看导入记录',
key: 'logout',
icon: renderIcon(EyeOutlineIcon),
},
])
function finish() {
// console.log('finish')
}
const masonryRef = ref(null)
function loadImg(src) {
const promise = new Promise((resolve, reject) => {
const img = document.createElement('img')
img.src = src
img.onload = function () {
resolve(src)
}
img.onerror = function () {
reject(new Error('加载失败'))
}
})
return promise
}
function previewImageUrl(img1, img2) {
setTimeout(async () => {
const img = document.getElementsByClassName('n-image-preview').item(0)
await loadImg(img2)
img.src = img2
}, 50)
}
defineExpose({
showLoginSuccessModal,
closeLoginSuccessModal,
})
</script>
<template>
<div class="wrapper">
<div class="wrapper-header">
<div class="left">
<SvgIcon size="32" name="magnifying" />
<span class="font" style="margin-left: 19.2px">AI一键查重</span>
</div>
<div class="flex-btn-icon">
<SvgIcon
v-show="checkTaskStatus !== 2 && loadref"
style="cursor: pointer"
size="105"
name="yijianchachong"
@click="oneCheck"
/>
<SvgIcon
v-show="checkTaskStatus !== 2 && !loadref"
style="cursor:not-allowed"
size="105"
name="yijianchachong"
/>
<SvgIcon
v-show="checkTaskStatus === 2"
style="cursor: pointer"
size="105"
name="shengchengrenwubao"
@click="showAddPackage"
/>
<n-dropdown trigger="hover" placement="bottom-start" :options="dropdownOptions">
<span class="expand-icon">
<Icon style="position: relative; top: 2px">
<EllipsisHorizontal style="color: #507afd" />
</Icon>
</span>
</n-dropdown>
</div>
</div>
<div class="wrapper-content">
<div style="display: flex; justify-content: space-between">
<div class="form">
<n-popselect v-model:value="viewMode" :options="viewOptions" trigger="click">
<div class="dropdown">
<span>视图</span>
<SvgIcon class="gap" name="arrow-botton" size="16" />
</div>
</n-popselect>
<div
style="margin-left: 15px; cursor: pointer; color: #323233"
@click="sortHandler('fromuptime')"
>
<span>时间排序</span>
<SvgIcon style="margin-left: 8px" name="sort" size="16" />
</div>
<div
style="margin-left: 15px; cursor: pointer; color: #323233"
@click="sortHandler('pictureResult')"
>
<span>相似度排序</span>
<SvgIcon style="margin-left: 8px" name="sort" size="16" />
</div>
</div>
<span style="font-size: 16px; color: #333">共
<span style="color: #507afd; font-weight: 500">{{ totalCount }}</span> 项</span>
</div>
<n-spin :show="loading">
<div v-if="viewMode != 'masonry'" ref="el" class="scroll" :style="listStyle">
<div class="grid" :style="{ 'grid-template-columns': `repeat(${cols}, 182px)` }">
<div
v-for="(item, index) in listData"
:key="item.id"
:style="{ height: `${gridHeight}px` }"
class="grid-item"
>
<img
:key="item.id"
v-lazy="item.thumburl"
:class="{
'img-fit': viewMode === 'horizontalVersion',
'img-full': viewMode === '3:4' || viewMode === 'verticalVersion',
}"
:style="{ height: `${viewMode === 'masonry' ? `${item.calHeight}px` : `${gridHeight - 25}px`}` }"
class="img"
alt="加载错误"
>
<n-image
ref="imageRef"
:key="item.imgUrl"
class="img"
:img-props="{
onClick: ($event) => {
hideDownload($event);
showClose = true;
},
}"
:class="{
'img-fit': viewMode === 'horizontalVersion',
'img-full': viewMode === '3:4' || viewMode === 'verticalVersion',
}"
:preview-src="item.thumburl"
:src="item.thumburl"
:fallback-src="bgLoadingImg"
:style="{ backgroundImage: `url(${loading ? bgLoadingImg : 'none'})` }"
style="display: none"
/>
<img
v-if="item.states == 3"
class="tag-status"
src="@/assets/images/task/tag-pass.png"
alt=""
>
<img
v-if="item.states == 5"
class="tag-status"
src="@/assets/images/task/tag-not-pass.png"
alt=""
>
<img
v-if="(item.states != 3 && item.states != 5) && item.isRepeatHis"
class="tag-status"
src="@/assets/images/reset.png"
alt=""
>
<img
v-if="(item.states == 3 || item.states == 5) && item.isRepeatHis"
style="top:35px"
class="tag-status"
src="@/assets/images/reset.png"
alt=""
>
<div v-if="item.similar != -1" class="percent">
<SvgIcon size="42" :name="item.similar == 100 ? 'error_tag' : 'tag'" />
<div class="val">
{{ `${item.similar}%` }}
</div>
</div>
<div v-if="isAllowDownload" class="glass">
<SvgIcon
size="16"
name="download"
style="margin-top: -6px; cursor: pointer"
@click="downloadImage(item)"
/>
</div>
<div class="info">
<div class="footer">
<div class="img-name">
<n-tooltip trigger="hover">
<template #trigger>
<span>{{ item.imgName }}</span>
</template>
<span
style="font-size: 12px; margin-top: 4px; margin-bottom: 16px"
>{{ item.imgName }}</span>
</n-tooltip>
</div>
<div
class="icon-wrap"
@click="
($event) => {
previewHandler(index, $event, item.thumburl, item.imgUrl);
hideDownload($event);
}
"
>
<SvgIcon
size="13"
name="magnifying-2"
style="cursor: pointer; color: #898481"
/>
</div>
</div>
<div class="left">
<n-avatar
:src="(item.uphead && getAvatar(item.uphead)) || defaultAvatar"
class="avatar"
round
/>
<span>{{ item.upname }}</span>
</div>
</div>
</div>
</div>
</div>
<div v-if="viewMode === 'masonry'" ref="el" class="scroll" :style="listStyle">
<waterfall ref="waterfallRef" :col="cols" :data="listData" :gutter-width="10" @finish="finish">
<div
v-for="(item, index) in listData"
:key="item.id"
class="grid-item"
>
<img
:key="item.id"
v-lazy="item.thumburl"
:class="{
'img-fit': viewMode === 'horizontalVersion',
'img-full': viewMode === '3:4' || viewMode === 'verticalVersion',
}"
:style="{ height: `${item.calHeight}px` }"
class="img"
alt="加载错误"
>
<n-image
ref="imageRef"
:key="item.imgUrl"
class="img"
:img-props="{
onClick: ($event) => {
hideDownload($event);
showClose = true;
},
}"
:class="{
'img-fit': viewMode === 'horizontalVersion',
'img-full': viewMode === '3:4' || viewMode === 'verticalVersion',
}"
:preview-src="item.thumburl"
:src="item.thumburl"
:fallback-src="bgLoadingImg"
:style="{ backgroundImage: `url(${loading ? bgLoadingImg : 'none'})` }"
style="display: none"
/>
<img
v-if="item.states == 3"
class="tag-status"
src="@/assets/images/task/tag-pass.png"
alt=""
>
<img
v-if="item.states == 5"
class="tag-status"
src="@/assets/images/task/tag-not-pass.png"
alt=""
>
<img
v-if="(item.states != 3 && item.states != 5) && item.isRepeatHis"
class="tag-status"
src="@/assets/images/reset.png"
alt=""
>
<img
v-if="(item.states == 3 || item.states == 5) && item.isRepeatHis"
style="top:35px"
class="tag-status"
src="@/assets/images/reset.png"
alt=""
>
<div v-if="item.similar != -1" class="percent">
<SvgIcon size="42" :name="item.similar == 100 ? 'error_tag' : 'tag'" />
<div class="val">
{{ `${item.similar}%` }}
</div>
</div>
<div v-if="isAllowDownload" class="glass">
<SvgIcon
size="16"
name="download"
style="margin-top: -6px; cursor: pointer"
@click="downloadImage(item)"
/>
</div>
<div class="info">
<div class="footer">
<div class="img-name">
<n-tooltip trigger="hover">
<template #trigger>
<span>{{ item.imgName }}</span>
</template>
<span
style="font-size: 12px; margin-top: 4px; margin-bottom: 16px"
>{{ item.imgName }}</span>
</n-tooltip>
</div>
<div
class="icon-wrap"
@click="
($event) => {
previewHandler(index, $event, item.thumburl, item.imgUrl);
hideDownload($event);
}
"
>
<SvgIcon
size="13"
name="magnifying-2"
style="cursor: pointer; color: #898481"
/>
</div>
</div>
<div class="left">
<n-avatar
:src="(item.uphead && getAvatar(item.uphead)) || defaultAvatar"
class="avatar"
round
/>
<span>{{ item.upname }}</span>
</div>
</div>
</div>
</waterfall>
</div>
</n-spin>
</div>
<PackageSettingsModal ref="packageModalRef" @commit="commitHandler" />
<GeneratePackageModal ref="generateModalRef" />
<QueryRepeatedTasksModal
ref="queryRepeatedTasksModalRef"
@close-callback="tasksLoadingCloseCallback"
/>
<LoginSuccessModal ref="LoginSuccessModalRef" />
<FinishPackageModal :id="packageIdRef" ref="finishPackageModal" />
<CheckingTaskModal ref="checkingTaskModalRef" @refresh="refresh" @cancel="cancel" />
<!-- <div class="close_box" v-show="showClose" @click="showClose = false">
<SvgIcon size="24" name="close-none-border" />
</div> -->
</div>
</template>
<style lang="less" scoped>
.wrapper {
display: flex;
flex: 1;
flex-direction: column;
box-sizing: border-box;
margin-left: 16px;
margin-right: 16px;
width: 100%;
::v-deep(.vue-waterfall) {
padding-top: 2px;
}
&-header {
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
padding: 0px 16px 0px 15px;
box-sizing: border-box;
height: 64px;
border: 1px solid rgb(239, 239, 245);
border-radius: 3px;
width: 100%;
.left {
display: flex;
align-items: center;
}
.font {
font-size: 18px;
font-weight: bold;
color: #333333;
line-height: 25px;
width: 89px;
height: 25px;
white-space: nowrap;
// margin-left: 12px;
}
}
&-content {
flex: 1;
padding: 16px 16px 0px 16px;
margin-top: 16px;
background: #fff;
border-radius: 3px;
border: 1px solid rgb(239, 239, 245);
.form {
display: flex;
align-items: center;
font-size: 14px;
padding-bottom: 16px;
}
.grid{
grid-gap: 10px 10px;
}
.img {
width: 182px;
max-width: 182px;
border-radius: 7px;
display: block;
opacity: 1 !important;
// height: calc(100% - 25px);
}
.img-fit {
width: 182px;
overflow: hidden;
}
.img-full {
width: 182px;
overflow: hidden;
::v-deep(img) {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.info {
display: flex;
justify-content: space-between;
margin-top: 4px;
position: relative;
color: #666666;
font-size: 16px;
.left {
display: flex;
align-items: center;
}
.right {
display: flex;
align-items: center;
}
.avatar {
width: 20px;
height: 20px;
margin-right: 5px;
}
}
.dropdown {
display: flex;
flex-direction: row;
align-items: center;
margin-right: 24px;
color: #323233;
.gap {
margin-left: 5px;
}
}
.grid-item {
width: 182px;
border-radius: 7px;
margin-bottom: 10px;
overflow-x: visible;
position: relative;
transition: 0.5s;
margin: 0 6px 10px 6px;
.tag-status {
width: 46px;
height: 22px;
position: absolute;
left: -3px;
top: 10px;
}
.glass {
position: absolute;
display: none !important;
background-color: #fff;
border-radius: 6px;
padding: 3px;
top: 10px;
right: 10px;
width: 22px;
height: 22px;
}
.footer {
display: none;
width: 100%;
height: 40px;
position: absolute;
top: -45px;
justify-content: space-between;
align-items: center;
padding: 0 10px;
background: rgba(0, 0, 0, 0.35);
border-radius: 7px;
.img-name {
// width: 70%;
font-size: 12px;
width: 115px;
height: 20px;
color: #fff;
/* 设置文本溢出时的样式为省略号 */
text-overflow: ellipsis;
/* 隐藏超出容器的文本 */
overflow: hidden;
/* 确保文本不换行 */
white-space: nowrap;
}
.icon-wrap {
width: 22px;
height: 22px;
line-height: 22px;
text-align: center;
background-color: #fff;
border-radius: 6px;
}
}
&:hover {
.percent {
display: none;
}
.glass {
display: block !important;
}
.info .footer {
display: flex;
}
}
}
.percent {
position: absolute;
text-align: center;
z-index: 3;
right: 10px;
top: -8px;
color: #fff;
.val {
position: absolute;
left: 0;
top: 0;
display: block;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-family: PingFang SC, PingFang SC-Semibold;
font-weight: Semibold;
text-align: left;
color: #ffffff;
line-height: 24px;
}
}
.scroll {
overflow-y: scroll;
// 滚动条整体部分
&::-webkit-scrollbar {
width: 6px;
height: 6px;
background: #f1f1f1;
visibility: hidden;
}
/* 定义滚动条轨道的样式 */
&::-webkit-scrollbar-track {
background-color: #e1e1e1;
border-radius: 10px; /* 圆角 */
visibility: hidden;
}
&:hover {
&::-webkit-scrollbar {
visibility: visible;
}
&::-webkit-scrollbar-track {
visibility: visible;
}
&::-webkit-scrollbar-thumb {
visibility: visible;
}
}
/* 定义滚动条滑块的样式 */
&::-webkit-scrollbar-thumb {
background-color: #888;
border-radius: 10px; /* 圆角 */
visibility: hidden;
}
/* 滑块hover时的样式 */
&::-webkit-scrollbar-thumb:hover {
background-color: #555;
}
}
}
.flex-btn-icon {
display: flex;
align-items: center;
}
.expand-icon {
display: block;
margin-left: 10px;
width: 30px;
height: 30px;
line-height: 30px;
background: linear-gradient(
144deg,
rgba(115, 167, 249, 0.1) 0%,
rgba(62, 110, 241, 0.1) 96%
);
border-radius: 8px;
text-align: center;
}
}
.close_box {
position: absolute;
right: 10%;
top: 20%;
z-index: 10000000000000000000;
}
</style>