smart-bid-web/src/views/common/FileOrImageDisplay.vue

310 lines
8.6 KiB
Vue
Raw Normal View History

2025-10-27 09:06:42 +08:00
<!-- FileOrImageDisplay.vue -->
<template>
<div class="detail-item">
<div class="item-label">{{ label }}</div>
<div class="item-value">
<!-- 文件类型 -->
<div v-if="isFile" class="file-display" @click="handleFileClick">
<i class="el-icon-document file-icon"></i>
<span class="file-name">{{ fileName }}</span>
</div>
<!-- 图片类型 -->
<div v-else-if="imageUrl" class="image-display" @click="handleImageClick">
2025-11-17 18:04:53 +08:00
<el-image
:src="imageUrl"
:preview-src-list="finalPreviewList"
class="license-image"
fit="fill"
hide-on-click-modal
:style="imageStyle"
@load="handleImageLoad"
>
2025-10-27 09:06:42 +08:00
<div slot="error" class="image-error">
<i class="el-icon-picture"></i>
<span>图片加载失败</span>
</div>
<div slot="placeholder" class="image-loading">
<i class="el-icon-loading"></i>
<span>图片加载中...</span>
</div>
</el-image>
</div>
<!-- 空状态 -->
<span v-else class="empty-text">--</span>
2025-11-17 10:36:27 +08:00
<el-dialog v-if="dialogVisible" :visible.sync="dialogVisible" width="50%" class="preview-dialog" title="文件预览">
<OnlyOfficeViewer :document-url="documentUrl" :document-title="documentTitle"
:document-key="documentKey" :mode="mode" :type="type" @document-ready="handleDocumentReady"
@app-ready="handleAppReady" @error="handleError" @initialized="handleInitialized" :force-save="true"
:save-timeout="15000" />
</el-dialog>
2025-10-27 09:06:42 +08:00
</div>
</div>
</template>
<script>
2025-11-17 10:36:27 +08:00
import OnlyOfficeViewer from './OnlyOfficeViewer';
2025-10-27 09:06:42 +08:00
export default {
name: 'FileOrImageDisplay',
2025-11-17 10:36:27 +08:00
components: {
OnlyOfficeViewer
},
data() {
return {
dialogVisible: false,
documentUrl: '',
documentTitle: '',
documentKey: '',
mode: 'view',
type: 'desktop',
2025-11-17 18:04:53 +08:00
imageDimensions: null,
2025-11-17 10:36:27 +08:00
}
},
2025-10-27 09:06:42 +08:00
props: {
// 标签名称
label: {
type: String,
2025-11-17 11:10:38 +08:00
default: ''
2025-10-27 09:06:42 +08:00
},
// 文件对象
file: {
type: Object,
default: () => null
},
// 图片URL
imageUrl: {
type: String,
default: ''
},
// 预览图片列表
previewSrcList: {
type: Array,
default: () => []
},
// 文件类型字段名
fileTypeKey: {
type: String,
default: 'fileType'
},
// 文件名字段名
fileNameKey: {
type: String,
default: 'name'
},
// 文件路径字段名
filePathKey: {
type: String,
default: 'lsFilePath'
},
// 判断为文件的类型值
fileTypeValue: {
type: [Number, String],
default: '2'
}
},
computed: {
// 是否为文件类型
isFile() {
return this.file && this.file[this.fileTypeKey] === this.fileTypeValue
},
// 文件名
fileName() {
return this.file?.[this.fileNameKey] || '未命名文件'
},
// 文件路径
filePath() {
return this.file?.[this.filePathKey]
},
// 最终预览列表
finalPreviewList() {
return this.previewSrcList.length > 0 ? this.previewSrcList : [this.imageUrl]
2025-11-17 18:04:53 +08:00
},
imageStyle() {
if (!this.imageDimensions) {
return {}
}
const maxWidth = 600
const maxHeight = 400
const widthRatio = maxWidth / this.imageDimensions.width
const heightRatio = maxHeight / this.imageDimensions.height
const ratio = Math.min(widthRatio, heightRatio, 1)
return {
width: `${Math.round(this.imageDimensions.width * ratio)}px`,
height: `${Math.round(this.imageDimensions.height * ratio)}px`
}
},
2025-10-27 09:06:42 +08:00
},
methods: {
// 文件点击事件
handleFileClick() {
2025-11-17 10:36:27 +08:00
this.documentUrl = this.file.filePath
this.documentTitle = this.file.name
this.documentKey = this.file.id + ''
this.dialogVisible = true
2025-10-27 09:06:42 +08:00
},
// 图片点击事件
handleImageClick() {
this.$emit('image-click', this.imageUrl)
2025-11-17 10:36:27 +08:00
},
2025-11-17 18:04:53 +08:00
handleImageLoad(event) {
const { naturalWidth, naturalHeight } = event?.target || {}
if (naturalWidth && naturalHeight) {
this.imageDimensions = {
width: naturalWidth,
height: naturalHeight
}
}
},
2025-11-17 10:36:27 +08:00
handleDocumentReady() {
console.log('文档已准备就绪')
},
handleAppReady() {
console.log('应用已准备就绪')
},
handleError(error) {
console.log('错误:', error)
},
handleInitialized() {
console.log('初始化完成')
},
2025-11-17 18:04:53 +08:00
},
watch: {
imageUrl() {
this.imageDimensions = null
}
2025-10-27 09:06:42 +08:00
}
}
</script>
<style scoped lang="scss">
.detail-item {
margin-bottom: 16px;
.item-label {
color: #424242;
font-size: 18px;
font-weight: 500;
margin-bottom: 8px;
}
.item-value {
color: #424242;
font-size: 16px;
line-height: 1.5;
.file-display {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
padding: 40px;
border: 2px dashed #d9d9d9;
border-radius: 8px;
background: #ffffff;
cursor: pointer;
transition: all 0.3s ease;
min-height: 240px;
width: 100%;
margin: 0 auto;
&:hover {
border-color: #409EFF;
background: #f8f9ff;
}
.file-icon {
font-size: 48px;
color: #409EFF;
}
.file-name {
font-size: 14px;
color: #333333;
text-align: center;
word-break: break-all;
line-height: 1.4;
}
}
.image-display {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0;
border: 2px dashed #d9d9d9;
border-radius: 8px;
background: #ffffff;
cursor: pointer;
transition: all 0.3s ease;
min-height: 120px;
width: 100%;
margin: 0 auto;
overflow: hidden;
&:hover {
border-color: #409EFF;
background: #f8f9ff;
}
.license-image {
2025-11-17 18:04:53 +08:00
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
2025-10-27 09:06:42 +08:00
display: block;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
object-fit: contain;
cursor: pointer;
transition: transform 0.3s ease;
&:hover {
transform: scale(1.02);
}
}
}
.empty-text {
color: #909399;
font-style: italic;
}
.image-error,
.image-loading {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 200px;
color: #909399;
gap: 8px;
border: 1px dashed #e0e0e0;
border-radius: 8px;
width: 100%;
max-width: 400px;
}
}
}
2025-11-17 10:36:27 +08:00
.preview-dialog ::v-deep .el-dialog {
height: 88vh;
display: flex;
flex-direction: column;
}
.preview-dialog ::v-deep .el-dialog__body {
flex: 1;
padding: 0;
height: 0;
/* 重要让flex布局生效 */
}
.preview-dialog ::v-deep .onlyoffice-container {
height: 100%;
}
2025-10-27 09:06:42 +08:00
</style>