npm package: @jrchan/office-preview
Description: 在线预览文档，支持docx、pptx、excel、pdf等格式，基于Vue3和@vue-office系列组件构建，适用于大屏展示和文档预览场景。
Latest version: 1.0.11

# Office Preview 组件

基于 Vue 3 的在线文档预览组件，支持 docx、pptx、excel、pdf 等格式。

## ⚠️ 重要说明

**本组件是一个纯粹的 UI 组件，不包含任何测试数据或硬编码的文件 URL。**

- ✅ `App.vue` 仅用于本地开发调试，**不会被打包到 npm 包中**
- ✅ 使用方项目必须自己传入 `file-url` 和 `file-type` 参数
- ✅ 组件不会主动加载任何测试文件

## 安装

```bash
npm install @jrchan/office-preview
```

## 样式引入

### 方式一：在 main.js/ts 中引入（推荐）

**main.ts**
```typescript
import { createApp } from 'vue'
import App from './App.vue'
import OfficePreview from '@jrchan/office-preview'
// ✅ 引入组件库样式
import '@jrchan/office-preview/style'
// 或者
// import '@jrchan/office-preview/dist/office-preview.css'

const app = createApp(App)
app.use(OfficePreview)
app.mount('#app')
```

### 方式二：在组件中按需引入

``vue
<template>
  <div class="app">
    <OfficePreview 
      :file-url="myFileUrl" 
      :file-type="myFileType"
    />
  </div>
</template>

<script setup lang="ts">
import { OfficePreview } from '@jrchan/office-preview'
// ✅ 单独引入样式
import '@jrchan/office-preview/style'

const myFileUrl = ref('https://your-server.com/files/document.docx')
const myFileType = ref('docx')
</script>
```

### 方式三：使用原始 CSS 文件路径

```
// main.js
import '@jrchan/office-preview/dist/office-preview.css'
```

## 使用方法

### 方式一：全局注册（推荐）

**main.ts**
```typescript
import { createApp } from 'vue'
import App from './App.vue'
import OfficePreview from '@jrchan/office-preview'

const app = createApp(App)
app.use(OfficePreview)
app.mount('#app')
```

**在组件中使用**
``vue
<template>
  <div class="app">
    <!-- ✅ 必须传入 file-url 和 file-type -->
    <OfficePreview 
      :file-url="myFileUrl" 
      :file-type="myFileType"
      @rendered="handleRendered"
    />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'

// ✅ 使用方自己提供文件 URL（可以是 HTTP/HTTPS 地址或 Blob URL）
const myFileUrl = ref('https://your-server.com/files/document.docx')
const myFileType = ref('docx')

const handleRendered = (type: string) => {
  console.log(`${type} 文件渲染完成`)
}
</script>
```

### 方式二：按需引入

``vue
<template>
  <OfficePreview 
    :file-url="fileUrl" 
    :file-type="fileType"
    height="600px"
  />
</template>

<script setup lang="ts">
import { OfficePreview } from '@jrchan/office-preview'

// ✅ 文件 URL 由使用方提供
const fileUrl = 'https://cdn.example.com/report.xlsx'
const fileType = 'xlsx'
</script>
```

## Props 参数

| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|--------|------|------|--------|------|
| file-url | `string` | ✅ 是 | `''` | 文件 URL 地址（支持 http/https/blob URL） |
| file-type | `string` | ✅ 是 | `''` | 文件类型（doc/docx/xls/xlsx/ppt/pptx/pdf） |
| height | `string` | ❌ 否 | `'100%'` | 预览区域高度 |

## Events 事件

| 事件名 | 回调参数 | 说明 |
|--------|----------|------|
| rendered | `(type: string)` | 文件渲染完成时触发 |

## 📦 样式文件说明

### 生成的样式文件

构建后会在 `dist` 目录生成 `office-preview.css` 文件，包含以下样式：

- `.file-preview-container` - 文件预览容器
- `.preview-area` - 预览区域
- `.loading-mask` - 加载遮罩层
- `.placeholder` - 空状态提示
- `.unsupported` - 不支持格式提示

### 自定义样式

如果需要覆盖默认样式，可以在使用方项目中：

``css
/* 覆盖组件默认样式 */
.file-preview-container {
  /* 你的自定义样式 */
}

.preview-area .loading-mask {
  background-color: rgba(255, 255, 255, 0.95);
}
```

## 完整示例

### 示例 1：从服务器加载文件

``vue
<template>
  <OfficePreview 
    :file-url="documentUrl" 
    :file-type="documentType"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue'

