@jrchan/office-preview 1.0.5 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,25 +1,287 @@
1
- # 在线预览文档,支持docx、pptx、excel、pdf等格式,基于Vue3和@vue-office系列组件构建,适用于大屏展示和文档预览场景。
1
+ # Office Preview 组件
2
2
 
3
- ## Project Setup
3
+ 基于 Vue 3 的在线文档预览组件,支持 docx、pptx、excel、pdf 等格式。
4
4
 
5
- ```sh
6
- pnpm install
5
+ ## ⚠️ 重要说明
6
+
7
+ **本组件是一个纯粹的 UI 组件,不包含任何测试数据或硬编码的文件 URL。**
8
+
9
+ - ✅ `App.vue` 仅用于本地开发调试,**不会被打包到 npm 包中**
10
+ - ✅ 使用方项目必须自己传入 `file-url` 和 `file-type` 参数
11
+ - ✅ 组件不会主动加载任何测试文件
12
+
13
+ ## 安装
14
+
15
+ ```bash
16
+ npm install @jrchan/office-preview
7
17
  ```
8
18
 
9
- ### Compile and Hot-Reload for Development
19
+ ## 使用方法
10
20
 
11
- ```sh
12
- pnpm dev
21
+ ### 方式一:全局注册(推荐)
22
+
23
+ **main.ts**
24
+ ```typescript
25
+ import { createApp } from 'vue'
26
+ import App from './App.vue'
27
+ import OfficePreview from '@jrchan/office-preview'
28
+
29
+ const app = createApp(App)
30
+ app.use(OfficePreview)
31
+ app.mount('#app')
32
+ ```
33
+
34
+ **在组件中使用**
35
+ ```vue
36
+ <template>
37
+ <div class="app">
38
+ <!-- ✅ 必须传入 file-url 和 file-type -->
39
+ <OfficePreview
40
+ :file-url="myFileUrl"
41
+ :file-type="myFileType"
42
+ @rendered="handleRendered"
43
+ />
44
+ </div>
45
+ </template>
46
+
47
+ <script setup lang="ts">
48
+ import { ref } from 'vue'
49
+
50
+ // ✅ 使用方自己提供文件 URL(可以是 HTTP/HTTPS 地址或 Blob URL)
51
+ const myFileUrl = ref('https://your-server.com/files/document.docx')
52
+ const myFileType = ref('docx')
53
+
54
+ const handleRendered = (type: string) => {
55
+ console.log(`${type} 文件渲染完成`)
56
+ }
57
+ </script>
58
+ ```
59
+
60
+ ### 方式二:按需引入
61
+
62
+ ```vue
63
+ <template>
64
+ <OfficePreview
65
+ :file-url="fileUrl"
66
+ :file-type="fileType"
67
+ height="600px"
68
+ />
69
+ </template>
70
+
71
+ <script setup lang="ts">
72
+ import { OfficePreview } from '@jrchan/office-preview'
73
+
74
+ // ✅ 文件 URL 由使用方提供
75
+ const fileUrl = 'https://cdn.example.com/report.xlsx'
76
+ const fileType = 'xlsx'
77
+ </script>
13
78
  ```
14
79
 
