@eternalheart/vue-file-preview

A modern, feature-rich file preview component for Vue 3 with support for images, videos, audio, PDFs, Office documents (Word, Excel, PowerPoint), Markdown, and code files.
8,021 35
Install
npm install @eternalheart/vue-file-preview
Latest Version:1.6.11
Vue:3
License:MIT
Last Updated:Oct 22, 2026
Links: GitHub  ·  npm
Maintainer: wh131462

Vue File Preview npm versionlicensedownloads

English | 简体中文

一个现代化、功能丰富的 Vue 3 文件预览组件,支持图片、视频、音频、PDF、Office 文档(Word、Excel、PowerPoint)、Markdown 和代码文件预览。

✨ 特性

  • 🎨 现代化 UI - 简洁现代的界面设计,流畅动画
  • 📁 多格式支持 - 支持 20+ 种文件格式
  • 🪟 两种展示模式 - 全屏弹窗 或 嵌入式内联预览
  • 🖼️ 强大的图片查看器 - 缩放、旋转、拖拽、滚轮缩放
  • 🎬 自定义视频播放器 - 基于 Video.js,支持多种视频格式
  • 🎵 自定义音频播放器 - 精美的音频控制界面
  • 📄 PDF 查看器 - 支持分页浏览
  • 📊 Office 文档支持 - Word、Excel、PowerPoint 文件预览
  • 📝 Markdown 渲染 - 支持 GitHub Flavored Markdown
  • 💻 代码高亮 - 支持 40+ 种编程语言
  • 📱 响应式设计 - 适配各种屏幕尺寸
  • ⌨️ 键盘导航 - 支持方向键、Home、End 和 ESC 键

📦 安装

# 使用 npm
npm install @eternalheart/vue-file-preview

# 使用 yarn
yarn add @eternalheart/vue-file-preview

# 使用 pnpm
pnpm add @eternalheart/vue-file-preview

重要提示: 你还需要导入 CSS 文件:

import '@eternalheart/vue-file-preview/style.css';

说明: PDF 预览所需的 pdfjs-dist 依赖会自动安装,无需额外操作。

PDF.js 配置(可选)

如果你需要预览 PDF 文件,建议配置 PDF.js 使用本地静态文件以提高性能和稳定性:

方式 1: 使用 CDN(默认)

默认情况下,组件会自动使用 unpkg CDN 加载 PDF.js,无需额外配置。

方式 2: 使用本地静态文件(推荐用于生产环境)
  1. 将 PDF.js 文件复制到你的 public 目录:
cp -r node_modules/pdfjs-dist/legacy/build/pdf.worker.min.mjs public/pdfjs/
cp -r node_modules/pdfjs-dist/cmaps public/pdfjs/
cp -r node_modules/pdfjs-dist/standard_fonts public/pdfjs/
cp -r node_modules/pdfjs-dist/wasm public/pdfjs/
  1. 在应用入口配置 PDF.js:
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf.mjs';
import { configurePdfWorker } from '@eternalheart/vue-file-preview';

configurePdfWorker(pdfjsLib, {
  workerSrc: '/pdfjs/pdf.worker.min.mjs',
  cMapUrl: '/pdfjs/cmaps/',
  cMapPacked: true,
  standardFontDataUrl: '/pdfjs/standard_fonts/',
  wasmUrl: '/pdfjs/wasm/',
});

请在客户端应用初始化阶段、首次渲染 PDF 前调用。Worker 必须使用与当前 pdfjs-dist 版本匹配的 legacy 构建。

使用 Vite 自动复制(推荐)

在 vite.config.ts 中配置自动复制:

import { defineConfig } from 'vite';
import { viteStaticCopy } from 'vite-plugin-static-copy';

export default defineConfig({
  plugins: [
    viteStaticCopy({
      targets: [
        {
          src: 'node_modules/pdfjs-dist/legacy/build/pdf.worker.min.mjs',
          dest: 'pdfjs'
        },
        {
          src: 'node_modules/pdfjs-dist/cmaps',
          dest: 'pdfjs'
        },
        {
          src: 'node_modules/pdfjs-dist/standard_fonts',
          dest: 'pdfjs'
        },
        {
          src: 'node_modules/pdfjs-dist/wasm',
          dest: 'pdfjs'
        }
      ]
    })
  ]
});

