420 lines
10 KiB
Vue
420 lines
10 KiB
Vue
<script lang="ts" setup>
|
|
// import 'viewerjs/dist/viewer.css'
|
|
// import { api as viewerApi } from 'v-viewer'
|
|
import { ImageOutline } from '@vicons/ionicons5'
|
|
import { useHisImage } from './hooks/hisImage'
|
|
import Panel from '@/components/Panel/index.vue'
|
|
import LImage from './components/LImage'
|
|
import JImage from './components/JImage'
|
|
import imageRoller from './components/imageRoller.vue'
|
|
|
|
|
|
|
|
|
|
const { sheshiData, showOrHideHisImage, getHisImages } = useHisImage()
|
|
|
|
// onMounted(() => {
|
|
// })
|
|
|
|
const timeRange = ref<[string, string] | undefined>()
|
|
// 处理时间选择
|
|
const handleTimeSelect = (selectedItem) => {
|
|
console.log('当前选中:', selectedItem)
|
|
// 这里可以更新大图显示或执行其他操作
|
|
}
|
|
|
|
const rangeShortcuts = {
|
|
近一天: () => {
|
|
const cur = new Date().getTime()
|
|
return [cur - 24 * 60 * 60 * 1000, cur]
|
|
},
|
|
近一周: () => {
|
|
const cur = new Date().getTime()
|
|
return [cur - 7 * 24 * 60 * 60 * 1000, cur]
|
|
},
|
|
近一月: () => {
|
|
const cur = new Date().getTime()
|
|
return [cur - 30 * 24 * 60 * 60 * 1000, cur]
|
|
},
|
|
近一年: () => {
|
|
const cur = new Date().getTime()
|
|
return [cur - 365 * 24 * 60 * 60 * 1000, cur]
|
|
},
|
|
}
|
|
const showPreview = ref(false)
|
|
const previewIndex = ref(0)
|
|
|
|
|
|
const imageList = ref([])
|
|
const isJ=ref(false);
|
|
|
|
const scrollRef = ref<HTMLElement | null>(null)
|
|
|
|
const getImage = async () => {
|
|
const imgRes = await getHisImages(timeRange.value)
|
|
// console.log(imgRes, 'imgRes')
|
|
// imageList.value = imgRes.map((item: any) => {
|
|
// return {
|
|
// ...item,
|
|
// imgPath: encodeURI(`/api/file?path=${item.parseInfo.imgPath}`),
|
|
// imgId: item.parseInfo.id,
|
|
// }
|
|
// })
|
|
imageList.value = new Array(3).fill(1).map((item, index) => {
|
|
return {
|
|
id: index,
|
|
imgPath: `/images/影像${index}.jpg`,
|
|
imgId: index,
|
|
time: '2025-04-' + (17 + index).toString().padStart(2, '0')
|
|
}
|
|
})
|
|
console.log(imageList.value,'-------largeImageList')
|
|
// console.log(imageList.value, 'imageList')
|
|
}
|
|
const btnText=ref('卷帘对比');
|
|
const images=ref([]);
|
|
const changeShow= ()=>{
|
|
btnText.value=isJ.value ? '卷帘对比' : '取消卷帘对比';
|
|
isJ.value= !isJ.value;
|
|
if(imageList.value.length > 1){
|
|
images.value = imageList.value.map(item=>{
|
|
return item.imgPath
|
|
})
|
|
}
|
|
console.log(images.value,'---imageLists')
|
|
// console.log('-------isJ',isJ.value)
|
|
|
|
}
|
|
|
|
const checkedImage = ref<null | string[]>(null)
|
|
|
|
const checkValue = e => {
|
|
e.stopPropagation()
|
|
if (checkedImage.value && checkedImage.value.length > 2) {
|
|
checkedImage.value.pop()
|
|
}
|
|
}
|
|
|
|
// watch(checkedImage, newCheck => {
|
|
// // console.log(newCheck, 'newCheck')
|
|
// })
|
|
const selectImg = (index: number) => {
|
|
// show()
|
|
// if (previewIndex.value === index) {
|
|
// showPreview.value = false
|
|
// setTimeout(() => {
|
|
// previewIndex.value = 0
|
|
// }, 300)
|
|
// return
|
|
// }
|
|
previewIndex.value = index
|
|
showPreview.value = true
|
|
activeIndex.value = index -1
|
|
}
|
|
const isCompare = ref(false)
|
|
const compareImages = () => {
|
|
if (!isCompare.value) {
|
|
if (checkedImage.value && checkedImage.value.length === 2) {
|
|
showPreview.value = true
|
|
activeIndex.value = -1
|
|
previewIndex.value = 0
|
|
showPreview.value = true
|
|
isCompare.value = true
|
|
}
|
|
} else {
|
|
isCompare.value = false
|
|
showPreview.value = false
|
|
activeIndex.value = -1
|
|
checkedImage.value = null
|
|
}
|
|
}
|
|
|
|
const largeImageList = computed(() => {
|
|
if (isCompare.value) {
|
|
return imageList.value.filter(img => checkedImage.value.includes(img.id))
|
|
} else {
|
|
return imageList.value[previewIndex.value - 1]
|
|
? [imageList.value[previewIndex.value - 1]]
|
|
: []
|
|
}
|
|
})
|
|
|
|
|
|
const activeIndex = ref(-1)
|
|
// const checkedImage = ref([])
|
|
|
|
// 计算节点位置
|
|
const getNodePosition = (index) => {
|
|
const itemWidth = 140 // 与图片项宽度一致
|
|
const gap = 16 // 与图片项间隔一致
|
|
return {
|
|
left: `${(itemWidth + gap) * index + itemWidth/2 - 4}px` // 4为时间点宽度的一半
|
|
}
|
|
}
|
|
|
|
// 时间格式化
|
|
const formatTime = (time) => {
|
|
return new Date(time).toLocaleDateString('zh-CN', {
|
|
month: '2-digit',
|
|
day: '2-digit'
|
|
})
|
|
}
|
|
|
|
// // 点击处理
|
|
// const handleSelect = (index) => {
|
|
// activeIndex.value = index;
|
|
// selectImg(index + 1)
|
|
// // 这里可以触发父组件事件
|
|
// }
|
|
</script>
|
|
|
|
<template>
|
|
<!-- <div :class="{ '!row-span-4 !row-start-2': previewIndex }"> -->
|
|
<div class="pre-container" :class="{ '!h-[95%]': showPreview }">
|
|
<panel
|
|
:title="`${sheshiData.sheShiName}历史图像`"
|
|
:show-close="true"
|
|
:closeClick="showOrHideHisImage"
|
|
>
|
|
<div class="flex flex-col w-h-full">
|
|
<div>
|
|
<n-button type="primary" @click="changeShow" v-if="isCompare">{{ btnText }}</n-button>
|
|
</div>
|
|
<div class="large-container">
|
|
<imageRoller :imgList="images" v-if="isJ"></imageRoller>
|
|
<l-image :imageList="largeImageList" v-else key="l-image" :isJ="isJ" />
|
|
</div>
|
|
<div ref="scrollRef" class="flex h-[210px] flex-col">
|
|
<div class="flex gap-2">
|
|
<n-date-picker
|
|
v-model:formatted-value="timeRange"
|
|
clearable
|
|
type="datetimerange"
|
|
:shortcuts="rangeShortcuts"
|
|
:update-value-on-close="true"
|
|
format="yyyy-MM-dd HH:mm:ss"
|
|
/>
|
|
<n-button type="primary" @click="getImage">搜索</n-button>
|
|
<n-button
|
|
type="primary"
|
|
:disabled="
|
|
!isCompare && (!checkedImage || checkedImage.length < 2)
|
|
"
|
|
@click="compareImages"
|
|
>{{ isCompare ? '取消对比' : '对比' }}</n-button
|
|
>
|
|
</div>
|
|
<div v-if="imageList.length === 0" class="m-auto flex">
|
|
<n-empty description="暂无数据"> </n-empty>
|
|
</div>
|
|
<n-scrollbar v-else x-scrollable>
|
|
<!-- <div> -->
|
|
<n-checkbox-group
|
|
class="flex h-full flex-nowrap image-list"
|
|
v-model:value="checkedImage"
|
|
>
|
|
<div
|
|
class="flex h-full w-[140px] cursor-pointer p-2"
|
|
v-for="(i, index) in imageList"
|
|
:key="i.id"
|
|
@click="selectImg(index + 1)"
|
|
>
|
|
<div
|
|
class="flex rounded-md p-2 outline-1 outline-blue-400 w-h-full hover:outline"
|
|
:class="{
|
|
'outline outline-1 outline-blue-400':
|
|
index + 1 === previewIndex,
|
|
}"
|
|
>
|
|
<n-checkbox
|
|
class="absolute"
|
|
:value="i.id"
|
|
label=""
|
|
@click.stop="checkValue"
|
|
/>
|
|
<n-image
|
|
class="m-auto w-[240px]"
|
|
object-fit="contain"
|
|
lazy
|
|
:intersection-observer-options="{ root: scrollRef }"
|
|
preview-disabled
|
|
:src="i.imgPath"
|
|
@click="selectImg(index + 1)"
|
|
><template #error>
|
|
<n-icon :size="100" color="lightGrey">
|
|
<image-outline />
|
|
</n-icon>
|
|
</template>
|
|
</n-image>
|
|
</div>
|
|
</div>
|
|
</n-checkbox-group>
|
|
<!-- 时间线 -->
|
|
<div class="timeline-container">
|
|
|
|
<div
|
|
v-for="(i, index) in imageList"
|
|
:key="i.id"
|
|
class="timeline-node"
|
|
:class="{ active: index === activeIndex }"
|
|
@click="selectImg(index + 1)"
|
|
:style="getNodePosition(index)"
|
|
>
|
|
<div class="time-label">{{ formatTime(i.time) }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- <div style="overflow: auto">
|
|
<TimeLine
|
|
:imageList="imageList"
|
|
@select="handleTimeSelect"
|
|
/>
|
|
</div> -->
|
|
<!-- </div> -->
|
|
</n-scrollbar>
|
|
</div>
|
|
</div>
|
|
</panel>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.pre-container {
|
|
transition: all 0.3s ease-in-out;
|
|
|
|
.large-container {
|
|
@apply flex h-0 flex-1 flex-col gap-2 overflow-hidden text-center;
|
|
//
|
|
}
|
|
}
|
|
|
|
:deep(.n-scrollbar-content) {
|
|
height: calc(100% - 0px);
|
|
// img {
|
|
// display: block;
|
|
// }
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.image-timeline-container {
|
|
position: relative;
|
|
padding-bottom: 40px; /* 给时间线留出空间 */
|
|
}
|
|
|
|
.image-list {
|
|
display: flex;
|
|
gap: 16px;
|
|
overflow-x: auto;
|
|
padding-bottom: 10px;
|
|
}
|
|
|
|
.image-item {
|
|
width: 140px;
|
|
flex-shrink: 0;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.image-wrapper {
|
|
position: relative;
|
|
border: 2px solid transparent;
|
|
border-radius: 8px;
|
|
transition: all 0.3s ease;
|
|
|
|
&:hover {
|
|
border-color: #409eff;
|
|
}
|
|
|
|
&.active {
|
|
border-color: #ecf007;
|
|
box-shadow: 0 4px 12px rgba(32, 128, 240, 0.2);
|
|
}
|
|
}
|
|
|
|
.preview-image {
|
|
width: 100%;
|
|
height: 120px;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.checkbox {
|
|
position: absolute;
|
|
top: 8px;
|
|
left: 8px;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* 时间线样式 */
|
|
.timeline-container {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 28px;
|
|
}
|
|
|
|
.timeline-line {
|
|
position: absolute;
|
|
top: 20px;
|
|
left: 0;
|
|
right: 0;
|
|
height: 2px;
|
|
background: #e4e7ed;
|
|
}
|
|
|
|
.timeline-node {
|
|
position: absolute;
|
|
top: 0;
|
|
width: 12px;
|
|
height: 12px;
|
|
background: #dcdee2;
|
|
border-radius: 50%;
|
|
transform: translateY(14px);
|
|
transition: all 0.3s ease;
|
|
|
|
&::after {
|
|
content: "";
|
|
position: absolute;
|
|
left: 12px;
|
|
top: 5px;
|
|
width: 156px;
|
|
height: 2px;
|
|
background: #e4e7ed;
|
|
}
|
|
|
|
&:last-child::after {
|
|
display: none;
|
|
}
|
|
|
|
&.active {
|
|
width: 12px;
|
|
height: 12px;
|
|
background: #ecf007;
|
|
box-shadow: 0 0 0 3px rgba(32, 128, 240, 0.2);
|
|
|
|
// &::after {
|
|
// background: #ecf007;
|
|
// }
|
|
}
|
|
}
|
|
|
|
.time-label {
|
|
position: absolute;
|
|
top: -24px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
white-space: nowrap;
|
|
font-size: 12px;
|
|
color: #999;
|
|
|
|
.active & {
|
|
color: #ecf007;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
</style>
|
|
|