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

287 lines
7.9 KiB
Vue

<!-- 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">
<el-image
:src="imageUrl"
:preview-src-list="finalPreviewList"
class="license-image"
fit="fill"
hide-on-click-modal
:style="imageStyle"
>
<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>
<!-- <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> -->
<ViewFile v-if="dialogVisible" :file="fileData" @closeDialog="handleCloseDialog" :without-modal="withoutModal" />
</div>
</div>
</template>
<script>
import ViewFile from './ViewFile';
export default {
name: 'FileOrImageDisplay',
components: {
ViewFile
},
data() {
return {
dialogVisible: false,
fileData: {},
}
},
props: {
// 标签名称
label: {
type: String,
default: ''
},
// 文件对象
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'
},
// 固定图片宽度
imageWidth: {
type: [Number, String],
default: 600
},
// 固定图片高度
imageHeight: {
type: [Number, String],
default: 400
},
withoutModal: {
type: Boolean,
default: false
}
},
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]
},
imageStyle() {
const width = typeof this.imageWidth === 'number' ? `${this.imageWidth}px` : this.imageWidth
const height = typeof this.imageHeight === 'number' ? `${this.imageHeight}px` : this.imageHeight
return {
width,
height
}
},
},
methods: {
// 文件点击事件
handleFileClick() {
console.log(this.file);
this.fileData = this.file
this.dialogVisible = true
},
// 图片点击事件
handleImageClick() {
this.$emit('image-click', this.imageUrl)
},
handleDocumentReady() {
console.log('文档已准备就绪')
},
handleAppReady() {
console.log('应用已准备就绪')
},
handleError(error) {
console.log('错误:', error)
},
handleInitialized() {
console.log('初始化完成')
},
handleCloseDialog() {
this.dialogVisible = false
this.fileData = {}
},
}
}
</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 {
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
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;
}
}
}
</style>