Files
open-anylink-web/src/views/message/components/MsgBoxImage.vue
2025-05-18 12:26:21 +08:00

196 lines
4.7 KiB
Vue

<script setup>
import { computed } from 'vue'
import { ElImage } from 'element-plus'
import { formatFileSize } from '@/js/utils/common'
import { useImageStore } from '@/stores'
import ImageloadfailedIcon from '@/assets/svg/imageloadfailed.svg'
const props = defineProps(['sessionId', 'imgId', 'isScreenShot', 'thumbWidth', 'thumbHeight'])
const emits = defineEmits(['load'])
const imageData = useImageStore()
const maxWidth = computed(() => {
return props.isScreenShot ? Math.min(props.thumbWidth, 360) : 360
})
const maxHeight = computed(() => {
return props.isScreenShot ? Math.min(props.thumbHeight, 270) : 270
})
const renderWidth = computed(() => {
if (!props.thumbWidth || !props.thumbHeight) {
return 360 // 如果拿不到缩略图大小,默认以 360*270 尺寸显示
} else if (props.thumbWidth / props.thumbHeight > maxWidth.value / maxHeight.value) {
return maxWidth.value
} else {
return (props.thumbWidth / props.thumbHeight) * maxHeight.value
}
})
const renderHeight = computed(() => {
if (!props.thumbWidth || !props.thumbHeight) {
return 270 // 如果拿不到缩略图大小,默认以 360*270 尺寸显示
} else if (props.thumbWidth / props.thumbHeight > maxWidth.value / maxHeight.value) {
return (props.thumbHeight / props.thumbWidth) * maxWidth.value
} else {
return maxHeight.value
}
})
const onLoad = async () => {
emits('load') //向父组件暴露load事件
}
const url = computed(() => {
return imageData.image[props.imgId]?.thumbUrl
})
const imageInSessionSort = computed(() => {
const imageList = Object.values(imageData.imageInSession[props.sessionId])
return imageList.sort((a, b) => {
const bTime = new Date(b.createdTime).getTime()
const aTime = new Date(a.createdTime).getTime()
return aTime - bTime
})
})
const srcList = computed(() => {
return imageInSessionSort.value.map((item) => item.originUrl)
})
const initialIndex = computed(() => {
const imgIdList = imageInSessionSort.value.map((item) => item.objectId.toString())
return imgIdList.indexOf(props.imgId.toString())
})
const fileName = computed(() => {
return props.isScreenShot ? '' : imageData.image[props.imgId]?.fileName
})
const size = computed(() => {
return props.isScreenShot ? '' : imageData.image[props.imgId]?.size
})
const formatSize = computed(() => {
return formatFileSize(size.value)
})
</script>
<template>
<div class="image-msg-wrapper">
<el-image
:src="url"
:alt="props.imgId"
:preview-src-list="srcList"
hide-on-click-modal
preview-teleported
:initial-index="initialIndex"
:infinite="false"
:lazy="false"
fit="contain"
:style="{ width: `${renderWidth}px`, height: `${renderHeight}px` }"
@load="onLoad"
>
<template #placeholder>
<div
class="image-msg-bgc loading"
:style="{ width: `${renderWidth}px`, height: `${renderHeight}px` }"
></div>
</template>
<template #error>
<div
class="image-msg-bgc error"
:style="{ width: `${renderWidth}px`, height: `${renderHeight}px` }"
>
<ImageloadfailedIcon style="width: 48px; height: 48px; fill: #fff" />
</div>
</template>
</el-image>
<div v-if="fileName || size > 0" class="info">
<span class="name item text-ellipsis" :title="fileName">
{{ fileName || '' }}
</span>
<span class="size item text-ellipsis" :title="formatSize">
{{ formatSize }}
</span>
</div>
</div>
</template>
<style lang="scss" scoped>
.image-msg-wrapper {
display: flex;
position: relative;
.el-image {
width: auto;
height: auto;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
:deep(.el-image__inner) {
margin: 0;
}
}
.info {
width: 100%;
height: 32px;
line-height: 32px;
left: 0;
bottom: 0;
display: flex;
justify-content: space-between;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.5));
position: absolute;
.item {
max-width: 40%;
color: #fff;
font-size: 12px;
}
.name {
margin-left: 8px;
}
.size {
margin-right: 8px;
}
}
}
.image-msg-bgc {
background-color: #000;
}
.image-msg-bgc.loading::before {
content: '';
box-sizing: border-box;
position: absolute;
top: 50%;
left: 50%;
width: 30px;
height: 30px;
margin-top: -15px;
margin-left: -15px;
border-radius: 50%;
border: 3px solid #ccc;
border-top-color: #007bff;
animation: spin 1s ease-in-out infinite;
}
.image-msg-bgc.error {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>