|
|
@ -16,16 +16,51 @@ |
|
|
</el-row> |
|
|
</el-row> |
|
|
<el-row> |
|
|
<el-row> |
|
|
<!-- 图像区域 --> |
|
|
<!-- 图像区域 --> |
|
|
<ul class="content-image" v-viewer> |
|
|
|
|
|
<li v-for="(item, index) in descImgs" :key="index" style="float: left;display: inline"> |
|
|
|
|
|
<img :src="item.url" style="width:70px;height: 70px"/> |
|
|
|
|
|
|
|
|
<div class="content-image" style="display: flex; flex-wrap: wrap; gap: 10px; min-height: 100px;" @click.stop> |
|
|
|
|
|
<div v-for="(item, index) in descImgs" :key="index" style="position: relative; width: 80px; height: 80px;"> |
|
|
|
|
|
<!-- 加载中状态 --> |
|
|
|
|
|
<div v-if="item.loading" style="width: 80px; height: 80px; border: 1px solid #ddd; border-radius: 4px; display: flex; justify-content: center; align-items: center; background: #f5f7fa;"> |
|
|
|
|
|
<i class="el-icon-loading" style="font-size: 24px; color: #409EFF;"></i> |
|
|
|
|
|
</div> |
|
|
|
|
|
<!-- 图片显示 --> |
|
|
|
|
|
<div v-else-if="item.blobUrl" @click.stop="previewImage(index)" style="width: 80px; height: 80px; border: 1px solid #67C23A; border-radius: 4px; overflow: hidden; cursor: pointer;"> |
|
|
|
|
|
<img :src="item.blobUrl" style="width: 100%; height: 100%; object-fit: cover;"> |
|
|
|
|
|
</div> |
|
|
|
|
|
<!-- 加载失败 --> |
|
|
|
|
|
<div v-else style="width: 80px; height: 80px; border: 1px solid #F56C6C; border-radius: 4px; display: flex; justify-content: center; align-items: center; background: #FEF0F0;"> |
|
|
|
|
|
<i class="el-icon-picture-outline" style="font-size: 24px; color: #F56C6C;"></i> |
|
|
|
|
|
</div> |
|
|
<!-- 删除图标 --> |
|
|
<!-- 删除图标 --> |
|
|
<div class="delete-img"> |
|
|
|
|
|
<i class="el-icon-delete" @click="deleteImage(index,item.id)"></i> |
|
|
|
|
|
|
|
|
<div v-if="!item.loading" @click.stop="deleteImage(index,item.id)" style="position: absolute; top: 2px; right: 2px; background: rgba(255,255,255,0.9); border-radius: 3px; padding: 2px; cursor: pointer; box-shadow: 0 0 3px rgba(0,0,0,0.3); z-index: 10;"> |
|
|
|
|
|
<i class="el-icon-delete" style="color: #f56c6c; font-size: 16px;"></i> |
|
|
</div> |
|
|
</div> |
|
|
</li> |
|
|
|
|
|
</ul> |
|
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
<!-- 空状态提示 --> |
|
|
|
|
|
<div v-if="descImgs.length === 0" style="width: 100%; text-align: center; padding: 20px; color: #909399;"> |
|
|
|
|
|
暂无图片 |
|
|
|
|
|
</div> |
|
|
|
|
|
</div> |
|
|
</el-row> |
|
|
</el-row> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 图片预览对话框 --> |
|
|
|
|
|
<el-dialog |
|
|
|
|
|
:visible.sync="previewVisible" |
|
|
|
|
|
width="80%" |
|
|
|
|
|
append-to-body |
|
|
|
|
|
:close-on-click-modal="true" |
|
|
|
|
|
@open="handlePreviewOpen" |
|
|
|
|
|
@close="handlePreviewClose" |
|
|
|
|
|
:modal-append-to-body="true" |
|
|
|
|
|
custom-class="image-preview-dialog"> |
|
|
|
|
|
<div style="text-align: center; background: #000; padding: 20px; min-height: 400px; display: flex; align-items: center; justify-content: center;"> |
|
|
|
|
|
<img :src="previewUrl" style="max-width: 100%; max-height: 70vh; display: block; margin: 0 auto;"> |
|
|
|
|
|
</div> |
|
|
|
|
|
<div slot="footer" style="text-align: center; padding: 10px;"> |
|
|
|
|
|
<el-button @click="prevImage" :disabled="previewIndex <= 0" icon="el-icon-arrow-left">上一张</el-button> |
|
|
|
|
|
<span style="margin: 0 20px; color: #606266;">{{ previewIndex + 1 }} / {{ allBlobUrls.length }}</span> |
|
|
|
|
|
<el-button @click="nextImage" :disabled="previewIndex >= allBlobUrls.length - 1">下一张<i class="el-icon-arrow-right el-icon--right"></i></el-button> |
|
|
|
|
|
</div> |
|
|
|
|
|
</el-dialog> |
|
|
<el-row> |
|
|
<el-row> |
|
|
<el-col :span="24"> |
|
|
<el-col :span="24"> |
|
|
<el-upload class="customer-upload" drag multiple :file-list="fileList" |
|
|
<el-upload class="customer-upload" drag multiple :file-list="fileList" |
|
|
@ -49,6 +84,7 @@ |
|
|
|
|
|
|
|
|
<script> |
|
|
<script> |
|
|
import {uploadEamObjectFile} from '@/api/eam/com_eam_object_upload_file.js'; |
|
|
import {uploadEamObjectFile} from '@/api/eam/com_eam_object_upload_file.js'; |
|
|
|
|
|
import {downLoadObjectFile} from '@/api/eam/eam_object_list.js'; |
|
|
import { |
|
|
import { |
|
|
searchItemFileUrl, // 查询文件路径 |
|
|
searchItemFileUrl, // 查询文件路径 |
|
|
imageDelete, // 删除图片 |
|
|
imageDelete, // 删除图片 |
|
|
@ -74,9 +110,72 @@ export default { |
|
|
}, |
|
|
}, |
|
|
dataListLoading: false, |
|
|
dataListLoading: false, |
|
|
descImgs: [], |
|
|
descImgs: [], |
|
|
|
|
|
previewVisible: false, |
|
|
|
|
|
previewUrl: '', |
|
|
|
|
|
previewIndex: 0, |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
computed: { |
|
|
|
|
|
// 所有图片Blob URL数组,供预览使用 |
|
|
|
|
|
allBlobUrls() { |
|
|
|
|
|
return this.descImgs.filter(item => item.blobUrl).map(item => item.blobUrl) |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
methods: { |
|
|
methods: { |
|
|
|
|
|
// 预览图片 |
|
|
|
|
|
previewImage(index) { |
|
|
|
|
|
// 找到实际有blobUrl的图片索引 |
|
|
|
|
|
const validImages = this.descImgs.filter(item => item.blobUrl) |
|
|
|
|
|
const actualIndex = validImages.findIndex((item, i) => { |
|
|
|
|
|
return this.descImgs[index] === item |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
if (actualIndex >= 0 && validImages[actualIndex]) { |
|
|
|
|
|
this.previewIndex = actualIndex |
|
|
|
|
|
this.previewUrl = validImages[actualIndex].blobUrl |
|
|
|
|
|
this.previewVisible = true |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 上一张 |
|
|
|
|
|
prevImage() { |
|
|
|
|
|
if (this.previewIndex > 0) { |
|
|
|
|
|
this.previewIndex-- |
|
|
|
|
|
this.previewUrl = this.allBlobUrls[this.previewIndex] |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 下一张 |
|
|
|
|
|
nextImage() { |
|
|
|
|
|
if (this.previewIndex < this.allBlobUrls.length - 1) { |
|
|
|
|
|
this.previewIndex++ |
|
|
|
|
|
this.previewUrl = this.allBlobUrls[this.previewIndex] |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 键盘事件处理 |
|
|
|
|
|
handleKeydown(e) { |
|
|
|
|
|
if (!this.previewVisible) return |
|
|
|
|
|
|
|
|
|
|
|
if (e.key === 'ArrowLeft' || e.keyCode === 37) { |
|
|
|
|
|
this.prevImage() |
|
|
|
|
|
} else if (e.key === 'ArrowRight' || e.keyCode === 39) { |
|
|
|
|
|
this.nextImage() |
|
|
|
|
|
} else if (e.key === 'Escape' || e.keyCode === 27) { |
|
|
|
|
|
this.previewVisible = false |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 预览打开时 |
|
|
|
|
|
handlePreviewOpen() { |
|
|
|
|
|
document.addEventListener('keydown', this.handleKeydown) |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 预览关闭时 |
|
|
|
|
|
handlePreviewClose() { |
|
|
|
|
|
document.removeEventListener('keydown', this.handleKeydown) |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
// 初始化组件的参数 |
|
|
// 初始化组件的参数 |
|
|
init (currentRow) { |
|
|
init (currentRow) { |
|
|
// 初始化参数 |
|
|
// 初始化参数 |
|
|
@ -84,21 +183,61 @@ export default { |
|
|
// 打开页面 |
|
|
// 打开页面 |
|
|
this.visible = true |
|
|
this.visible = true |
|
|
this.descImgs = [] |
|
|
this.descImgs = [] |
|
|
|
|
|
this.previewVisible = false |
|
|
this.searchItemFileUrl() |
|
|
this.searchItemFileUrl() |
|
|
}, |
|
|
}, |
|
|
// 查询图片列表 |
|
|
// 查询图片列表 |
|
|
searchItemFileUrl () { |
|
|
searchItemFileUrl () { |
|
|
|
|
|
// 释放旧的Blob URL |
|
|
|
|
|
this.descImgs.forEach(item => { |
|
|
|
|
|
if (item.blobUrl) { |
|
|
|
|
|
URL.revokeObjectURL(item.blobUrl) |
|
|
|
|
|
} |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
this.descImgs = [] |
|
|
this.descImgs = [] |
|
|
searchItemFileUrl(this.pageData).then(({data}) => { |
|
|
searchItemFileUrl(this.pageData).then(({data}) => { |
|
|
if (data.code === 0) { |
|
|
if (data.code === 0) { |
|
|
for (let i = 0; i < data.rows.length; i++) { |
|
|
|
|
|
this.descImgs.push(data.rows[i]) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 先添加原始数据,显示占位符 |
|
|
|
|
|
this.descImgs = data.rows.map(item => ({ |
|
|
|
|
|
...item, |
|
|
|
|
|
blobUrl: null, |
|
|
|
|
|
loading: true |
|
|
|
|
|
})) |
|
|
|
|
|
|
|
|
|
|
|
// 逐个下载图片并转换为Blob URL |
|
|
|
|
|
this.descImgs.forEach((item, index) => { |
|
|
|
|
|
this.loadImageAsBlob(item, index) |
|
|
|
|
|
}) |
|
|
} else { |
|
|
} else { |
|
|
this.$message.warning(data.msg) |
|
|
this.$message.warning(data.msg) |
|
|
} |
|
|
} |
|
|
}) |
|
|
}) |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 下载图片并转换为Blob URL |
|
|
|
|
|
loadImageAsBlob(item, index) { |
|
|
|
|
|
if (!item.id) { |
|
|
|
|
|
console.error('图片没有ID:', item) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
downLoadObjectFile({id: item.id}).then(({data}) => { |
|
|
|
|
|
// 创建Blob对象 |
|
|
|
|
|
const blob = new Blob([data], {type: 'image/png'}) |
|
|
|
|
|
// 生成本地URL |
|
|
|
|
|
const blobUrl = URL.createObjectURL(blob) |
|
|
|
|
|
|
|
|
|
|
|
// 更新图片URL |
|
|
|
|
|
this.$set(this.descImgs[index], 'blobUrl', blobUrl) |
|
|
|
|
|
this.$set(this.descImgs[index], 'loading', false) |
|
|
|
|
|
|
|
|
|
|
|
console.log('✅ 图片加载成功:', item.url, '-> Blob URL') |
|
|
|
|
|
}).catch(error => { |
|
|
|
|
|
console.error('❌ 图片下载失败:', item.url, error) |
|
|
|
|
|
this.$set(this.descImgs[index], 'loading', false) |
|
|
|
|
|
}) |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
// 上传之前 |
|
|
// 上传之前 |
|
|
beforeUploadHandle (file) { |
|
|
beforeUploadHandle (file) { |
|
|
@ -116,6 +255,12 @@ export default { |
|
|
this.fileList = [] |
|
|
this.fileList = [] |
|
|
// 清空文件上传记录 |
|
|
// 清空文件上传记录 |
|
|
this.$refs.uploadFile.clearFiles() |
|
|
this.$refs.uploadFile.clearFiles() |
|
|
|
|
|
// 释放Blob URL,避免内存泄漏 |
|
|
|
|
|
this.descImgs.forEach(item => { |
|
|
|
|
|
if (item.blobUrl) { |
|
|
|
|
|
URL.revokeObjectURL(item.blobUrl) |
|
|
|
|
|
} |
|
|
|
|
|
}) |
|
|
// 刷新报工的页面 |
|
|
// 刷新报工的页面 |
|
|
//this.$emit('refreshPageTables2') |
|
|
//this.$emit('refreshPageTables2') |
|
|
// 关闭当前的页面 |
|
|
// 关闭当前的页面 |
|
|
@ -186,8 +331,35 @@ export default { |
|
|
//this.$emit('refreshPageTables2') |
|
|
//this.$emit('refreshPageTables2') |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
beforeDestroy() { |
|
|
|
|
|
// 组件销毁时清理事件监听 |
|
|
|
|
|
document.removeEventListener('keydown', this.handleKeydown) |
|
|
|
|
|
// 释放所有Blob URL |
|
|
|
|
|
this.descImgs.forEach(item => { |
|
|
|
|
|
if (item.blobUrl) { |
|
|
|
|
|
URL.revokeObjectURL(item.blobUrl) |
|
|
|
|
|
} |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
</script> |
|
|
</script> |
|
|
<style scoped lang="scss"> |
|
|
<style scoped lang="scss"> |
|
|
|
|
|
|
|
|
</style> |
|
|
</style> |
|
|
|
|
|
<style> |
|
|
|
|
|
/* 预览对话框样式 */ |
|
|
|
|
|
.image-preview-dialog .el-dialog__header { |
|
|
|
|
|
padding: 10px 20px; |
|
|
|
|
|
background: #f5f7fa; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.image-preview-dialog .el-dialog__body { |
|
|
|
|
|
padding: 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.image-preview-dialog .el-dialog__footer { |
|
|
|
|
|
padding: 10px 20px; |
|
|
|
|
|
background: #f5f7fa; |
|
|
|
|
|
} |
|
|
|
|
|
</style> |