Vite 打包提示(AVIF 解码器)

如果你的项目使用 Vite 打包,且项目里安装了 @jsquash/avif(直接安装或被其它依赖间接引入),生产构建时可能报错:

[commonjs--resolver] Invalid value "iife" for option "worker.format"
- UMD and IIFE output formats are not supported for code-splitting builds.
file: .../@jsquash/avif/codec/enc/avif_enc_mt.js

原因:@jsquash/avif 内部包含一个使用代码分割的多线程 worker,而 Vite 默认的 worker.format 是 'iife',不支持多 chunk 拆分。

解决方法 —— 在你的 vite.config.ts 中加入:

export default defineConfig({
  // ... 原有配置
  worker: {
    format: 'es',
  },
});

'es' 会产生 module worker(type: 'module'),现代浏览器全部支持,且兼容代码分割。

说明:@jsquash/avif 仅在浏览器不原生支持 AVIF 时作为兜底使用(Chrome 85+、Firefox 93+、Safari 16+ 均已原生支持)。如果你的目标浏览器都覆盖原生支持范围,也可以直接从依赖中移除 @jsquash/avif。

🚀 快速开始

基础用法

<script setup lang="ts">
import { ref } from 'vue';
import { FilePreviewModal } from '@eternalheart/vue-file-preview';
import '@eternalheart/vue-file-preview/style.css';

const files = ref<File[]>([]);
const currentIndex = ref(0);
const isOpen = ref(false);

const handleFileSelect = (e: Event) => {
  const file = (e.target as HTMLInputElement).files?.[0];
  if (file) {
    files.value = [file];
    currentIndex.value = 0;
    isOpen.value = true;
  }
};
</script>

<template>
  <input type="file" @change="handleFileSelect" />

  <FilePreviewModal
    :files="files"
    :current-index="currentIndex"
    :is-open="isOpen"
    @close="isOpen = false"
    @navigate="currentIndex = $event"
  />
</template>

多种输入类型

组件支持三种类型的文件输入:

<script setup lang="ts">
import { ref } from 'vue';
import { FilePreviewModal, type PreviewFileInput } from '@eternalheart/vue-file-preview';
import '@eternalheart/vue-file-preview/style.css';

// 假设 file1 来自 File API:<input type="file">、拖拽、
// 剪贴板粘贴,或 fetch().then(r => r.blob())
const file1 = new File(['content'], 'example.txt', { type: 'text/plain' });

const files: PreviewFileInput[] = [
  // 1. 原生 File 对象(组件卸载时自动释放)
  file1,

  // 2. HTTP URL 字符串(按需加载)
  'https://example.com/image.jpg',

  // 3. 带元数据的文件对象(推荐用于远程资源)
  {
    name: 'document.pdf',
    type: 'application/pdf',
    url: '/path/to/document.pdf',
    size: 1024,
  },
];

// 内存提示: 如果你通过 URL.createObjectURL() 生成 URL,
// 文件移除时请调用 URL.revokeObjectURL() 避免内存泄漏。

const isOpen = ref(true);
</script>

<template>
  <FilePreviewModal
    :files="files"
    :current-index="0"
    :is-open="isOpen"
    @close="isOpen = false"
  />
</template>

嵌入模式 (FilePreviewEmbed)

除了全屏弹窗,组件库还提供了嵌入式变体,可以将预览内联渲染到任意容器中,适合详情面板、左右分栏布局、仪表盘等场景。

<script setup lang="ts">
import { ref } from 'vue';
import { FilePreviewEmbed } from '@eternalheart/vue-file-preview';
import '@eternalheart/vue-file-preview/style.css';

const index = ref(0);

const files = [
  'https://example.com/image.jpg',
  { name: 'document.pdf', type: 'application/pdf', url: '/doc.pdf' },
];
</script>

<template>
  <div style="width: 100%; height: 520px">
    <FilePreviewEmbed
      :files="files"
      :current-index="index"
      @navigate="index = $event"
    />
  </div>
</template>

