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

1074 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 { audit } from '@/api/task/task';
import {
getTaskDetailInfo,
getTaskDetailPictureList
} from "@/api/work/work";
import NotPassed from '@/components/Approval/NotPassed.vue';
import { fieldMap } from "@/config/workorder";
import { TASK_STATUS_OBJ } from '@/enums/index';
import { useWorkOrder } from "@/store/modules/workOrder";
import { isEmpty } from "@/utils";
import { formatToDateHMS } from "@/utils/dateUtil";
import { hideDownload } from "@/utils/image";
import emitter from '@/utils/mitt';
import { useInfiniteScroll } from "@vueuse/core";
import { format } from 'date-fns';
import imagesloaded from "imagesloaded";
import { clone, debounce, pickBy } from "lodash-es";
import { useDialog, useMessage } from "naive-ui";
import { computed, onUnmounted, onUpdated, reactive, ref, unref, watch } from "vue";
import ConfrimModal from "../modal/ConfrimModal.vue";
import type { ApprovalParam, SimilarityPictureSortParam } from "/#/api";
const batch = ref(false);
const selectItems = ref<any[]>([]);
const message = useMessage();
const dialog = useDialog();
const totalCount = ref(0);
let _imagesload: any;
function setBatch(value: boolean) {
batch.value = value;
if (value === false) {
selectItems.value.forEach((item) => (item.checked = false));
selectItems.value.length = 0;
}
}
function onCheckChange(checked: any, item: any) {
const index = selectItems.value.indexOf(item);
item.checked = checked;
if (index === -1 && checked) selectItems.value.push(item);
else selectItems.value.splice(index, 1);
}
const showActions = computed(() => {
return selectItems.value.length > 0 && batch;
});
const taskpagination = reactive({
pageNo: 1,
pageSize: 10,
});
const sortBy: SimilarityPictureSortParam = {
orderType: "asc",
orderName: "similarityScore",
};
const workStore = useWorkOrder();
const selectTask = ref<any>(null);
const overTask = ref<any>(null);
const taskDetailInfo = ref<any>({});
const confrimModalRef = ref(null);
const imageRef = ref<ComponentElRef | null>();
const listData = ref<any[]>([]);
const loading = ref(false);
const el = ref<HTMLDivElement | null>(null);
const selectedSortName = ref('');
const isFullScreen = ref(false);
const notPassModalRef = ref(null)
let canloadMore = true;
let processItems: any[] = [];
function validate(items: any[]) {
if (items.length === 0) return "至少选中一个任务";
// for (const item of items) {
// const { iztrueorfalse, history, states } = item
// if (iztrueorfalse !== null)
// return '存在已经辨识过的任务'
// else if (history)
// return '包含历史数据'
// else if (states !== 1 && states !== 2)
// return '审批状态不合法'
// }
return null;
}
function forwardHandler() {
workStore.forward();
}
function backHandler() {
workStore.back();
}
function reset() {
taskpagination.pageNo = 0;
taskpagination.pageSize = 20;
listData.value.length = 0;
loading.value = false;
canloadMore = true;
}
async function refreshHandler() {
reset();
useInfiniteScroll(
el as any,
() => {
loadMore();
},
{ distance: 10, canLoadMore: () => canloadMore }
);
}
async function loadMore() {
if (loading.value || el.value == null) return;
const more = await featchList();
listData.value.push(...more);
}
async function featchList() {
loading.value = true;
try {
taskpagination.pageNo += 1;
const { data, total, pageCount } = await getTaskDetailPictureList(
{ ...taskpagination, ...sortBy, checkDuplicateId: workStore.activeId, pictureId: taskDetailInfo.value.pictureid }
);
totalCount.value = total;
canloadMore = pageCount >= taskpagination.pageNo && pageCount > 0;
return data;
} catch (error) {
canloadMore = false;
return [];
}
}
const layout = debounce(() => {
if (el.value == null) return;
_imagesload = imagesloaded(".grid-item");
_imagesload.on("done", (instance) => {
if (!el.value) return;
loading.value = false;
});
_imagesload.on("fail", (instance) => {
message.error("图片错误");
loading.value = false;
});
}, 300);
const fullscreenStyles = computed<any>(() => ({
'width': isFullScreen.value ? '100vw' : '',
'height': isFullScreen.value ? '100vh' : '',
'position': isFullScreen.value ? 'fixed' : '',
'top': isFullScreen.value ? '0' : '',
'left': isFullScreen.value ? '0' : '',
'zIndex': isFullScreen.value ? '9999' : '',
}));
// 切换全屏状态
const toggleFullScreen = () => {
isFullScreen.value = !isFullScreen.value;
};
onUpdated(() => {
layout();
});
watch(
() => workStore.activeId,
async (newValue, oldValue) => {
const packageid = workStore.getActiveId;
if (isEmpty(packageid)) {
listData.value.length = 0;
totalCount.value = 0;
return;
}
queryDetail(packageid)
// const res = await getPackageTaskList(newValue, packagepagination);
// const { data } = res;
// taskList.value = data;
// if (taskList.value.length > 0) handleSelect(taskList.value[0]); // 暂时注释掉
}
);
const packageName = computed(() => {
const index = workStore.getCurrentIndex;
return workStore.getOrderList[index]?.name || "";
});
async function queryDetail(checkDuplicateId: any) {
taskDetailInfo.value = await getTaskDetailInfo(checkDuplicateId);
const packageid = workStore.getActiveId;
if (isEmpty(packageid)) {
listData.value.length = 0;
totalCount.value = 0;
return;
}
refreshHandler();
}
async function handleSelect(item: any) {
taskDetailInfo.value = await getTaskDetailInfo(workStore.activeId);
const packageid = workStore.getActiveId;
if (isEmpty(packageid)) {
listData.value.length = 0;
totalCount.value = 0;
return;
}
// refreshHandler();
}
async function sortHandler(orderby: "similarityScore" | "createdate") {
selectedSortName.value = orderby;
sortBy.orderName = orderby;
sortBy.orderType = sortBy.orderType === "asc" ? "desc" : "asc";
refreshHandler();
}
const propertys = computed(() => {
const { ocrPicture } = taskDetailInfo.value;
const v = pickBy(ocrPicture, (value, key: string) => {
return key.startsWith("field") && value !== null;
});
return v;
});
function overTaskHandelr(item: any) {
if (item?.historyStates === 2 || item?.historyStates == 3) {
overTask.value = null;
return;
}
if (validate([item]) == null && batch.value === false) overTask.value = item;
}
function leaveTaskHandler() {
overTask.value = null;
}
onUnmounted(() => {
workStore.reset();
});
function immersionHandler() {
// class="wrapper"
// workStore.updateImmersion();
toggleFullScreen();
}
function showAction() {
const item = taskDetailInfo.value;
if (batch.value === false) overTask.value = item;
}
function hideAction() {
overTask.value = null;
}
function previewHandler(event: MouseEvent) {
event.stopImmediatePropagation();
event.stopPropagation();
if (imageRef.value && (imageRef.value as any).src)
(imageRef.value as any).mergedOnClick();
}
function rejectHandler() {
const modal = unref(notPassModalRef)! as any
modal.showModal(selectItems.value)
}
function reject(idOrDesc: string, backId: string, isOther: boolean) {
const formIds: string[] = processItems.map(item => item.id)
const taskIds: string[] = processItems.map(item => item.taskId)
const tasknames: string[] = processItems.map(item => item.taskname)
const param: ApprovalParam = {
formid: formIds,
taskId: taskIds,
approvd: false,
taskComment: idOrDesc,
taskname: isOther ? tasknames : ['其他'],
}
doAudit(param)
}
function approvalHandler(items?: any) {
let cloneItem: any
if (batch.value) {
processItems = selectItems.value
}
else if (overTask.value) {
// 主图点击 => 点击 => 通过/不通过按钮
cloneItem = clone(overTask.value)
// 接口问题,暂时先这样处理
// if (!cloneItem.taskId) {
// cloneItem.taskId = "taskIdtaskIdtaskIdtaskIdtaskId"
// }
processItems = [cloneItem]
}
// 任务包图片 => 点击 => 通过/不通过按钮
if (items !== undefined && !(items instanceof PointerEvent))
processItems = [items]
const msg = validate(processItems)
if (msg !== null) {
message.error(msg)
return
}
dialog.info({
title: '确认提示',
content: '确认给该任务审批为【通过】吗?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
approval()
},
onNegativeClick: () => { },
})
}
function approval() {
const formIds: string[] = processItems.map(item => item.id)
const taskIds: string[] = processItems.map(item => item.taskId)
const tasknames: string[] = processItems.map(item => item.taskname)
const param: ApprovalParam = {
formid: formIds,
taskId: taskIds,
approvd: true,
taskComment: 'approval',
taskname: tasknames,
}
doAudit(param)
}
function doAudit(param: any) {
audit(param).then((res) => {
const { code } = res
setBatch(false)
if (code === 'OK') {
emitter.emit('refresh')
refreshHandler()
}
else message.error(res.message)
})
}
function reloadList() {
loadMore()
}
</script>
<template>
<div class="wrapper fullscreen-container" :style="fullscreenStyles">
<div class="wrapper-header">
<div class="left">
<span class="font">{{ packageName }}</span>
<SvgIcon size="22" class="forward" name="arrow-left" @click="backHandler" />
<SvgIcon size="22" class="back" name="arrow-right" @click="forwardHandler" />
</div>
<div class="right">
<div v-show="!showActions" style="display: flex; align-items: center">
<div class="btn" style="margin: 0px 10px" @click="setBatch(true)">
<SvgIcon style="margin-right: 6px" size="14" name="tf" />
批量审批
</div>
<div class="icon-wrap">
<SvgIcon size="20" name="immersion-model" @click="immersionHandler" />
</div>
</div>
<div v-show="showActions" class="batch">
<n-button text @click="setBatch(false)">
<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" />
</div>
<SvgIcon size="24" name="vs" />
<div style="cursor: pointer" @click.stop="approvalHandler">
<SvgIcon width="64" height="28" name="a2" />
</div>
</div>
</div>
</div>
<n-spin :show="loading">
<div ref="el" class="scroll">
<div class="wrapper-detail">
<!-- 左侧大图 图片信息 -->
<div class="left" :style="{ 'background-image': `url(${taskDetailInfo?.imgurl})` }" @click="showAction"
@mouseleave="leaveTaskHandler">
<div class="footer-times">
<div class="time" style="margin-bottom: 4px;">
<SvgIcon color="#FFF" size="16" name="camera" />
<span class="time-value">{{ taskDetailInfo?.submitDateTimestamp ? format(
taskDetailInfo.submitDateTimestamp, 'yyyy-MM-dd HH:mm:ss') : '-' }} </span>
</div>
<div class="time">
<SvgIcon color="#FFF" size="16" name="save" />
<span class="time-value">{{
taskDetailInfo?.photoDateTimestamp ? format(taskDetailInfo?.photoDateTimestamp, 'yyyy-MM-dd HH:mm:ss') :
'-'
}} </span>
</div>
</div>
<div class="status">
<img v-show="taskDetailInfo?.states === 3" class="img-status" src="@/assets/images/task/pass.png" alt="">
<img v-show="taskDetailInfo?.states === 5" class="img-status" src="@/assets/images/task/not_pass.png"
alt="">
</div>
<!-- 右下信息 -->
<div class="info img-info">
<n-grid x-gap="12" y-gap="10" :cols="12">
<n-gi span="4" class="gi1">
<span>
<img class="icon-status" src="@/assets/images/task/status.png" alt="">
</span>
</n-gi>
<n-gi span="8" class="gi2">
<span class="value">{{ TASK_STATUS_OBJ[taskDetailInfo.states] }}</span>
<span class="label">审批状态</span>
</n-gi>
<n-gi span="4" class="gi1">
<span>
<img class="icon-status" src="@/assets/images/task/similarity.png" alt="">
</span>
</n-gi>
<n-gi span="8" class="gi2">
<span class="value num">{{
totalCount
}}<span class="unit"></span> </span>
<span class="label">相似匹配</span>
</n-gi>
</n-grid>
</div>
<!-- 右上点击大图 -->
<div class="preview" @click="previewHandler">
<SvgIcon size="16" name="zoom-out" />
</div>
<!-- 预览大图组件 -->
<div style="display: none">
<n-image ref="imageRef" :img-props="{ onClick: hideDownload }" :src="taskDetailInfo?.imgurl" />
</div>
<!-- 操作 -->
<div v-show="overTask && overTask.id === taskDetailInfo.id" class="action" @click.stop="hideAction">
<SvgIcon style="cursor: pointer" name="t1" @click.stop="approvalHandler" />
<SvgIcon style="cursor: pointer; margin-left: 30px" name="t2" @click.stop="rejectHandler" />
</div>
</div>
<!-- 右侧任务 标题信息 -->
<div class="right">
<n-scrollbar style="max-height: 100%">
<span class="task-name">任务ID{{ taskDetailInfo.taskname }}</span>
<div class="tags">
<div class="tag tag-submiting" v-if="taskDetailInfo.states == 1">{{ TASK_STATUS_OBJ[taskDetailInfo.states]
}}</div>
<div class="tag tag-approve" v-else-if="taskDetailInfo.states == 2">{{
TASK_STATUS_OBJ[taskDetailInfo.states] }}</div>
<div class="tag tag-passed" v-else-if="taskDetailInfo.states == 3">{{
TASK_STATUS_OBJ[taskDetailInfo.states]
}}</div>
<div class="tag tag-approved" v-else-if="taskDetailInfo.states == 4">{{
TASK_STATUS_OBJ[taskDetailInfo.states] }}</div>
</div>
<n-divider class="divider-line" />
<div class="property">
<span class="property-name top" style=" color: #666666">图片大小
</span>
<span style=" color: #333333; font-size: 16px">{{ taskDetailInfo?.pictureInfo &&
(taskDetailInfo?.pictureInfo?.imgSize / 1000).toFixed(2) }}KB</span>
</div>
<div class="property">
<span class="property-name top" style=" color: #666666">图片格式
</span>
<span style=" color: #333333; font-size: 16px">{{ taskDetailInfo?.pictureInfo?.imgFormat }}</span>
</div>
<div class="property">
<span class="property-name top" style=" color: #666666">图片尺寸
</span>
<span style=" color: #333333; font-size: 16px">{{ taskDetailInfo?.pictureInfo?.imgMeasure }}</span>
</div>
<div class="property">
<span class="property-name top" style=" color: #666666">色彩空间
</span>
<span style=" color: #333333; font-size: 16px">{{ taskDetailInfo?.pictureInfo?.imgSpace }}</span>
</div>
<div class="property">
<span class="property-name top" style=" color: #666666">提报人
</span>
<span style=" color: #333333; font-size: 16px">{{ taskDetailInfo?.pictureInfo?.source || "-" }}</span>
</div>
<div class="property">
<span class="property-name top" style=" color: #666666">创建时间
</span>
<span style=" color: #333333; font-size: 16px">{{ taskDetailInfo?.pictureInfo &&
format(taskDetailInfo?.pictureInfo?.createTime, 'yyyy-MM-dd HH:mm:ss') }}</span>
</div>
<div class="property">
<span class="property-name top" style=" color: #666666">提报时间
</span>
<span style=" color: #333333; font-size: 16px">{{ taskDetailInfo?.uploadTime &&
format(taskDetailInfo?.pictureInfo?.uploadTime, 'yyyy-MM-dd HH:mm:ss') || '-' }}</span>
</div>
<div v-for="key in Object.keys(propertys)" :key="key" class="property">
<span class="property-name">{{ fieldMap[key] }}</span>
<span class="property-content">{{ propertys[key] }}</span>
</div>
</n-scrollbar>
</div>
</div>
<div style="display: flex; justify-content: space-between; padding: 12px 0px 3px 0">
<div>
<span
style="font-size: 18px; font-weight: Medium;color: #333333;font-family: PingFang SC, PingFang SC-Medium;">任务包图片</span>
</div>
<div style="display: flex; align-items: center;font-size: 14px;margin-right: 25px;color:#323233">
<div style="cursor: pointer" @click="sortHandler('createdate')">
<span>时间排序</span>
<SvgIcon style="margin-left: 5px" name="sort" size="12" v-show="selectedSortName !== 'createdate'" />
<SvgIcon style="margin-left: 5px" name="active-sort" size="12" v-show="selectedSortName === 'createdate'" />
</div>
<div style="margin-left: 15px; cursor: pointer" @click="sortHandler('similarityScore')">
<span>相似度排序</span>
<SvgIcon style="margin-left: 5px" name="sort" size="12" v-show="selectedSortName !== 'similarityScore'" />
<SvgIcon style="margin-left: 5px" name="active-sort" size="12"
v-show="selectedSortName === 'similarityScore'" />
</div>
</div>
</div>
<div class="wrapper-list">
<div v-for="(item, index) in listData" :key="index" :class="{ 'item-selected': item === selectTask }"
class="grid-item" @click="handleSelect(item)" @mouseover="overTaskHandelr(item)"
@mouseleave="leaveTaskHandler">
<div class="img-wrapper" :style="{ 'background-image': `url(${item.imgurl})` }" />
<div class="time-wrapper">
<div class="time">
<SvgIcon color="#FFF" size="16" name="camera" />
<span class="current-time">{{ item.photoDateTimestamp ? formatToDateHMS(Number(item.photoDateTimestamp) ||
0) : '-'
}}</span>
</div>
<div class="time">
<SvgIcon color="#FFF" size="16" name="save" />
<span class="current-time">{{ item.submitDateTimestamp ? formatToDateHMS(Number(item.submitDateTimestamp)
|| 0) : '-'
}}</span>
</div>
</div>
<div class="check">
<n-checkbox v-show="batch" v-model:checked="item.checked" @click.stop
@update:checked="onCheckChange($event, item)" />
</div>
<div class="percent">
<SvgIcon size="42" name="tag" />
<div class="val">
{{ item?.maxSimilarity && Number(item?.maxSimilarity).toFixed(0) }}%
</div>
</div>
<div class="pass-status" v-if="item.historyStates === 2">
<SvgIcon name="pass-icon" style="width:52;height:24px" />
</div>
<div class="pass-status" v-else-if="item.historyStates === 3">
<SvgIcon name="no-pass-icon" style="width:52;height:24px" />
</div>
<!--
重复标签
<div class="pass-status" v-if="item.historyStates === 1">
<SvgIcon name="repeat-icon" style="width:52;height:24px" />
</div> -->
<div v-show="overTask && overTask.id === item.id" class="action">
<SvgIcon style="cursor: pointer" name="t1" @click.stop="approvalHandler" />
<SvgIcon style="cursor: pointer;margin-left: 40px;" name="t2" @click.stop="rejectHandler" />
</div>
</div>
</div>
</div>
</n-spin>
<ConfrimModal ref="confrimModalRef" @commit="reject" />
<NotPassed ref="notPassModalRef" @success="reloadList" />
</div>
</template>
<style lang="less" scoped>
.fullscreen-container {
/* 可添加其他样式 */
width: 100vw;
height: 100vh;
margin: 0px !important;
}
.wrapper {
display: flex;
flex: 1;
flex-direction: column;
box-sizing: border-box;
margin-left: 16px;
padding: 16px 16px 0px 16px;
background: #fff;
border-radius: 3px;
border: 1px solid rgb(239, 239, 245);
.scroll {
height: calc(100vh - 88px - 72px);
overflow-y: scroll;
}
&-header {
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
box-sizing: border-box;
border-radius: 3px;
margin-bottom: 16px;
width: 100%;
height: 36px;
.left {
display: flex;
align-items: center;
.forward {
cursor: pointer;
margin-left: 16px;
}
.back {
cursor: pointer;
margin-left: 6px;
}
}
.right {
display: flex;
align-items: center;
.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;
cursor: pointer;
}
.batch {
display: flex;
align-items: center;
}
}
.font {
font-size: 20px;
font-family: PingFang SC, PingFang SC-Medium;
font-weight: 500;
color: #0d0b22;
}
}
&-detail {
display: flex;
height: 350px;
.left {
flex: 0.6;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border-radius: 8px;
position: relative;
.preview {
position: absolute;
right: 10px;
top: 10px;
z-index: 3;
width: 30px;
height: 30px;
background: rgba(255, 255, 255, 0.20);
border-radius: 6px;
backdrop-filter: blur(10px);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.mark {
position: absolute;
z-index: 3;
right: 15%;
top: 0px;
width: 128px;
height: 80px;
overflow: hidden;
overflow: hidden;
svg {
position: absolute;
top: -48px;
}
}
.status {
position: absolute;
z-index: 3;
left: 0;
top: 0;
.img-status {
width: 133px;
height: 129px;
}
}
.footer-times {
width: 100%;
height: 80px;
background: linear-gradient(180deg, rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 0.71) 100%);
border-radius: 0px 8px 8px 0px;
position: absolute;
bottom: 0;
padding-left: 17px;
display: flex;
flex-direction: column;
justify-content: center;
font-weight: Medium;
.time {
color: white;
font-size: 15px;
line-height: 21px;
&-value {
margin-left: 7px;
}
}
}
.action {
position: absolute;
z-index: 3;
width: 100%;
height: 100%;
display: flex;
border-radius: 8px;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.48);
}
.info {
position: absolute;
z-index: 3;
right: 2%;
bottom: 2%;
width: 136px;
height: 119px;
background: rgba(216, 216, 216, 0.4);
border-radius: 7px;
padding: 16px 0 0 23px;
color: #fff;
}
.icon-wrap {
display: flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
background: rgba(80, 122, 253, .1);
border-radius: 8px;
margin-left: 10px;
cursor: pointer;
}
.img-info {
color: lime !important;
.icon-status {
width: 32px;
height: 32px;
}
.label {
font-size: 11px;
font-family: PingFang SC, PingFang SC-Medium;
font-weight: 500;
color: #ffffff;
display: block;
}
.value {
font-size: 15px;
font-family: PingFang SC, PingFang SC-Semibold;
font-weight: 600;
color: #ffffff;
}
.num {
font-size: 18px;
font-family: PingFang SC, PingFang SC-Semibold;
font-weight: 600;
color: #ffffff;
}
.unit {
font-size: 11px;
}
}
}
.right {
// flex: 1;
// display: flex;
// flex-direction: column;
// justify-content: space-between;
// // background: #fafafa;
// border-radius: 8px;
// margin-left: 20px;
// padding: 24px;
// width: 24vw;
flex: 0.5;
background: #f9f9f9;
border-radius: 8px;
margin-left: 20px;
padding: 24px;
// background: lime;
.task-name {
display: inline-block;
width: 100%;
font-size: 18px;
color: #0d0b22;
line-height: 25px;
font-family: PingFang SC, PingFang SC-Medium;
font-weight: Medium;
max-width: 445px;
/* 设置文本溢出时的样式为省略号 */
text-overflow: ellipsis;
/* 隐藏超出容器的文本 */
overflow: hidden;
/* 确保文本不换行 */
white-space: nowrap;
}
// ::v-deep(.n-divider:not(.n-divider--vertical)) {
::v-deep(.divider-line .n-divider__line) {
background-color: #e8e8e8;
}
.tags {
display: flex;
margin-top: 8px;
}
.tag {
border: 1px solid #d8d8d8;
box-sizing: border-box;
border-radius: 8px;
color: #666666;
margin-right: 12px;
padding: 4px 8px;
font-size: 12px;
line-height: 16px;
}
.tag-submiting {
color: #feaf2d;
border: 1px solid #feaf2d;
}
.tag-approve {
color: #398ade;
border: 1px solid #398ade;
}
.tag-passed {
color: #02C984;
border: 1px solid #02C984;
}
.tag-approved {
color: #507afd;
border: 1px solid #507afd;
}
.property {
display: flex;
align-items: center;
margin-top: 12px;
line-height: 20px;
}
.property-name {
width: 56px;
text-align: right;
color: #999999;
position: relative;
margin-right: 32px;
}
.top {}
.property-content {
flex: 1;
color: #333333;
}
}
}
&-list {
display: flex;
flex-wrap: wrap;
flex-direction: row;
justify-content: flex-start;
flex-shrink: 0;
.item-selected {
box-shadow: 0px 2px 8px 0px rgba(0, 65, 207, 0.28);
background-color: #fff;
}
.grid-item {
box-sizing: border-box;
border-radius: 8px;
padding: 9px 5px;
position: relative;
.img-wrapper {
width: 230px;
height: 130px;
overflow: hidden;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border-radius: 8px;
}
.time-wrapper {
position: absolute;
bottom: 9px;
width: calc(100% - 11px);
height: 58px;
background: linear-gradient(180deg, rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 0.71) 100%);
border-radius: 8px;
display: flex;
flex-direction: column;
justify-content: center;
font-weight: Medium;
padding-left: 12px;
color: white;
.time .current-time {
margin-left: 9px;
font-weight: Medium;
}
}
.check {
position: absolute;
z-index: 3;
left: 12px;
top: 12px;
}
.percent {
position: absolute;
text-align: center;
z-index: 3;
right: 17px;
top: 2px;
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;
}
}
.pass-status {
position: absolute;
left: 0px;
top: 15px;
}
.mark {
position: absolute;
z-index: 3;
right: 62px;
top: 9px;
width: 64px;
height: 40px;
overflow: hidden;
overflow: hidden;
svg {
position: absolute;
top: -20px;
}
}
.action {
position: absolute;
z-index: 5;
left: 4px;
top: 9px;
width: 230px;
height: 130px;
display: flex;
border-radius: 8px;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.48);
}
}
}
}
::v-deep(.n-divider:not(.n-divider--vertical)) {
margin-top: 12px;
margin-bottom: 12px;
}
</style>