15
- ### Type-Check, Compile and Minify for Production
80
+ ## Props 参数
81
+
82
+ | 参数名 | 类型 | 必填 | 默认值 | 说明 |
83
+ |--------|------|------|--------|------|
84
+ | file-url | `string` | ✅ 是 | `''` | 文件 URL 地址(支持 http/https/blob URL) |
85
+ | file-type | `string` | ✅ 是 | `''` | 文件类型(doc/docx/xls/xlsx/ppt/pptx/pdf) |
86
+ | height | `string` | ❌ 否 | `'100%'` | 预览区域高度 |
87
+
88
+ ## Events 事件
89
+
90
+ | 事件名 | 回调参数 | 说明 |
91
+ |--------|----------|------|
92
+ | rendered | `(type: string)` | 文件渲染完成时触发 |
93
+
94
+ ## 完整示例
95
+
96
+ ### 示例 1:从服务器加载文件
97
+
98
+ ```vue
99
+ <template>
100
+ <OfficePreview
101
+ :file-url="documentUrl"
102
+ :file-type="documentType"
103
+ />
104
+ </template>
105
+
106
+ <script setup lang="ts">
107
+ import { ref } from 'vue'
108
+
109
+ const documentUrl = ref('https://example.com/contracts/2024.docx')
110
+ const documentType = ref('docx')
111
+ </script>
112
+ ```
113
+
114
+ ### 示例 2:上传文件后预览
115
+
116
+ ```vue
117
+ <template>
118
+ <div>
119
+ <input type="file" @change="handleFileChange" accept=".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx" />
120
+ <OfficePreview
121
+ v-if="fileUrl"
122
+ :file-url="fileUrl"
123
+ :file-type="fileType"
124
+ />
125
+ </div>
126
+ </template>
127
+
128
+ <script setup lang="ts">
129
+ import { ref } from 'vue'
130
+ import { OfficePreview } from '@jrchan/office-preview'
131
+
132
+ const fileUrl = ref<string>('')
133
+ const fileType = ref<string>('')
134
+
135
+ const handleFileChange = (e: Event) => {
136
+ const target = e.target as HTMLInputElement
137
+ const file = target.files?.[0]
138
+ if (file) {
139
+ // 创建 Blob URL
140
+ fileUrl.value = URL.createObjectURL(file)
141
+ fileType.value = file.name // 组件会根据文件名自动识别类型
142
+ }
143
+ }
144
+ </script>
145
+ ```
146
+
147
+ ### 示例 3:文件列表切换预览
148
+
149
+ ```vue
150
+ <template>
151
+ <div class="document-viewer">
152
+ <ul>
153
+ <li v-for="file in fileList" :key="file.id" @click="selectFile(file)">
154
+ {{ file.name }}
155
+ </li>
156
+ </ul>
157
+
158
+ <OfficePreview
159
+ v-if="selectedFile"
160
+ :file-url="selectedFile.url"
161
+ :file-type="selectedFile.type"
162
+ height="600px"
163
+ />
164
+ </div>
165
+ </template>
166
+
167
+ <script setup lang="ts">
168
+ import { ref } from 'vue'
169
+ import { OfficePreview } from '@jrchan/office-preview'
16
170
 
17
- ```sh
171
+ interface FileItem {
172
+ id: number
173
+ name: string
174
+ url: string
175
+ type: string
176
+ }
177
+
178
+ const fileList = ref<FileItem[]>([
179
+ { id: 1, name: '合同.docx', url: 'https://cdn.example.com/contract.docx', type: 'docx' },
180
+ { id: 2, name: '报表.xlsx', url: 'https://cdn.example.com/report.xlsx', type: 'xlsx' },
181
+ { id: 3, name: '演示.pptx', url: 'https://cdn.example.com/demo.pptx', type: 'pptx' },
182
+ ])
183
+
184
+ const selectedFile = ref<FileItem | null>(null)
185
+
186
+ const selectFile = (file: FileItem) => {
187
+ selectedFile.value = file
188
+ }
189
+ </script>
190
+ ```
191
+
192
+ ## ⚠️ CSP 配置说明
193
+
194
+ 本组件使用 Web Worker 处理文档解析,如遇到 CSP 报错,请在使用方项目中添加以下配置:
195
+
196
+ ### Nginx 配置
197
+ ```nginx
198
+ add_header Content-Security-Policy "worker-src 'self' blob: data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:;";
199
+ ```
200
+
201
+ ### HTML Meta 标签
202
+ ```html
203
+ <meta http-equiv="Content-Security-Policy"
204
+ content="worker-src 'self' blob: data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:;">
205
+ ```
206
+
207
+ ### Vite 开发环境
208
+ ```typescript
209
+ // vite.config.ts
210
+ export default defineConfig({
211
+ server: {
212
+ headers: {
213
+ 'Content-Security-Policy': "worker-src 'self' blob: data:;",
214
+ },
215
+ },
216
+ })
217
+ ```
218
+
219
+ ## 支持的文档格式
220
+
221
+ | 格式类型 | 扩展名 | 使用的组件 |
222
+ |----------|--------|-----------|
223
+ | PDF | `.pdf` | @vue-office/pdf |
224
+ | Word | `.doc`, `.docx` | @vue-office/docx |
225
+ | Excel | `.xls`, `.xlsx`, `.csv` | @vue-office/excel |
226
+ | PowerPoint | `.ppt`, `.pptx` | @vue-office/pptx |
227
+
228
+ ## 常见问题
229
+
230
+ ### Q1: 为什么组件会加载 `/file/234.docx`?
231
+
232
+ **A:** 这是误解。组件**不会**加载任何测试文件。请检查:
233
+
234
+ 1. ✅ 是否正确传入了 `file-url` 参数
235
+ 2. ✅ 参数值是否为你自己的文件 URL
236
+ 3. ✅ 是否误将本地测试代码复制到了使用方项目
237
+
238
+ 组件的 `App.vue` 仅用于本地开发调试,**不会被打包到 npm 包中**。
239
+
240
+ ### Q2: 不传参数会怎样?
241
+
242
+ **A:** 如果不传入 `file-url` 和 `file-type`,组件将显示"请选择文件进行预览"提示,不会加载任何文件。
243
+
244
+ ### Q3: 如何查看构建产物确认没有 App.vue?
245
+
246
+ **A:** 执行以下命令:
247
+ ```bash
248
+ npm run build
249
+ grep -r "234.docx" dist/ # 应该无结果
250
+ ```
251
+
252
+ ## 开发指南
253
+
254
+ ### 本地开发调试
255
+
256
+ ```bash
257
+ # 安装依赖
258
+ pnpm install
259
+
260
+ # 启动开发服务器(访问 localhost:5173 查看测试页面)
261
+ pnpm dev
262
+
263
+ # 构建生产包
18
264
  pnpm build