与 FilePreviewModal 的区别:

  • 不使用 Teleport、无全屏遮罩、没有 isOpen / @close
  • 不显示关闭按钮
  • 键盘导航 (←/→/Home/End) 作用域限定在嵌入容器内 (基于 focus)
  • 尺寸默认 width: 100%; height: 100%,可通过 width / height props 覆盖
<!-- 显式指定尺寸 -->
<FilePreviewEmbed :files="files" :width="800" :height="500" />

📖 支持的文件格式

图片

  • 格式: JPG, PNG, GIF, WebP, SVG, BMP, ICO, HEIC/HEIF, AVIF, TIFF, RAW, PSD, JPEG 2000
  • 功能: 缩放 (0.01x - 10x)、旋转、拖拽、滚轮缩放、双击重置、多页 TIFF
  • 解码: HEIC/RAW/PSD 优先通过 Worker 解码,失败时回退主线程;其他高级格式按需加载

视频

  • 格式: MP4, WebM, OGG, MOV, AVI, MKV, M4V, 3GP, FLV
  • 功能: 自定义播放器、进度控制、音量调节、全屏播放

音频

  • 格式: MP3, WAV, OGG, M4A, AAC, FLAC
  • 功能: 自定义播放器、进度条、音量控制、快进/快退

文档

  • PDF: 分页浏览、缩放
  • Word: DOCX 与旧版 DOC(97–2003)支持
  • Excel: XLSX 与旧版 XLS(97–2003)支持
  • PowerPoint: PPTX 与旧版 PPT(97–2003)幻灯片预览

字体

  • 格式: TTF, OTF, WOFF, WOFF2
  • 功能: 字体元数据(字体家族、设计师、版本)、字符集预览、自定义文本输入、多字号展示

CAD / 3D 模型

  • 格式: DXF, STL, OBJ, GLTF, GLB
  • 功能: 交互式 3D 查看器(旋转/缩放/平移)、线框/实体切换、网格与坐标轴显示、自动居中

代码 & 文本

  • Markdown: GitHub Flavored Markdown,代码高亮
  • 代码文件: Vue、Svelte、Astro、JS/TS、Dart、GraphQL、Protobuf、Prisma、Terraform、PowerShell、Scala 等 40+ 种语言
  • 配置 / 日志: YAML, TOML, INI, ENV, PROPERTIES, BAT, CMD, TEX, MAP, LOG, DIFF, PATCH 等

结构化数据

  • JSON: 自动格式化 + 语法高亮
  • CSV / TSV: 零依赖解析,表格视图 + 行列统计
  • XML: DOMParser 校验 + 自动缩进 + 语法高亮

字幕 / 歌词

  • SRT / WebVTT: 零依赖解析,结构化 cue 列表(索引、时间区间、文本)
  • LRC / Enhanced LRC: 歌词文件,[mm:ss.xx] 行时间戳(ELRC 额外支持行内 <mm:ss.xx> 逐字时间戳),自动解析 [ti:][ar:][al:] 等元数据
  • ASS / SSA: Advanced SubStation Alpha,提取 Dialogue 事件,自动剥离 \N \h 与 {...} 样式覆盖码,展示 Style 标签
  • TTML / DFXP: W3C / Apple Music 使用的 XML 字幕,支持 begin / end / dur 与 <br/>

压缩包

  • ZIP: 树形目录 + 内嵌预览文本/代码/图片,其他类型可下载导出

Outlook 邮件

  • MSG: 邮件头、正文、附件列表

电子书

  • EPUB: 章节导航、翻页

⚠️ 功能限制与性能说明

支持等级

✅ 完全支持(生产可用)

  • 图片(JPG, PNG, GIF, WebP, SVG, BMP, ICO)
  • 视频(MP4, WebM, OGG)
  • 音频(MP3, WAV, OGG)
  • PDF
  • Markdown
  • 代码文件(通过 Shiki 支持 40+ 种语言,按需加载)
  • JSON, CSV, XML

⚠️ 部分支持(仅供预览)

  • Office(DOCX, XLSX, PPTX): 基础布局和文本渲染。复杂格式(图表、宏、嵌入对象)可能无法准确渲染。
  • ZIP: 目录树浏览 + 文本/代码/图片内联预览。大型压缩包(>100MB)可能导致性能问题。
  • 字体(TTF, OTF, WOFF): 元数据 + 字符预览。不支持完整字体特性测试。