const documentUrl = ref('https://example.com/contracts/2024.docx')
const documentType = ref('docx')
</script>
```

### 示例 2：上传文件后预览

``vue
<template>
  <div>
    <input type="file" @change="handleFileChange" accept=".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx" />
    <OfficePreview 
      v-if="fileUrl"
      :file-url="fileUrl" 
      :file-type="fileType"
    />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { OfficePreview } from '@jrchan/office-preview'

const fileUrl = ref<string>('')
const fileType = ref<string>('')

const handleFileChange = (e: Event) => {
  const target = e.target as HTMLInputElement
  const file = target.files?.[0]
  if (file) {
    // 创建 Blob URL
    fileUrl.value = URL.createObjectURL(file)
    fileType.value = file.name // 组件会根据文件名自动识别类型
  }
}
</script>
```

### 示例 3：文件列表切换预览

``vue
<template>
  <div class="document-viewer">
    <ul>
      <li v-for="file in fileList" :key="file.id" @click="selectFile(file)">
        {{ file.name }}
      </li>
    </ul>
    
    <OfficePreview 
      v-if="selectedFile"
      :file-url="selectedFile.url"
      :file-type="selectedFile.type"
      height="600px"
    />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { OfficePreview } from '@jrchan/office-preview'

interface FileItem {
  id: number
  name: string
  url: string
  type: string
}

const fileList = ref<FileItem[]>([
  { id: 1, name: '合同.docx', url: 'https://cdn.example.com/contract.docx', type: 'docx' },
  { id: 2, name: '报表.xlsx', url: 'https://cdn.example.com/report.xlsx', type: 'xlsx' },
  { id: 3, name: '演示.pptx', url: 'https://cdn.example.com/demo.pptx', type: 'pptx' },
])

const selectedFile = ref<FileItem | null>(null)

const selectFile = (file: FileItem) => {
  selectedFile.value = file
}
</script>
```

## ⚠️ CSP 配置说明

本组件使用 Web Worker 处理文档解析，如遇到 CSP 报错，请在使用方项目中添加以下配置：

### Nginx 配置
```
add_header Content-Security-Policy "worker-src 'self' blob: data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:;";
```

### HTML Meta 标签
```
<meta http-equiv="Content-Security-Policy" 
      content="worker-src 'self' blob: data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:;">
```

### Vite 开发环境
```
// vite.config.ts
export default defineConfig({
  server: {
    headers: {
      'Content-Security-Policy': "worker-src 'self' blob: data:;",
    },
  },
})
```

## 支持的文档格式

| 格式类型 | 扩展名 | 使用的组件 |
|----------|--------|-----------|
| PDF | `.pdf` | @vue-office/pdf |
| Word | `.doc`, `.docx` | @vue-office/docx |
| Excel | `.xls`, `.xlsx`, `.csv` | @vue-office/excel |
| PowerPoint | `.ppt`, `.pptx` | @vue-office/pptx |

## 常见问题

### Q1: 为什么组件会加载 `/file/234.docx`？

**A:** 这是误解。组件**不会**加载任何测试文件。请检查：

1. ✅ 是否正确传入了 `file-url` 参数
2. ✅ 参数值是否为你自己的文件 URL
3. ✅ 是否误将本地测试代码复制到了使用方项目

组件的 `App.vue` 仅用于本地开发调试，**不会被打包到 npm 包中**。

### Q2: 不传参数会怎样？

**A:** 如果不传入 `file-url` 和 `file-type`，组件将显示"请选择文件进行预览"提示，不会加载任何文件。

### Q3: 如何查看构建产物确认没有 App.vue？

**A:** 执行以下命令：
```
npm run build
grep -r "234.docx" dist/  # 应该无结果
```

## 开发指南

### 本地开发调试

```
# 安装依赖
pnpm install

# 启动开发服务器（访问 localhost:5173 查看测试页面）
pnpm dev

# 构建生产包
pnpm build

# 发布到 npm
npm publish --access public
```

### 项目结构

```
@jrchan/office-preview/
├── src/
│   ├── components/
│   │   └── office-preview.vue   # ✅ 核心组件（会被打包）
│   ├── index.ts                 # ✅ 导出入口（会被打包）
│   ├── App.vue                  # ❌ 仅本地测试（不会打包）
│   └── main.ts                  # ❌ 仅本地测试（不会打包）
├── dist/                        # 构建输出（发布到 npm）
├── package.json
└── vite.config.ts
```

## License

MIT