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.
729 lines
20 KiB
729 lines
20 KiB
<script lang="ts" setup>
|
|
import { createPackage, getCheckDuplicateStatus, getLastCheckNo, getPictureList, oneClickCheckTaskPackage, queryPageListByCheckNo, removeCheckDuplicate } from '@/api/home/main'
|
|
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 emitter from '@/utils/mitt'
|
|
import { getImgUrl } from '@/utils/urlUtils'
|
|
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 { useMessage } from 'naive-ui'
|
|
import { computed, nextTick, 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"
|
|
|
|
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 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 sortBy: PictureSortParam = {
|
|
orderbyname: "asc",
|
|
orderbyvalue: "pictureResult",
|
|
};
|
|
const imageRef = ref<ComponentElRef | null>()
|
|
const checkDuplicateNo = ref('')
|
|
const checkTaskStatus = ref(null) // 1.执行中 2.执行完毕 3.执行失败
|
|
const isRefresh = ref(true) // 生成任务包前,点击刷新数据,并将【一键查重】切换按钮为【生成任务包】
|
|
|
|
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`,
|
|
}
|
|
})
|
|
|
|
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,
|
|
})
|
|
|
|
_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 },
|
|
)
|
|
|
|
onUpdated(() => {
|
|
layout()
|
|
})
|
|
|
|
const timeRange = ref('')
|
|
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[]>([])
|
|
|
|
async function featchList() {
|
|
loading.value = true
|
|
try {
|
|
const contentParams = {
|
|
search_month: timeRange.value,
|
|
search_history: 0,
|
|
}
|
|
|
|
pagination.pageNo += 1
|
|
|
|
const asideParams = unref(configStore.getAsideValue)
|
|
const params = filterId ? { userSearchId: filterId } : asideParams
|
|
let result = {
|
|
pageCount: 0,
|
|
data: [],
|
|
total: 0,
|
|
}
|
|
if (checkTaskStatus.value === 2 && isRefresh) {
|
|
result = await queryPageListByCheckNo({ ...pagination, ...contentParams, ...params, ...sortBy, checkDuplicateNo: checkDuplicateNo.value })
|
|
} else {
|
|
result = await getPictureList({ ...pagination, ...contentParams, ...params, ...sortBy })
|
|
}
|
|
const { data, pageCount, total } = result
|
|
totalCount.value = total
|
|
canloadMore = pageCount >= pagination.pageNo && pageCount > 0
|
|
|
|
const list = data.map((item) => {
|
|
return {
|
|
imgUrl: item.imgurl,
|
|
thumburl: item.serverThumbnailUrl || item.imgurl,
|
|
upname: item.upname,
|
|
ocrPictureclass: item.ocrPictureclass,
|
|
uphead: item.uphead,
|
|
similar: item.similarityscore || -1,
|
|
imgName: item.imgname
|
|
}
|
|
})
|
|
|
|
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)
|
|
}
|
|
|
|
const gridHeight = computed(() => {
|
|
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
|
|
})
|
|
|
|
async function oneCheck() {
|
|
const asideVal = cloneDeep(configStore.getAsideValue)
|
|
asideVal.izyear = dayjs(asideVal.izyear[0]).format("YYYY/MM/DD") + '-' + dayjs(asideVal.izyear[1]).format("YYYY/MM/DD")
|
|
const tasksLoadingModal = queryRepeatedTasksModalRef.value as any
|
|
|
|
// 查重任务编号,状态不为空,或者状态执行中..
|
|
if(checkDuplicateNo.value && checkTaskStatus.value && checkTaskStatus.value===1){
|
|
tasksLoadingModal.showModal()
|
|
return;
|
|
}
|
|
|
|
//调用查重任务开启流程
|
|
oneClickCheckTaskPackage(asideVal).then((res) => {
|
|
if (res.code === "OK") {
|
|
checkDuplicateNo.value = res.data.checkDuplicateNo
|
|
checkTaskStatus.value = res.data.status
|
|
tasksLoadingModal.showModal()
|
|
} 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()
|
|
}
|
|
|
|
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.data);
|
|
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
|
|
|
|
}
|
|
|
|
onMounted(() => {
|
|
emitter.on('filter', refreshHandler)
|
|
// 一件键重——获取任务编号
|
|
getLastCheckNo().then((res) => {
|
|
if (res.code === "OK") {
|
|
checkDuplicateNo.value = res.data
|
|
}
|
|
})
|
|
nextTick(() => {
|
|
computeListHeight()
|
|
// 登录后展示成功
|
|
// showLoginSuccessModal()
|
|
})
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
emitter.off('filter', refreshHandler)
|
|
})
|
|
|
|
watch(timeRange, () => {
|
|
refreshHandler()
|
|
})
|
|
|
|
watch(() => configStore.asideValue, (newVal, oldVal) => {
|
|
refreshHandler()
|
|
}, { deep: true })
|
|
|
|
function reset() {
|
|
pagination.pageNo = 0
|
|
pagination.pageSize = 30
|
|
listData.value.length = 0
|
|
loading.value = false
|
|
canloadMore = true
|
|
filterId = null
|
|
|
|
layout()
|
|
}
|
|
|
|
async function refreshHandler(filtersearchId?: any) {
|
|
reset()
|
|
|
|
if (filtersearchId)
|
|
filterId = filtersearchId
|
|
|
|
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.thumburl) {
|
|
message.error('请输入有效的图片链接地址')
|
|
return;
|
|
}
|
|
try {
|
|
// 使用 fetch 发送 GET 请求获取图片
|
|
const response = await fetch(item.thumburl, {
|
|
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) {
|
|
event.stopImmediatePropagation();
|
|
event.stopPropagation();
|
|
if (imageRef.value?.[index] && (imageRef.value[index] as any).src)
|
|
(imageRef.value?.[index] as any).mergedOnClick();
|
|
}
|
|
|
|
/**
|
|
* 检查查重状态
|
|
*/
|
|
function refresh(val) {
|
|
// 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 && isRefresh) {
|
|
checkingTaskModal.closeModal()
|
|
isRefresh.value = false
|
|
message.success('任务执行完毕,正在刷新数据...');
|
|
reset()
|
|
loadMore()
|
|
} else if (checkTaskStatus.value === 1) {
|
|
// checkingTaskModal.showModal()
|
|
return
|
|
}
|
|
}
|
|
})
|
|
return
|
|
}
|
|
}
|
|
/**
|
|
* 取消查重任务
|
|
*/
|
|
function cancel(val) {
|
|
if (checkTaskStatus.value === 1) {
|
|
removeCheckDuplicate(checkDuplicateNo.value).then((res) => {
|
|
if (res.code === "OK") {
|
|
checkDuplicateNo.value='';
|
|
checkTaskStatus.value = null;
|
|
message.success("查重任务取消成功")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<div class="wrapper">
|
|
<div class="wrapper-header">
|
|
<div class="left">
|
|
<SvgIcon size="32" name="magnifying" />
|
|
<span class="font">AI一键查重</span>
|
|
</div>
|
|
<SvgIcon v-show="checkTaskStatus !== 2" style="cursor: pointer;" size="105" name="yijianchachong"
|
|
@click="oneCheck" />
|
|
<SvgIcon v-show="checkTaskStatus === 2" style="cursor: pointer;" size="105" name="shengchengrenwubao"
|
|
@click="showAddPackage" />
|
|
</div>
|
|
<div class="wrapper-content">
|
|
<div style="display: flex;justify-content: space-between;">
|
|
<div class="form">
|
|
<!-- <n-popselect v-model:value="timeRange" :options="timeOptions" trigger="click">
|
|
<div class="dropdown">
|
|
<span>{{ timeLabel || '请选择' }}</span>
|
|
<SvgIcon class="gap" name="arrow-botton" size="14" />
|
|
</div>
|
|
</n-popselect> -->
|
|
<n-popselect v-model:value="viewMode" :options="viewOptions" trigger="click">
|
|
<div class="dropdown">
|
|
<!-- <span>{{ viewLabel || '请选择' }}</span> -->
|
|
<span>视图</span>
|
|
<SvgIcon class="gap" name="arrow-botton" size="14" />
|
|
</div>
|
|
</n-popselect>
|
|
<div style="margin-left: 15px;cursor: pointer;color:#323233" @click="sortHandler('pictureResult')">
|
|
<span>相似度排序</span>
|
|
<SvgIcon style="margin-left: 8px;" name="sort" size="12" />
|
|
</div>
|
|
<div style="margin-left: 15px;cursor: pointer;color:#323233" @click="sortHandler('fromuptime')">
|
|
<span>时间排序</span>
|
|
<SvgIcon style="margin-left: 8px;" name="sort" size="12" />
|
|
</div>
|
|
</div>
|
|
<span style="font-size: 16px;color:#494949">共 <span style="color:#7899fd;font-weight: 500;">{{ totalCount
|
|
}}</span> 项</span>
|
|
</div>
|
|
<n-spin :show="loading">
|
|
<div ref="el" class="scroll" :style="listStyle">
|
|
<!-- <n-scrollbar :on-scroll="scrollHandler"> -->
|
|
<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="img" :img-props="{ onClick: hideDownload }"
|
|
:class="{ 'img-fit': viewMode === 'horizontalVersion', 'img-full': viewMode === '3:4' || viewMode === 'verticalVersion' }"
|
|
:preview-src="item.imgUrl" :src="item.thumburl" ref="imageRef" />
|
|
<div class="percent" v-if="item.similar!=-1">
|
|
<SvgIcon size="42" name="tag" />
|
|
<div class="val">
|
|
{{ `${item.similar}%` }}
|
|
</div>
|
|
</div>
|
|
<div 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">{{ item.imgName }}</div>
|
|
<div class="icon-wrap" @click="previewHandler(index, $event)">
|
|
<SvgIcon size="13" name="magnifying-2" style="cursor: pointer;margin-left: 3px;color:#898481" />
|
|
</div>
|
|
</div>
|
|
<div class="left">
|
|
<n-avatar :src="getAvatar(item.uphead)" class="avatar" round />
|
|
<span>{{ item.upname }}</span>
|
|
</div>
|
|
<!-- <div class="right">
|
|
<span :style="{ marginRight: '5px' }">分类</span>
|
|
<span>{{ item.ocrPictureclass?.classname }}</span>
|
|
</div> -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- </n-scrollbar> -->
|
|
</div>
|
|
</n-spin>
|
|
</div>
|
|
<PackageSettingsModal ref="packageModalRef" @commit="commitHandler" />
|
|
<GeneratePackageModal ref="generateModalRef" />
|
|
<QueryRepeatedTasksModal ref="queryRepeatedTasksModalRef" @closeCallback="tasksLoadingCloseCallback"/>
|
|
<LoginSuccessModal ref="LoginSuccessModalRef" />
|
|
<FinishPackageModal ref="finishPackageModal" />
|
|
<CheckingTaskModal ref="checkingTaskModalRef" @refresh="refresh" @cancel="cancel" />
|
|
</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;
|
|
}
|
|
|
|
.img {
|
|
border-radius: 7px;
|
|
display: block;
|
|
height: calc(100% - 25px);
|
|
}
|
|
|
|
.img-fit {
|
|
width: 100%;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.img-full {
|
|
width: 100%;
|
|
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: hidden;
|
|
position: relative;
|
|
transition: 0.5s;
|
|
|
|
.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, .35);
|
|
border-radius: 7px;
|
|
|
|
.img-name {
|
|
width: 70%;
|
|
color: #FFF;
|
|
/* 设置文本溢出时的样式为省略号 */
|
|
text-overflow: ellipsis;
|
|
/* 隐藏超出容器的文本 */
|
|
overflow: hidden;
|
|
/* 确保文本不换行 */
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.icon-wrap {
|
|
width: 22px;
|
|
height: 22px;
|
|
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: 0px;
|
|
top: -6px;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
</style>
|