🧪 实验性支持

  • MSG(Outlook 邮件): 邮件头和纯文本正文。复杂 HTML 正文可能无法正确渲染。
  • EPUB: 基础章节导航。CSS 样式可能与原生阅读器有差异。不支持 DRM 保护文件。
  • 字幕格式(SRT, ASS, TTML, LRC): 仅文本显示。不支持视频同步或高级样式。

性能边界

文件大小 状态 说明
< 50MB ✅ 推荐 流畅的预览体验
50-100MB ⚠️ 可能卡顿 加载时 UI 可能无响应
> 100MB ❌ 不推荐 可能超出浏览器内存限制

特殊情况:

  • ZIP 压缩包: 性能取决于文件数量,而非仅体积
  • Office 文档: 复杂文件(>200 页、大量图片)可能超时
  • 代码高亮: >5MB 的文件可能需要 3-5 秒高亮时间

浏览器兼容性

最低要求:

  • Chrome 90+ / Edge 90+
  • Firefox 88+
  • Safari 14+

已知限制:

  • Safari iOS: 视频自动播放需要用户交互
  • Firefox: AVIF 支持需要 Firefox 93+(已包含降级解码器)
  • Office 格式: 不同浏览器渲染质量有差异
  • EPUB: 旧版浏览器可能不支持某些 CSS 特性

🎮 API 参考

FilePreviewModal Props

属性 类型 必填 说明
files PreviewFileInput[] ✅ 文件列表(支持 File 对象、文件对象或 URL 字符串)
currentIndex number ✅ 当前文件索引
isOpen boolean ✅ 是否打开预览
customRenderers CustomRenderer[] ❌ 自定义渲染器
locale Locale ❌ 界面语言(默认 'en-US',内置 'zh-CN')
messages Partial<Record<Locale, Partial<Messages>>> ❌ 自定义翻译覆盖
headless boolean ❌ 无头模式,隐藏工具栏和导航箭头
theme Theme ❌ 主题模式: 'auto' | 'dark' | 'light'(默认 'dark')
showDownload boolean ❌ 是否显示下载按钮(默认 true)
showClose boolean ❌ 是否显示关闭按钮(modal 模式默认 true)
showNavigation boolean ❌ 是否显示文件导航箭头(默认 true)
loopNavigation boolean ❌ 是否循环导航文件(默认 false)

FilePreviewModal 事件

事件 参数 说明
close - 关闭预览时触发
navigate number 导航到其他文件时触发

FilePreviewEmbed Props

属性 类型 必填 默认值 说明
files PreviewFileInput[] ✅ - 文件列表
currentIndex number ❌ 0 当前文件索引
customRenderers CustomRenderer[] ❌ - 自定义渲染器
width number | string ❌ '100%' 容器宽度
height number | string ❌ '100%' 容器高度
locale Locale ❌ 'en-US' 界面语言('zh-CN' 或 'en-US')
messages Partial<Record<Locale, Partial<Messages>>> ❌ - 自定义翻译覆盖
headless boolean ❌ false 无头模式,隐藏工具栏和导航箭头
theme Theme ❌ 'dark' 主题模式: 'auto' | 'dark' | 'light'
showDownload boolean ❌ true 是否显示下载按钮
showClose boolean ❌ false 是否显示关闭按钮(embed 模式默认 false)
showNavigation boolean ❌ true 是否显示文件导航箭头
loopNavigation boolean ❌ false 是否循环导航文件

FilePreviewEmbed 事件

事件 参数 说明
navigate number 导航到其他文件时触发

FilePreviewContent(高级用法)

FilePreviewModal 和 FilePreviewEmbed 都是基于底层 FilePreviewContent 组件的薄包装。当你需要构建完全自定义的容器时,可以直接使用它:

<FilePreviewContent
  mode="embed"
  :files="files"
  :current-index="index"
  @navigate="index = $event"
/>

文件类型定义

// 支持三种文件输入类型
type PreviewFileInput = File | PreviewFileLink | string;

// 1. 原生 File 对象(浏览器 File API)
const file: File = ...;