265
+
266
+ # 发布到 npm
267
+ npm publish --access public
19
268
  ```
20
269
 
21
- ### Lint with [ESLint](https://eslint.org/)
270
+ ### 项目结构
22
271
 
23
- ```sh
24
- pnpm lint
25
272
  ```
273
+ @jrchan/office-preview/
274
+ ├── src/
275
+ │ ├── components/
276
+ │ │ └── office-preview.vue # ✅ 核心组件(会被打包)
277
+ │ ├── index.ts # ✅ 导出入口(会被打包)
278
+ │ ├── App.vue # ❌ 仅本地测试(不会打包)
279
+ │ └── main.ts # ❌ 仅本地测试(不会打包)
280
+ ├── dist/ # 构建输出(发布到 npm)
281
+ ├── package.json
282
+ └── vite.config.ts
283
+ ```
284
+
285
+ ## License
286
+
287
+ MIT
@@ -1 +1 @@
1
- .file-preview-container[data-v-6c253a5b],.preview-area[data-v-6c253a5b]{height:100%}
1
+ .file-preview-container[data-v-a3fccd66],.preview-area[data-v-a3fccd66]{height:100%}
@@ -1,4 +1,4 @@
1
- import { defineComponent as f, ref as a, watch as u, onMounted as v, openBlock as r, createElementBlock as d, createElementVNode as s, toDisplayString as m, unref as _, createCommentVNode as x } from "vue";
1
+ import { defineComponent as f, ref as a, watch as u, onMounted as v, openBlock as d, createElementBlock as r, createElementVNode as s, toDisplayString as m, unref as _, createCommentVNode as x } from "vue";
2
2
  const g = { class: "file-preview-container" }, w = { class: "preview-area" }, y = {
3
3
  key: 0,
4
4
  class: "loading-mask"
@@ -23,12 +23,12 @@ const g = { class: "file-preview-container" }, w = { class: "preview-area" }, y
23
23
  }, {
24
24
  immediate: !0
25
25
  }), v(async () => {
26
- }), (o, t) => (r(), d("div", g, [
26
+ }), (o, t) => (d(), r("div", g, [
27
27
  s("div", w, [
28
28
  t[0] || (t[0] = s("div", null, "在线预览", -1)),
29
29
  s("div", null, m(l.fileUrl), 1)
30
30
  ]),
31
- _(c) ? (r(), d("div", y, [...t[1] || (t[1] = [
31
+ _(c) ? (d(), r("div", y, [...t[1] || (t[1] = [
32
32
  s("div", { class: "loading-content" }, [
33
33
  s("div", { class: "spinner" }),
34
34
  s("p", null, "正在加载文件...")
@@ -41,7 +41,7 @@ const g = { class: "file-preview-container" }, w = { class: "preview-area" }, y
41
41
  for (const [c, i] of n)
42
42
  e[c] = i;
43
43
  return e;
44
- }, C = /* @__PURE__ */ k(h, [["__scopeId", "data-v-6c253a5b"]]), E = [C], O = (l) => {
44
+ }, C = /* @__PURE__ */ k(h, [["__scopeId", "data-v-a3fccd66"]]), E = [C], O = (l) => {
45
45
  E.forEach((n) => {
46
46
  l.component(n.name, n);
47
47
  });
@@ -1 +1 @@
1
- (function(o,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(o=typeof globalThis<"u"?globalThis:o||self,e(o.OfficePreview={},o.Vue))})(this,(function(o,e){"use strict";const f={class:"file-preview-container"},a={class:"preview-area"},p={key:0,class:"loading-mask"},r=((l,s)=>{const t=l.__vccOpts||l;for(const[d,c]of s)t[d]=c;return t})(e.defineComponent({name:"OfficePreview",__name:"office-preview",props:{fileUrl:{default:""},fileType:{default:""},height:{default:"100%"}},emits:["rendered"],setup(l,{emit:s}){let t=e.ref("pdf"),d=e.ref(!1);const c=l,_=i=>{console.log("fileName",i);const n=i.toLowerCase().split(".").pop();["pdf"].includes(n)?t.value="pdf":["doc","docx"].includes(n)?t.value="doc":["xls","xlsx","csv"].includes(n)?t.value="xls":["ppt","pptx"].includes(n)?(t.value="ppt",console.log("pptxPrviewer",i)):t.value="other"};return e.watch([()=>c.fileType,()=>c.fileUrl],i=>{t.value=i[0],_(i[1])},{immediate:!0}),e.onMounted(async()=>{}),(i,n)=>(e.openBlock(),e.createElementBlock("div",f,[e.createElementVNode("div",a,[n[0]||(n[0]=e.createElementVNode("div",null,"在线预览",-1)),e.createElementVNode("div",null,e.toDisplayString(l.fileUrl),1)]),e.unref(d)?(e.openBlock(),e.createElementBlock("div",p,[...n[1]||(n[1]=[e.createElementVNode("div",{class:"loading-content"},[e.createElementVNode("div",{class:"spinner"}),e.createElementVNode("p",null,"正在加载文件...")],-1)])])):e.createCommentVNode("",!0)]))}}),[["__scopeId","data-v-6c253a5b"]]),m=[r],u={install:l=>{m.forEach(s=>{l.component(s.name,s)})}};o.OfficePreview=r,o.default=u,Object.defineProperties(o,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
1
+ (function(o,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(o=typeof globalThis<"u"?globalThis:o||self,e(o.OfficePreview={},o.Vue))})(this,(function(o,e){"use strict";const f={class:"file-preview-container"},a={class:"preview-area"},p={key:0,class:"loading-mask"},r=((l,s)=>{const t=l.__vccOpts||l;for(const[d,c]of s)t[d]=c;return t})(e.defineComponent({name:"OfficePreview",__name:"office-preview",props:{fileUrl:{default:""},fileType:{default:""},height:{default:"100%"}},emits:["rendered"],setup(l,{emit:s}){let t=e.ref("pdf"),d=e.ref(!1);const c=l,_=i=>{console.log("fileName",i);const n=i.toLowerCase().split(".").pop();["pdf"].includes(n)?t.value="pdf":["doc","docx"].includes(n)?t.value="doc":["xls","xlsx","csv"].includes(n)?t.value="xls":["ppt","pptx"].includes(n)?(t.value="ppt",console.log("pptxPrviewer",i)):t.value="other"};return e.watch([()=>c.fileType,()=>c.fileUrl],i=>{t.value=i[0],_(i[1])},{immediate:!0}),e.onMounted(async()=>{}),(i,n)=>(e.openBlock(),e.createElementBlock("div",f,[e.createElementVNode("div",a,[n[0]||(n[0]=e.createElementVNode("div",null,"在线预览",-1)),e.createElementVNode("div",null,e.toDisplayString(l.fileUrl),1)]),e.unref(d)?(e.openBlock(),e.createElementBlock("div",p,[...n[1]||(n[1]=[e.createElementVNode("div",{class:"loading-content"},[e.createElementVNode("div",{class:"spinner"}),e.createElementVNode("p",null,"正在加载文件...")],-1)])])):e.createCommentVNode("",!0)]))}}),[["__scopeId","data-v-a3fccd66"]]),m=[r],u={install:l=>{m.forEach(s=>{l.component(s.name,s)})}};o.OfficePreview=r,o.default=u,Object.defineProperties(o,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jrchan/office-preview",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "private": false,
5
5
  "description": "在线预览文档,支持docx、pptx、excel、pdf等格式,基于Vue3和@vue-office系列组件构建,适用于大屏展示和文档预览场景。",
6
6
  "keywords": [