新增:图片文件预览功能
This commit is contained in:
@@ -108,7 +108,30 @@
|
|||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-card title="📝 文件内容">
|
<a-card title="📝 文件内容">
|
||||||
<a-space direction="vertical" :size="8" style="width: 100%">
|
<a-space direction="vertical" :size="8" style="width: 100%">
|
||||||
<div
|
|
||||||
|
<!-- 图片预览 -->
|
||||||
|
<div v-if="isImageFile" class="image-preview-container">
|
||||||
|
<div class="image-preview-wrapper">
|
||||||
|
<img
|
||||||
|
:src="imagePreviewUrl"
|
||||||
|
class="preview-image"
|
||||||
|
@load="onImageLoad"
|
||||||
|
@error="onImageError"
|
||||||
|
alt="图片预览"
|
||||||
|
/>
|
||||||
|
<div v-if="imageLoading" class="image-loading">
|
||||||
|
<a-spin />
|
||||||
|
<span>加载中...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="image-info">
|
||||||
|
<a-tag>{{ getImageType() }}</a-tag>
|
||||||
|
<span v-if="imageSize">{{ formatBytes(imageSize) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 文本编辑器 -->
|
||||||
|
<div v-else
|
||||||
class="file-content-wrapper"
|
class="file-content-wrapper"
|
||||||
:style="{ height: fileContentHeight + 'px' }"
|
:style="{ height: fileContentHeight + 'px' }"
|
||||||
>
|
>
|
||||||
@@ -118,7 +141,9 @@
|
|||||||
placeholder="文件内容将显示在这里,点击文件列表中的文件即可查看"
|
placeholder="文件内容将显示在这里,点击文件列表中的文件即可查看"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
v-if="!isImageFile"
|
||||||
class="resize-handle"
|
class="resize-handle"
|
||||||
@mousedown="startResize"
|
@mousedown="startResize"
|
||||||
title="拖拽调整高度"
|
title="拖拽调整高度"
|
||||||
@@ -130,9 +155,9 @@
|
|||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-file />
|
<icon-file />
|
||||||
</template>
|
</template>
|
||||||
读取
|
{{ isImageFile ? '预览' : '读取' }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button @click="writeFile" :loading="fileLoading">
|
<a-button @click="writeFile" :loading="fileLoading" v-if="!isImageFile">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-save />
|
<icon-save />
|
||||||
</template>
|
</template>
|
||||||
@@ -260,6 +285,12 @@ const pathHistory = ref([])
|
|||||||
const fileContentHeight = ref(250)
|
const fileContentHeight = ref(250)
|
||||||
const favoriteFiles = ref([])
|
const favoriteFiles = ref([])
|
||||||
|
|
||||||
|
// 图片预览相关
|
||||||
|
const isImageFile = ref(false)
|
||||||
|
const imagePreviewUrl = ref('')
|
||||||
|
const imageLoading = ref(false)
|
||||||
|
const imageSize = ref(0)
|
||||||
|
|
||||||
// 格式化文件大小
|
// 格式化文件大小
|
||||||
const formatBytes = (bytes) => {
|
const formatBytes = (bytes) => {
|
||||||
if (!bytes) return '0 B'
|
if (!bytes) return '0 B'
|
||||||
@@ -382,13 +413,10 @@ const readFile = async () => {
|
|||||||
// 检查文件类型
|
// 检查文件类型
|
||||||
const ext = filePath.value.split('.').pop()?.toLowerCase() || ''
|
const ext = filePath.value.split('.').pop()?.toLowerCase() || ''
|
||||||
|
|
||||||
// 图片文件 - 提示无法预览
|
// 图片文件 - 显示预览
|
||||||
const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp', 'ico', 'heic', 'heif']
|
const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp', 'ico']
|
||||||
if (imageExts.includes(ext)) {
|
if (imageExts.includes(ext)) {
|
||||||
Message.warning({
|
await previewImage()
|
||||||
content: '图片文件暂不支持预览,请使用图片查看器打开',
|
|
||||||
duration: 3000
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,8 +466,71 @@ const readFile = async () => {
|
|||||||
await performFileRead()
|
await performFileRead()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 预览图片
|
||||||
|
const previewImage = async () => {
|
||||||
|
if (!filePath.value) {
|
||||||
|
Message.error('请输入文件路径')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isImageFile.value = true
|
||||||
|
imageLoading.value = true
|
||||||
|
imagePreviewUrl.value = ''
|
||||||
|
imageSize.value = 0
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用文件协议直接显示图片
|
||||||
|
// 转换 Windows 路径格式
|
||||||
|
let imagePath = filePath.value.replace(/\\/g, '/')
|
||||||
|
imagePreviewUrl.value = `file:///${imagePath}`
|
||||||
|
|
||||||
|
// 获取文件大小
|
||||||
|
const file = fileList.value.find(f => f.path === filePath.value)
|
||||||
|
if (file && file.size) {
|
||||||
|
imageSize.value = file.size
|
||||||
|
}
|
||||||
|
|
||||||
|
Message.success('图片加载成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('图片预览失败:', error)
|
||||||
|
Message.error('图片预览失败: ' + (error.message || error))
|
||||||
|
isImageFile.value = false
|
||||||
|
} finally {
|
||||||
|
imageLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图片加载完成
|
||||||
|
const onImageLoad = () => {
|
||||||
|
imageLoading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图片加载失败
|
||||||
|
const onImageError = () => {
|
||||||
|
imageLoading.value = false
|
||||||
|
Message.error('图片加载失败,可能是格式不支持或文件损坏')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取图片类型标签
|
||||||
|
const getImageType = () => {
|
||||||
|
if (!filePath.value) return ''
|
||||||
|
const ext = filePath.value.split('.').pop()?.toLowerCase() || ''
|
||||||
|
const typeMap = {
|
||||||
|
'jpg': 'JPEG',
|
||||||
|
'jpeg': 'JPEG',
|
||||||
|
'png': 'PNG',
|
||||||
|
'gif': 'GIF',
|
||||||
|
'bmp': 'BMP',
|
||||||
|
'svg': 'SVG',
|
||||||
|
'webp': 'WebP',
|
||||||
|
'ico': 'ICO'
|
||||||
|
}
|
||||||
|
return typeMap[ext] || ext.toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
// 执行文件读取
|
// 执行文件读取
|
||||||
const performFileRead = async () => {
|
const performFileRead = async () => {
|
||||||
|
isImageFile.value = false
|
||||||
fileLoading.value = true
|
fileLoading.value = true
|
||||||
try {
|
try {
|
||||||
const content = await readFileApi(filePath.value)
|
const content = await readFileApi(filePath.value)
|
||||||
@@ -526,6 +617,8 @@ const deleteFile = async () => {
|
|||||||
// 清空内容
|
// 清空内容
|
||||||
const clearContent = () => {
|
const clearContent = () => {
|
||||||
fileContent.value = ''
|
fileContent.value = ''
|
||||||
|
isImageFile.value = false
|
||||||
|
imagePreviewUrl.value = ''
|
||||||
Message.info('内容已清空')
|
Message.info('内容已清空')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -750,6 +843,54 @@ onMounted(() => {
|
|||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 图片预览容器 */
|
||||||
|
.image-preview-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 300px;
|
||||||
|
max-height: 600px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--color-fill-1);
|
||||||
|
border: 1px solid var(--color-border-2);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-image {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 600px;
|
||||||
|
object-fit: contain;
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-loading {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 8px 0;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
}
|
||||||
|
|
||||||
.file-content-wrapper {
|
.file-content-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
Reference in New Issue
Block a user