// 2. 文件对象
interface PreviewFileLink {
  id?: string;       // 可选的唯一标识符
  name: string;      // 文件名
  type: string;      // MIME 类型
  url: string;       // 文件 URL (支持 blob URL 和 HTTP URL)
  size?: number;     // 文件大小(字节)
}

// 3. HTTP URL 字符串
const url: string = 'https://example.com/file.pdf';

🧩 自定义渲染器

本库支持自定义渲染器以处理内置不支持的文件类型。自定义渲染器可以可选地提供工具栏配置并集成到本库的架构中。

事件驱动的工具栏更新

自定义渲染器可以通过 Vue 3 的响应式系统实现实时工具栏更新:

优势:

  • 实时更新:工具栏通过 Vue 响应式立即反映状态变化
  • 更好的性能:无轮询开销,利用 Vue 的高效变更检测
  • 类型安全:完整的 TypeScript 接口支持

实现方式:

<script setup lang="ts">
import { ref, watch } from 'vue';
import { ChevronLeft, ChevronRight } from 'lucide-vue-next';
import type { ToolbarGroup } from '@eternalheart/vue-file-preview';

interface Props {
  url: string;
}

const props = defineProps<Props>();
const emit = defineEmits<{
  pageChange: [current: number, total: number];
}>();

const currentPage = ref(1);
const totalPages = ref(10);

// 发送页码变化
watch([currentPage, totalPages], () => {
  emit('pageChange', currentPage.value, totalPages.value);
});

const getToolbarGroups = (): ToolbarGroup[] => [
  {
    items: [
      {
        type: 'button',
        icon: ChevronLeft,
        tooltip: '上一页',
        action: () => currentPage.value = Math.max(1, currentPage.value - 1),
        disabled: currentPage.value <= 1
      },
      {
        type: 'text',
        content: `${currentPage.value} / ${totalPages.value}`,
        minWidth: '4rem'
      },
      {
        type: 'button',
        icon: ChevronRight,
        tooltip: '下一页',
        action: () => currentPage.value = Math.min(totalPages.value, currentPage.value + 1),
        disabled: currentPage.value >= totalPages.value
      }
    ]
  }
];

// 暴露给父组件
defineExpose({
  getToolbarGroups
});
</script>

<template>
  <div>你的自定义渲染器 UI</div>
</template>

主组件使用:

<script setup>
import { CustomRenderer } from './CustomRenderer.vue';

const files = [
  { name: 'custom.xyz', type: 'application/custom', url: '/path/to/file' }
];
</script>

<template>
  <FilePreviewModal
    :files="files"
    :custom-renderers="[
      {
        test: (file) => file.type === 'application/custom',
        render: () => CustomRenderer
      }
    ]"
  />
</template>

主组件通过 Vue 的响应式系统自动追踪 getToolbarGroups() 的响应式变化。无需手动订阅。

Renderer 懒加载

所有内置渲染器通过 defineAsyncComponent 实现代码分割,以最小化主包体积并提升初始加载性能。

架构:

  • 注册:渲染器在 src/renderers/lazy.ts 中注册,使用 defineAsyncComponent 包装
  • 加载:每个渲染器是独立的 chunk,按需加载
  • 回退:RendererLoading 组件处理加载状态

每个渲染器都会输出为独立异步 chunk,并仅在需要时加载。

实现示例:

// src/renderers/lazy.ts
import { defineAsyncComponent } from 'vue';

const wrap = (loader: () => Promise<any>) =>
  defineAsyncComponent({
    loader,
    loadingComponent: RendererLoading,
    delay: 0
  });

export const CustomRenderer = wrap(() => import('./Custom/index.vue'));
<!-- src/FilePreviewContent.vue -->
<script setup>
import { CustomRenderer } from './renderers/lazy';  // ✅ 懒加载导入
// 禁止: import CustomRenderer from './renderers/Custom/index.vue';  // ❌ 直接导入会破坏代码分割
</script>

<template>
  <CustomRenderer
    v-if="fileType === 'custom'"
    ref="rendererRef"
    :url="currentFile.url"
  />
</template>

用于自定义渲染器:

如果你希望自定义渲染器也享受代码分割,可以使用相同的模式:

<script setup>
import { defineAsyncComponent } from 'vue';

const MyCustomRenderer = defineAsyncComponent(() => import('./MyCustomRenderer.vue'));

