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

455 lines
11 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 { useInfiniteScroll } from '@vueuse/core'
import imagesloaded from 'imagesloaded'
import { debounce } from 'lodash-es'
import Masonry from 'masonry-layout'
import { useMessage } from 'naive-ui'
import { computed, nextTick, onBeforeMount, onMounted, onUnmounted, onUpdated, reactive, ref, unref, watch } from 'vue'
import GeneratePackageModal from './modal/GeneratePackageModal.vue'
import PackageSettingsModal from './modal/PackageSettingsModal.vue'
import LoginSuccessModal from './modal/LoginSuccessModal.vue'
import { getPictureList, oneClickCheck } from '@/api/home/main'
import img1 from '@/assets/images/1.jpg'
import img2 from '@/assets/images/2.jpg'
import img3 from '@/assets/images/3.jpg'
import img4 from '@/assets/images/4.jpg'
import img5 from '@/assets/images/5.jpg'
import avatar from '@/assets/images/avatar.jpg'
import { timeOptions, viewOptions } from '@/config/home'
import { useWindowSizeFn } from '@/hooks/event/useWindowSizeFn'
import { useConfig } from '@/store/modules/asideConfig'
import { getViewportOffset } from '@/utils/domUtils'
import { hideDownload } from '@/utils/image'
import { randomInt } from '@/utils/index'
import emitter from '@/utils/mitt'
import { getImgUrl } from '@/utils/urlUtils'
const deviceHeight = ref(600)
let _masonry: null | Masonry = null
let _imagesload: any
const masonryRef = ref<ComponentRef>(null)
const el = ref<HTMLDivElement | null>(null)
const viewMode = ref('masonry')
const pagination = reactive({
pageNo: 0,
pageSize: 30,
})
const configStore = useConfig()
const packageModalRef = ref(null)
const generateModalRef = ref(null)
const LoginSuccessModalRef = ref(null)
const loading = ref(false)
const message = useMessage()
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`,
}
})
const layout = debounce(() => {
if (masonryRef.value == null || el.value == null)
return
if (_masonry !== null)
(_masonry as any).destroy()
_masonry = new Masonry(masonryRef.value as any, {
itemSelector: '.grid-item',
gutter: 18,
columnWidth: 182,
percentPosition: true,
stagger: 10,
})
_imagesload = imagesloaded('.grid-item')
// _imagesload.on('always', (instance) => {
// console.log('always')
// })
_imagesload.on('done', (instance) => {
(_masonry as any).layout()
if (!el.value)
return
const scrollHeight = el.value!.scrollHeight
const clientHeight = el.value!.clientHeight
const top = scrollHeight - clientHeight - 20
el.value!.scrollTo({ top, behavior: 'instant' })
loading.value = false
})
_imagesload.on('fail', (instance) => {
message.error('图片错误')
loading.value = false
})
// imagesloaded('.grid-item', () => {
// (_masonry as any).layout()
// const scrollHeight = el.value!.scrollHeight
// const clientHeight = el.value!.clientHeight
// const top = scrollHeight - clientHeight - 20
// el.value!.scrollTo({ top, behavior: 'instant' })
// loading.value = false
// })
}, 300)
let canloadMore = true
useInfiniteScroll(
el as any,
() => {
loadMore()
},
{ distance: 10, canLoadMore: () => canloadMore },
)
onUpdated(() => {
layout()
})
const timeRange = ref('99')
const timeLabel = computed(() => {
const item = timeOptions.find((option) => {
return option.value === timeRange.value
})
return item?.label
})
const viewLabel = computed(() => {
const item = viewOptions.find((option) => {
return option.value === viewMode.value
})
return item?.label
})
const listData = ref<any[]>([])
const urls = [
img1,
img2,
img3,
img4,
img5,
]
function randomUrl() {
const index = randomInt(0, urls.length)
return urls[index]
}
async function featchList() {
loading.value = true
try {
const contentParams = {
search_month: timeRange.value,
search_history: 0,
}
pagination.pageNo += 1
const result = await getPictureList({ ...pagination, ...contentParams })
// TODO测试数据
// result.data = Array.from({ length: 30 })
// result.pageCount = 1
const { data, pageCount } = result
canloadMore = pageCount >= pagination.pageNo && pageCount > 0
const list = data.map((item) => {
return {
imgUrl: item.imgurl,
upname: item.upname,
ocrPictureclass: item.ocrPictureclass,
uphead: item.uphead,
}
})
return list
}
catch (error) {
canloadMore = false
return []
}
}
async function loadMore() {
if (loading.value || el.value == null)
return
const more = await featchList()
listData.value.push(...more)
}
// onBeforeMount(async () => {
// const list = await featchList()
// listData.value = list
// })
const gridHeight = computed(() => {
return viewMode.value !== 'masonry' ? '145px' : ''
})
async function oneCheck() {
const modal = packageModalRef.value as any
modal.showModal()
}
async function showLoginSuccessModal() {
const modal = LoginSuccessModalRef.value as any
// modal.showModal()
}
async function commitHandler(settingParam) {
const contentParams = {
search_month: timeRange.value,
}
const asideVal = configStore.getAsideValue
const finalParam = { ...contentParams, ...asideVal }
finalParam.buessinessno = settingParam.packagename
finalParam.search_history = settingParam.comparehistory ? 1 : 0
// TODO添加重复标识参数
const modal = generateModalRef.value as any
modal.showModal()
oneClickCheck(finalParam).then(() => {
modal.closeModal()
}, () => {
modal.closeModal()
})
}
onMounted(() => {
emitter.on('filter', refreshHandler)
nextTick(() => {
computeListHeight()
// 登录后展示成功
showLoginSuccessModal()
})
})
onUnmounted(() => {
emitter.off('filter', refreshHandler)
})
watch(timeRange, () => {
refreshHandler()
})
watch(() => configStore.asideValue, (newVal, oldVal) => {
refreshHandler()
}, { deep: true })
async function refreshHandler() {
pagination.pageNo = 0
pagination.pageSize = 30
listData.value.length = 0
loading.value = true
canloadMore = true
const contentParams = {
search_month: timeRange.value,
search_history: 0,
}
const filterParams = unref(configStore.getAsideValue)
const result = await getPictureList({ ...pagination, ...contentParams, ...filterParams })
const { data, pageCount } = result
pagination.pageNo += 1
canloadMore = pageCount >= pagination.pageNo
const list = data.map((item) => {
return {
imgUrl: item.imgurl,
upname: item.upname,
ocrPictureclass: item.ocrPictureclass,
uphead: item.uphead,
}
})
listData.value = list
}
function getAvatar(url: string): string {
return url ? getImgUrl(url) : avatar
}
</script>
<template>
<div class="wrapper">
<div class="wrapper-header">
<div class="wrapper-header-left">
<SvgIcon size="32" name="magnifying" />
<span class="wrapper-header-font">AI一键查重</span>
</div>
<SvgIcon style="cursor: pointer;" size="105" name="yijianchachong" @click="oneCheck" />
</div>
<div class="wrapper-content">
<div class="wrapper-content-form">
<n-popselect v-model:value="timeRange" :options="timeOptions" trigger="click">
<div class="wrapper-content-dropdown">
<span>{{ timeLabel || '请选择' }}</span>
<SvgIcon class="wrapper-content-dropdown-gap" name="arrow-botton" size="14" />
</div>
</n-popselect>
<n-popselect v-model:value="viewMode" :options="viewOptions" trigger="click">
<div class="wrapper-content-dropdown">
<span>{{ viewLabel || '请选择' }}</span>
<SvgIcon class="wrapper-content-dropdown-gap" name="arrow-botton" size="14" />
</div>
</n-popselect>
</div>
<div ref="el" class="scroll" :style="listStyle">
<!-- <n-scrollbar :on-scroll="scrollHandler"> -->
<n-spin :show="loading">
<div ref="masonryRef" class="grid">
<div v-for="(item, index) in listData" :key="index" :style="{ height: gridHeight }" class="grid-item">
<!-- <div :style="{ 'background-color': randomColor(0.2) }" class="wrapper-content-item-img" /> -->
<!-- <img
class="wrapper-content-item-img" :class="{ 'wrapper-content-item-img-fit': viewMode !== 'masonry' }"
:src="item.imgUrl"
> -->
<n-image class="wrapper-content-item-img" :img-props="{ onClick: hideDownload }" :class="{ 'wrapper-content-item-img-fit': viewMode !== 'masonry' }" :src="item.imgUrl" />
<div class="wrapper-content-item-info">
<div class="wrapper-content-item-info-left">
<n-avatar :src="getAvatar(item.uphead)" class="wrapper-content-item-info-avatar" round />
<span>{{ item.upname }}</span>
</div>
<div class="wrapper-content-item-info-right">
<span :style="{ marginRight: '5px' }"></span>
<span>{{ item.ocrPictureclass?.classname }}</span>
</div>
</div>
</div>
</div>
</n-spin>
<!-- </n-scrollbar> -->
</div>
</div>
<PackageSettingsModal ref="packageModalRef" @commit="commitHandler" />
<GeneratePackageModal ref="generateModalRef" />
<LoginSuccessModal ref="LoginSuccessModalRef" />
</div>
</template>
<style lang="less" scoped>
.wrapper {
display: flex;
flex: 1;
flex-direction: column;
box-sizing: border-box;
margin-left: 16px;
width: 100%;
&-header {
display: flex;
justify-content: space-between;
align-items: center;
background: #FFF;
padding: 0px 15px 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;
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;
}
&-item {
&-img {
border-radius: 7px;
display: block;
height: calc(100% - 20px);
}
&-img-fit {
width: 100%;
object-fit: cover;
}
&-info {
display: flex;
justify-content: space-between;
margin-top: 4px;
&-left {
display: flex;
align-items: center;
}
&-right {
display: flex;
align-items: center;
}
&-avatar {
width: 15px;
height: 15px;
margin-right: 5px;
}
}
}
&-dropdown {
display: flex;
flex-direction: row;
align-items: center;
margin-right: 24px;
&-gap {
margin-left: 5px;
}
}
.grid-item {
width: 182px;
border-radius: 7px;
margin-bottom: 20px;
}
.scroll {
overflow-y: scroll;
}
}
}
</style>