diff --git a/web/src/components/FileSystem.vue b/web/src/components/FileSystem.vue index 9087338..d07f64c 100644 --- a/web/src/components/FileSystem.vue +++ b/web/src/components/FileSystem.vue @@ -108,7 +108,30 @@ -
+
+
+ 图片预览 +
+ + 加载中... +
+
+
+ {{ getImageType() }} + {{ formatBytes(imageSize) }} +
+
+ + +
@@ -118,7 +141,9 @@ placeholder="文件内容将显示在这里,点击文件列表中的文件即可查看" />
+
- 读取 + {{ isImageFile ? '预览' : '读取' }} - + @@ -260,6 +285,12 @@ const pathHistory = ref([]) const fileContentHeight = ref(250) const favoriteFiles = ref([]) +// 图片预览相关 +const isImageFile = ref(false) +const imagePreviewUrl = ref('') +const imageLoading = ref(false) +const imageSize = ref(0) + // 格式化文件大小 const formatBytes = (bytes) => { if (!bytes) return '0 B' @@ -382,13 +413,10 @@ const readFile = async () => { // 检查文件类型 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)) { - Message.warning({ - content: '图片文件暂不支持预览,请使用图片查看器打开', - duration: 3000 - }) + await previewImage() return } @@ -438,8 +466,71 @@ const readFile = async () => { 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 () => { + isImageFile.value = false fileLoading.value = true try { const content = await readFileApi(filePath.value) @@ -526,6 +617,8 @@ const deleteFile = async () => { // 清空内容 const clearContent = () => { fileContent.value = '' + isImageFile.value = false + imagePreviewUrl.value = '' Message.info('内容已清空') } @@ -750,6 +843,54 @@ onMounted(() => { 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 { position: relative; overflow: hidden;