const files = [...];
</script>

<template>
  <FilePreviewModal
    :files="files"
    :custom-renderers="[
      {
        test: (file) => file.type === 'application/custom',
        render: () => MyCustomRenderer
      }
    ]"
  />
</template>

i18n 集成

自定义渲染器可以通过 useTranslator() composable 访问本库的 i18n 系统,实现一致的多语言支持。

架构:

  • 字典源:file-preview-core/src/i18n/messages/(zh-CN.ts、en-US.ts)
  • 禁止硬编码:所有用户可见文案必须使用翻译 key
  • 自动切换语言:跟随 FilePreviewModal 的 locale prop

在自定义渲染器中使用:

<script setup lang="ts">
import { ref } from 'vue';
import { useTranslator } from '@eternalheart/vue-file-preview';

interface Props {
  url: string;
}

const props = defineProps<Props>();
const { t } = useTranslator();
const error = ref<string | null>(null);
</script>

<template>
  <div v-if="error" class="vfp-text-fg-primary">
    {{ t('custom.load_failed') }}: {{ error }}
  </div>
  <div v-else>
    <button>{{ t('common.download') }}</button>
    <span>{{ t('custom.loading') }}</span>
  </div>
</template>

使用说明:

  • 在 <template> 中:直接使用 t('key')(自动解包)
  • 在 <script> 中:命令式调用使用 t.value('key')

新增自定义翻译键:

对于自定义渲染器,通过 messages prop 扩展翻译(不要修改 node_modules 中的源文件):

<template>
  <FilePreviewModal
    :files="files"
    locale="en-US"
    :messages="{
      'en-US': {
        'custom.load_failed': 'Failed to load custom file',
        'custom.file_size': 'File size: {size} KB'
      },
      'zh-CN': {
        'custom.load_failed': '自定义文件加载失败',
        'custom.file_size': '文件大小: {size} KB'
      }
    }"
    :custom-renderers="[...]"
  />
</template>

指南:

  • 使用 <scope>.<snake_name> 格式(如 custom.load_failed、custom.parse_error)
  • 为所有启用的语言(zh-CN 和 en-US)提供翻译
  • 已有通用 key:common.loading、common.download、common.close、toolbar.*

参数化翻译:

<template>
  <!-- 字典: 'custom.file_size': '文件大小: {size} KB' -->
  <span>{{ t('custom.file_size', { size: 1024 }) }}</span>
  <!-- → "文件大小: 1024 KB" -->
</template>

工具栏集成:

工具栏项也应使用翻译字符串:

const getToolbarGroups = (): ToolbarGroup[] => [
  {
    items: [
      {
        type: 'button',
        icon: Download,
        tooltip: t.value('common.download'),  // ✅ 已翻译(script 中用 .value)
        action: handleDownload
      }
    ]
  }
];

主题适配

自定义渲染器必须使用语义化颜色 token 以支持本库的 'auto' | 'dark' | 'light' 主题系统。

语义化 Token 系统:

所有颜色定义为 CSS 变量(--fp-*),通过 Tailwind 类暴露,前缀为 vfp-:

用途 类名 说明
文字(fg)
主文本 vfp-text-fg-primary 最高对比度
正文 vfp-text-fg-secondary 默认文字
次要文本 vfp-text-fg-tertiary 副本、计数器
弱化文本 vfp-text-fg-muted 占位符
禁用文本 vfp-text-fg-disabled 禁用按钮
背景(surface)
表面层 1 vfp-bg-surface-1 卡片、最弱
表面层 2 vfp-bg-surface-2 hover 状态
表面层 3 vfp-bg-surface-3 强调
工具栏 vfp-bg-surface-toolbar 顶部工具栏
边框
弱边框 vfp-border-line-weak 细线
标准边框 vfp-border-line 默认边框
强边框 vfp-border-line-strong 强调
代码
代码背景 vfp-bg-code-bg Dark:#1e1e1e / Light:#f6f8fa
代码文字 vfp-text-code-fg 跟随主题
强调(accent)
强调背景 vfp-bg-accent 主按钮
强调 hover vfp-bg-accent-hover hover 状态

✅ 正确用法:

