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/layout/components/Header/RecycleModal.vue

1129 lines
30 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 Masonry from 'masonry-layout'
import { useDialog, useMessage } from 'naive-ui'
import { computed, getCurrentInstance, nextTick, onBeforeMount, onMounted, onUpdated, reactive, ref, unref, watch } from 'vue'
import { useInfiniteScroll } from '@vueuse/core'
import imagesloaded from 'imagesloaded'
import { cloneDeep, debounce } from 'lodash-es'
import ConfrimModal from './ConfrimModal.vue'
import { audit, dubiousfilelist, removeFiles } from '@/api/task/task'
import { viewOptions } from '@/config/home'
import NotPassed from '@/components/Approval/NotPassed.vue'
import { formatToDateHMS } from '@/utils/dateUtil'
import { off, on } from '@/utils/domUtils'
import bgLoading from '@/assets/images/bg-loading.png'
const cardStyle = {
'--n-padding-bottom': '40px',
'--n-padding-left': '120px',
}
const bgLoadingImg = ref(bgLoading)
let startTime = 0
let endTime = 0
let startCalTime = false
const message = useMessage()
const timeRange = ref('desc')
const similarRange = ref('desc')
const batch = ref(false)
const imageRef = ref<ComponentElRef | null>()
const filterId = null
const isTop = ref(false)
let sortObj: any = {
orderByUptime: 'desc',
}
const cols = ref(8)
const waterfallRef = ref(null)
const timeOptions = [{
label: '升序',
value: 'asc',
}, {
label: '降序',
value: 'desc',
}]
// const similarOptions = [{
// label: '升序',
// value: 'asc',
// }, {
// label: '降序',
// value: 'desc',
// }]
const timeLabel = computed(() => {
const item = timeOptions.find((option) => {
return option.value === timeRange.value
})
return item?.label
})
const viewMode = ref('horizontalVersion')
const viewLabel = computed(() => {
const item = viewOptions.find((option) => {
return option.value === viewMode.value
})
return item?.label
})
const maxHeight = computed(() => {
let height = '800'
const screenWidth = window.screen.width
if (screenWidth <= 1920)
height = '600'
return `${height}px`
})
const masonryRef = ref<ComponentRef>(null)
const el = ref<HTMLDivElement | null>(null)
const elwc = ref<HTMLDivElement | null>(null)
const listData = ref<any[]>([])
const pagination = reactive({
pageNo: 1,
pageSize: 30,
})
let loading = false
const _masonry: null | Masonry = null
const show = ref(false)
const selectedApproveItems = ref<any[]>([]) // 审核批量选中
const dialog = useDialog()
let processItems: any[] = []
const confrimModalRef = ref<any>()
// const layout = debounce(() => {
// if (!show.value)
// return
// if (_masonry !== null)
// (_masonry as any).destroy()
// _masonry = new Masonry(masonryRef.value as any, {
// itemSelector: '.grid-item',
// columnWidth: 214,
// percentPosition: true,
// stagger: 10,
// })
// imagesloaded('.grid-item', () => {
// (_masonry as any).layout()
// const scrollHeight = el.value!.scrollHeight
// const clientHeight = el.value!.clientHeight
// const top = scrollHeight - clientHeight - 20
// if (isTop.value)
// el.value!.scrollTo({ top: 0, behavior: 'instant' })
// else
// el.value!.scrollTo({ top, behavior: 'instant' })
// // let height = 800 - 15;
// // const screenWidth = window.screen.width;
// // if(screenWidth <= 1920) {
// // height = 600 - 15;
// // }
// // el.value!.scrollTo({ top: height, behavior: 'instant' })
// loading = false
// if (pagination.pageNo == 3) {
// let timer
// if (timer)
// clearTimeout(timer)
// timer = setTimeout(() => {
// isTop.value = false
// }, 1000)
// }
// })
// }, 300)
watch(viewMode, (newVal, oldVal) => {
if (newVal) {
isTop.value = true
reset()
loadMore()
}
// layout()
})
onBeforeMount(async () => {
})
let canloadMore = true
useInfiniteScroll(
el as any,
() => {
// loading = false
loadMore()
},
{ distance: 10, canLoadMore: () => canloadMore },
)
async function featchList() {
loading = true
try {
// const result = await dubiousfilelist({ ...pagination, orderbyname: timeRange.value })
pagination.pageNo += 1
const result = await dubiousfilelist({ ...pagination, ...sortObj })
// TODO测试数据
// result.data = Array.from({ length: 30 })
const { data, pageCount, current } = result
pagination.pageNo = data.current
// canloadMore = pageCount >= pagination.pageNo && pageCount > 0;
canloadMore = data.pages >= pagination.pageNo && data.pages > 0
loading = false
return result.data.records
// const list = data.map((item) => {
// return {
// imgUrl: randomUrl(),
// }
// })
// return list
}
catch (error) {
loading = false
canloadMore = false
return []
}
}
const app = getCurrentInstance()
const updateHtml = debounce(() => {
// app?.appContext.app.$waterfall?.forceUpdate()
waterfallRef.value?.resize()
}, 200)
async function loadMore() {
if (loading || el.value == null)
return
// loading = true
canloadMore = false
// pagination.pageNo = pagination.pageNo + 1
const more = await featchList()
more.forEach((item) => {
item.calHeight = 182 * item.high / item.wide
item.thumburl = item.serverThumbnailUrl || item.imgUrl
})
if (pagination.pageNo <= 1) {
listData.value = []
listData.value = listData.value.concat(more)
if (pagination.pageNo <= 1)
waterfallRef.value?.resize()
}
else {
listData.value = listData.value.concat(more)
// waterfallRef.value?.mix()
updateHtml()
}
// layout()
}
onUpdated(() => {
if (startCalTime) {
endTime = new Date().getTime()
if (endTime - startTime > 300)
createDom()
}
else {
removeDom()
}
nextTick(() => {
setTimeout(() => {
// layout()
}, 50)
})
})
let start: { x: number, y: number } | null = null
let selectionBox: HTMLDivElement | null
const selectIds = ref<string[]>([])
function downHandler(event: MouseEvent) {
startTime = new Date().getTime()
startCalTime = true
if (!selectionBox)
return
const classname = (event.target as any).className
if (!classname.includes('checkbox')) {
selectIds.value.length = 0
start = { x: event.clientX, y: event.clientY }
selectionBox.style.width = '0'
selectionBox.style.height = '0'
selectionBox.style.left = `${start.x}px`
selectionBox.style.top = `${start.y}px`
selectionBox.style.display = 'block'
selectionBox.style.zIndex = '9999'
}
}
function imUpdateSelectIds(x: number, y: number, w: number, h: number) {
const items = document.querySelectorAll('.grid-item')
items.forEach((item: HTMLDivElement) => {
const rect = item.getBoundingClientRect()
const index = selectIds.value.indexOf(item.dataset.id!)
if (rect.right > x && rect.bottom > y && rect.left < x + w && rect.top < y + h)
index === -1 && selectIds.value.push(item.dataset.id!)
else index !== -1 && selectIds.value.splice(index, 1)
})
selectedApproveItems.value = []
// 选中复选框
listData.value.forEach((item) => {
if (selectIds.value.includes(String(item.pictureId))) {
item.checked = true
selectedApproveItems.value.push(item)
}
})
updateHtml()
}
function isSelected(pictureId: number) {
return selectIds.value.includes(String(pictureId))
}
function moveHandler(e: MouseEvent) {
if (startCalTime) {
endTime = new Date().getTime()
if (endTime - startTime > 300)
createDom()
}
else {
removeDom()
}
if (!selectionBox || !start)
return
const x = Math.min(e.clientX, start.x)
const y = Math.min(e.clientY, start.y)
const w = Math.abs(e.clientX - start.x)
const h = Math.abs(e.clientY - start.y)
selectionBox.style.width = `${w}px`
selectionBox.style.height = `${h}px`
selectionBox.style.left = `${x}px`
selectionBox.style.top = `${y}px`
imUpdateSelectIds(x, y, w, h)
}
function createDom() {
const dom = document.createElement('div')
dom.className = 'selection-box'
dom.style.width = '1px'
dom.style.border = '1px dotted black'
dom.style.position = 'absolute'
dom.style.display = 'block'
dom.style.zIndex = '9999'
document.body.appendChild(dom)
selectionBox = document.querySelector('.selection-box') as HTMLDivElement
}
function removeDom() {
const dom = document.getElementsByClassName('selection-box')[0]
if (dom)
document.body.removeChild(dom)
}
function upHandler(event: MouseEvent) {
endTime = new Date().getTime()
startCalTime = false
if (endTime - startTime > 300)
createDom()
else
removeDom()
if (!selectionBox)
return
selectionBox.style.display = 'none'
start = null
if (selectionBox.style.width == '0px') {
removeDom()
if (event.target?.className.indexOf('n-checkbox-box__border') != -1)
return
selectIds.value = []
selectedApproveItems.value.forEach(item => (item.checked = false))
selectedApproveItems.value.length = 0
listData.value.forEach((item) => {
item.checked = false
})
// batch.value = false;
}
}
const gridHeight = computed(() => {
// return viewMode.value !== 'masonry' ? '157px' : ''
let height = ''
if (viewMode.value === 'masonry')
height = ''
else if (viewMode.value === 'horizontalVersion')
height = '145px'
else if (viewMode.value === 'verticalVersion')
height = '300px'
else if (viewMode.value === '3:4')
height = '240px'
return height
})
function addListeners() {
// selectionBox.style['z-index'] = '-1'
on(el.value!, 'mousedown', downHandler)
on(el.value!, 'mousemove', moveHandler)
on(document, 'mouseup', upHandler)
// on(elwc.value!, 'mouseup', upHandler)
}
function removeListeners() {
off(el.value!, 'mousedown', downHandler)
on(el.value!, 'mousemove', moveHandler)
on(document, 'mouseup', upHandler)
// on(elwc.value!, 'mouseup', upHandler)
updateHtml()
}
function afterEnter() {
addListeners()
}
function afterLeave() {
removeListeners()
}
onMounted(async () => {
show.value && addListeners()
removeDom()
})
async function showModal() {
show.value = true
// reset()
// pagination.pageNo = 1
// const list = await featchList()
// listData.value = list
reset()
loadMore()
// layout()
}
async function onChange() {
if (timeRange.value == 'desc')
timeRange.value = 'asc'
else
timeRange.value = 'desc'
sortObj = {
// orderbyname: val,
orderByUptime: timeRange.value,
}
// pagination.pageNo = 1
// canloadMore = true
// const list = await featchList()
// listData.value = list
isTop.value = true
reset()
loadMore()
// layout()
}
async function onChangeView() {
if (similarRange.value == 'desc')
similarRange.value = 'asc'
else
similarRange.value = 'desc'
sortObj = {
// orderbyname: val,
orderBySimilarity: similarRange.value,
}
// pagination.pageNo = 1
// canloadMore = true
// const list = await featchList()
// listData.value = list
isTop.value = true
reset()
loadMore()
// layout()
}
function closeModal(event: MouseEvent) {
selectedApproveItems.value = []
show.value = false
}
async function commit() {
const ids = selectIds.value.join(',')
const res = await removeFiles({ pictureid: ids })
if (res.code === 'OK') {
message.success('移除成功')
// pagination.pageNo = 1
// const list = await featchList()
// listData.value = list
reset()
loadMore()
// layout()
setBatch(false)
}
}
function remove() {
if (!selectIds.value || selectIds.value.length === 0) {
message.error('至少选中一个')
return
}
confrimModalRef?.value?.showModal()
}
function setBatch(value, isUpdate?) {
if (value && batch.value)
batch.value = !value
else
batch.value = value
if (!isUpdate)
updateHtml()
else
waterfallRef.value?.resize()
if (value === false) {
selectIds.value = []
selectedApproveItems.value.forEach(item => (item.checked = false))
selectedApproveItems.value.length = 0
}
}
defineExpose({
showModal,
})
const notPassModalRef = ref(null)
const showActions = computed(() => {
return selectedApproveItems.value.length > 0 && batch
})
watch(() => showActions.value, () => {
updateHtml()
})
function onCheckChange(checked: any, item: any) {
item.checked = checked
const index = selectedApproveItems.value.indexOf(item)
const picIndex = selectIds.value.indexOf(item.pictureId)
if (index === -1 && checked)
selectedApproveItems.value.push(item)
else
selectedApproveItems.value.splice(index, 1)
if (picIndex === -1 && checked)
selectIds.value.push(item.pictureId)
else
selectIds.value.splice(picIndex, 1)
setTimeout(() => {
nextTick(() => {
batch.value = true
})
}, 100)
}
function rejectHandler() {
const modal = unref(notPassModalRef)! as any
modal.showModal(selectedApproveItems.value)
}
function reset() {
batch.value = false
// pagination.pageNo = 1;
pagination.pageNo = 0
pagination.pageSize = 30
selectIds.value = []
selectedApproveItems.value.length = 0
loading = false
canloadMore = true
// layout()
}
function validate(items: any[]) {
if (items.length === 0)
return '至少选中一个任务'
return null
}
function approvalHandler(items?: any) {
// if (batch.value) {
processItems = selectedApproveItems.value
// }
// 任务包图片 => 点击 => 通过/不通过按钮
if (items !== undefined && !(items instanceof PointerEvent))
processItems = [items]
const msg = validate(processItems)
if (msg !== null) {
message.error(msg)
return
}
const list: any = []
processItems.forEach((item) => {
list.push({
formId: item.id,
taskId: item.taskId,
taskName: item.fromTaskName,
})
})
const param = {
result: true,
comment: '',
disposeType: '',
disposeTypeId: '',
failCauseId: '',
failCauseName: '',
flowTaskInfoList: list,
}
dialog.info({
title: '确认提示',
content: '确认给该任务审批为【通过】吗?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
doAudit(param)
},
onNegativeClick: () => { },
})
}
function doAudit(param: any) {
audit(param).then((res) => {
const { code } = res
setBatch(false)
if (code === 'OK') {
message.info(res.message)
// emitter.emit('refresh')
// refreshHandler()
reset()
loadMore()
}
else { message.error(res.message) }
})
}
function reloadList() {
// refreshHandler()
reset()
loadMore()
}
async function refreshHandler(filtersearchId?: any) {
// 注释 rao start
// reset()
// pagination.pageNo = 1
// const list = await featchList()
// listData.value = list
reset()
loadMore()
// layout()
// 注释 rao end
// reset();
// if (filtersearchId) {
// filterId = filtersearchId;
// }
// nextTick(() => {
// setTimeout(() => {
// useInfiniteScroll(
// el as any,
// () => {
// loadMore()
// },
// { distance: 10, canLoadMore: () => canloadMore },
// )
// }, 300)
// })
}
function finish() {
}
onBeforeMount(async () => {
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 = 5
})
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()
}
watch(() => show.value, async (newVal) => {
if (show.value) {
// pagination.pageNo = 1
// const list = await featchList()
// listData.value = list
// layout()
setTimeout(() => {
reset()
loadMore()
}, 50)
}
})
</script>
<template>
<div>
<n-modal
v-model:show="show" :mask-closable="false" style="position: relative;" transform-origin="center"
@after-enter="afterEnter" @after-leave="afterLeave"
>
<n-card
ref="elwc" :style="cardStyle" class="card card-1" style="position: fixed;top:64px" :bordered="false" size="huge"
role="dialog" aria-modal="true"
>
<div class="wrapper">
<div class="wrapper-m32">
<SvgIcon name="recycle" size="16" />
<span style="margin-left: 8px;">可疑图片文件夹</span>
</div>
<div class="wrapper-title wrapper-m32">
可疑图片文件夹
</div>
<div class="wrapper-content">
<div class="wrapper-content-form wrapper-m32">
<div>
<n-popselect v-model:value="viewMode" :options="viewOptions" trigger="click">
<div class="wrapper-form-dropdown">
<span>视图模式</span>
<SvgIcon class="wrapper-content-form-gap" name="arrow-botton" size="14" />
</div>
</n-popselect>
<div
style="margin-left: 15px; cursor: pointer; color: #323233"
@click="onChange()"
>
<span>时间排序</span>
<SvgIcon style="margin-left: 8px" name="sort" size="12" />
</div>
<div
style="margin-left: 15px; cursor: pointer; color: #323233"
@click="onChangeView()"
>
<span>相似度排序</span>
<SvgIcon style="margin-left: 8px" name="sort" size="12" />
</div>
</div>
<div>
<div class="remove" @click="remove">
</div>
<div v-show="!showActions" class="wrapper-content-form-button" @click="setBatch(true, true)">
<SvgIcon style="margin-right: 6px;" size="14" name="tf" />
批量审批
</div>
<div v-show="showActions" class="batch">
<n-button text style="margin-left: 20px;" @click="setBatch(false, true)">
<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" /> -->
<img class="btn-approval btn-left" style="margin-left: 16px" src="@/assets/images/task/btn-not-pass.png" alt="">
</div>
<SvgIcon size="24" name="vs" />
<div style="cursor: pointer" @click.stop="approvalHandler">
<!-- <SvgIcon width="64" height="28" name="a2" /> -->
<img class="btn-approval" src="@/assets/images/task/btn-pass.png" alt="">
</div>
</div>
</div>
</div>
<div ref="el" class="scroll" :style="{ height: maxHeight, marginTop: '16px' }">
<div v-if="viewMode != 'masonry'" class="grid" :style="{ 'grid-template-columns': `repeat(${cols}, 182px)` }">
<div
v-for="(item, index) in listData" :key="item.pictureId" :data-id="item.pictureId"
:class="{ 'grid-item-selected': isSelected(item.pictureId) }" :style="{ height: gridHeight }"
class="grid-item"
>
<img
:key="item.pictureId"
v-lazy="item.thumburl"
:class="{
'img-fit': viewMode === 'horizontalVersion',
'img-full': viewMode === '3:4' || viewMode === 'verticalVersion',
}"
class="img"
alt="加载错误"
@click="
($event) => {
previewHandler(index, $event, item.thumburl, item.imgUrl);
}
"
>
<n-image
ref="imageRef"
:src="item?.thumburl ? item.thumburl : item.imgUrl"
:preview-src="item.imgUrl"
:fallback-src="bgLoadingImg"
class="img "
:class="{
'img-fit': viewMode === 'horizontalVersion',
'img-full': viewMode === '3:4' || viewMode === 'verticalVersion' }"
:style="{ backgroundImage: `url(${loading ? bgLoadingImg : 'none'})`, display: 'none' }"
/>
<n-checkbox
v-if="batch && item.historyStates === 1" v-model:checked="item.checked"
style="position:absolute;left:20px;top:20px" @click.prevent.stop
@update:checked="onCheckChange($event, item)"
/>
<img v-if="item.historyStates === 2" class="tag-status" src="@/assets/images/task/tag-pass.png" alt="">
<img
v-if="item.historyStates === 3" class="tag-status" src="@/assets/images/task/tag-not-pass.png"
alt=""
>
<div :class="{ 'percent-red': item.similarityScore === 100 }" class="percent">
{{ item.similarityScore }}<span class="percent-unit">%</span>
</div>
<div class="time">
<div class="time-item" style="margin-bottom: 4px;">
<SvgIcon color="#FFF" size="16" name="camera" style="margin-right: 4px;" />
<span class="time-value">{{ item?.photoDateTimestamp
? formatToDateHMS(Number(item.photoDateTimestamp)) : '- -' }} </span>
</div>
<div class="time-item">
<SvgIcon class="svg-time" color="#FFF" size="16" name="time" />
<span>{{ item.submitDateTimestamp
? formatToDateHMS(Number(item.submitDateTimestamp)) : '- -' }}</span>
</div>
</div>
</div>
</div>
<waterfall v-if="viewMode === 'masonry'" ref="waterfallRef" :col="cols" :data="listData" :gutter-width="10" @finish="finish">
<div
v-for="(item, index) in listData" :key="item.pictureId" :data-id="item.pictureId"
:class="{ 'grid-item-selected': isSelected(item.pictureId) }" :style="{ height: gridHeight }"
class="grid-item"
>
<img
:key="item.pictureId"
v-lazy="item.thumburl"
:class="{
'img-fit': viewMode === 'horizontalVersion',
'img-full': viewMode === '3:4' || viewMode === 'verticalVersion',
}"
:style="{ height: `${item.calHeight}px` }"
class="img"
alt="加载错误"
@click="
($event) => {
previewHandler(index, $event, item.thumburl, item.imgUrl);
}
"
>
<n-image
ref="imageRef"
:src="item?.thumburl ? item.thumburl : item.imgUrl"
:preview-src="item.imgUrl"
:fallback-src="bgLoadingImg"
class="img "
:class="{
'img-fit': viewMode === 'horizontalVersion',
'img-full': viewMode === '3:4' || viewMode === 'verticalVersion' }"
:style="{ backgroundImage: `url(${loading ? bgLoadingImg : 'none'})`, display: 'none' }"
/>
<n-checkbox
v-if="batch && item.historyStates === 1" v-model:checked="item.checked"
style="position:absolute;left:20px;top:20px" @click.prevent.stop
@update:checked="onCheckChange($event, item)"
/>
<img v-if="item.historyStates === 2" class="tag-status" src="@/assets/images/task/tag-pass.png" alt="">
<img
v-if="item.historyStates === 3" class="tag-status" src="@/assets/images/task/tag-not-pass.png"
alt=""
>
<div :class="{ 'percent-red': item.similarityScore === 100 }" class="percent">
{{ item.similarityScore }}<span class="percent-unit">%</span>
</div>
<div class="time">
<div class="time-item" style="margin-bottom: 4px;">
<SvgIcon color="#FFF" size="16" name="camera" style="margin-right: 4px;" />
<span class="time-value">{{ item?.photoDateTimestamp
? formatToDateHMS(Number(item.photoDateTimestamp)) : '- -' }} </span>
</div>
<div class="time-item">
<SvgIcon class="svg-time" color="#FFF" size="16" name="time" />
<span>{{ item.submitDateTimestamp
? formatToDateHMS(Number(item.submitDateTimestamp)) : '- -' }}</span>
</div>
</div>
</div>
</waterfall>
</div>
</div>
<div class="close" @pointerdown="closeModal">
<div class="icon" />
</div>
</div>
</n-card>
</n-modal>
<NotPassed ref="notPassModalRef" @success="reloadList" />
<ConfrimModal ref="confrimModalRef" :selected-total="selectIds.length" @commit="commit" />
</div>
</template>
<style lang="less" scoped>
.btn-approval{
width: 68px;
height: 28px;
cursor: pointer;
}
.card {
width: 100vw;
height: calc(100vh - 64px);
user-select: none;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.remove {
cursor: pointer;
}
.close {
position: absolute;
right: -90px;
top: -70px;
width: 18px;
height: 18px;
cursor: pointer;
}
.icon {
background: #FFF;
display: inline-block;
width: 18px;
height: 1px;
transform: rotate(45deg);
-webkit-transform: rotate(45deg);
&:after {
content: '';
display: block;
width: 18px;
height: 1px;
background: #FFF;
transform: rotate(-90deg);
-webkit-transform: rotate(-90deg);
}
}
::v-deep(.vue-waterfall) {
overflow-x: hidden;
.vue-waterfall-column {
max-width: 190px;
}
}
.img {
width: 182px;
max-width: 182px;
border-radius: 7px;
display: block;
opacity: 1 !important;
height: 100%;
}
.wrapper {
display: flex;
flex-direction: column;
position: relative;
&-title {
font-weight: bold;
font-size: 21px;
padding: 24px 0px 12px 0px;
}
&-m32 {
margin-left: 32px;
}
.grid-item-selected {
background-color: #dae3ff;
}
&-content {
&-form {
display: flex;
justify-content: space-between;
&-dropdown {
display: flex;
flex-direction: row;
align-items: center;
margin-right: 24px;
&-gap {
margin-left: 5px;
}
}
&-button {
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-left: 18px;
}
div {
display: flex;
align-items: center;
}
}
&-item {
&-img {
width: 182px;
border-radius: 7px;
display: block;
height: 100%;
}
&-img-fit {
width: 182px;
object-fit: cover;
}
}
.grid{
grid-gap: 0px 20px;
}
.grid-item {
box-sizing: content-box;
width: 182px;
padding: 6px;
transition: 0.5s;
margin-bottom: 20px;
position: relative;
// background-color: #fff;
.tag-status {
width: 46px;
height: 22px;
position: absolute;
left: 2px;
top: 20px;
}
}
.percent {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 35px;
height: 18px;
opacity: 0.9;
background: #6f92fd;
border-radius: 6px 0px 6px 0px;
z-index: 5;
right: 16px;
top: 12px;
color: #fff;
font-size: 14px;
.percent-unit {
font-size: 8px;
margin-top: 4px;
}
}
.percent-red {
background: #ff4e4f;
}
.time {
position: absolute;
width: 182px;
padding-left: 10px;
z-index: 3;
left: 6px;
bottom: 6px;
background: linear-gradient(180deg,rgba(6,0,0,0.01),rgba(0, 0, 0, 0.44) 100%);
border-radius: 0 0 7px 7px;
.time-item {
display: flex;
align-items: center;
font-size: 14px;
font-family: PingFang SC, PingFang SC-Medium;
font-weight: 500;
color: #ffffff;
margin-bottom: 4px;
}
.time-item2 {
margin-bottom: 0;
}
.svg-time {
margin-right: 5px;
}
}
.scroll {
overflow-y: auto;
overflow-x: hidden;
// height: calc(100vh - 282px);
height: 800px;
margin-left: 20px;
}
}
}
.wrapper-content-form-button {
cursor: pointer;
}
.img-fit {
width: 100%;
overflow: hidden;
}
.img-full {
width: 100%;
overflow: hidden;
::v-deep(img) {
width: 100%;
height: 100%;
object-fit: cover;
}
}
</style>