fix bug
This commit is contained in:
@@ -0,0 +1,333 @@
|
||||
<template>
|
||||
<div class="vehicle-attachment-table">
|
||||
<div class="vehicle-attachment-table__toolbar">
|
||||
<el-button type="primary" :disabled="!rows.length" @click="batchDownload">
|
||||
批量下载
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="rows" border @selection-change="selectedRows = $event">
|
||||
<el-table-column v-if="!readonly" type="selection" width="55" align="center" />
|
||||
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||
<el-table-column label="文件名" min-width="240" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span
|
||||
class="vehicle-attachment-table__file-name"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="previewAttachment(row)"
|
||||
@keydown.enter="previewAttachment(row)"
|
||||
@keydown.space.prevent="previewAttachment(row)"
|
||||
>
|
||||
{{ attachmentName(row) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="附件描述" min-width="220">
|
||||
<template #default="{ row, $index }">
|
||||
<span v-if="readonly">{{ row.description || '-' }}</span>
|
||||
<el-input
|
||||
v-else
|
||||
:model-value="row.description"
|
||||
maxlength="200"
|
||||
placeholder="请输入"
|
||||
@update:model-value="value => updateDescription($index, value)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文件大小" width="120" align="center">
|
||||
<template #default="{ row }">{{ formatFileSize(row.size) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="uploadUserName" label="上传人" width="140" align="center" />
|
||||
<el-table-column prop="uploadTime" label="上传时间" width="180" align="center" sortable />
|
||||
<el-table-column v-if="!readonly" label="操作" width="100" align="center" fixed="right">
|
||||
<template #default="{ $index }">
|
||||
<el-link type="danger" @click="removeAttachment($index)">删除</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div v-if="!readonly" class="vehicle-attachment-table__upload">
|
||||
<vehicle-attachment-upload
|
||||
:model-value="rows"
|
||||
:file-types="attachmentFileTypes"
|
||||
:max-size="500"
|
||||
:show-tip="true"
|
||||
tip="支持pdf、bmp、jpeg、png、jpg、doc、docx、ppt、pptx、xlsx、xls、eml、msg、zip的文件格式,单个文件不超过500M"
|
||||
:show-file-list="false"
|
||||
button-text="上传附件"
|
||||
@update:model-value="handleUploadChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
v-model="documentPreviewVisible"
|
||||
:title="previewFile.name || '附件预览'"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
width="90%"
|
||||
top="4vh"
|
||||
class="vehicle-attachment-table__viewer-dialog"
|
||||
>
|
||||
<pdf-preview
|
||||
v-if="documentPreviewVisible && previewFile.url && previewFile.isPdf"
|
||||
:source="previewFile.url"
|
||||
@error="handlePreviewError"
|
||||
/>
|
||||
<open-file-viewer
|
||||
v-else-if="documentPreviewVisible && previewFile.url"
|
||||
:file="previewFile.url"
|
||||
:file-name="previewFile.name"
|
||||
:mime-type="previewFile.mimeType"
|
||||
width="100%"
|
||||
height="72vh"
|
||||
fit="contain"
|
||||
theme="auto"
|
||||
locale="zh-CN"
|
||||
:toolbar="viewerToolbar"
|
||||
:plugins="viewerPlugins"
|
||||
@unsupported="handlePreviewUnsupported"
|
||||
@error="handlePreviewError"
|
||||
/>
|
||||
</el-dialog>
|
||||
|
||||
<el-image-viewer
|
||||
v-if="imagePreviewVisible"
|
||||
:url-list="imagePreviewUrls"
|
||||
:initial-index="imagePreviewIndex"
|
||||
@close="imagePreviewVisible = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ElImageViewer } from 'element-plus';
|
||||
import { OpenFileViewer } from '@open-file-viewer/vue';
|
||||
import { fallbackPlugin, imagePlugin, officePlugin, textPlugin } from '@open-file-viewer/core';
|
||||
import '@open-file-viewer/core/style.css';
|
||||
import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
|
||||
import PdfPreview from '@/components/pdf-preview/main.vue';
|
||||
import { downloadFileByUrl } from '@/utils/util';
|
||||
|
||||
const attachmentFileTypes = [
|
||||
'pdf',
|
||||
'bmp',
|
||||
'jpeg',
|
||||
'png',
|
||||
'jpg',
|
||||
'doc',
|
||||
'docx',
|
||||
'ppt',
|
||||
'pptx',
|
||||
'xlsx',
|
||||
'xls',
|
||||
'eml',
|
||||
'msg',
|
||||
'zip',
|
||||
];
|
||||
|
||||
const viewerPlugins = [
|
||||
imagePlugin(),
|
||||
officePlugin({ pdf: { workerSrc: pdfWorkerSrc, useFetchData: true } }),
|
||||
textPlugin(),
|
||||
fallbackPlugin(),
|
||||
];
|
||||
|
||||
export default {
|
||||
name: 'VehicleAttachmentTable',
|
||||
components: {
|
||||
ElImageViewer,
|
||||
OpenFileViewer,
|
||||
PdfPreview,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [Array, String, Object],
|
||||
default: () => [],
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
data() {
|
||||
return {
|
||||
attachmentFileTypes,
|
||||
selectedRows: [],
|
||||
imagePreviewVisible: false,
|
||||
imagePreviewUrls: [],
|
||||
imagePreviewIndex: 0,
|
||||
documentPreviewVisible: false,
|
||||
previewFile: {},
|
||||
viewerPlugins,
|
||||
viewerToolbar: {
|
||||
download: true,
|
||||
fullscreen: true,
|
||||
print: true,
|
||||
rotate: true,
|
||||
zoom: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
rows() {
|
||||
if (Array.isArray(this.modelValue)) return this.modelValue;
|
||||
if (this.modelValue && typeof this.modelValue === 'object') return [this.modelValue];
|
||||
if (!this.modelValue) return [];
|
||||
try {
|
||||
const rows = JSON.parse(this.modelValue);
|
||||
return Array.isArray(rows) ? rows : [];
|
||||
} catch (error) {
|
||||
return String(this.modelValue)
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean)
|
||||
.map(item => ({ name: item, url: item }));
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
attachmentName(row = {}) {
|
||||
return row.originalName || row.name || row.fileName || '附件';
|
||||
},
|
||||
attachmentUrl(row = {}) {
|
||||
return row.url || row.link || row.fileUrl || row.downloadUrl || row.domain || row.src || '';
|
||||
},
|
||||
attachmentExtension(row = {}) {
|
||||
const source = String(this.attachmentName(row) || this.attachmentUrl(row)).split('?')[0];
|
||||
const index = source.lastIndexOf('.');
|
||||
return index > -1 ? source.slice(index + 1).toLowerCase() : '';
|
||||
},
|
||||
isImage(row) {
|
||||
return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(this.attachmentExtension(row));
|
||||
},
|
||||
isPdf(row) {
|
||||
const mimeType = String(row.mimeType || row.contentType || '').toLowerCase();
|
||||
return mimeType.includes('application/pdf') || this.attachmentExtension(row) === 'pdf';
|
||||
},
|
||||
emitRows(rows) {
|
||||
this.$emit('update:modelValue', rows);
|
||||
},
|
||||
handleUploadChange(rows) {
|
||||
const uploadUserName = this.$store.getters.userInfo?.realName || '';
|
||||
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
this.emitRows(
|
||||
(rows || []).map(row => ({
|
||||
...row,
|
||||
description: row.description || '',
|
||||
uploadUserName: row.uploadUserName || uploadUserName,
|
||||
uploadTime: row.uploadTime || uploadTime,
|
||||
}))
|
||||
);
|
||||
},
|
||||
updateDescription(index, description) {
|
||||
const rows = this.rows.map((row, rowIndex) =>
|
||||
rowIndex === index ? { ...row, description } : row
|
||||
);
|
||||
this.emitRows(rows);
|
||||
},
|
||||
removeAttachment(index) {
|
||||
const rows = [...this.rows];
|
||||
rows.splice(index, 1);
|
||||
this.emitRows(rows);
|
||||
},
|
||||
formatFileSize(size) {
|
||||
if (!size) return '';
|
||||
const value = Number(size);
|
||||
if (!Number.isFinite(value)) return String(size);
|
||||
if (value < 1024) return `${value}B`;
|
||||
if (value < 1024 * 1024) return `${(value / 1024).toFixed(1)}KB`;
|
||||
return `${(value / 1024 / 1024).toFixed(1)}MB`;
|
||||
},
|
||||
downloadAttachment(row) {
|
||||
const url = this.attachmentUrl(row);
|
||||
if (!url) {
|
||||
this.$message.warning('附件地址为空,无法下载');
|
||||
return;
|
||||
}
|
||||
downloadFileByUrl(url, this.attachmentName(row));
|
||||
},
|
||||
batchDownload() {
|
||||
const rows = this.selectedRows.length ? this.selectedRows : this.rows;
|
||||
rows.forEach(this.downloadAttachment);
|
||||
},
|
||||
previewAttachment(row) {
|
||||
const url = this.attachmentUrl(row);
|
||||
if (!url) {
|
||||
this.$message.warning('附件地址为空,无法预览');
|
||||
return;
|
||||
}
|
||||
if (this.isImage(row)) {
|
||||
this.imagePreviewUrls = this.rows
|
||||
.filter(item => this.isImage(item) && this.attachmentUrl(item))
|
||||
.map(item => this.attachmentUrl(item));
|
||||
this.imagePreviewIndex = Math.max(this.imagePreviewUrls.indexOf(url), 0);
|
||||
this.imagePreviewVisible = true;
|
||||
return;
|
||||
}
|
||||
const isPdf = this.isPdf(row);
|
||||
this.previewFile = {
|
||||
name: this.attachmentName(row),
|
||||
url,
|
||||
mimeType: isPdf ? 'application/pdf' : row.mimeType || row.contentType || '',
|
||||
isPdf,
|
||||
};
|
||||
this.documentPreviewVisible = true;
|
||||
},
|
||||
handlePreviewUnsupported() {
|
||||
this.$message.warning('当前文件暂不支持在线预览');
|
||||
},
|
||||
handlePreviewError() {
|
||||
this.$message.error('附件预览失败');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.vehicle-attachment-table {
|
||||
width: 100%;
|
||||
|
||||
&__toolbar {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
&__file-name {
|
||||
color: #409eff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__file-name:hover,
|
||||
&__file-name:focus-visible {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&__upload {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
margin-top: 12px;
|
||||
|
||||
:deep(.vehicle-attachment-upload) {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-table th.el-table__cell) {
|
||||
height: 42px;
|
||||
padding: 8px 0;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
:deep(.el-table td.el-table__cell) {
|
||||
height: 52px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.vehicle-attachment-table__viewer-dialog .el-dialog__body) {
|
||||
padding: 12px 16px 16px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user