<template>
  <div class="vfp-bg-surface-1 vfp-border vfp-border-line-weak vfp-rounded">
    <h2 class="vfp-text-fg-primary vfp-text-lg">标题</h2>
    <p class="vfp-text-fg-secondary">正文内容</p>
    <button class="vfp-bg-surface-2 hover:vfp-bg-surface-3 vfp-text-fg-primary">
      点击
    </button>
    <pre class="vfp-bg-code-bg vfp-text-code-fg">{{ code }}</pre>
  </div>
</template>

对于 <style scoped> 块,使用 CSS 变量:

<style scoped>
.my-block {
  color: var(--fp-fg-primary);
  background: var(--fp-surface-2);
  border: 1px solid var(--fp-line);
}

.my-code {
  background: var(--fp-code-bg);
  color: var(--fp-code-fg);
}
</style>

❌ 错误用法(禁止使用):

<!-- ❌ 字面色类 — 会破坏主题切换 -->
<div class="vfp-text-white/90 vfp-bg-white/10 vfp-border-white/15">
<div class="vfp-text-gray-700 vfp-bg-gray-100">

<!-- ❌ 内联字面色 -->
<div :style="{ color: '#ffffff', background: '#1f2937' }">

<!-- ❌ scoped style 中硬编码暗色 -->
<style scoped>
.foo { color: rgba(255, 255, 255, 0.75); }   /* 应该用 var(--fp-fg-secondary) */
.foo { background: #1e1e1e; }                 /* 应该用 var(--fp-code-bg) */
</style>

支持主题的三方库:

对于具有 theme prop 的库(如 shiki),使用 useResolvedTheme():

<script setup lang="ts">
import { ref, watch } from 'vue';
import { codeToHtml } from 'shiki';
import { useResolvedTheme } from '@eternalheart/vue-file-preview';

const props = defineProps<{ code: string; lang: string }>();
const resolvedTheme = useResolvedTheme();  // Ref<'dark' | 'light'>
const highlighted = ref('');

const highlightCode = async () => {
  highlighted.value = await codeToHtml(props.code, {
    lang: props.lang,
    theme: resolvedTheme.value === 'light' ? 'github-light' : 'dark-plus'
  });
};

// 主题切换时重新高亮
watch(resolvedTheme, highlightCode, { immediate: true });
</script>

<template>
  <div v-html="highlighted"></div>
</template>

测试:

务必在 Light 和 Dark 两个主题下测试你的自定义渲染器:

<FilePreviewModal
  :files="files"
  theme="light"  // 在 'light'、'dark'、'auto' 间切换
  :custom-renderers="[...]"
/>

验证:

  • 文字在两个主题下都可读(无白底白字或黑底黑字)
  • 边框和分隔线清晰可见
  • hover 状态有足够对比度
  • 代码块跟随主题(不固定为暗色)

⌨️ 键盘快捷键

  • ESC - 关闭预览
  • ← - 上一个文件
  • → - 下一个文件
  • Home - 第一个文件
  • End - 最后一个文件
  • 滚轮 - 缩放图片 (仅图片预览)

📚 文档

🛠️ 开发

# 克隆仓库
git clone https://github.com/wh131462/file-preview.git

# 安装依赖
pnpm install

# 启动开发服务器(Vue 演示应用)
pnpm dev:vue-example

# 构建库
pnpm build:vue

📄 许可证

MIT © EternalHeart

🤝 贡献

欢迎提交 Issue 和 Pull Request!

🔗 相关链接

Related Packages

document-viewer-vue3

Browser-native Vue 3 viewer for Word, Excel, PDF, PowerPoint, and image files.

871 0
@file-preview-viewer/viewer

Universal file preview library for Web, React, Angular, and Vue. 100% client-sid...

3,271 2
jit-viewer

JitViewer核心SDK - 跨框架文档预览引擎,支持PDF、Office、CSV、HTML、图片、视频与...

25,129 350
file-viewer3

Compatibility alias package for File Viewer Vue 3

11,202 2,607

Version History

Version Released Vue License
1.6.11 ^3.4.0 MIT
1.6.10 ^3.4.0 MIT
1.6.9 ^3.4.0 MIT
1.6.8 ^3.4.0 MIT
1.6.7 ^3.4.0 MIT