<template>
|
<div class="attachment-preview-pane">
|
<div v-if="!file" class="attachment-preview-pane__tip">
|
请选择左侧文件预览
|
</div>
|
<template v-else>
|
<div v-if="fileType === 'image'" class="attachment-preview-pane__toolbar">
|
<el-button-group>
|
<el-button
|
size="mini"
|
icon="el-icon-zoom-out"
|
title="缩小"
|
@click="zoom(-0.2)"
|
/>
|
<el-button size="mini" class="attachment-preview-pane__scale">
|
{{ Math.round(scale * 100) }}%
|
</el-button>
|
<el-button
|
size="mini"
|
icon="el-icon-zoom-in"
|
title="放大"
|
@click="zoom(0.2)"
|
/>
|
</el-button-group>
|
<el-button-group>
|
<el-button
|
size="mini"
|
icon="el-icon-refresh-left"
|
title="向左旋转"
|
@click="rotate(-90)"
|
/>
|
<el-button
|
size="mini"
|
icon="el-icon-refresh-right"
|
title="向右旋转"
|
@click="rotate(90)"
|
/>
|
</el-button-group>
|
<el-button size="mini" icon="el-icon-refresh" @click="reset"
|
>重置</el-button
|
>
|
</div>
|
|
<div v-if="fileType === 'image'" class="attachment-preview-pane__wrap">
|
<img
|
:src="file.url"
|
class="attachment-preview-pane__image"
|
:class="{ 'is-dragging': dragging }"
|
:style="imageStyle"
|
draggable="false"
|
alt="图片预览"
|
@mousedown="startDrag"
|
@click="toggleZoom"
|
@dragstart.prevent
|
/>
|
</div>
|
<iframe
|
v-else-if="fileType === 'pdf'"
|
:src="file.url"
|
class="attachment-preview-pane__pdf"
|
title="PDF预览"
|
/>
|
<div v-else class="attachment-preview-pane__tip">
|
该文件类型暂不支持预览,请下载后查看
|
</div>
|
</template>
|
</div>
|
</template>
|
|
<script>
|
export default {
|
name: "AttachmentPreviewPane",
|
props: {
|
file: {
|
type: Object,
|
default: null
|
}
|
},
|
data() {
|
return {
|
scale: 1,
|
deg: 0,
|
offsetX: 0,
|
offsetY: 0,
|
dragging: false,
|
dragMoved: false,
|
dragStartX: 0,
|
dragStartY: 0,
|
dragOriginX: 0,
|
dragOriginY: 0
|
};
|
},
|
computed: {
|
imageStyle() {
|
return {
|
transform:
|
"translate(" +
|
this.offsetX +
|
"px," +
|
this.offsetY +
|
"px) scale(" +
|
this.scale +
|
") rotate(" +
|
this.deg +
|
"deg)"
|
};
|
},
|
fileType() {
|
if (!this.file) {
|
return "other";
|
}
|
if (this.file.type) {
|
return this.file.type;
|
}
|
const ext = String(this.file.name || "")
|
.split(".")
|
.pop()
|
.toLowerCase();
|
if (ext === "pdf") {
|
return "pdf";
|
}
|
if (["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(ext)) {
|
return "image";
|
}
|
return "other";
|
}
|
},
|
watch: {
|
file() {
|
this.reset();
|
}
|
},
|
methods: {
|
zoom(delta) {
|
const next = Math.round((this.scale + delta) * 100) / 100;
|
this.scale = Math.min(5, Math.max(0.2, next));
|
},
|
rotate(deg) {
|
this.deg += deg;
|
},
|
reset() {
|
this.scale = 1;
|
this.deg = 0;
|
this.offsetX = 0;
|
this.offsetY = 0;
|
},
|
startDrag(e) {
|
if (e.button !== 0) return;
|
this.dragging = true;
|
this.dragMoved = false;
|
this.dragStartX = e.clientX;
|
this.dragStartY = e.clientY;
|
this.dragOriginX = this.offsetX;
|
this.dragOriginY = this.offsetY;
|
document.addEventListener("mousemove", this.onDragMove);
|
document.addEventListener("mouseup", this.onDragEnd);
|
},
|
onDragMove(e) {
|
if (!this.dragging) return;
|
const dx = e.clientX - this.dragStartX;
|
const dy = e.clientY - this.dragStartY;
|
if (Math.abs(dx) > 3 || Math.abs(dy) > 3) {
|
this.dragMoved = true;
|
}
|
this.offsetX = this.dragOriginX + dx;
|
this.offsetY = this.dragOriginY + dy;
|
},
|
onDragEnd() {
|
this.dragging = false;
|
document.removeEventListener("mousemove", this.onDragMove);
|
document.removeEventListener("mouseup", this.onDragEnd);
|
},
|
toggleZoom() {
|
if (this.dragMoved) {
|
this.dragMoved = false;
|
return;
|
}
|
this.scale = this.scale === 1 ? 1.5 : 1;
|
}
|
},
|
beforeDestroy() {
|
this.onDragEnd();
|
}
|
};
|
</script>
|
|
<style scoped>
|
.attachment-preview-pane {
|
width: 100%;
|
height: 100%;
|
min-height: 420px;
|
display: flex;
|
flex-direction: column;
|
align-items: stretch;
|
justify-content: flex-start;
|
}
|
.attachment-preview-pane__toolbar {
|
flex: 0 0 auto;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
gap: 8px;
|
padding: 4px 0 10px;
|
}
|
.attachment-preview-pane__scale {
|
min-width: 64px;
|
cursor: default;
|
}
|
.attachment-preview-pane__wrap {
|
flex: 1 1 auto;
|
min-height: 0;
|
overflow: auto;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
.attachment-preview-pane__image {
|
max-width: 100%;
|
max-height: 100%;
|
transition: transform 0.2s ease;
|
transform-origin: center center;
|
cursor: grab;
|
user-select: none;
|
}
|
.attachment-preview-pane__image.is-dragging {
|
cursor: grabbing;
|
transition: none;
|
}
|
.attachment-preview-pane__pdf {
|
flex: 1 1 auto;
|
width: 100%;
|
min-height: 420px;
|
border: 0;
|
}
|
.attachment-preview-pane__tip {
|
color: #909399;
|
font-size: 14px;
|
text-align: center;
|
padding: 20px;
|
}
|
</style>
|