├── .babelrc
├── .env.dev
├── .env.docdev
├── .env.docpro
├── .env.iframe
├── .env.pro
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .npmrc
├── README.md
├── babel.config.js
├── config
├── config.build.iframe.js
├── config.build.js
├── config.dev.js
└── config.doc.js
├── doc
├── App.vue
├── babel.config.js
├── components
│ ├── CodeSnippet.vue
│ └── Collapse.vue
├── config.js
├── main.js
├── router.js
├── style
│ ├── imports.scss
│ ├── md-colors.scss
│ ├── mixins.scss
│ └── vars.scss
└── views
│ ├── Home.vue
│ └── Start.vue
├── docs
├── favicon.ico
├── file-viewer
│ ├── favicon.ico
│ ├── fileTest
│ │ ├── mp3.mp3
│ │ ├── mp4.mp4
│ │ ├── pdf.pdf
│ │ ├── pic.png
│ │ ├── ppt.pptx
│ │ └── word.docx
│ ├── index.html
│ └── static
│ │ ├── css
│ │ ├── chunk-vendors.053867c8.css
│ │ └── index.30170716.css
│ │ └── js
│ │ ├── chunk-vendors.101c5e96.js
│ │ ├── chunk-vendors.101c5e96.js.map
│ │ ├── index.a286462a.js
│ │ ├── index.a286462a.js.map
│ │ ├── pdf.worker.d60c6c06.worker.js
│ │ ├── pdf.worker.d60c6c06.worker.js.map
│ │ ├── pptx.worker.3c5d3299.worker.js
│ │ └── pptx.worker.3c5d3299.worker.js.map
├── fileTest
│ ├── mp3.mp3
│ ├── mp4.mp4
│ ├── pdf.pdf
│ ├── pic.png
│ ├── ppt.pptx
│ └── word.docx
├── index.html
└── static
│ ├── css
│ ├── chunk-vendors.5e3ffc13.css
│ └── index.28319711.css
│ └── js
│ ├── chunk-vendors.e1710af5.js
│ ├── chunk-vendors.e1710af5.js.map
│ ├── index.f0ffebef.js
│ ├── index.f0ffebef.js.map
│ ├── pdf.worker.d60c6c06.worker.js
│ ├── pdf.worker.d60c6c06.worker.js.map
│ ├── pptx.worker.3c5d3299.worker.js
│ └── pptx.worker.3c5d3299.worker.js.map
├── examples
├── App.vue
└── main.js
├── jsconfig.json
├── package.json
├── packages
├── index.js
├── index.scss
├── index.vue
├── pollify.js
├── renders.js
├── util.js
├── util
│ └── EventBus.js
└── vendors
│ ├── audio
│ └── index.js
│ ├── error
│ ├── error.vue
│ └── index.js
│ ├── image
│ ├── ImageViewer.vue
│ └── index.js
│ ├── mp4
│ └── index.js
│ ├── notFind
│ ├── index.js
│ └── notFind.vue
│ ├── officeOnline
│ └── index.js
│ ├── pdf
│ ├── PdfView.vue
│ ├── images
│ │ ├── loading-icon.gif
│ │ └── shadow.png
│ ├── index.js
│ └── pdf.css
│ ├── pptx
│ ├── backup
│ │ ├── index.js
│ │ ├── pptx_css.js
│ │ ├── process_pptx.js
│ │ ├── t_xml.js
│ │ └── worker.js
│ ├── index.js
│ ├── lib
│ │ ├── dingbat.js
│ │ ├── divs2slides.js
│ │ └── tXml.js
│ ├── options.js
│ ├── pptx.vue
│ ├── process.js
│ ├── styles
│ │ └── pptxjs.css
│ ├── support
│ │ └── vendor.js
│ └── worker
│ │ └── pptx.worker.js
│ ├── text
│ ├── CodeViewer.vue
│ └── index.js
│ └── xlsx
│ ├── Table.vue
│ ├── color.js
│ ├── index.js
│ └── util.js
├── prettier.config.js
├── public
├── favicon.ico
├── file-viewer
│ ├── favicon.ico
│ ├── fileTest
│ │ ├── mp3.mp3
│ │ ├── mp4.mp4
│ │ ├── pdf.pdf
│ │ ├── pic.png
│ │ ├── ppt.pptx
│ │ └── word.docx
│ ├── index.html
│ └── static
│ │ ├── css
│ │ ├── chunk-vendors.053867c8.css
│ │ └── index.30170716.css
│ │ └── js
│ │ ├── chunk-vendors.101c5e96.js
│ │ ├── chunk-vendors.101c5e96.js.map
│ │ ├── index.a286462a.js
│ │ ├── index.a286462a.js.map
│ │ ├── pdf.worker.d60c6c06.worker.js
│ │ ├── pdf.worker.d60c6c06.worker.js.map
│ │ ├── pptx.worker.3c5d3299.worker.js
│ │ └── pptx.worker.3c5d3299.worker.js.map
├── fileTest
│ ├── mp3.mp3
│ ├── mp4.mp4
│ ├── pdf.pdf
│ ├── pic.png
│ ├── ppt.pptx
│ └── word.docx
└── index.html
├── vue.config.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@vue/cli-plugin-babel/preset"
4 | ]
5 | }
--------------------------------------------------------------------------------
/.env.dev:
--------------------------------------------------------------------------------
1 | NODE_ENV = development
2 |
3 | VUE_APP_ENV = development
--------------------------------------------------------------------------------
/.env.docdev:
--------------------------------------------------------------------------------
1 | NODE_ENV = development
2 |
3 | VUE_APP_ENV = doc
--------------------------------------------------------------------------------
/.env.docpro:
--------------------------------------------------------------------------------
1 | NODE_ENV = production
2 |
3 | VUE_APP_ENV = doc
--------------------------------------------------------------------------------
/.env.iframe:
--------------------------------------------------------------------------------
1 | NODE_ENV = production
2 |
3 | VUE_APP_ENV = iframe
--------------------------------------------------------------------------------
/.env.pro:
--------------------------------------------------------------------------------
1 | NODE_ENV = production
2 |
3 | VUE_APP_ENV = production
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | lib
2 | dist
3 | docs
4 | file-viewer
5 | vendors
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2023-01-04 13:49:25
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-10 00:16:51
6 | * @Descripttion:
7 | */
8 | module.exports = {
9 | root: true,
10 | env: {
11 | node: true
12 | },
13 | extends: ['plugin:vue/essential', 'eslint:recommended'],
14 | parserOptions: {
15 | parser: '@babel/eslint-parser'
16 | },
17 | rules: {
18 | 'no-console': 'off',
19 | 'no-debugger': 'off',
20 | 'no-unused-vars': ['error', { args: 'none' }], // 允许函数参数未使用
21 | 'no-control-regex': 0, // 允许在正则表达式中使用控制字符
22 | //关闭组件命名规则
23 | 'vue/multi-word-component-names': 'off',
24 | // 强制使用单引号
25 | quotes: ['error', 'single'],
26 | // 强制不使用分号结尾
27 | semi: ['error', 'never']
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 | /lib
5 | .cache
6 | .temp
7 | doc/.vuepress/dist/
8 |
9 | # local env files
10 | .env.local
11 | .env.*.local
12 |
13 | # Log files
14 | npm-debug.log*
15 | yarn-debug.log*
16 | yarn-error.log*
17 | pnpm-debug.log*
18 |
19 | # Editor directories and files
20 | .idea
21 | .vscode
22 | *.suo
23 | *.ntvs*
24 | *.njsproj
25 | *.sln
26 | *.sw?
27 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | shamefully-hoist=true
2 | engine-strict=false
3 | save-exact=true
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-file-viewer
2 |
3 | ❤️ ❤️ ❤️ 开源不易,请各位大佬给个 Start✨✨✨,同时欢迎提 PR 和 Issure!:smile:
4 |
5 | #### 介绍:
6 |
7 | 这是以一个跨框架的解决方案(通过iframe引入)
8 |
9 | 该组件集成了市面上纯前端渲染多种常用文档格式的主流方案,如 pptx,docx,xlsx,pdf,视频,音频,纯文本和图片。
10 |
11 | 后期计划增加更多文件格式的在线浏览支持。
12 |
13 | ##### [☞☞☞ 项目 api 文档和演示戳这里](https://file-viewer.me7.cn/index.html)
14 |
15 | ##### 这里 特别鸣谢 [小爬的老粉丝](https://blog.csdn.net/wybaby168) 分享的文章、源码内核和思路,在此基础上进行了封包和优化
16 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2022-11-27 19:21:21
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2022-11-28 20:19:04
6 | * @Descripttion: babe配置
7 | */
8 | module.exports = {
9 | presets: ['@vue/cli-plugin-babel/preset']
10 | }
11 |
--------------------------------------------------------------------------------
/config/config.build.iframe.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2022-11-28 09:13:53
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-11 15:23:46
6 | * @Descripttion: iframe开发环境配置
7 | */
8 | const path = require('path')
9 | const fs = require('fs-extra')
10 | // 获取基于当前路径的目标文件
11 | const resolve = (dir) => path.join(__dirname, '../', dir)
12 |
13 | // 添加日志查看路径
14 | console.log('Source path:', resolve('public/file-viewer'))
15 | console.log('Target path:', resolve('docs'))
16 |
17 | module.exports = {
18 | publicPath: './',
19 | // 放在public下供项目演示时作为iframe入口使用
20 | outputDir: resolve('public/file-viewer'),
21 | pages: {
22 | index: {
23 | entry: 'examples/main.js',
24 | template: 'public/index.html',
25 | filename: 'index.html'
26 | }
27 | },
28 | assetsDir: 'static',
29 | // 扩展 webpack 配置
30 | chainWebpack: (config) => {
31 | // set worker-loader
32 | config.module
33 | .rule('worker')
34 | .test(/\.worker\.js$/)
35 | .use('worker-loader')
36 | .loader('worker-loader')
37 | .end()
38 |
39 | // 解决:worker 热更新问题
40 | config.module.rule('js').exclude.add(/\.worker\.js$/)
41 |
42 | // packages 加入编译
43 | config.module
44 | .rule('js')
45 | .include.add('/packages')
46 | .end()
47 | .use('babel')
48 | .loader('babel-loader')
49 |
50 | // 别名配置
51 | config.resolve.alias.set('@', '/examples').set('@packages', '/packages')
52 |
53 | // 使用 fs-extra 复制文件
54 | config.plugin('copy-file-viewer').use({
55 | apply: (compiler) => {
56 | compiler.hooks.done.tap('CopyFileViewerPlugin', (stats) => {
57 | const sourceDir = resolve('public/file-viewer')
58 | const targetDir = resolve('docs/file-viewer')
59 |
60 | // 添加日志
61 | console.log('开始复制文件...')
62 | console.log('源目录:', sourceDir)
63 | console.log('目标目录:', targetDir)
64 |
65 | try {
66 | fs.copySync(sourceDir, targetDir, { overwrite: true })
67 | console.log('文件复制成功!')
68 | } catch (err) {
69 | console.error('复制失败:', err)
70 | }
71 | })
72 | }
73 | })
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/config/config.build.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2022-11-28 09:13:53
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-09 21:49:06
6 | * @Descripttion: 生产环境配置
7 | */
8 |
9 | const path = require('path')
10 | // 获取基于当前路径的目标文件
11 | const resolve = (dir) => path.join(__dirname, '../', dir)
12 |
13 | const buildConfig = {
14 | // 输出文件目录
15 | outputDir: resolve('lib'),
16 | productionSourceMap: false,
17 | // webpack配置
18 | configureWebpack: {
19 | // 入口文件(这里获取到的是每个独立包的入口文件地址数组)
20 | entry: {
21 | // 全量引入的文件入口
22 | index: resolve('packages')
23 | },
24 | // 输出配置
25 | output: {
26 | // 主入口文件名称
27 | filename: '[name]/index.js',
28 | // 构建依赖类型
29 | libraryTarget: 'umd',
30 | // 库中被导出的项
31 | libraryExport: 'default',
32 | // 引用时的依赖名
33 | library: 'vue-file-viewer'
34 | }
35 | },
36 | css: {
37 | sourceMap: false,
38 | // 是否将组件中的css提取至一个独立的css,库构建时可以设置为false,免得用户需要自己导入css
39 | extract: false
40 | // extract: {
41 | // filename: '[name]/index.css',
42 | // },
43 | },
44 | chainWebpack: (config) => {
45 | // set worker-loader
46 | config.module
47 | .rule('worker')
48 | .test(/\.worker\.js$/)
49 | .use('worker-loader')
50 | .loader('worker-loader')
51 | .end()
52 |
53 | // 解决:worker 热更新问题
54 | config.module.rule('js').exclude.add(/\.worker\.js$/)
55 |
56 | // 一些构建优化
57 | // 删除splitChunks,因为每个组件是独立打包,不需要抽离每个组件的公共js出来。
58 | config.optimization.delete('splitChunks')
59 | // 删除copy,不要复制public文件夹内容到lib文件夹中。
60 | config.plugins.delete('copy')
61 | // 删除preload以及prefetch,因为不生成html页面,所以这两个也没用。
62 | config.plugins.delete('preload')
63 | config.plugins.delete('prefetch')
64 | // 删除html,只打包组件,不生成html页面。
65 | config.plugins.delete('html')
66 | // 删除hmr,删除热更新。
67 | config.plugins.delete('hmr')
68 | // 删除自动加上的入口App。
69 | config.entryPoints.delete('app')
70 | }
71 | }
72 |
73 | module.exports = buildConfig
74 |
--------------------------------------------------------------------------------
/config/config.dev.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2022-11-28 09:13:53
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-09 23:53:58
6 | * @Descripttion: 开发环境配置
7 | */
8 |
9 | module.exports = {
10 | pages: {
11 | index: {
12 | entry: 'examples/main.js',
13 | template: 'public/index.html',
14 | filename: 'index.html'
15 | }
16 | },
17 | // 扩展 webpack 配置
18 | chainWebpack: (config) => {
19 | // set worker-loader
20 | config.module
21 | .rule('worker')
22 | .test(/\.worker\.js$/)
23 | .use('worker-loader')
24 | .loader('worker-loader')
25 | .end()
26 |
27 | // 解决:worker 热更新问题
28 | config.module.rule('js').exclude.add(/\.worker\.js$/)
29 |
30 | // packages 加入编译
31 | config.module
32 | .rule('js')
33 | .include.add('/packages')
34 | .end()
35 | .use('babel')
36 | .loader('babel-loader')
37 |
38 | // 别名配置
39 | config.resolve.alias.set('@', '/examples').set('@packages', '/packages')
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/config/config.doc.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2022-11-28 09:13:53
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-11 13:33:12
6 | * @Descripttion: 文档环境配置
7 | */
8 |
9 | const path = require('path')
10 |
11 | module.exports = {
12 | outputDir: './docs',
13 | publicPath: './',
14 | assetsDir: 'static',
15 | pages: {
16 | index: {
17 | entry: 'doc/main.js',
18 | template: 'public/index.html',
19 | filename: 'index.html'
20 | }
21 | },
22 | // 扩展 webpack 配置
23 | chainWebpack: (config) => {
24 | // set worker-loader
25 | config.module
26 | .rule('worker')
27 | .test(/\.worker\.js$/)
28 | .use('worker-loader')
29 | .loader('worker-loader')
30 | .end()
31 |
32 | // 解决:worker 热更新问题
33 | config.module.rule('js').exclude.add(/\.worker\.js$/)
34 |
35 | // packages 加入编译
36 | config.module
37 | .rule('js')
38 | .include.add('/packages')
39 | .end()
40 | .use('babel')
41 | .loader('babel-loader')
42 |
43 | // 别名配置
44 | config.resolve.alias
45 | .set('@', path.resolve(__dirname, '../doc'))
46 | .set('@packages', path.resolve(__dirname, '../packages'))
47 | },
48 | css: {
49 | loaderOptions: {
50 | sass: {
51 | sassOptions: {
52 | // 设置 Sass 的严格模式,这有助于减少一些警告
53 | charset: false,
54 | outputStyle: 'expanded'
55 | },
56 | additionalData: `
57 | @use "sass:color";
58 | @use "@/style/mixins" as mix;
59 | @use "@/style/vars" as var;
60 | @use "@/style/md-colors" as colors;
61 | `
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/doc/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | vue-file-viewer
5 | {{ config.description }}
6 |
15 |
19 |
推荐 iframe 引入方式
20 |
21 |
22 |
STEP 1: 下载最新资源包
23 |
32 |
33 |
34 |
35 | STEP 2: 将项目中的 file-viewer 文件夹拷贝到您自己的项目公共目录(如Pubilc文件夹下)
36 |
37 |
38 |
39 |
STEP 3: iframe 引入
40 |
41 |
42 | <iframe
43 | src="./file-viewer/index.html?fileUrl=https://file-viewer.me7.cn/fileTest/pdf.pdf"
44 | scrolling="auto"
45 | id="file-viewer"
46 | style="border:0;height: 500px;width:100%"
47 | />
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | (©) 2023 - {{ new Date().getFullYear() }} copy by
60 |
追寻
61 |
62 | 提供源码内核思路
63 |
64 |
70 |
71 |
72 |
73 |
74 |
98 |
99 |
295 |
--------------------------------------------------------------------------------
/doc/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/app'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/doc/components/CodeSnippet.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
{{ lang }}
11 |
14 |
15 |
16 |
17 |
18 |
46 |
47 |
89 |
--------------------------------------------------------------------------------
/doc/components/Collapse.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 | {{ title }}
12 | ▼
13 |
14 |
15 |
16 |
17 |
18 |
32 |
33 |
61 |
--------------------------------------------------------------------------------
/doc/config.js:
--------------------------------------------------------------------------------
1 | export const config = require('../package.json')
2 |
3 | export const upName = config.name.replace(/(^\w|-\w)/g, m => m.replace('-', '').toUpperCase())
4 |
--------------------------------------------------------------------------------
/doc/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2023-01-10 14:28:32
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-10 15:37:42
6 | * @Descripttion:
7 | */
8 |
9 | import Vue from 'vue'
10 | import App from './App.vue'
11 | import router from './router'
12 | import VueFileViewer from '../packages'
13 | Vue.use(VueFileViewer)
14 |
15 | new Vue({
16 | el: '#app',
17 | router,
18 | render: (h) => h(App)
19 | })
20 |
--------------------------------------------------------------------------------
/doc/router.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2023-01-10 14:28:32
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-11 11:00:38
6 | * @Descripttion:
7 | */
8 | import Vue from 'vue'
9 | import VueRouter from 'vue-router'
10 | import Home from './views/Home.vue'
11 | import Start from './views/Start.vue'
12 |
13 | Vue.use(VueRouter)
14 |
15 | export const routes = [
16 | { path: '/', name: '快速上手', component: Home },
17 | { path: '/start', name: '使用文档', component: Start },
18 | { path: '*', redirect: '/' }
19 | ]
20 |
21 | const router = new VueRouter({ routes })
22 |
23 | export default router
24 |
--------------------------------------------------------------------------------
/doc/style/imports.scss:
--------------------------------------------------------------------------------
1 | @use './mixins' as mix;
2 | @use './vars' as var;
3 | @use './md-colors' as colors;
4 |
5 |
--------------------------------------------------------------------------------
/doc/style/md-colors.scss:
--------------------------------------------------------------------------------
1 | $md-red: #f44336;
2 | $md-red-50: #ffebee;
3 | $md-red-100: #ffcdd2;
4 | $md-red-200: #ef9a9a;
5 | $md-red-300: #e57373;
6 | $md-red-400: #ef5350;
7 | $md-red-500: #f44336;
8 | $md-red-600: #e53935;
9 | $md-red-700: #d32f2f;
10 | $md-red-800: #c62828;
11 | $md-red-900: #b71c1c;
12 | $md-red-a100: #ff8a80;
13 | $md-red-a200: #ff5252;
14 | $md-red-a400: #ff1744;
15 | $md-red-a700: #d50000;
16 |
17 | $md-pink: #e91e63;
18 | $md-pink-50: #fce4ec;
19 | $md-pink-100: #f8bbd0;
20 | $md-pink-200: #f48fb1;
21 | $md-pink-300: #f06292;
22 | $md-pink-400: #ec407a;
23 | $md-pink-500: #e91e63;
24 | $md-pink-600: #d81b60;
25 | $md-pink-700: #c2185b;
26 | $md-pink-800: #ad1457;
27 | $md-pink-900: #880e4f;
28 | $md-pink-a100: #ff80ab;
29 | $md-pink-a200: #ff4081;
30 | $md-pink-a400: #f50057;
31 | $md-pink-a700: #c51162;
32 |
33 | $md-purple: #9c27b0;
34 | $md-purple-50: #f3e5f5;
35 | $md-purple-100: #e1bee7;
36 | $md-purple-200: #ce93d8;
37 | $md-purple-300: #ba68c8;
38 | $md-purple-400: #ab47bc;
39 | $md-purple-500: #9c27b0;
40 | $md-purple-600: #8e24aa;
41 | $md-purple-700: #7b1fa2;
42 | $md-purple-800: #6a1b9a;
43 | $md-purple-900: #4a148c;
44 | $md-purple-a100: #ea80fc;
45 | $md-purple-a200: #e040fb;
46 | $md-purple-a400: #d500f9;
47 | $md-purple-a700: #aa00ff;
48 |
49 | $md-deep-purple: #673ab7;
50 | $md-deep-purple-50: #ede7f6;
51 | $md-deep-purple-100: #d1c4e9;
52 | $md-deep-purple-200: #b39ddb;
53 | $md-deep-purple-300: #9575cd;
54 | $md-deep-purple-400: #7e57c2;
55 | $md-deep-purple-500: #673ab7;
56 | $md-deep-purple-600: #5e35b1;
57 | $md-deep-purple-700: #512da8;
58 | $md-deep-purple-800: #4527a0;
59 | $md-deep-purple-900: #311b92;
60 | $md-deep-purple-a100: #b388ff;
61 | $md-deep-purple-a200: #7c4dff;
62 | $md-deep-purple-a400: #651fff;
63 | $md-deep-purple-a700: #6200ea;
64 |
65 | $md-indigo: #3f51b5;
66 | $md-indigo-50: #e8eaf6;
67 | $md-indigo-100: #c5cae9;
68 | $md-indigo-200: #9fa8da;
69 | $md-indigo-300: #7986cb;
70 | $md-indigo-400: #5c6bc0;
71 | $md-indigo-500: #3f51b5;
72 | $md-indigo-600: #3949ab;
73 | $md-indigo-700: #303f9f;
74 | $md-indigo-800: #283593;
75 | $md-indigo-900: #1a237e;
76 | $md-indigo-a100: #8c9eff;
77 | $md-indigo-a200: #536dfe;
78 | $md-indigo-a400: #3d5afe;
79 | $md-indigo-a700: #304ffe;
80 |
81 | $md-blue: #2196f3;
82 | $md-blue-50: #e3f2fd;
83 | $md-blue-100: #bbdefb;
84 | $md-blue-200: #90caf9;
85 | $md-blue-300: #64b5f6;
86 | $md-blue-400: #42a5f5;
87 | $md-blue-500: #2196f3;
88 | $md-blue-600: #1e88e5;
89 | $md-blue-700: #1976d2;
90 | $md-blue-800: #1565c0;
91 | $md-blue-900: #0d47a1;
92 | $md-blue-a100: #82b1ff;
93 | $md-blue-a200: #448aff;
94 | $md-blue-a400: #2979ff;
95 | $md-blue-a700: #2962ff;
96 |
97 | $md-light-blue: #03a9f4;
98 | $md-light-blue-50: #e1f5fe;
99 | $md-light-blue-100: #b3e5fc;
100 | $md-light-blue-200: #81d4fa;
101 | $md-light-blue-300: #4fc3f7;
102 | $md-light-blue-400: #29b6f6;
103 | $md-light-blue-500: #03a9f4;
104 | $md-light-blue-600: #039be5;
105 | $md-light-blue-700: #0288d1;
106 | $md-light-blue-800: #0277bd;
107 | $md-light-blue-900: #01579b;
108 | $md-light-blue-a100: #80d8ff;
109 | $md-light-blue-a200: #40c4ff;
110 | $md-light-blue-a400: #00b0ff;
111 | $md-light-blue-a700: #0091ea;
112 |
113 | $md-cyan: #00bcd4;
114 | $md-cyan-50: #e0f7fa;
115 | $md-cyan-100: #b2ebf2;
116 | $md-cyan-200: #80deea;
117 | $md-cyan-300: #4dd0e1;
118 | $md-cyan-400: #26c6da;
119 | $md-cyan-500: #00bcd4;
120 | $md-cyan-600: #00acc1;
121 | $md-cyan-700: #0097a7;
122 | $md-cyan-800: #00838f;
123 | $md-cyan-900: #006064;
124 | $md-cyan-a100: #84ffff;
125 | $md-cyan-a200: #18ffff;
126 | $md-cyan-a400: #00e5ff;
127 | $md-cyan-a700: #00b8d4;
128 |
129 | $md-teal: #009688;
130 | $md-teal-50: #e0f2f1;
131 | $md-teal-100: #b2dfdb;
132 | $md-teal-200: #80cbc4;
133 | $md-teal-300: #4db6ac;
134 | $md-teal-400: #26a69a;
135 | $md-teal-500: #009688;
136 | $md-teal-600: #00897b;
137 | $md-teal-700: #00796b;
138 | $md-teal-800: #00695c;
139 | $md-teal-900: #004d40;
140 | $md-teal-a100: #a7ffeb;
141 | $md-teal-a200: #64ffda;
142 | $md-teal-a400: #1de9b6;
143 | $md-teal-a700: #00bfa5;
144 |
145 | $md-green: #4caf50;
146 | $md-green-50: #e8f5e9;
147 | $md-green-100: #c8e6c9;
148 | $md-green-200: #a5d6a7;
149 | $md-green-300: #81c784;
150 | $md-green-400: #66bb6a;
151 | $md-green-500: #4caf50;
152 | $md-green-600: #43a047;
153 | $md-green-700: #388e3c;
154 | $md-green-800: #2e7d32;
155 | $md-green-900: #1b5e20;
156 | $md-green-a100: #b9f6ca;
157 | $md-green-a200: #69f0ae;
158 | $md-green-a400: #00e676;
159 | $md-green-a700: #00c853;
160 |
161 | $md-light-green: #8bc34a;
162 | $md-light-green-50: #f1f8e9;
163 | $md-light-green-100: #dcedc8;
164 | $md-light-green-200: #c5e1a5;
165 | $md-light-green-300: #aed581;
166 | $md-light-green-400: #9ccc65;
167 | $md-light-green-500: #8bc34a;
168 | $md-light-green-600: #7cb342;
169 | $md-light-green-700: #689f38;
170 | $md-light-green-800: #558b2f;
171 | $md-light-green-900: #33691e;
172 | $md-light-green-a100: #ccff90;
173 | $md-light-green-a200: #b2ff59;
174 | $md-light-green-a400: #76ff03;
175 | $md-light-green-a700: #64dd17;
176 |
177 | $md-lime: #cddc39;
178 | $md-lime-50: #f9fbe7;
179 | $md-lime-100: #f0f4c3;
180 | $md-lime-200: #e6ee9c;
181 | $md-lime-300: #dce775;
182 | $md-lime-400: #d4e157;
183 | $md-lime-500: #cddc39;
184 | $md-lime-600: #c0ca33;
185 | $md-lime-700: #afb42b;
186 | $md-lime-800: #9e9d24;
187 | $md-lime-900: #827717;
188 | $md-lime-a100: #f4ff81;
189 | $md-lime-a200: #eeff41;
190 | $md-lime-a400: #c6ff00;
191 | $md-lime-a700: #aeea00;
192 |
193 | $md-yellow: #ffeb3b;
194 | $md-yellow-50: #fffde7;
195 | $md-yellow-100: #fff9c4;
196 | $md-yellow-200: #fff59d;
197 | $md-yellow-300: #fff176;
198 | $md-yellow-400: #ffee58;
199 | $md-yellow-500: #ffeb3b;
200 | $md-yellow-600: #fdd835;
201 | $md-yellow-700: #fbc02d;
202 | $md-yellow-800: #f9a825;
203 | $md-yellow-900: #f57f17;
204 | $md-yellow-a100: #ffff8d;
205 | $md-yellow-a200: #ffff00;
206 | $md-yellow-a400: #ffea00;
207 | $md-yellow-a700: #ffd600;
208 |
209 | $md-amber: #ffc107;
210 | $md-amber-50: #fff8e1;
211 | $md-amber-100: #ffecb3;
212 | $md-amber-200: #ffe082;
213 | $md-amber-300: #ffd54f;
214 | $md-amber-400: #ffca28;
215 | $md-amber-500: #ffc107;
216 | $md-amber-600: #ffb300;
217 | $md-amber-700: #ffa000;
218 | $md-amber-800: #ff8f00;
219 | $md-amber-900: #ff6f00;
220 | $md-amber-a100: #ffe57f;
221 | $md-amber-a200: #ffd740;
222 | $md-amber-a400: #ffc400;
223 | $md-amber-a700: #ffab00;
224 |
225 | $md-orange: #ff9800;
226 | $md-orange-50: #fff3e0;
227 | $md-orange-100: #ffe0b2;
228 | $md-orange-200: #ffcc80;
229 | $md-orange-300: #ffb74d;
230 | $md-orange-400: #ffa726;
231 | $md-orange-500: #ff9800;
232 | $md-orange-600: #fb8c00;
233 | $md-orange-700: #f57c00;
234 | $md-orange-800: #ef6c00;
235 | $md-orange-900: #e65100;
236 | $md-orange-a100: #ffd180;
237 | $md-orange-a200: #ffab40;
238 | $md-orange-a400: #ff9100;
239 | $md-orange-a700: #ff6d00;
240 |
241 | $md-deep-orange: #ff5722;
242 | $md-deep-orange-50: #fbe9e7;
243 | $md-deep-orange-100: #ffccbc;
244 | $md-deep-orange-200: #ffab91;
245 | $md-deep-orange-300: #ff8a65;
246 | $md-deep-orange-400: #ff7043;
247 | $md-deep-orange-500: #ff5722;
248 | $md-deep-orange-600: #f4511e;
249 | $md-deep-orange-700: #e64a19;
250 | $md-deep-orange-800: #d84315;
251 | $md-deep-orange-900: #bf360c;
252 | $md-deep-orange-a100: #ff9e80;
253 | $md-deep-orange-a200: #ff6e40;
254 | $md-deep-orange-a400: #ff3d00;
255 | $md-deep-orange-a700: #dd2c00;
256 |
257 | $md-brown: #795548;
258 | $md-brown-50: #efebe9;
259 | $md-brown-100: #d7ccc8;
260 | $md-brown-200: #bcaaa4;
261 | $md-brown-300: #a1887f;
262 | $md-brown-400: #8d6e63;
263 | $md-brown-500: #795548;
264 | $md-brown-600: #6d4c41;
265 | $md-brown-700: #5d4037;
266 | $md-brown-800: #4e342e;
267 | $md-brown-900: #3e2723;
268 |
269 | $md-grey: #9e9e9e;
270 | $md-grey-50: #fafafa;
271 | $md-grey-100: #f5f5f5;
272 | $md-grey-200: #eeeeee;
273 | $md-grey-300: #e0e0e0;
274 | $md-grey-400: #bdbdbd;
275 | $md-grey-500: #9e9e9e;
276 | $md-grey-600: #757575;
277 | $md-grey-700: #616161;
278 | $md-grey-800: #424242;
279 | $md-grey-900: #212121;
280 |
281 | $md-blue-grey: #607d8b;
282 | $md-blue-grey-50: #eceff1;
283 | $md-blue-grey-100: #cfd8dc;
284 | $md-blue-grey-200: #b0bec5;
285 | $md-blue-grey-300: #90a4ae;
286 | $md-blue-grey-400: #78909c;
287 | $md-blue-grey-500: #607d8b;
288 | $md-blue-grey-600: #546e7a;
289 | $md-blue-grey-700: #455a64;
290 | $md-blue-grey-800: #37474f;
291 | $md-blue-grey-900: #263238;
292 |
293 | $md-black: #000000;
294 | $md-white: #ffffff;
295 |
--------------------------------------------------------------------------------
/doc/style/mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin ellipsis {
2 | overflow: hidden;
3 | -ms-text-overflow: ellipsis;
4 | text-overflow: ellipsis;
5 | white-space: nowrap;
6 | }
7 |
8 | @mixin bounds($distance) {
9 | top: $distance;
10 | bottom: $distance;
11 | right: $distance;
12 | left: $distance;
13 | }
14 |
15 | @mixin overlay {
16 | position: absolute;
17 | @include bounds(0);
18 | }
19 |
20 | @mixin flex-box {
21 | display: flex;
22 |
23 | & > * {
24 | flex: auto 0 0;
25 | }
26 | }
27 |
28 | @mixin h-box {
29 | display: flex;
30 |
31 | & > * {
32 | flex: auto 0 0;
33 | }
34 |
35 | flex-direction: row;
36 | }
37 |
38 | @mixin v-box {
39 | display: flex;
40 |
41 | & > * {
42 | flex: auto 0 0;
43 | }
44 |
45 | flex-direction: column;
46 | }
47 |
48 | @mixin flex-control {
49 | width: 0 !important;
50 | }
51 |
52 | @mixin box-center {
53 | align-items: center;
54 | justify-content: center;
55 | }
56 |
57 | @function fade($color, $amount) {
58 | @return color.adjust($color, $alpha: -$amount / 100%);
59 | }
60 |
61 | @mixin toolbar-btn ($bg) {
62 | background: fade($bg, 80%);
63 | color: black;
64 | transition: background 0.2s;
65 |
66 | &:hover {
67 | color: black;
68 | background: $bg;
69 | }
70 | }
71 |
72 | @mixin space-between-x($margin) {
73 | margin-right: $margin;
74 |
75 | &:last-child {
76 | margin-right: 0;
77 | }
78 | }
79 |
80 | @mixin space-between-y($margin) {
81 | margin-bottom: $margin;
82 |
83 | &:last-child {
84 | margin-bottom: 0;
85 | }
86 | }
87 |
88 | @mixin unselectable {
89 | -moz-user-select: none;
90 | -webkit-user-select: none;
91 | -ms-user-select: none;
92 | user-select: none;
93 | }
94 |
--------------------------------------------------------------------------------
/doc/style/vars.scss:
--------------------------------------------------------------------------------
1 | $primary-color: #40b883;
2 |
--------------------------------------------------------------------------------
/doc/views/Home.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
20 |
21 |
22 |
32 |
--------------------------------------------------------------------------------
/doc/views/Start.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
属性配置
12 |
13 |
14 |
fileUrl
15 |
string
16 |
上传的地址(必传)
17 |
18 |
19 |
useOfficeMicroOnline
20 |
boolean
21 |
是否开启使用微软提供的office文件在线访问接口(仅office文件格式有效: [docx, pptx, xlsx], 内网使用无效,可选,默认false)
22 |
23 |
24 |
showCtrolBtn
25 |
boolean
26 |
v1.3.2 是否显示控制工具按钮块(可选,默认true)
27 |
28 |
29 |
showScaleBtn
30 |
boolean
31 |
v1.3.2 是否显示放大缩小按钮(可选,默认true)
32 |
33 |
34 |
showDownLoadBtn
35 |
boolean
36 |
v1.3.2 是否显示下载按钮(可选,默认true)
37 |
38 |
39 |
40 |
事件
41 |
42 |
43 |
fileLoaded
44 |
function
45 |
46 | 文档加载完成的回调函数
47 |
48 |
49 |
50 |
51 |
52 |
注意事项
53 |
54 |
55 | - 被浏览的文件链接必须同源或本身支持跨域才能访问和下载文件。
56 | -
57 | 若发布生产的项目不在服务器域名根目录,记得配置打包的前缀路径,打包后资源才能被正常引用。(以vue-cli为例,配置
58 |
vue.config.js
59 | 的
60 | publicPath: './'
61 | ; 其他框架请自行选择合适的配置文件进行配置)
62 |
63 | -
64 | 由于office系列格式的文件解析微软不开源,无法保证百分百还原文档效果,若实际需求没有严格要求百分百还原一致,您可以使用纯前端渲染office文件的这种方式,否则建议通过后端统一转为PDF或者图片格式进行浏览,PDF和图片格式的文件浏览效果最佳,内核渲染方案也较为成熟。
65 |
66 | -
67 | 或者您也可以通过iframe或者组件形式传递
68 |
useOfficeMicroOnline = true
69 | 这个属性开启office系列文件使用内置的微软文档在线访问接口,浏览效果会比纯前端渲染好。
70 |
71 |
72 |
73 |
74 |
75 |
76 |
86 |
87 |
88 |
89 |
90 |
91 |
使用说明
92 |
93 |
94 | 推荐直接部署本项目最新版本的独立构建产物
95 |
100 | file-viewer
101 |
102 | ,并使用iframe方式引入以减少引入构建代价,提升构建效率。
103 |
104 |
此种方式支持跨框架使用。
105 |
106 |
107 |
使用步骤
108 |
109 | -
110 | 需要自行前往本项目源码处下载编译构建后的产物
111 |
116 | file-viewer
117 |
118 | 。
119 |
120 | -
121 | 将下载后的 file-viewer
122 | 文件夹整个放在项目公共文件夹中作为外部公共资源使用。 (以 vue-cli
123 | 为例,放置在项目 public 文件夹下;其他框架自行选择合适的公共路径放置)
124 |
125 |
126 |
127 |
使用示例
128 |
129 |
130 |
136 |
137 |
138 |
139 |
140 |
141 |
+
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
微软文档在线访问
150 |
151 | 可通过
152 | useOfficeMicroOnline = true
153 | 开启微软文档在线访问接口,该接口兼容不带后缀x的低版本文档,如:doc或者docx;
154 | 开启后属于office的文件会通过微软提供的api接口进行访问
155 | http://view.officeapps.live.com/op/view.aspx?src=文件地址
156 | (隐私文件不推荐开启,该选项内网无联网时不可用)
157 |
158 |
159 | 注意事项:若手机端通过iframe访问微软文档,整个父页面会被重定向,自
160 | v1.2.1
161 | 版本开始,手机端访问时需要用户手动点击跳转查看,pc端可正常访问。
162 |
163 |
164 |
165 |
166 |
167 |
172 |
173 |
174 |
175 |
176 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
安装
192 |
193 |
194 |
195 |
或者页面内按需引入:
196 |
197 |
198 |
199 |
使用示例
200 |
201 |
202 |
207 |
208 |
209 |
210 |
211 |
212 |
+
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
注意事项
225 |
226 | -
227 | 若为node环境,且发布生产的项目不在服务器域名根目录,这将导致生产和开发环境的引用路径不一致(由于动态赋值的路径只会被编译器原样解析,需要特别注意通过配置打包的前缀路径也不会对此次生效,例如
228 |
vue-cli
229 | 的
230 | publicPath
231 | ),可通过
232 | process.env.NODE_ENV
233 | 进行手动判断。
234 |
235 | - 若为浏览器环境,若有需要可在前面配置统一前缀路径即可。
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
+
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
513 |
514 |
803 |
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/file-viewer/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/docs/file-viewer/favicon.ico
--------------------------------------------------------------------------------
/docs/file-viewer/fileTest/mp3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/docs/file-viewer/fileTest/mp3.mp3
--------------------------------------------------------------------------------
/docs/file-viewer/fileTest/mp4.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/docs/file-viewer/fileTest/mp4.mp4
--------------------------------------------------------------------------------
/docs/file-viewer/fileTest/pdf.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/docs/file-viewer/fileTest/pdf.pdf
--------------------------------------------------------------------------------
/docs/file-viewer/fileTest/pic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/docs/file-viewer/fileTest/pic.png
--------------------------------------------------------------------------------
/docs/file-viewer/fileTest/ppt.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/docs/file-viewer/fileTest/ppt.pptx
--------------------------------------------------------------------------------
/docs/file-viewer/fileTest/word.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/docs/file-viewer/fileTest/word.docx
--------------------------------------------------------------------------------
/docs/file-viewer/index.html:
--------------------------------------------------------------------------------
1 |
@zuiyouliao/vue-file-viewer
--------------------------------------------------------------------------------
/docs/fileTest/mp3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/docs/fileTest/mp3.mp3
--------------------------------------------------------------------------------
/docs/fileTest/mp4.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/docs/fileTest/mp4.mp4
--------------------------------------------------------------------------------
/docs/fileTest/pdf.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/docs/fileTest/pdf.pdf
--------------------------------------------------------------------------------
/docs/fileTest/pic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/docs/fileTest/pic.png
--------------------------------------------------------------------------------
/docs/fileTest/ppt.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/docs/fileTest/ppt.pptx
--------------------------------------------------------------------------------
/docs/fileTest/word.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/docs/fileTest/word.docx
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 | @zuiyouliao/vue-file-viewer
--------------------------------------------------------------------------------
/examples/App.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
26 |
--------------------------------------------------------------------------------
/examples/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2022-11-27 19:21:21
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-13 13:13:14
6 | * @Descripttion: 主入口文件
7 | */
8 |
9 | import Vue from 'vue'
10 | import App from './App.vue'
11 |
12 | // import VueFileViewer from '../packages'
13 | // Vue.use(VueFileViewer)
14 |
15 | Vue.config.productionTip = false
16 |
17 | new Vue({
18 | render: (h) => h(App)
19 | }).$mount('#app')
20 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "esnext",
5 | "baseUrl": "./",
6 | "moduleResolution": "node",
7 | "paths": {
8 | "@/*": [
9 | "examples/*"
10 | ]
11 | },
12 | "lib": [
13 | "esnext",
14 | "dom",
15 | "dom.iterable",
16 | "scripthost"
17 | ]
18 | }
19 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@zuiyouliao/vue-file-viewer",
3 | "version": "1.3.2",
4 | "private": false,
5 | "author": "zhanghan <294333196@qq.com>",
6 | "license": "MIT",
7 | "description": "一个基于vue开发的跨框架、多格式、纯前端渲染的文件浏览解决方案(支持格式:pptx,docx,xlsx,pdf,视频,音频,纯文本和图片)",
8 | "directories": {
9 | "doc": "docs",
10 | "example": "examples",
11 | "lib": "lib"
12 | },
13 | "files": [
14 | "packages",
15 | "lib"
16 | ],
17 | "main": "lib/index",
18 | "scripts": {
19 | "serve": "vue-cli-service serve --mode dev --open",
20 | "build": "vue-cli-service build --mode pro",
21 | "build:iframe": "vue-cli-service build --mode iframe",
22 | "doc:dev": "vue-cli-service serve --mode docdev --open",
23 | "doc:build": "vue-cli-service build --mode docpro",
24 | "lint": "vue-cli-service lint"
25 | },
26 | "dependencies": {
27 | "@handsontable/vue": "^12.0.1",
28 | "axios": "^0.27.2",
29 | "core-js": "^3.22.7",
30 | "d3": "^3.5.17",
31 | "docx-preview": "^0.1.11",
32 | "exceljs": "^4.3.0",
33 | "handsontable": "^12.0.1",
34 | "jquery": "^3.6.0",
35 | "jszip": "^3.10.0",
36 | "nvd3": "^1.8.6",
37 | "pdfjs-dist": "^2.15.349",
38 | "qs": "^6.10.3",
39 | "tinycolor2": "^1.4.2",
40 | "v-viewer": "^1.6.4"
41 | },
42 | "peerDependencies": {
43 | "vue": "^2.6.14"
44 | },
45 | "devDependencies": {
46 | "@babel/core": "^7.12.16",
47 | "@babel/eslint-parser": "^7.19.1",
48 | "@vue/cli-plugin-babel": "^4.5.19",
49 | "@vue/cli-plugin-eslint": "^4.5.19",
50 | "@vue/cli-service": "^4.5.19",
51 | "copy-webpack-plugin": "^5.1.2",
52 | "eslint": "^6.8.0",
53 | "eslint-plugin-vue": "^7.20.0",
54 | "fs-extra": "^9.1.0",
55 | "highlight.js": "^9.17.1",
56 | "prettier": "^1.19.1",
57 | "prettier-eslint": "^9.0.1",
58 | "sass": "1.32.13",
59 | "sass-loader": "10.1.1",
60 | "screenfull": "^5.0.0",
61 | "vue": "^2.6.14",
62 | "vue-router": "^3.1.3",
63 | "vue-template-compiler": "^2.6.14",
64 | "worker-loader": "^3.0.8"
65 | },
66 | "repository": {
67 | "type": "git",
68 | "url": "git+https://github.com/zyl-ui/vue-file-viewer.git"
69 | },
70 | "keywords": [
71 | "vue",
72 | "zyl-ui"
73 | ],
74 | "bugs": {
75 | "url": "https://github.com/zyl-ui/vue-file-viewer/issues"
76 | },
77 | "homepage": "https://github.com/zyl-ui/vue-file-viewer#readme",
78 | "browserslist": [
79 | "> 1%",
80 | "last 2 versions",
81 | "not ie <= 8",
82 | "iOS >= 8",
83 | "Firefox >= 20",
84 | "Android >= 4.4"
85 | ],
86 | "engines": {
87 | "node": ">=12.18.0"
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/packages/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2023-01-04 13:49:25
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-13 13:15:34
6 | * @Descripttion: 注册组件
7 | */
8 | import VueFileViewer from './index.vue'
9 |
10 | VueFileViewer.install = (Vue) =>
11 | Vue.component(VueFileViewer.name, VueFileViewer)
12 |
13 | export { VueFileViewer }
14 | export default VueFileViewer
15 |
--------------------------------------------------------------------------------
/packages/index.scss:
--------------------------------------------------------------------------------
1 | .vue-file-viewer {
2 | position: relative;
3 | width: 100%;
4 | height: 100%;
5 | overflow-y: hidden;
6 | .banner {
7 | color: #000;
8 | position: absolute;
9 | z-index: 2014;
10 | width: 100%;
11 | }
12 |
13 | .banner div {
14 | color: #000;
15 | }
16 |
17 | .banner h1 {
18 | font-size: 20px;
19 | text-align: center;
20 | }
21 |
22 | .file-select {
23 | padding: 0 20px;
24 | height: 45px;
25 | position: absolute;
26 | top: 2.5vw;
27 | }
28 |
29 | .file-select button {
30 | background: #fafafa;
31 | }
32 |
33 | .overlay button {
34 | background: #12b6ff;
35 | color: white;
36 | }
37 |
38 | button {
39 | outline: none;
40 | border-radius: 20px;
41 | border: 1px solid #e3e3e3;
42 | line-height: 19px;
43 | padding: 5px 12px;
44 | cursor: pointer;
45 | }
46 |
47 | .overlay input[type='text'] {
48 | line-height: 19px;
49 | height: 30px;
50 | outline: none;
51 | border: 1px solid silver;
52 | border-radius: 6px;
53 | margin-right: 10px;
54 | }
55 |
56 | .overlay {
57 | z-index: 9999;
58 | opacity: 0.4;
59 | padding: 10px;
60 | border-radius: 5px;
61 | background: white;
62 | border: 1px solid silver;
63 | margin-top: 5px;
64 | position: absolute;
65 | }
66 |
67 | .overlay:hover {
68 | opacity: 1;
69 | }
70 |
71 | .loading {
72 | text-align: center;
73 | padding-top: 50px;
74 | }
75 |
76 | .ctrol_btn {
77 | position: absolute;
78 | right: 2.5vw;
79 | margin-top: 2.5vh;
80 | z-index: 999999;
81 | display: flex;
82 | justify-content: center;
83 | align-items: center;
84 | padding: 8px 15px;
85 | border-radius: 20px;
86 | background-color: rgba(0, 0, 0, 0.4);
87 | color: white;
88 | font-size: 1rem;
89 | backdrop-filter: blur(5px);
90 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
91 | transition: all 0.3s ease;
92 | user-select: none;
93 | -webkit-tap-highlight-color: transparent;
94 |
95 | &:hover {
96 | background-color: rgba(0, 0, 0, 0.5);
97 | }
98 |
99 | > span {
100 | cursor: pointer;
101 | display: inline-flex;
102 | align-items: center;
103 | user-select: none;
104 | }
105 |
106 | .scale_add{
107 | margin-right: 15px;
108 | }
109 |
110 | .scale_add,
111 | .scale_reduce {
112 | width: 28px;
113 | height: 28px;
114 | display: inline-flex;
115 | align-items: center;
116 | justify-content: center;
117 | border-radius: 50%;
118 | transition: all 0.2s ease;
119 | position: relative;
120 | -webkit-tap-highlight-color: transparent;
121 |
122 | &:hover {
123 | background-color: rgba(255, 255, 255, 0.15);
124 | }
125 |
126 | &:before {
127 | content: "";
128 | width: 18px;
129 | height: 18px;
130 | background-repeat: no-repeat;
131 | background-position: center;
132 | background-size: contain;
133 | }
134 | }
135 |
136 | .scale_add:before {
137 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='18' height='18' fill='white' stroke='white' stroke-width='0.5'%3E%3Cpath d='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z'/%3E%3C/svg%3E");
138 | }
139 |
140 | .scale_add:hover:before {
141 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='18' height='18' fill='%23ffffff' stroke='%23ffffff' stroke-width='0.5'%3E%3Cpath d='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z'/%3E%3C/svg%3E");
142 | }
143 |
144 | .scale_reduce:before {
145 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='18' height='18' fill='white' stroke='white' stroke-width='0.5'%3E%3Cpath d='M19 13H5v-2h14v2z'/%3E%3C/svg%3E");
146 | }
147 |
148 | .scale_reduce:hover:before {
149 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='18' height='18' fill='%23ffffff' stroke='%23ffffff' stroke-width='0.5'%3E%3Cpath d='M19 13H5v-2h14v2z'/%3E%3C/svg%3E");
150 | }
151 |
152 | .download {
153 | position: relative;
154 | padding-left: 28px;
155 | font-size: 0.95rem;
156 | font-weight: 500;
157 | user-select: none;
158 | -webkit-tap-highlight-color: transparent;
159 |
160 | &:before {
161 | content: "";
162 | position: absolute;
163 | left: 0;
164 | top: 50%;
165 | transform: translateY(-50%);
166 | width: 20px;
167 | height: 20px;
168 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='20' height='20' fill='white' stroke='white' stroke-width='0.5'%3E%3Cpath d='M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z'/%3E%3C/svg%3E");
169 | background-repeat: no-repeat;
170 | background-position: center;
171 | background-size: contain;
172 | }
173 |
174 | &:hover {
175 | color: #12b6ff;
176 |
177 | &:before {
178 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='20' height='20' fill='%2312b6ff' stroke='%2312b6ff' stroke-width='0.5'%3E%3Cpath d='M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z'/%3E%3C/svg%3E");
179 | }
180 | }
181 | }
182 | }
183 |
184 | :deep(.output > div) {
185 | height: 100%;
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/packages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
64 | |
65 |
66 |
70 | 下载
71 |
72 |
73 |
74 |
75 |
正在加载中,请耐心等待...
76 |
87 |
88 |
89 |
90 |
91 |
405 |
406 |
407 |
--------------------------------------------------------------------------------
/packages/pollify.js:
--------------------------------------------------------------------------------
1 | var base64EncodeChars =
2 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
3 | var base64DecodeChars = new Array(
4 | -1,
5 | -1,
6 | -1,
7 | -1,
8 | -1,
9 | -1,
10 | -1,
11 | -1,
12 | -1,
13 | -1,
14 | -1,
15 | -1,
16 | -1,
17 | -1,
18 | -1,
19 | -1,
20 | -1,
21 | -1,
22 | -1,
23 | -1,
24 | -1,
25 | -1,
26 | -1,
27 | -1,
28 | -1,
29 | -1,
30 | -1,
31 | -1,
32 | -1,
33 | -1,
34 | -1,
35 | -1,
36 | -1,
37 | -1,
38 | -1,
39 | -1,
40 | -1,
41 | -1,
42 | -1,
43 | -1,
44 | -1,
45 | -1,
46 | -1,
47 | 62,
48 | -1,
49 | -1,
50 | -1,
51 | 63,
52 | 52,
53 | 53,
54 | 54,
55 | 55,
56 | 56,
57 | 57,
58 | 58,
59 | 59,
60 | 60,
61 | 61,
62 | -1,
63 | -1,
64 | -1,
65 | -1,
66 | -1,
67 | -1,
68 | -1,
69 | 0,
70 | 1,
71 | 2,
72 | 3,
73 | 4,
74 | 5,
75 | 6,
76 | 7,
77 | 8,
78 | 9,
79 | 10,
80 | 11,
81 | 12,
82 | 13,
83 | 14,
84 | 15,
85 | 16,
86 | 17,
87 | 18,
88 | 19,
89 | 20,
90 | 21,
91 | 22,
92 | 23,
93 | 24,
94 | 25,
95 | -1,
96 | -1,
97 | -1,
98 | -1,
99 | -1,
100 | -1,
101 | 26,
102 | 27,
103 | 28,
104 | 29,
105 | 30,
106 | 31,
107 | 32,
108 | 33,
109 | 34,
110 | 35,
111 | 36,
112 | 37,
113 | 38,
114 | 39,
115 | 40,
116 | 41,
117 | 42,
118 | 43,
119 | 44,
120 | 45,
121 | 46,
122 | 47,
123 | 48,
124 | 49,
125 | 50,
126 | 51,
127 | -1,
128 | -1,
129 | -1,
130 | -1,
131 | -1
132 | )
133 |
134 | export function btoa(str) {
135 | var out, i, len
136 | var c1, c2, c3
137 | len = str.length
138 | i = 0
139 | out = ''
140 | while (i < len) {
141 | c1 = str.charCodeAt(i++) & 0xff
142 | if (i == len) {
143 | out += base64EncodeChars.charAt(c1 >> 2)
144 | out += base64EncodeChars.charAt((c1 & 0x3) << 4)
145 | out += '=='
146 | break
147 | }
148 | c2 = str.charCodeAt(i++)
149 | if (i == len) {
150 | out += base64EncodeChars.charAt(c1 >> 2)
151 | out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4))
152 | out += base64EncodeChars.charAt((c2 & 0xf) << 2)
153 | out += '='
154 | break
155 | }
156 | c3 = str.charCodeAt(i++)
157 | out += base64EncodeChars.charAt(c1 >> 2)
158 | out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4))
159 | out += base64EncodeChars.charAt(((c2 & 0xf) << 2) | ((c3 & 0xc0) >> 6))
160 | out += base64EncodeChars.charAt(c3 & 0x3f)
161 | }
162 | return out
163 | }
164 |
165 | export function atob(str) {
166 | var c1, c2, c3, c4
167 | var i, len, out
168 | len = str.length
169 | i = 0
170 | out = ''
171 | while (i < len) {
172 | do {
173 | c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff]
174 | } while (i < len && c1 == -1)
175 | if (c1 == -1) break
176 | do {
177 | c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff]
178 | } while (i < len && c2 == -1)
179 | if (c2 == -1) break
180 | out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4))
181 | do {
182 | c3 = str.charCodeAt(i++) & 0xff
183 | if (c3 == 61) return out
184 | c3 = base64DecodeChars[c3]
185 | } while (i < len && c3 == -1)
186 | if (c3 == -1) break
187 | out += String.fromCharCode(((c2 & 0xf) << 4) | ((c3 & 0x3c) >> 2))
188 | do {
189 | c4 = str.charCodeAt(i++) & 0xff
190 | if (c4 == 61) return out
191 | c4 = base64DecodeChars[c4]
192 | } while (i < len && c4 == -1)
193 | if (c4 == -1) break
194 | out += String.fromCharCode(((c3 & 0x03) << 6) | c4)
195 | }
196 | return out
197 | }
198 |
--------------------------------------------------------------------------------
/packages/renders.js:
--------------------------------------------------------------------------------
1 | import { defaultOptions, renderAsync } from 'docx-preview'
2 | import EventBus from './util/EventBus'
3 | import renderPptx from './vendors/pptx'
4 | import renderSheet from './vendors/xlsx'
5 | import renderPdf from './vendors/pdf'
6 | import renderImage from './vendors/image'
7 | import renderText from './vendors/text'
8 | import renderMp4 from './vendors/mp4'
9 | import renderMusic from './vendors/audio'
10 | import renderOfficeOnline from './vendors/officeOnline'
11 | import renderError from './vendors/error'
12 | import renderNotFind from './vendors/notFind'
13 |
14 | // 假装构造一个vue的渲染容器
15 | const VueWrapper = (el) => ({
16 | $el: el
17 | })
18 |
19 | const handlers = [
20 | // 使用docxjs支持,目前效果最好的渲染器
21 | {
22 | parentType: 'office',
23 | accepts: ['docx'],
24 | handler: async (buffer, target) => {
25 | const docxOptions = Object.assign(defaultOptions, {
26 | debug: true,
27 | experimental: true
28 | })
29 | await renderAsync(buffer, target, null, docxOptions)
30 | EventBus.$emit('fileLoaded', { fileType: 'doc', success: true })
31 | return VueWrapper(target)
32 | }
33 | },
34 | // 使用pptx2html,已通过默认值更替
35 | {
36 | parentType: 'office',
37 | accepts: ['pptx'],
38 | handler: async (buffer, target) => {
39 | await renderPptx(buffer, target, null)
40 | window.dispatchEvent(new Event('resize'))
41 | return VueWrapper(target)
42 | }
43 | },
44 | // 使用sheetjs + handsontable,无样式
45 | {
46 | parentType: 'office',
47 | accepts: ['xlsx'],
48 | handler: async (buffer, target) => {
49 | return renderSheet(buffer, target)
50 | }
51 | },
52 | // 使用pdfjs,渲染pdf,效果最好
53 | {
54 | parentType: 'pdf',
55 | accepts: ['pdf'],
56 | handler: async (buffer, target) => {
57 | return renderPdf(buffer, target)
58 | }
59 | },
60 | // 图片过滤器
61 | {
62 | parentType: 'image',
63 | accepts: ['gif', 'jpg', 'jpeg', 'bmp', 'tiff', 'tif', 'png', 'svg'],
64 | handler: async (buffer, target) => {
65 | return renderImage(buffer, target)
66 | }
67 | },
68 | // 纯文本预览
69 | {
70 | parentType: 'text',
71 | accepts: [
72 | 'txt',
73 | 'json',
74 | 'js',
75 | 'css',
76 | 'java',
77 | 'py',
78 | 'html',
79 | 'jsx',
80 | 'ts',
81 | 'tsx',
82 | 'xml',
83 | 'md',
84 | 'log'
85 | ],
86 | handler: async (buffer, target) => {
87 | return renderText(buffer, target)
88 | }
89 | },
90 | // 视频预览,仅支持MP4
91 | {
92 | parentType: 'video',
93 | accepts: ['mp4', 'webm', 'ogv'],
94 | handler: async (buffer, target) => {
95 | renderMp4(buffer, target)
96 | return VueWrapper(target)
97 | }
98 | },
99 | // 音频预览,仅支持MP3, WAV, OGG
100 | {
101 | parentType: 'audio',
102 | accepts: ['mp3', 'wav', 'ogg'],
103 | handler: async (buffer, target) => {
104 | renderMusic(buffer, target)
105 | return VueWrapper(target)
106 | }
107 | },
108 | {
109 | parentType: 'officeOnline',
110 | accepts: ['officeOnline'],
111 | handler: async (url, target, type, name) => {
112 | renderOfficeOnline(url, target, type, name)
113 | return VueWrapper(target)
114 | }
115 | },
116 | // 不支持格式处理
117 | {
118 | parentType: 'error',
119 | accepts: ['error'],
120 | handler: async (buffer, target, type, name) => {
121 | renderError(buffer, target, type, name)
122 | return VueWrapper(target)
123 | }
124 | },
125 | // 找不到文件处理
126 | {
127 | parentType: 'notFind',
128 | accepts: ['notFind'],
129 | handler: async (url, target, type, name) => {
130 | renderNotFind(url, target, type, name)
131 | return VueWrapper(target)
132 | }
133 | }
134 | ]
135 |
136 | // 获取类型从属关联关系集合
137 | export const typeInfo = handlers.reduce((result, { parentType, accepts }) => {
138 | const types = result[parentType] || []
139 | result[parentType] = [...types, ...accepts]
140 | return result
141 | }, {})
142 |
143 | // 提取处理方法为对象键值对
144 | export default handlers.reduce((result, { accepts, handler }) => {
145 | accepts.forEach((type) => (result[type] = handler))
146 | return result
147 | }, {})
148 |
--------------------------------------------------------------------------------
/packages/util.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2023-01-09 21:22:17
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-13 11:59:23
6 | * @Descripttion:
7 | */
8 | import { atob } from './pollify'
9 | import renders from './renders'
10 |
11 | /**
12 | * 文件对象转文件流
13 | * @param {object} file 文件对象
14 | */
15 | export async function readBuffer(file) {
16 | return new Promise((resolve, reject) => {
17 | const reader = new FileReader()
18 | reader.onload = (loadEvent) => resolve(loadEvent.target.result)
19 | reader.onerror = (e) => reject(e)
20 | reader.readAsArrayBuffer(file)
21 | })
22 | }
23 |
24 | /**
25 | * 文件流转base64
26 | * @param {object} buffer 文件流
27 | */
28 | export async function readDataURL(buffer) {
29 | return new Promise((resolve, reject) => {
30 | const reader = new FileReader()
31 | reader.onload = (loadEvent) => resolve(loadEvent.target.result)
32 | reader.onerror = (e) => reject(e)
33 | reader.readAsDataURL(new Blob([buffer]))
34 | })
35 | }
36 |
37 | /**
38 | * 文件流转utf8文本
39 | * @param {object} buffer 文件流
40 | */
41 | export async function readText(buffer) {
42 | return new Promise((resolve, reject) => {
43 | const reader = new FileReader()
44 | reader.onload = (loadEvent) => resolve(loadEvent.target.result)
45 | reader.onerror = (e) => reject(e)
46 | reader.readAsText(new Blob([buffer]), 'utf-8')
47 | })
48 | }
49 |
50 | /**
51 | * 获取文件后缀
52 | * @param {string} name 文件名
53 | */
54 | export function getExtend(name) {
55 | // 移除 URL 参数和哈希片段
56 | const cleanName = name.split('?')[0].split('#')[0]
57 | const dot = cleanName.lastIndexOf('.')
58 | return dot === -1 ? '' : cleanName.substring(dot + 1)
59 | }
60 |
61 | /**
62 | * 文件下载函数
63 | * @param {string | blob} file 文件,支持传入url/blob/base64格式
64 | * @param {string} name 文件名称,需要带后缀如:abc.jpg(为url可不传入,会自动获取文件名)
65 | */
66 | export function fileDownload(file, name) {
67 | if (!file) {
68 | throw new Error('文件不能为空')
69 | }
70 |
71 | // file是url
72 | if (file.indexOf('http') > -1) {
73 | name = name ? name : getUrlFileName(file)
74 | const link = document.createElement('a')
75 | link.style.display = 'none'
76 | link.href = file
77 | link.target = '_blank'
78 | link.setAttribute('download', name) // 自定义下载文件名(如exemple.txt)
79 | document.body.appendChild(link)
80 | link.click()
81 | return
82 | }
83 |
84 | if (!name) {
85 | throw new Error('文件名不能为空')
86 | }
87 |
88 | // file是base64先转blob
89 | if (typeof file === 'string') {
90 | file = base64toBlob(file)
91 | }
92 |
93 | // file是blob
94 | if (window.navigator.msSaveBlob) {
95 | window.navigator.msSaveOrOpenBlob(file, name)
96 | } else {
97 | const url = window.URL.createObjectURL(new Blob([file]))
98 | const link = document.createElement('a')
99 | link.style.display = 'none'
100 | link.href = url
101 | link.target = '_blank'
102 | link.setAttribute('download', name) // 自定义下载文件名(如exemple.txt)
103 | document.body.appendChild(link)
104 | link.click()
105 | }
106 | }
107 |
108 | /**
109 | * Base64 转 Blob
110 | * @param {string} base64String Blob格式数据
111 | */
112 | export function base64toBlob(base64String) {
113 | var arr = base64String.split(','),
114 | mime = arr[0].match(/:(.*?);/)[1],
115 | bstr = atob(arr[1]),
116 | n = bstr.length,
117 | u8arr = new Uint8Array(n)
118 | while (n--) {
119 | u8arr[n] = bstr.charCodeAt(n)
120 | }
121 | return new Blob([u8arr], {
122 | type: mime
123 | })
124 | }
125 |
126 | /**
127 | * 获取链接文件名+后缀
128 | * @param {string} url 文件地址
129 | */
130 | export function getUrlFileName(url) {
131 | if (!url) return ''
132 | const file = url.split('/')
133 | return file[file.length - 1] || ''
134 | }
135 |
136 | /**
137 | * 根据文件类型渲染对应容器
138 | * @param {buffer} buffer 文件流
139 | * @param {buffer} target 渲染目标元素
140 | * @param {buffer} type 文件类型
141 | * @param {buffer} name 文件名称
142 | **/
143 | export async function render(buffer, target, type, name) {
144 | const handler = renders[type]
145 | if (handler) {
146 | return handler(...arguments)
147 | }
148 | return renders.error(...arguments)
149 | }
150 |
--------------------------------------------------------------------------------
/packages/util/EventBus.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | export default new Vue()
3 |
--------------------------------------------------------------------------------
/packages/vendors/audio/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2023-01-09 21:21:33
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-13 11:11:21
6 | * @Descripttion: 渲染音频
7 | */
8 |
9 | import EventBus from '../../util/EventBus';
10 |
11 | /**
12 | * 渲染音频
13 | */
14 | export default function(buffer, target) {
15 | const audio = document.createElement('audio')
16 | audio.controls = true
17 | audio.style.width = '100%';
18 | audio.style.margin = 'auto';
19 | target.style.display = 'flex';
20 | target.style.alignItems = 'center';
21 | target.style.justifyContent = 'center';
22 | target.style.height = '100%';
23 | const source = document.createElement('source')
24 | source.src = URL.createObjectURL(new Blob([buffer]))
25 | audio.appendChild(source)
26 | audio.addEventListener('loadeddata', function() {
27 | // Emit custom event when Audio is fully loaded
28 | EventBus.$emit('fileLoaded', { fileType: 'audio', success: true });
29 | });
30 | target.appendChild(audio)
31 | }
32 |
--------------------------------------------------------------------------------
/packages/vendors/error/error.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 | 不支持
12 | .{{ fileType }}
13 | 格式的在线预览,请下载后预览或转换为支持的格式
14 |
15 | 支持pptx,docx,xlsx,pdf,视频,音频,纯文本和图片的在线预览
16 |
17 |
20 |
21 |
22 |
23 |
48 |
49 |
88 |
--------------------------------------------------------------------------------
/packages/vendors/error/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2023-01-09 21:21:33
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-12 14:31:37
6 | * @Descripttion: 错误渲染
7 | */
8 | import Vue from 'vue'
9 | import error from './error'
10 | import { readDataURL } from '../../util'
11 |
12 | /**
13 | * 错误渲染
14 | */
15 | export default async function renderError(buffer, target, fileType, fileName) {
16 | const url = await readDataURL(buffer)
17 | return new Vue({
18 | render: (h) => h(error, { props: { fileType, fileName, url } })
19 | }).$mount(target)
20 | }
21 |
--------------------------------------------------------------------------------
/packages/vendors/image/ImageViewer.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
18 |
19 |
20 |
21 |
46 |
47 |
67 |
--------------------------------------------------------------------------------
/packages/vendors/image/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2023-01-09 21:21:33
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-09 21:27:28
6 | * @Descripttion: 图片渲染
7 | */
8 | import Vue from 'vue'
9 | import ImageViewer from './ImageViewer'
10 | import { readDataURL } from '../../util'
11 |
12 | /**
13 | * 图片渲染
14 | */
15 | export default async function renderImage(buffer, target) {
16 | const url = await readDataURL(buffer)
17 | return new Vue({
18 | render: (h) => h(ImageViewer, { props: { image: url } })
19 | }).$mount(target)
20 | }
21 |
--------------------------------------------------------------------------------
/packages/vendors/mp4/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2023-01-09 21:21:33
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-13 11:11:21
6 | * @Descripttion: 渲染mp4
7 | */
8 |
9 | import EventBus from '../../util/EventBus';
10 |
11 | /**
12 | * 渲染mp4
13 | */
14 | export default function(buffer, target) {
15 | const mp4 = document.createElement('video')
16 | mp4.controls = true
17 | mp4.style.width = '100%'
18 | mp4.style.height = '100%'
19 | mp4.style.display = 'table'
20 | mp4.style.margin = 'auto'
21 | mp4.style.objectFit = 'contain'
22 | const source = document.createElement('source')
23 | source.src = URL.createObjectURL(new Blob([buffer]))
24 | mp4.appendChild(source)
25 | mp4.addEventListener('loadeddata', function() {
26 | // Emit custom event when MP4 is fully loaded
27 | EventBus.$emit('fileLoaded', { fileType: 'video', success: true });
28 | });
29 | target.appendChild(mp4)
30 | }
31 |
--------------------------------------------------------------------------------
/packages/vendors/notFind/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2023-01-09 21:21:33
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-12 14:31:25
6 | * @Descripttion: 错误渲染
7 | */
8 | import Vue from 'vue'
9 | import notFind from './notFind'
10 |
11 | /**
12 | * 错误渲染
13 | */
14 | export default async function renderNotFind(url, target, fileType, fileName) {
15 | return new Vue({
16 | render: (h) => h(notFind, { props: { fileType, fileName, url } })
17 | }).$mount(target)
18 | }
19 |
--------------------------------------------------------------------------------
/packages/vendors/notFind/notFind.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 | {{ fileName }}文件不存在,请检查地址是否存在此文件:
12 |
{{ url }}
13 |
14 |
15 |
16 |
17 |
38 |
39 |
61 |
--------------------------------------------------------------------------------
/packages/vendors/officeOnline/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2023-01-09 21:21:33
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-15 23:44:23
6 | * @Descripttion: 渲染微软在线office浏览容器
7 | */
8 | import EventBus from '../../util/EventBus'
9 | /**
10 | * 渲染微软在线office浏览容器
11 | */
12 | export default function(url, target) {
13 | // 跟随文件的协议访问
14 | const link = `${
15 | url.indexOf('https') > -1 ? 'https' : 'http'
16 | }://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}`
17 | // 判断是否为手机
18 | const isMobile = /phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone/i.test(
19 | navigator.userAgent
20 | )
21 | // 手机端会被重定向将整个页面刷新,暂时采用a标签跳转的方案
22 | if (isMobile) {
23 | const span = document.createElement('span')
24 | const fileName = url.substr(url.lastIndexOf('/') + 1)
25 | span.innerHTML = `请点击查看 ${fileName} 文件`
26 | span.style = `
27 | font-size: 4rem;
28 | display: flex;
29 | justify-content: center;
30 | align-items: center;
31 | height: 100%;
32 | line-height: 100%;
33 | color: skyblue;
34 | text-decoration: underline;
35 | cursor: pointer;
36 | `
37 | span.onclick = function() {
38 | window.top.location.href = link
39 | }
40 | target.appendChild(span)
41 | } else {
42 | const iframe = document.createElement('iframe')
43 | iframe.src = link
44 | iframe.style = 'border:0;height: 100%;width:100%'
45 | // 允许执行脚本;允许同域请求;允许进行提交表单;允许模态框
46 | iframe.sandbox = 'allow-scripts allow-same-origin allow-forms allow-modals'
47 |
48 | iframe.onload = function() {
49 | // Emit custom event when Office document is fully loaded
50 | EventBus.$emit('fileLoaded', { fileType: 'office', success: true })
51 | }
52 | target.appendChild(iframe)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/packages/vendors/pdf/PdfView.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
100 |
101 |
112 |
--------------------------------------------------------------------------------
/packages/vendors/pdf/images/loading-icon.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/packages/vendors/pdf/images/loading-icon.gif
--------------------------------------------------------------------------------
/packages/vendors/pdf/images/shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/packages/vendors/pdf/images/shadow.png
--------------------------------------------------------------------------------
/packages/vendors/pdf/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2023-01-09 15:04:10
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-09 16:36:08
6 | * @Descripttion:
7 | */
8 | import Vue from 'vue'
9 | import PdfView from './PdfView.vue'
10 |
11 | export default async function renderPdf(buffer, target) {
12 | return new Vue({
13 | render: (h) => h(PdfView, { props: { data: buffer } }),
14 | }).$mount(target)
15 | }
16 |
--------------------------------------------------------------------------------
/packages/vendors/pdf/pdf.css:
--------------------------------------------------------------------------------
1 | /* Copyright 2014 Mozilla Foundation
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | .textLayer {
17 | position: absolute;
18 | text-align: initial;
19 | left: 0;
20 | top: 0;
21 | right: 0;
22 | bottom: 0;
23 | overflow: hidden;
24 | opacity: 0.2;
25 | line-height: 1;
26 | -webkit-text-size-adjust: none;
27 | -moz-text-size-adjust: none;
28 | text-size-adjust: none;
29 | forced-color-adjust: none;
30 | }
31 |
32 | .textLayer span,
33 | .textLayer br {
34 | color: transparent;
35 | position: absolute;
36 | white-space: pre;
37 | cursor: text;
38 | transform-origin: 0% 0%;
39 | }
40 |
41 | /* Only necessary in Google Chrome, see issue 14205, and most unfortunately
42 | * the problem doesn't show up in "text" reference tests. */
43 | .textLayer span.markedContent {
44 | top: 0;
45 | height: 0;
46 | }
47 |
48 | .textLayer .highlight {
49 | margin: -1px;
50 | padding: 1px;
51 | background-color: rgba(180, 0, 170, 1);
52 | border-radius: 4px;
53 | }
54 |
55 | .textLayer .highlight.appended {
56 | position: initial;
57 | }
58 |
59 | .textLayer .highlight.begin {
60 | border-radius: 4px 0 0 4px;
61 | }
62 |
63 | .textLayer .highlight.end {
64 | border-radius: 0 4px 4px 0;
65 | }
66 |
67 | .textLayer .highlight.middle {
68 | border-radius: 0;
69 | }
70 |
71 | .textLayer .highlight.selected {
72 | background-color: rgba(0, 100, 0, 1);
73 | }
74 |
75 | .textLayer ::-moz-selection {
76 | background: rgba(0, 0, 255, 1);
77 | }
78 |
79 | .textLayer ::selection {
80 | background: rgba(0, 0, 255, 1);
81 | }
82 |
83 | /* Avoids https://github.com/mozilla/pdf.js/issues/13840 in Chrome */
84 | .textLayer br::-moz-selection {
85 | background: transparent;
86 | }
87 | .textLayer br::selection {
88 | background: transparent;
89 | }
90 |
91 | .textLayer .endOfContent {
92 | display: block;
93 | position: absolute;
94 | left: 0;
95 | top: 100%;
96 | right: 0;
97 | bottom: 0;
98 | z-index: -1;
99 | cursor: default;
100 | -webkit-user-select: none;
101 | -moz-user-select: none;
102 | user-select: none;
103 | }
104 |
105 | .textLayer .endOfContent.active {
106 | top: 0;
107 | }
108 |
109 | :root {
110 | --annotation-unfocused-field-background: url("data:image/svg+xml;charset=UTF-8,");
111 | }
112 |
113 | @media (forced-colors: active) {
114 | .annotationLayer .textWidgetAnnotation input:required,
115 | .annotationLayer .textWidgetAnnotation textarea:required,
116 | .annotationLayer .choiceWidgetAnnotation select:required,
117 | .annotationLayer .buttonWidgetAnnotation.checkBox input:required,
118 | .annotationLayer .buttonWidgetAnnotation.radioButton input:required {
119 | outline: 1.5px solid selectedItem;
120 | }
121 | }
122 |
123 | .annotationLayer {
124 | position: absolute;
125 | top: 0;
126 | left: 0;
127 | pointer-events: none;
128 | transform-origin: 0 0;
129 | }
130 |
131 | .annotationLayer section {
132 | position: absolute;
133 | text-align: initial;
134 | pointer-events: auto;
135 | box-sizing: border-box;
136 | transform-origin: 0 0;
137 | }
138 |
139 | .annotationLayer .linkAnnotation > a,
140 | .annotationLayer .buttonWidgetAnnotation.pushButton > a {
141 | position: absolute;
142 | font-size: 1em;
143 | top: 0;
144 | left: 0;
145 | width: 100%;
146 | height: 100%;
147 | }
148 |
149 | .annotationLayer .buttonWidgetAnnotation.pushButton > canvas {
150 | width: 100%;
151 | height: 100%;
152 | }
153 |
154 | .annotationLayer .linkAnnotation > a:hover,
155 | .annotationLayer .buttonWidgetAnnotation.pushButton > a:hover {
156 | opacity: 0.2;
157 | background: rgba(255, 255, 0, 1);
158 | box-shadow: 0 2px 10px rgba(255, 255, 0, 1);
159 | }
160 |
161 | .annotationLayer .textAnnotation img {
162 | position: absolute;
163 | cursor: pointer;
164 | width: 100%;
165 | height: 100%;
166 | }
167 |
168 | .annotationLayer .textWidgetAnnotation input,
169 | .annotationLayer .textWidgetAnnotation textarea,
170 | .annotationLayer .choiceWidgetAnnotation select,
171 | .annotationLayer .buttonWidgetAnnotation.checkBox input,
172 | .annotationLayer .buttonWidgetAnnotation.radioButton input {
173 | background-image: var(--annotation-unfocused-field-background);
174 | border: 1px solid transparent;
175 | box-sizing: border-box;
176 | font: calc(9px * var(--scale-factor)) sans-serif;
177 | height: 100%;
178 | margin: 0;
179 | vertical-align: top;
180 | width: 100%;
181 | }
182 |
183 | .annotationLayer .textWidgetAnnotation input:required,
184 | .annotationLayer .textWidgetAnnotation textarea:required,
185 | .annotationLayer .choiceWidgetAnnotation select:required,
186 | .annotationLayer .buttonWidgetAnnotation.checkBox input:required,
187 | .annotationLayer .buttonWidgetAnnotation.radioButton input:required {
188 | outline: 1.5px solid red;
189 | }
190 |
191 | .annotationLayer .choiceWidgetAnnotation select option {
192 | padding: 0;
193 | }
194 |
195 | .annotationLayer .buttonWidgetAnnotation.radioButton input {
196 | border-radius: 50%;
197 | }
198 |
199 | .annotationLayer .textWidgetAnnotation textarea {
200 | resize: none;
201 | }
202 |
203 | .annotationLayer .textWidgetAnnotation input[disabled],
204 | .annotationLayer .textWidgetAnnotation textarea[disabled],
205 | .annotationLayer .choiceWidgetAnnotation select[disabled],
206 | .annotationLayer .buttonWidgetAnnotation.checkBox input[disabled],
207 | .annotationLayer .buttonWidgetAnnotation.radioButton input[disabled] {
208 | background: none;
209 | border: 1px solid transparent;
210 | cursor: not-allowed;
211 | }
212 |
213 | .annotationLayer .textWidgetAnnotation input:hover,
214 | .annotationLayer .textWidgetAnnotation textarea:hover,
215 | .annotationLayer .choiceWidgetAnnotation select:hover,
216 | .annotationLayer .buttonWidgetAnnotation.checkBox input:hover,
217 | .annotationLayer .buttonWidgetAnnotation.radioButton input:hover {
218 | border: 1px solid rgba(0, 0, 0, 1);
219 | }
220 |
221 | .annotationLayer .textWidgetAnnotation input:focus,
222 | .annotationLayer .textWidgetAnnotation textarea:focus,
223 | .annotationLayer .choiceWidgetAnnotation select:focus {
224 | background: none;
225 | border: 1px solid transparent;
226 | }
227 |
228 | .annotationLayer .textWidgetAnnotation input :focus,
229 | .annotationLayer .textWidgetAnnotation textarea :focus,
230 | .annotationLayer .choiceWidgetAnnotation select :focus,
231 | .annotationLayer .buttonWidgetAnnotation.checkBox :focus,
232 | .annotationLayer .buttonWidgetAnnotation.radioButton :focus {
233 | background-image: none;
234 | background-color: transparent;
235 | outline: auto;
236 | }
237 |
238 | .annotationLayer .buttonWidgetAnnotation.checkBox input:checked:before,
239 | .annotationLayer .buttonWidgetAnnotation.checkBox input:checked:after,
240 | .annotationLayer .buttonWidgetAnnotation.radioButton input:checked:before {
241 | background-color: CanvasText;
242 | content: '';
243 | display: block;
244 | position: absolute;
245 | }
246 |
247 | .annotationLayer .buttonWidgetAnnotation.checkBox input:checked:before,
248 | .annotationLayer .buttonWidgetAnnotation.checkBox input:checked:after {
249 | height: 80%;
250 | left: 45%;
251 | width: 1px;
252 | }
253 |
254 | .annotationLayer .buttonWidgetAnnotation.checkBox input:checked:before {
255 | transform: rotate(45deg);
256 | }
257 |
258 | .annotationLayer .buttonWidgetAnnotation.checkBox input:checked:after {
259 | transform: rotate(-45deg);
260 | }
261 |
262 | .annotationLayer .buttonWidgetAnnotation.radioButton input:checked:before {
263 | border-radius: 50%;
264 | height: 50%;
265 | left: 30%;
266 | top: 20%;
267 | width: 50%;
268 | }
269 |
270 | .annotationLayer .textWidgetAnnotation input.comb {
271 | font-family: monospace;
272 | padding-left: 2px;
273 | padding-right: 0;
274 | }
275 |
276 | .annotationLayer .textWidgetAnnotation input.comb:focus {
277 | /*
278 | * Letter spacing is placed on the right side of each character. Hence, the
279 | * letter spacing of the last character may be placed outside the visible
280 | * area, causing horizontal scrolling. We avoid this by extending the width
281 | * when the element has focus and revert this when it loses focus.
282 | */
283 | width: 103%;
284 | }
285 |
286 | .annotationLayer .buttonWidgetAnnotation.checkBox input,
287 | .annotationLayer .buttonWidgetAnnotation.radioButton input {
288 | -webkit-appearance: none;
289 | -moz-appearance: none;
290 | appearance: none;
291 | }
292 |
293 | .annotationLayer .popupTriggerArea {
294 | height: 100%;
295 | width: 100%;
296 | }
297 |
298 | .annotationLayer .popupWrapper {
299 | position: absolute;
300 | font-size: calc(9px * var(--scale-factor));
301 | width: 100%;
302 | min-width: calc(180px * var(--scale-factor));
303 | pointer-events: none;
304 | }
305 |
306 | .annotationLayer .popup {
307 | position: absolute;
308 | z-index: 200;
309 | max-width: calc(180px * var(--scale-factor));
310 | background-color: rgba(255, 255, 153, 1);
311 | box-shadow: 0 calc(2px * var(--scale-factor)) calc(5px * var(--scale-factor))
312 | rgba(136, 136, 136, 1);
313 | border-radius: calc(2px * var(--scale-factor));
314 | padding: calc(6px * var(--scale-factor));
315 | margin-left: calc(5px * var(--scale-factor));
316 | cursor: pointer;
317 | font: message-box;
318 | white-space: normal;
319 | word-wrap: break-word;
320 | pointer-events: auto;
321 | }
322 |
323 | .annotationLayer .popup > * {
324 | font-size: calc(9px * var(--scale-factor));
325 | }
326 |
327 | .annotationLayer .popup h1 {
328 | display: inline-block;
329 | }
330 |
331 | .annotationLayer .popupDate {
332 | display: inline-block;
333 | margin-left: calc(5px * var(--scale-factor));
334 | }
335 |
336 | .annotationLayer .popupContent {
337 | border-top: 1px solid rgba(51, 51, 51, 1);
338 | margin-top: calc(2px * var(--scale-factor));
339 | padding-top: calc(2px * var(--scale-factor));
340 | }
341 |
342 | .annotationLayer .richText > * {
343 | white-space: pre-wrap;
344 | font-size: calc(9px * var(--scale-factor));
345 | }
346 |
347 | .annotationLayer .highlightAnnotation,
348 | .annotationLayer .underlineAnnotation,
349 | .annotationLayer .squigglyAnnotation,
350 | .annotationLayer .strikeoutAnnotation,
351 | .annotationLayer .freeTextAnnotation,
352 | .annotationLayer .lineAnnotation svg line,
353 | .annotationLayer .squareAnnotation svg rect,
354 | .annotationLayer .circleAnnotation svg ellipse,
355 | .annotationLayer .polylineAnnotation svg polyline,
356 | .annotationLayer .polygonAnnotation svg polygon,
357 | .annotationLayer .caretAnnotation,
358 | .annotationLayer .inkAnnotation svg polyline,
359 | .annotationLayer .stampAnnotation,
360 | .annotationLayer .fileAttachmentAnnotation {
361 | cursor: pointer;
362 | }
363 |
364 | .annotationLayer section svg {
365 | position: absolute;
366 | width: 100%;
367 | height: 100%;
368 | }
369 |
370 | :root {
371 | --xfa-unfocused-field-background: url("data:image/svg+xml;charset=UTF-8,");
372 | }
373 |
374 | @media (forced-colors: active) {
375 | .xfaLayer *:required {
376 | outline: 1.5px solid selectedItem;
377 | }
378 | }
379 |
380 | .xfaLayer .highlight {
381 | margin: -1px;
382 | padding: 1px;
383 | background-color: rgba(239, 203, 237, 1);
384 | border-radius: 4px;
385 | }
386 |
387 | .xfaLayer .highlight.appended {
388 | position: initial;
389 | }
390 |
391 | .xfaLayer .highlight.begin {
392 | border-radius: 4px 0 0 4px;
393 | }
394 |
395 | .xfaLayer .highlight.end {
396 | border-radius: 0 4px 4px 0;
397 | }
398 |
399 | .xfaLayer .highlight.middle {
400 | border-radius: 0;
401 | }
402 |
403 | .xfaLayer .highlight.selected {
404 | background-color: rgba(203, 223, 203, 1);
405 | }
406 |
407 | .xfaLayer ::-moz-selection {
408 | background: rgba(0, 0, 255, 1);
409 | }
410 |
411 | .xfaLayer ::selection {
412 | background: rgba(0, 0, 255, 1);
413 | }
414 |
415 | .xfaPage {
416 | overflow: hidden;
417 | position: relative;
418 | }
419 |
420 | .xfaContentarea {
421 | position: absolute;
422 | }
423 |
424 | .xfaPrintOnly {
425 | display: none;
426 | }
427 |
428 | .xfaLayer {
429 | position: absolute;
430 | text-align: initial;
431 | top: 0;
432 | left: 0;
433 | transform-origin: 0 0;
434 | line-height: 1.2;
435 | }
436 |
437 | .xfaLayer * {
438 | color: inherit;
439 | font: inherit;
440 | font-style: inherit;
441 | font-weight: inherit;
442 | font-kerning: inherit;
443 | letter-spacing: -0.01px;
444 | text-align: inherit;
445 | text-decoration: inherit;
446 | box-sizing: border-box;
447 | background-color: transparent;
448 | padding: 0;
449 | margin: 0;
450 | pointer-events: auto;
451 | line-height: inherit;
452 | }
453 |
454 | .xfaLayer *:required {
455 | outline: 1.5px solid red;
456 | }
457 |
458 | .xfaLayer div {
459 | pointer-events: none;
460 | }
461 |
462 | .xfaLayer svg {
463 | pointer-events: none;
464 | }
465 |
466 | .xfaLayer svg * {
467 | pointer-events: none;
468 | }
469 |
470 | .xfaLayer a {
471 | color: blue;
472 | }
473 |
474 | .xfaRich li {
475 | margin-left: 3em;
476 | }
477 |
478 | .xfaFont {
479 | color: black;
480 | font-weight: normal;
481 | font-kerning: none;
482 | font-size: 10px;
483 | font-style: normal;
484 | letter-spacing: 0;
485 | text-decoration: none;
486 | vertical-align: 0;
487 | }
488 |
489 | .xfaCaption {
490 | overflow: hidden;
491 | flex: 0 0 auto;
492 | }
493 |
494 | .xfaCaptionForCheckButton {
495 | overflow: hidden;
496 | flex: 1 1 auto;
497 | }
498 |
499 | .xfaLabel {
500 | height: 100%;
501 | width: 100%;
502 | }
503 |
504 | .xfaLeft {
505 | display: flex;
506 | flex-direction: row;
507 | align-items: center;
508 | }
509 |
510 | .xfaRight {
511 | display: flex;
512 | flex-direction: row-reverse;
513 | align-items: center;
514 | }
515 |
516 | .xfaLeft > .xfaCaption,
517 | .xfaLeft > .xfaCaptionForCheckButton,
518 | .xfaRight > .xfaCaption,
519 | .xfaRight > .xfaCaptionForCheckButton {
520 | max-height: 100%;
521 | }
522 |
523 | .xfaTop {
524 | display: flex;
525 | flex-direction: column;
526 | align-items: flex-start;
527 | }
528 |
529 | .xfaBottom {
530 | display: flex;
531 | flex-direction: column-reverse;
532 | align-items: flex-start;
533 | }
534 |
535 | .xfaTop > .xfaCaption,
536 | .xfaTop > .xfaCaptionForCheckButton,
537 | .xfaBottom > .xfaCaption,
538 | .xfaBottom > .xfaCaptionForCheckButton {
539 | width: 100%;
540 | }
541 |
542 | .xfaBorder {
543 | background-color: transparent;
544 | position: absolute;
545 | pointer-events: none;
546 | }
547 |
548 | .xfaWrapped {
549 | width: 100%;
550 | height: 100%;
551 | }
552 |
553 | .xfaTextfield:focus,
554 | .xfaSelect:focus {
555 | background-image: none;
556 | background-color: transparent;
557 | outline: auto;
558 | outline-offset: -1px;
559 | }
560 |
561 | .xfaCheckbox:focus,
562 | .xfaRadio:focus {
563 | outline: auto;
564 | }
565 |
566 | .xfaTextfield,
567 | .xfaSelect {
568 | height: 100%;
569 | width: 100%;
570 | flex: 1 1 auto;
571 | border: none;
572 | resize: none;
573 | background-image: var(--xfa-unfocused-field-background);
574 | }
575 |
576 | .xfaTop > .xfaTextfield,
577 | .xfaTop > .xfaSelect,
578 | .xfaBottom > .xfaTextfield,
579 | .xfaBottom > .xfaSelect {
580 | flex: 0 1 auto;
581 | }
582 |
583 | .xfaButton {
584 | cursor: pointer;
585 | width: 100%;
586 | height: 100%;
587 | border: none;
588 | text-align: center;
589 | }
590 |
591 | .xfaLink {
592 | width: 100%;
593 | height: 100%;
594 | position: absolute;
595 | top: 0;
596 | left: 0;
597 | }
598 |
599 | .xfaCheckbox,
600 | .xfaRadio {
601 | width: 100%;
602 | height: 100%;
603 | flex: 0 0 auto;
604 | border: none;
605 | }
606 |
607 | .xfaRich {
608 | white-space: pre-wrap;
609 | width: 100%;
610 | height: 100%;
611 | }
612 |
613 | .xfaImage {
614 | -o-object-position: left top;
615 | object-position: left top;
616 | -o-object-fit: contain;
617 | object-fit: contain;
618 | width: 100%;
619 | height: 100%;
620 | }
621 |
622 | .xfaLrTb,
623 | .xfaRlTb,
624 | .xfaTb {
625 | display: flex;
626 | flex-direction: column;
627 | align-items: stretch;
628 | }
629 |
630 | .xfaLr {
631 | display: flex;
632 | flex-direction: row;
633 | align-items: stretch;
634 | }
635 |
636 | .xfaRl {
637 | display: flex;
638 | flex-direction: row-reverse;
639 | align-items: stretch;
640 | }
641 |
642 | .xfaTb > div {
643 | justify-content: left;
644 | }
645 |
646 | .xfaPosition {
647 | position: relative;
648 | }
649 |
650 | .xfaArea {
651 | position: relative;
652 | }
653 |
654 | .xfaValignMiddle {
655 | display: flex;
656 | align-items: center;
657 | }
658 |
659 | .xfaTable {
660 | display: flex;
661 | flex-direction: column;
662 | align-items: stretch;
663 | }
664 |
665 | .xfaTable .xfaRow {
666 | display: flex;
667 | flex-direction: row;
668 | align-items: stretch;
669 | }
670 |
671 | .xfaTable .xfaRlRow {
672 | display: flex;
673 | flex-direction: row-reverse;
674 | align-items: stretch;
675 | flex: 1;
676 | }
677 |
678 | .xfaTable .xfaRlRow > div {
679 | flex: 1;
680 | }
681 |
682 | .xfaNonInteractive input,
683 | .xfaNonInteractive textarea,
684 | .xfaDisabled input,
685 | .xfaDisabled textarea,
686 | .xfaReadOnly input,
687 | .xfaReadOnly textarea {
688 | background: initial;
689 | }
690 |
691 | @media print {
692 | .xfaTextfield,
693 | .xfaSelect {
694 | background: transparent;
695 | }
696 |
697 | .xfaSelect {
698 | -webkit-appearance: none;
699 | -moz-appearance: none;
700 | appearance: none;
701 | text-indent: 1px;
702 | text-overflow: '';
703 | }
704 | }
705 |
706 | :root {
707 | --focus-outline: solid 2px red;
708 | --hover-outline: dashed 2px blue;
709 | --freetext-line-height: 1.35;
710 | --freetext-padding: 2px;
711 | }
712 |
713 | @media (forced-colors: active) {
714 | :root {
715 | --focus-outline: solid 3px ButtonText;
716 | --hover-outline: dashed 3px ButtonText;
717 | }
718 | }
719 |
720 | [data-editor-rotation='90'] {
721 | transform: rotate(90deg);
722 | }
723 | [data-editor-rotation='180'] {
724 | transform: rotate(180deg);
725 | }
726 | [data-editor-rotation='270'] {
727 | transform: rotate(270deg);
728 | }
729 |
730 | .annotationEditorLayer {
731 | background: transparent;
732 | position: absolute;
733 | top: 0;
734 | left: 0;
735 | font-size: calc(100px * var(--scale-factor));
736 | transform-origin: 0 0;
737 | }
738 |
739 | .annotationEditorLayer .selectedEditor {
740 | outline: var(--focus-outline);
741 | resize: none;
742 | }
743 |
744 | .annotationEditorLayer .freeTextEditor {
745 | position: absolute;
746 | background: transparent;
747 | border-radius: 3px;
748 | padding: calc(var(--freetext-padding) * var(--scale-factor));
749 | resize: none;
750 | width: auto;
751 | height: auto;
752 | z-index: 1;
753 | transform-origin: 0 0;
754 | touch-action: none;
755 | }
756 |
757 | .annotationEditorLayer .freeTextEditor .internal {
758 | background: transparent;
759 | border: none;
760 | top: 0;
761 | left: 0;
762 | overflow: visible;
763 | white-space: nowrap;
764 | resize: none;
765 | font: 10px sans-serif;
766 | line-height: var(--freetext-line-height);
767 | }
768 |
769 | .annotationEditorLayer .freeTextEditor .overlay {
770 | position: absolute;
771 | display: none;
772 | background: transparent;
773 | top: 0;
774 | left: 0;
775 | width: 100%;
776 | height: 100%;
777 | }
778 |
779 | .annotationEditorLayer .freeTextEditor .overlay.enabled {
780 | display: block;
781 | }
782 |
783 | .annotationEditorLayer .freeTextEditor .internal:empty::before {
784 | content: attr(default-content);
785 | color: gray;
786 | }
787 |
788 | .annotationEditorLayer .freeTextEditor .internal:focus {
789 | outline: none;
790 | }
791 |
792 | .annotationEditorLayer .inkEditor.disabled {
793 | resize: none;
794 | }
795 |
796 | .annotationEditorLayer .inkEditor.disabled.selectedEditor {
797 | resize: horizontal;
798 | }
799 |
800 | .annotationEditorLayer .freeTextEditor:hover:not(.selectedEditor),
801 | .annotationEditorLayer .inkEditor:hover:not(.selectedEditor) {
802 | outline: var(--hover-outline);
803 | }
804 |
805 | .annotationEditorLayer .inkEditor {
806 | position: absolute;
807 | background: transparent;
808 | border-radius: 3px;
809 | overflow: auto;
810 | width: 100%;
811 | height: 100%;
812 | z-index: 1;
813 | transform-origin: 0 0;
814 | cursor: auto;
815 | }
816 |
817 | .annotationEditorLayer .inkEditor.editing {
818 | resize: none;
819 | cursor: var(--editorInk-editing-cursor), pointer;
820 | }
821 |
822 | .annotationEditorLayer .inkEditor .inkEditorCanvas {
823 | position: absolute;
824 | top: 0;
825 | left: 0;
826 | width: 100%;
827 | height: 100%;
828 | touch-action: none;
829 | }
830 |
831 | :root {
832 | --viewer-container-height: 0;
833 | --pdfViewer-padding-bottom: 0;
834 | --page-margin: 1px auto -8px;
835 | --page-border: 9px solid transparent;
836 | --page-border-image: url(images/shadow.png) 9 9 repeat;
837 | --spreadHorizontalWrapped-margin-LR: -3.5px;
838 | --scale-factor: 1;
839 | }
840 |
841 | @media screen and (forced-colors: active) {
842 | :root {
843 | --pdfViewer-padding-bottom: 9px;
844 | --page-margin: 8px auto -1px;
845 | --page-border: 1px solid CanvasText;
846 | --page-border-image: none;
847 | --spreadHorizontalWrapped-margin-LR: 3.5px;
848 | }
849 | }
850 |
851 | [data-main-rotation='90'] {
852 | transform: rotate(90deg) translateY(-100%);
853 | }
854 | [data-main-rotation='180'] {
855 | transform: rotate(180deg) translate(-100%, -100%);
856 | }
857 | [data-main-rotation='270'] {
858 | transform: rotate(270deg) translateX(-100%);
859 | }
860 |
861 | .pdfViewer {
862 | padding-bottom: var(--pdfViewer-padding-bottom);
863 | }
864 |
865 | .pdfViewer .canvasWrapper {
866 | overflow: hidden;
867 | }
868 |
869 | .pdfViewer .page {
870 | direction: ltr;
871 | margin: var(--page-margin);
872 | position: relative;
873 | overflow: visible;
874 | border: var(--page-border);
875 | -o-border-image: var(--page-border-image);
876 | border-image: var(--page-border-image);
877 | background-clip: content-box;
878 | background-color: rgba(255, 255, 255, 1);
879 | }
880 |
881 | .pdfViewer .dummyPage {
882 | position: relative;
883 | width: 0;
884 | height: var(--viewer-container-height);
885 | }
886 |
887 | .pdfViewer.removePageBorders .page {
888 | margin: 0 auto 10px;
889 | border: none;
890 | }
891 |
892 | .pdfViewer.singlePageView {
893 | display: inline-block;
894 | }
895 |
896 | .pdfViewer.singlePageView .page {
897 | margin: 0;
898 | border: none;
899 | }
900 |
901 | .pdfViewer.scrollHorizontal,
902 | .pdfViewer.scrollWrapped,
903 | .spread {
904 | margin-left: 3.5px;
905 | margin-right: 3.5px;
906 | text-align: center;
907 | }
908 |
909 | .pdfViewer.scrollHorizontal,
910 | .spread {
911 | white-space: nowrap;
912 | }
913 |
914 | .pdfViewer.removePageBorders,
915 | .pdfViewer.scrollHorizontal .spread,
916 | .pdfViewer.scrollWrapped .spread {
917 | margin-left: 0;
918 | margin-right: 0;
919 | }
920 |
921 | .spread .page,
922 | .spread .dummyPage,
923 | .pdfViewer.scrollHorizontal .page,
924 | .pdfViewer.scrollWrapped .page,
925 | .pdfViewer.scrollHorizontal .spread,
926 | .pdfViewer.scrollWrapped .spread {
927 | display: inline-block;
928 | vertical-align: middle;
929 | }
930 |
931 | .spread .page,
932 | .pdfViewer.scrollHorizontal .page,
933 | .pdfViewer.scrollWrapped .page {
934 | margin-left: var(--spreadHorizontalWrapped-margin-LR);
935 | margin-right: var(--spreadHorizontalWrapped-margin-LR);
936 | }
937 |
938 | .pdfViewer.removePageBorders .spread .page,
939 | .pdfViewer.removePageBorders.scrollHorizontal .page,
940 | .pdfViewer.removePageBorders.scrollWrapped .page {
941 | margin-left: 5px;
942 | margin-right: 5px;
943 | }
944 |
945 | .pdfViewer .page canvas {
946 | margin: 0;
947 | display: block;
948 | }
949 |
950 | .pdfViewer .page canvas[hidden] {
951 | display: none;
952 | }
953 |
954 | .pdfViewer .page .loadingIcon {
955 | position: absolute;
956 | display: block;
957 | left: 0;
958 | top: 0;
959 | right: 0;
960 | bottom: 0;
961 | background: url('images/loading-icon.gif') center no-repeat;
962 | }
963 | .pdfViewer .page .loadingIcon.notVisible {
964 | background: none;
965 | }
966 |
967 | .pdfViewer.enablePermissions .textLayer span {
968 | -webkit-user-select: none !important;
969 | -moz-user-select: none !important;
970 | user-select: none !important;
971 | cursor: not-allowed;
972 | }
973 |
974 | .pdfPresentationMode .pdfViewer {
975 | padding-bottom: 0;
976 | }
977 |
978 | .pdfPresentationMode .spread {
979 | margin: 0;
980 | }
981 |
982 | .pdfPresentationMode .pdfViewer .page {
983 | margin: 0 auto;
984 | border: 2px solid transparent;
985 | }
986 |
--------------------------------------------------------------------------------
/packages/vendors/pptx/backup/index.js:
--------------------------------------------------------------------------------
1 | /* global $, dimple */
2 | 'use strict'
3 |
4 | import processPptx from './process_pptx'
5 | import pptxStyle from './pptx_css'
6 | import $ from 'jquery'
7 |
8 | /**
9 | * @param {ArrayBuffer} pptx
10 | * @param {Element|String} resultElement
11 | * @param {Element|String} [thumbElement]
12 | */
13 | const renderPptx = (pptx, resultElement, thumbElement) => {
14 | const $result = $(resultElement)
15 | const $wrapper = $('')
16 | $result.html('')
17 | $result.append($wrapper)
18 | $wrapper.append(``)
19 | let isDone = false
20 |
21 | return new Promise((resolve, reject) => {
22 | const processMessage = (msg) => {
23 | if (isDone) return
24 | switch (msg.type) {
25 | case 'slide':
26 | $wrapper.append(msg.data)
27 | break
28 | case 'pptx-thumb':
29 | if (thumbElement)
30 | $(thumbElement).attr('src', `data:image/jpeg;base64,${msg.data}`)
31 | break
32 | case 'slideSize':
33 | break
34 | case 'globalCSS':
35 | $wrapper.append(``)
36 | break
37 | case 'Done':
38 | isDone = true
39 | processCharts(msg.data.charts)
40 | resolve(msg.data.time)
41 | break
42 | case 'WARN':
43 | console.warn('PPTX processing warning: ', msg.data)
44 | break
45 | case 'ERROR':
46 | isDone = true
47 | console.error('PPTX processing error: ', msg.data)
48 | reject(new Error(msg.data))
49 | break
50 | case 'DEBUG':
51 | // console.debugf('Worker: ', msg.data);
52 | break
53 | case 'INFO':
54 | default:
55 | // console.info('Worker: ', msg.data);
56 | }
57 | }
58 | /*
59 | // Actual Web Worker - If you want to use this, switching worker's url to Blob is probably better
60 | const worker = new Worker('./dist/worker.js')
61 | worker.addEventListener('message', event => processMessage(event.data), false)
62 | const stopWorker = setInterval(() => { // Maybe this should be done in the message processing
63 | if (isDone) {
64 | worker.terminate()
65 | // console.log("worker terminated");
66 | clearInterval(stopWorker)
67 | }
68 | }, 500)
69 | */
70 | const worker = {
71 | // shim worker
72 | postMessage: () => {},
73 | terminate: () => {}
74 | }
75 | processPptx((func) => {
76 | worker.postMessage = func
77 | }, processMessage)
78 | worker.postMessage({
79 | type: 'processPPTX',
80 | data: pptx
81 | })
82 | }).then((time) => {
83 | const resize = () => {
84 | const slidesWidth = Math.max(
85 | ...Array.from($wrapper.children('section')).map((s) => s.offsetWidth)
86 | )
87 | const wrapperWidth = $wrapper[0].offsetWidth
88 | $wrapper.css({
89 | transform: `scale(${wrapperWidth / slidesWidth})`,
90 | 'transform-origin': 'top left'
91 | })
92 | }
93 | resize()
94 | window.addEventListener('resize', resize)
95 | setNumericBullets($('.block'))
96 | setNumericBullets($('table td'))
97 | return time
98 | })
99 | }
100 |
101 | export default renderPptx
102 |
103 | function processCharts(queue) {
104 | for (let i = 0; i < queue.length; i++) {
105 | processSingleChart(queue[i].data)
106 | }
107 | }
108 |
109 | function convertChartData(chartData) {
110 | const data = []
111 | const xLabels = []
112 | const groupLabels = []
113 | chartData.forEach((group, i) => {
114 | const groupName = group.key
115 | groupLabels[i] = group.key
116 | group.values.forEach((value, j) => {
117 | const labelName = group.xlabels[j]
118 | xLabels[j] = group.xlabels[j]
119 | data.push({ name: labelName, group: groupName, value: value.y })
120 | })
121 | })
122 | // console.log('TRANSFORMED DATA:', (data))
123 | return { data, xLabels, groupLabels }
124 | }
125 |
126 | function processSingleChart(d) {
127 | const chartID = d.chartID
128 | const chartType = d.chartType
129 | const chartData = d.chartData
130 | // console.log(`WRITING GRAPH OF TYPE ${chartType} TO ID #${chartID}:`, chartData)
131 |
132 | let data = []
133 |
134 | switch (chartType) {
135 | case 'lineChart': {
136 | const { data: data_, xLabels, groupLabels } = convertChartData(chartData)
137 | data = data_
138 | const container = document.getElementById(chartID)
139 | const svg = dimple.newSvg(
140 | `#${chartID}`,
141 | container.style.width,
142 | container.style.height
143 | )
144 |
145 | // eslint-disable-next-line new-cap
146 | const myChart = new dimple.chart(svg, data)
147 | const xAxis = myChart.addCategoryAxis('x', 'name')
148 | xAxis.addOrderRule(xLabels)
149 | xAxis.addGroupOrderRule(groupLabels)
150 | xAxis.title = null
151 | const yAxis = myChart.addMeasureAxis('y', 'value')
152 | yAxis.title = null
153 | myChart.addSeries('group', dimple.plot.line)
154 | myChart.addLegend(60, 10, 500, 20, 'right')
155 | myChart.draw()
156 |
157 | break
158 | }
159 | case 'barChart': {
160 | const { data: data_, xLabels, groupLabels } = convertChartData(chartData)
161 | data = data_
162 | const container = document.getElementById(chartID)
163 | const svg = dimple.newSvg(
164 | '#' + chartID,
165 | container.style.width,
166 | container.style.height
167 | )
168 |
169 | // eslint-disable-next-line new-cap
170 | const myChart = new dimple.chart(svg, data)
171 | const xAxis = myChart.addCategoryAxis('x', ['name', 'group'])
172 | xAxis.addOrderRule(xLabels)
173 | xAxis.addGroupOrderRule(groupLabels)
174 | xAxis.title = null
175 | const yAxis = myChart.addMeasureAxis('y', 'value')
176 | yAxis.title = null
177 | myChart.addSeries('group', dimple.plot.bar)
178 | myChart.addLegend(60, 10, 500, 20, 'right')
179 | myChart.draw()
180 | break
181 | }
182 | case 'pieChart':
183 | case 'pie3DChart': {
184 | // data = chartData[0].values
185 | // chart = nv.models.pieChart()
186 | // nvDraw(chart, data)
187 | const { data: data_, groupLabels } = convertChartData(chartData)
188 | data = data_
189 | const container = document.getElementById(chartID)
190 | const svg = dimple.newSvg(
191 | `#${chartID}`,
192 | container.style.width,
193 | container.style.height
194 | )
195 |
196 | // eslint-disable-next-line new-cap
197 | const myChart = new dimple.chart(svg, data)
198 | const pieAxis = myChart.addMeasureAxis('p', 'value')
199 | pieAxis.addOrderRule(groupLabels)
200 | myChart.addSeries('name', dimple.plot.pie)
201 | myChart.addLegend(50, 20, 400, 300, 'left')
202 | myChart.draw()
203 | break
204 | }
205 | case 'areaChart': {
206 | const { data: data_, xLabels, groupLabels } = convertChartData(chartData)
207 | data = data_
208 | const container = document.getElementById(chartID)
209 | const svg = dimple.newSvg(
210 | '#' + chartID,
211 | container.style.width,
212 | container.style.height
213 | )
214 |
215 | // eslint-disable-next-line new-cap
216 | const myChart = new dimple.chart(svg, data)
217 | const xAxis = myChart.addCategoryAxis('x', 'name')
218 | xAxis.addOrderRule(xLabels)
219 | xAxis.addGroupOrderRule(groupLabels)
220 | xAxis.title = null
221 | const yAxis = myChart.addMeasureAxis('y', 'value')
222 | yAxis.title = null
223 | myChart.addSeries('group', dimple.plot.area)
224 | myChart.addLegend(60, 10, 500, 20, 'right')
225 | myChart.draw()
226 |
227 | break
228 | }
229 | case 'scatterChart': {
230 | for (let i = 0; i < chartData.length; i++) {
231 | const arr = []
232 | for (let j = 0; j < chartData[i].length; j++) {
233 | arr.push({ x: j, y: chartData[i][j] })
234 | }
235 | data.push({ key: 'data' + (i + 1), values: arr })
236 | }
237 |
238 | // data = chartData;
239 | // chart = nv.models.scatterChart()
240 | // .showDistX(true)
241 | // .showDistY(true)
242 | // .color(d3.scale.category10().range())
243 | // chart.xAxis.axisLabel('X').tickFormat(d3.format('.02f'))
244 | // chart.yAxis.axisLabel('Y').tickFormat(d3.format('.02f'))
245 | // nvDraw(chart, data)
246 | break
247 | }
248 | default:
249 | }
250 | }
251 |
252 | function setNumericBullets(elem) {
253 | const paragraphsArray = elem
254 | for (let i = 0; i < paragraphsArray.length; i++) {
255 | const buSpan = $(paragraphsArray[i]).find('.numeric-bullet-style')
256 | if (buSpan.length > 0) {
257 | // console.log("DIV-"+i+":");
258 | let prevBultTyp = ''
259 | let prevBultLvl = ''
260 | let buletIndex = 0
261 | const tmpArry = []
262 | let tmpArryIndx = 0
263 | const buletTypSrry = []
264 | for (let j = 0; j < buSpan.length; j++) {
265 | const bulletType = $(buSpan[j]).data('bulltname')
266 | const bulletLvl = $(buSpan[j]).data('bulltlvl')
267 | // console.log(j+" - "+bult_typ+" lvl: "+bult_lvl );
268 | if (buletIndex === 0) {
269 | prevBultTyp = bulletType
270 | prevBultLvl = bulletLvl
271 | tmpArry[tmpArryIndx] = buletIndex
272 | buletTypSrry[tmpArryIndx] = bulletType
273 | buletIndex++
274 | } else {
275 | if (bulletType === prevBultTyp && bulletLvl === prevBultLvl) {
276 | prevBultTyp = bulletType
277 | prevBultLvl = bulletLvl
278 | buletIndex++
279 | tmpArry[tmpArryIndx] = buletIndex
280 | buletTypSrry[tmpArryIndx] = bulletType
281 | } else if (bulletType !== prevBultTyp && bulletLvl === prevBultLvl) {
282 | prevBultTyp = bulletType
283 | prevBultLvl = bulletLvl
284 | tmpArryIndx++
285 | tmpArry[tmpArryIndx] = buletIndex
286 | buletTypSrry[tmpArryIndx] = bulletType
287 | buletIndex = 1
288 | } else if (
289 | bulletType !== prevBultTyp &&
290 | Number(bulletLvl) > Number(prevBultLvl)
291 | ) {
292 | prevBultTyp = bulletType
293 | prevBultLvl = bulletLvl
294 | tmpArryIndx++
295 | tmpArry[tmpArryIndx] = buletIndex
296 | buletTypSrry[tmpArryIndx] = bulletType
297 | buletIndex = 1
298 | } else if (
299 | bulletType !== prevBultTyp &&
300 | Number(bulletLvl) < Number(prevBultLvl)
301 | ) {
302 | prevBultTyp = bulletType
303 | prevBultLvl = bulletLvl
304 | tmpArryIndx--
305 | buletIndex = tmpArry[tmpArryIndx] + 1
306 | }
307 | }
308 | // console.log(buletTypSrry[tmpArryIndx]+" - "+buletIndex);
309 | const numIdx = getNumTypeNum(buletTypSrry[tmpArryIndx], buletIndex)
310 | $(buSpan[j]).html(numIdx)
311 | }
312 | }
313 | }
314 | }
315 |
316 | function getNumTypeNum(numTyp, num) {
317 | let rtrnNum = ''
318 | switch (numTyp) {
319 | case 'arabicPeriod':
320 | rtrnNum = num + '. '
321 | break
322 | case 'arabicParenR':
323 | rtrnNum = num + ') '
324 | break
325 | case 'alphaLcParenR':
326 | rtrnNum = alphaNumeric(num, 'lowerCase') + ') '
327 | break
328 | case 'alphaLcPeriod':
329 | rtrnNum = alphaNumeric(num, 'lowerCase') + '. '
330 | break
331 |
332 | case 'alphaUcParenR':
333 | rtrnNum = alphaNumeric(num, 'upperCase') + ') '
334 | break
335 | case 'alphaUcPeriod':
336 | rtrnNum = alphaNumeric(num, 'upperCase') + '. '
337 | break
338 |
339 | case 'romanUcPeriod':
340 | rtrnNum = romanize(num) + '. '
341 | break
342 | case 'romanLcParenR':
343 | rtrnNum = romanize(num) + ') '
344 | break
345 | case 'hebrew2Minus':
346 | rtrnNum = hebrew2Minus.format(num) + '-'
347 | break
348 | default:
349 | rtrnNum = num
350 | }
351 | return rtrnNum
352 | }
353 |
354 | function romanize(num) {
355 | if (!+num) return false
356 | const digits = String(+num).split('')
357 | const key = [
358 | '',
359 | 'C',
360 | 'CC',
361 | 'CCC',
362 | 'CD',
363 | 'D',
364 | 'DC',
365 | 'DCC',
366 | 'DCCC',
367 | 'CM',
368 | '',
369 | 'X',
370 | 'XX',
371 | 'XXX',
372 | 'XL',
373 | 'L',
374 | 'LX',
375 | 'LXX',
376 | 'LXXX',
377 | 'XC',
378 | '',
379 | 'I',
380 | 'II',
381 | 'III',
382 | 'IV',
383 | 'V',
384 | 'VI',
385 | 'VII',
386 | 'VIII',
387 | 'IX'
388 | ]
389 | let roman = ''
390 | let i = 3
391 | while (i--) roman = (key[+digits.pop() + i * 10] || '') + roman
392 | return new Array(+digits.join('') + 1).join('M') + roman
393 | }
394 |
395 | const hebrew2Minus = archaicNumbers([
396 | [1000, ''],
397 | [400, 'ת'],
398 | [300, 'ש'],
399 | [200, 'ר'],
400 | [100, 'ק'],
401 | [90, 'צ'],
402 | [80, 'פ'],
403 | [70, 'ע'],
404 | [60, 'ס'],
405 | [50, 'נ'],
406 | [40, 'מ'],
407 | [30, 'ל'],
408 | [20, 'כ'],
409 | [10, 'י'],
410 | [9, 'ט'],
411 | [8, 'ח'],
412 | [7, 'ז'],
413 | [6, 'ו'],
414 | [5, 'ה'],
415 | [4, 'ד'],
416 | [3, 'ג'],
417 | [2, 'ב'],
418 | [1, 'א'],
419 | [/יה/, 'ט״ו'],
420 | [/יו/, 'ט״ז'],
421 | [/([א-ת])([א-ת])$/, '$1״$2'],
422 | [/^([א-ת])$/, '$1׳']
423 | ])
424 |
425 | function archaicNumbers(arr) {
426 | // const arrParse = arr.slice().sort(function (a, b) { return b[1].length - a[1].length })
427 | return {
428 | format: function(n) {
429 | let ret = ''
430 | $.each(arr, function() {
431 | const num = this[0]
432 | if (parseInt(num) > 0) {
433 | for (; n >= num; n -= num) ret += this[1]
434 | } else {
435 | ret = ret.replace(num, this[1])
436 | }
437 | })
438 | return ret
439 | }
440 | }
441 | }
442 |
443 | function alphaNumeric(num, upperLower) {
444 | num = Number(num) - 1
445 | let aNum = ''
446 | if (upperLower === 'upperCase') {
447 | aNum = (
448 | (num / 26 >= 1 ? String.fromCharCode(num / 26 + 64) : '') +
449 | String.fromCharCode((num % 26) + 65)
450 | ).toUpperCase()
451 | } else if (upperLower === 'lowerCase') {
452 | aNum = (
453 | (num / 26 >= 1 ? String.fromCharCode(num / 26 + 64) : '') +
454 | String.fromCharCode((num % 26) + 65)
455 | ).toLowerCase()
456 | }
457 | return aNum
458 | }
459 |
--------------------------------------------------------------------------------
/packages/vendors/pptx/backup/pptx_css.js:
--------------------------------------------------------------------------------
1 | export default `
2 | .slide {
3 | position: relative;
4 | border: 1px solid #333;
5 | border-radius: 10px;
6 | overflow: hidden;
7 | margin-bottom: 50px;
8 | margin-left: auto;
9 | margin-right: auto;
10 | }
11 |
12 | .slide div.block {
13 | position: absolute;
14 | top: 0px;
15 | left: 0px;
16 | width: 100%;
17 | line-height: 1;
18 | }
19 |
20 | .slide div.content {
21 | display: flex;
22 | flex-direction: column;
23 | }
24 | .slide div.diagram-content{
25 | display: flex;
26 | flex-direction: column;
27 | }
28 |
29 | .slide div.content-rtl {
30 | display: flex;
31 | flex-direction: column;
32 | direction: rtl;
33 | }
34 | .slide .pregraph-rtl{
35 | direction: rtl;
36 | }
37 | .slide .pregraph-ltr{
38 | direction: ltr;
39 | }
40 | .slide .pregraph-inherit{
41 | direction: inherit;
42 | }
43 | .slide .slide-prgrph{
44 | width: 100%;
45 | /* overflow-wrap:break-word;
46 | word-wrap: break-word; */
47 |
48 | /* word-break: break-word; */
49 | /* unicode-bidi: bidi-override; */
50 | /* hyphens: auto;
51 | overflow-wrap: break-word; */
52 |
53 | }
54 |
55 | .slide .line-break-br::before{
56 | content: "\A";
57 | white-space: pre;
58 | }
59 | .slide div.v-up {
60 | justify-content: flex-start;
61 | }
62 | .slide div.v-mid {
63 | justify-content: center;
64 | }
65 | .slide div.v-down {
66 | justify-content: flex-end;
67 | }
68 |
69 | .slide div.h-left {
70 | justify-content: flex-start;
71 | align-items: flex-start;
72 | text-align: left;
73 | }
74 | .slide div.h-left-rtl {
75 | justify-content: flex-end;
76 | align-items: flex-end;
77 | text-align: left;
78 | }
79 | .slide div.h-mid {
80 | justify-content: center;
81 | align-items: center;
82 | text-align: center;
83 | }
84 | .slide div.h-right {
85 | justify-content: flex-end;
86 | align-items: flex-end;
87 | text-align: right;
88 | }
89 | .slide div.h-right-rtl {
90 | justify-content: flex-start;
91 | align-items: flex-start;
92 | text-align: right;
93 | }
94 |
95 | .slide div.h-just,
96 | .slide div.h-dist {
97 | text-align: justify;
98 | }
99 |
100 |
101 | .slide div.up-left {
102 | justify-content: flex-start;
103 | align-items: flex-start;
104 | text-align: left;
105 | }
106 | .slide div.up-center {
107 | justify-content: flex-start;
108 | align-items: center;
109 | }
110 | .slide div.up-right {
111 | justify-content: flex-start;
112 | align-items: flex-end;
113 | }
114 | .slide div.center-left {
115 | justify-content: center;
116 | align-items: flex-start;
117 | text-align: left;
118 | }
119 | .slide div.center-center {
120 | justify-content: center;
121 | align-items: center;
122 | }
123 | .slide div.center-right {
124 | justify-content: center;
125 | align-items: flex-end;
126 | }
127 | .slide div.down-left {
128 | justify-content: flex-end;
129 | align-items: flex-start;
130 | text-align: left;
131 | }
132 | .slide div.down-center {
133 | justify-content: flex-end;
134 | align-items: center;
135 | }
136 | .slide div.down-right {
137 | justify-content: flex-end;
138 | align-items: flex-end;
139 | }
140 |
141 |
142 | .slide li.slide {
143 | margin: 10px 0px;
144 | font-size: 18px;
145 | }
146 |
147 | .slide table {
148 | position: absolute;
149 | }
150 |
151 | .slide svg.drawing {
152 | position: absolute;
153 | overflow: visible;
154 | }
155 | `
156 |
--------------------------------------------------------------------------------
/packages/vendors/pptx/backup/t_xml.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | let _order = 1
4 |
5 | export default function t_xml (S) {
6 | const openBracket = '<'
7 | const openBracketCC = '<'.charCodeAt(0)
8 | const closeBracket = '>'
9 | const closeBracketCC = '>'.charCodeAt(0)
10 | const minusCC = '-'.charCodeAt(0)
11 | const slashCC = '/'.charCodeAt(0)
12 | const exclamationCC = '!'.charCodeAt(0)
13 | const singleQuoteCC = '\''.charCodeAt(0)
14 | const doubleQuoteCC = '"'.charCodeAt(0)
15 | const questionMarkCC = '?'.charCodeAt(0)
16 |
17 | /**
18 | * returns text until the first nonAlphebetic letter
19 | */
20 | const nameSpacer = '\r\n\t>/= '
21 |
22 | let pos = 0
23 |
24 | /**
25 | * Parsing a list of entries
26 | */
27 | function parseChildren () {
28 | const children = []
29 | while (S[pos]) {
30 | if (S.charCodeAt(pos) === openBracketCC) {
31 | if (S.charCodeAt(pos + 1) === slashCC) { //
32 | // while (S[pos]!=='>') { pos++; }
33 | pos = S.indexOf(closeBracket, pos)
34 | return children
35 | } else if (S.charCodeAt(pos + 1) === exclamationCC) { // 64 && c < 91) || (c > 96 && c < 123)) {
69 | startNamePos = pos
70 | for (; nameSpacer.indexOf(S[pos]) === -1; pos++) {}
71 | const name = S.slice(startNamePos, pos)
72 | // search beginning of the string
73 | let code = S.charCodeAt(pos)
74 | while (code !== singleQuoteCC && code !== doubleQuoteCC) {
75 | pos++
76 | code = S.charCodeAt(pos)
77 | }
78 |
79 | const startChar = S[pos]
80 | const startStringPos = ++pos
81 | pos = S.indexOf(startChar, startStringPos)
82 | const value = S.slice(startStringPos, pos)
83 | if (!attrFound) {
84 | nodeAttributes = {}
85 | attrFound = true
86 | }
87 | nodeAttributes[name] = value
88 | }
89 | }
90 |
91 | // Optional parsing of children
92 | let nodeChildren
93 | if (S.charCodeAt(pos - 1) !== slashCC) {
94 | pos++
95 | nodeChildren = parseChildren()
96 | }
97 |
98 | children.push({
99 | 'children': nodeChildren,
100 | 'tagName': nodeTagName,
101 | 'attrs': nodeAttributes
102 | })
103 | } else {
104 | const startTextPos = pos
105 | pos = S.indexOf(openBracket, pos) - 1 // Skip characters until '<'
106 | if (pos === -2) {
107 | pos = S.length
108 | }
109 | const text = S.slice(startTextPos, pos + 1)
110 | if (text.trim().length > 0) {
111 | children.push(text)
112 | }
113 | }
114 | pos++
115 | }
116 | return children
117 | }
118 |
119 | _order = 1
120 | return simplefy(parseChildren())
121 | }
122 |
123 | function simplefy (children) {
124 | const node = {}
125 |
126 | if (children === undefined) {
127 | return {}
128 | }
129 |
130 | // Text node (e.g. This is text.)
131 | if (children.length === 1 && (typeof children[0] === 'string' || children[0] instanceof String)) {
132 | // eslint-disable-next-line no-new-wrappers
133 | return new String(children[0])
134 | }
135 |
136 | // map each object
137 | children.forEach(function (child) {
138 | if (!node[child.tagName]) {
139 | node[child.tagName] = []
140 | }
141 |
142 | if (typeof child === 'object') {
143 | const kids = simplefy(child.children)
144 | if (child.attrs) {
145 | kids.attrs = child.attrs
146 | }
147 |
148 | if (kids['attrs'] === undefined) {
149 | kids['attrs'] = {'order': _order}
150 | } else {
151 | kids['attrs']['order'] = _order
152 | }
153 | _order++
154 | node[child.tagName].push(kids)
155 | }
156 | })
157 |
158 | for (let i in node) {
159 | if (node[i].length === 1) {
160 | node[i] = node[i][0]
161 | }
162 | }
163 |
164 | return node
165 | }
166 |
--------------------------------------------------------------------------------
/packages/vendors/pptx/backup/worker.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import processPptx from './process_pptx'
4 |
5 | processPptx(
6 | func => { self.onmessage = e => func(e.data) },
7 | msg => self.postMessage(msg)
8 | )
9 |
--------------------------------------------------------------------------------
/packages/vendors/pptx/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2023-01-09 15:04:10
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-09 16:36:35
6 | * @Descripttion: pptx渲染
7 | */
8 |
9 | import Vue from 'vue'
10 | import Pptx from './pptx.vue'
11 |
12 | /**
13 | * pptx渲染逻辑,使用vue组件,重构自pptxjs,感谢大神让我站在巨人的肩膀上
14 | * @param buffer 二进制数据
15 | * @param target 目标
16 | */
17 | export default async function renderPptx(buffer, target) {
18 | return new Vue({
19 | render: (h) => h(Pptx, { props: { data: buffer } }),
20 | }).$mount(target)
21 | }
22 |
--------------------------------------------------------------------------------
/packages/vendors/pptx/lib/tXml.js:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////tXml///////////////////////////
2 | /*
3 | This is my custom tXml.js file
4 | */
5 | function tXml(t, r) {
6 | 'use strict'
7 | function e() {
8 | for (var r = []; t[l]; )
9 | if (t.charCodeAt(l) == s) {
10 | if (t.charCodeAt(l + 1) === h)
11 | return (l = t.indexOf(u, l)), l + 1 && (l += 1), r
12 | if (t.charCodeAt(l + 1) === v) {
13 | if (t.charCodeAt(l + 2) == m) {
14 | for (
15 | ;
16 | -1 !== l &&
17 | (t.charCodeAt(l) !== d ||
18 | t.charCodeAt(l - 1) != m ||
19 | t.charCodeAt(l - 2) != m ||
20 | -1 == l);
21 |
22 | )
23 | l = t.indexOf(u, l + 1)
24 | ;-1 === l && (l = t.length)
25 | } else for (l += 2; t.charCodeAt(l) !== d && t[l]; ) l++
26 | l++
27 | continue
28 | }
29 | var e = a()
30 | r.push(e)
31 | } else {
32 | var i = n()
33 | i.trim().length > 0 && r.push(i), l++
34 | }
35 | return r
36 | }
37 | function n() {
38 | var r = l
39 | return (
40 | (l = t.indexOf(c, l) - 1), -2 === l && (l = t.length), t.slice(r, l + 1)
41 | )
42 | }
43 | function i() {
44 | for (var r = l; -1 === A.indexOf(t[l]) && t[l]; ) l++
45 | return t.slice(r, l)
46 | }
47 | function a() {
48 | var r = {}
49 | l++, (r.tagName = i())
50 | for (var n = !1; t.charCodeAt(l) !== d && t[l]; ) {
51 | var a = t.charCodeAt(l)
52 | if ((a > 64 && 91 > a) || (a > 96 && 123 > a)) {
53 | for (
54 | var f = i(), c = t.charCodeAt(l);
55 | c &&
56 | c !== p &&
57 | c !== g &&
58 | !((c > 64 && 91 > c) || (c > 96 && 123 > c)) &&
59 | c !== d;
60 |
61 | )
62 | l++, (c = t.charCodeAt(l))
63 | if ((n || ((r.attributes = {}), (n = !0)), c === p || c === g)) {
64 | var s = o()
65 | if (-1 === l) return r
66 | } else (s = null), l--
67 | r.attributes[f] = s
68 | }
69 | l++
70 | }
71 | if (t.charCodeAt(l - 1) !== h)
72 | if ('script' == r.tagName) {
73 | var u = l + 1
74 | ;(l = t.indexOf('', l)),
75 | (r.children = [t.slice(u, l - 1)]),
76 | (l += 8)
77 | } else if ('style' == r.tagName) {
78 | var u = l + 1
79 | ;(l = t.indexOf('', l)),
80 | (r.children = [t.slice(u, l - 1)]),
81 | (l += 7)
82 | } else -1 == C.indexOf(r.tagName) && (l++, (r.children = e(f)))
83 | else l++
84 | return r
85 | }
86 | function o() {
87 | var r = t[l],
88 | e = ++l
89 | return (l = t.indexOf(r, e)), t.slice(e, l)
90 | }
91 | function f() {
92 | var e = new RegExp(
93 | '\\s' + r.attrName + '\\s*=[\'"]' + r.attrValue + '[\'"]'
94 | ).exec(t)
95 | return e ? e.index : -1
96 | }
97 | r = r || {}
98 | var l = r.pos || 0,
99 | c = '<',
100 | s = '<'.charCodeAt(0),
101 | u = '>',
102 | d = '>'.charCodeAt(0),
103 | m = '-'.charCodeAt(0),
104 | h = '/'.charCodeAt(0),
105 | v = '!'.charCodeAt(0),
106 | p = "'".charCodeAt(0),
107 | g = '"'.charCodeAt(0),
108 | A = '\n >/= ',
109 | C = ['img', 'br', 'input', 'meta', 'link'],
110 | y = null
111 | if (void 0 !== r.attrValue) {
112 | r.attrName = r.attrName || 'id'
113 | for (var y = []; -1 !== (l = f()); )
114 | (l = t.lastIndexOf('<', l)),
115 | -1 !== l && y.push(a()),
116 | (t = t.substr(l)),
117 | (l = 0)
118 | } else y = r.parseNode ? a() : e()
119 | return (
120 | r.filter && (y = tXml.filter(y, r.filter)),
121 | r.simplify && (y = tXml.simplify(y)),
122 | (y.pos = l),
123 | y
124 | )
125 | }
126 | var _order = 1
127 | ;(tXml.simplify = function(t) {
128 | var r = {}
129 | if (void 0 === t) return {}
130 | if (1 === t.length && 'string' == typeof t[0]) return t[0]
131 | t.forEach(function(t) {
132 | if ('object' == typeof t) {
133 | r[t.tagName] || (r[t.tagName] = [])
134 | var e = tXml.simplify(t.children || [])
135 | r[t.tagName].push(e),
136 | t.attributes && (e.attrs = t.attributes),
137 | void 0 === e.attrs
138 | ? (e.attrs = { order: _order })
139 | : (e.attrs.order = _order),
140 | _order++
141 | }
142 | })
143 | for (var e in r) 1 == r[e].length && (r[e] = r[e][0])
144 | return r
145 | }),
146 | (tXml.filter = function(t, r) {
147 | var e = []
148 | return (
149 | t.forEach(function(t) {
150 | if (('object' == typeof t && r(t) && e.push(t), t.children)) {
151 | var n = tXml.filter(t.children, r)
152 | e = e.concat(n)
153 | }
154 | }),
155 | e
156 | )
157 | }),
158 | (tXml.stringify = function(t) {
159 | function r(t) {
160 | if (t)
161 | for (var r = 0; r < t.length; r++)
162 | 'string' == typeof t[r] ? (n += t[r].trim()) : e(t[r])
163 | }
164 | function e(t) {
165 | n += '<' + t.tagName
166 | for (var e in t.attributes)
167 | n +=
168 | null === t.attributes[e]
169 | ? ' ' + e
170 | : -1 === t.attributes[e].indexOf('"')
171 | ? ' ' + e + '="' + t.attributes[e].trim() + '"'
172 | : ' ' + e + "='" + t.attributes[e].trim() + "'"
173 | ;(n += '>'), r(t.children), (n += '' + t.tagName + '>')
174 | }
175 | var n = ''
176 | return r(t), n
177 | }),
178 | (tXml.toContentString = function(t) {
179 | if (Array.isArray(t)) {
180 | var r = ''
181 | return (
182 | t.forEach(function(t) {
183 | ;(r += ' ' + tXml.toContentString(t)), (r = r.trim())
184 | }),
185 | r
186 | )
187 | }
188 | return 'object' == typeof t ? tXml.toContentString(t.children) : ' ' + t
189 | }),
190 | (tXml.getElementById = function(t, r, e) {
191 | var n = tXml(t, { attrValue: r, simplify: e })
192 | return e ? n : n[0]
193 | }),
194 | (tXml.getElementsByClassName = function(t, r, e) {
195 | return tXml(t, {
196 | attrName: 'class',
197 | attrValue: '[a-zA-Z0-9-s ]*' + r + '[a-zA-Z0-9-s ]*',
198 | simplify: e
199 | })
200 | }),
201 | (tXml.parseStream = function(t, r) {
202 | if (
203 | ('function' == typeof r && ((cb = r), (r = 0)),
204 | 'string' == typeof r && (r = r.length + 2),
205 | 'string' == typeof t)
206 | ) {
207 | var e = require('fs')
208 | ;(t = e.createReadStream(t, { start: r })), (r = 0)
209 | }
210 | var n = r,
211 | i = '',
212 | a = 0
213 | return (
214 | t.on('data', function(r) {
215 | a++, (i += r)
216 | for (var e = 0; ; ) {
217 | n = i.indexOf('<', n) + 1
218 | var o = tXml(i, { pos: n, parseNode: !0 })
219 | if (((n = o.pos), n > i.length - 1 || e > n))
220 | return void (e && ((i = i.slice(e)), (n = 0), (e = 0)))
221 | t.emit('xml', o), (e = n)
222 | }
223 | ;(i = i.slice(n)), (n = 0)
224 | }),
225 | t.on('end', function() {
226 | console.log('end')
227 | }),
228 | t
229 | )
230 | }),
231 | 'object' == typeof module && (module.exports = tXml)
232 |
--------------------------------------------------------------------------------
/packages/vendors/pptx/options.js:
--------------------------------------------------------------------------------
1 | export const DefaultOptions = () => ({
2 | // These are the defaults.
3 | // pptxFileUrl: "",
4 | // fileInputId: "",
5 | slidesScale: '', //Change Slides scale by percent
6 | slideMode: false, /** true,false*/
7 | slideType: 'divs2slidesjs', /*'divs2slidesjs' (default) , 'revealjs'(https://revealjs.com) -TODO*/
8 | revealjsPath: '', /*path to js file of revealjs - TODO*/
9 | keyBoardShortCut: false, /** true,false ,condition: slideMode: true XXXXX - need to remove - this is doublcated*/
10 | mediaProcess: true, /** true,false: if true then process video and audio files */
11 | jsZipV2: false,
12 | themeProcess: true, /*true (default) , false, "colorsAndImageOnly"*/
13 | incSlide:{
14 | width: 0,
15 | height: 0
16 | },
17 | slideModeConfig: {
18 | first: 1,
19 | nav: true, /** true,false : show or not nav buttons*/
20 | navTxtColor: 'black', /** color */
21 | keyBoardShortCut: true, /** true,false ,condition: */
22 | showSlideNum: true, /** true,false */
23 | showTotalSlideNum: true, /** true,false */
24 | autoSlide: true, /** false or seconds , F8 to active ,keyBoardShortCut: true */
25 | randomAutoSlide: false, /** true,false ,autoSlide:true */
26 | loop: false, /** true,false */
27 | background: false, /** false or color*/
28 | transition: 'default', /** transition type: "slid","fade","default","random" , to show transition efects :transitionTime > 0.5 */
29 | transitionTime: 1 /** transition time between slides in seconds */
30 | },
31 | revealjsConfig: {}
32 | })
33 |
--------------------------------------------------------------------------------
/packages/vendors/pptx/pptx.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
124 |
125 |
132 |
--------------------------------------------------------------------------------
/packages/vendors/pptx/process.js:
--------------------------------------------------------------------------------
1 | import JSZip from 'jszip'
2 |
3 | import {
4 | genGlobalCSS,
5 | getContentTypes,
6 | getSlideSizeAndSetDefaultTextStyle,
7 | processSingleSlide,
8 | readXmlFile,
9 | setters
10 | } from './support/vendor'
11 |
12 | /**
13 | * 导出唯一的处理入口,交由worker处置
14 | * @param setOnMessage worker消息处理器设置入口
15 | * @param postMessage 发送回主线程的消息回调
16 | */
17 | export default function process(setOnMessage, postMessage) {
18 | // 设置worker通信回调处理器
19 | setOnMessage(async ({ type, data, options, IE11 }) => {
20 | if (type === 'processPPTX') {
21 | try {
22 | setters.settings = options
23 | setters.processFullTheme = options.themeProcess
24 | setters.IE11 = IE11
25 | await processPPTX(data)
26 | } catch (e) {
27 | console.error('AN ERROR HAPPENED DURING processPPTX', e)
28 | postMessage({
29 | type: 'ERROR',
30 | data: e.toString()
31 | })
32 | }
33 | }
34 | })
35 |
36 | /**
37 | * 从zip压缩格式读取内容
38 | */
39 | async function readZip(file) {
40 | if (file.byteLength < 10) return console.error('读取pptx文件失败!')
41 | // 异步加载
42 | return JSZip.loadAsync(file)
43 | }
44 |
45 | /**
46 | * 处理pptx文件,唯一主入口
47 | * @param data 二进制数据
48 | */
49 | async function processPPTX(data) {
50 | const zip = await readZip(data)
51 | const dateBefore = new Date()
52 |
53 | // 声明一个发送函数
54 | const sendIfPossible = (index) => {
55 | if (finished[index] && current === index) {
56 | postMessage(finished[current++])
57 | delete finished[index]
58 | sendIfPossible(current)
59 | }
60 | }
61 |
62 | // 提前完成的缓存
63 | const finished = {}
64 | // 下标记录,要求有序
65 | let current = -1
66 | // 获取缩略图
67 | if (zip.file('docProps/thumbnail.jpeg')) {
68 | const pptxThumbImg = await zip
69 | .file('docProps/thumbnail.jpeg')
70 | .async('base64')
71 | postMessage({
72 | type: 'pptx-thumb',
73 | data: pptxThumbImg,
74 | slide_num: current++
75 | })
76 | } else {
77 | current = 0
78 | }
79 |
80 | // 获取内容类型
81 | const filesInfo = await getContentTypes(zip)
82 | // 获取总幻灯片张数,并获取默认字体风格
83 | const slideSize = await getSlideSizeAndSetDefaultTextStyle(zip)
84 | // 获取表格样式
85 | setters.tableStyles = await readXmlFile(zip, 'ppt/tableStyles.xml')
86 | console.log('slideSize: ', slideSize)
87 | // 发送一个大小
88 | postMessage({
89 | type: 'slideSize',
90 | data: slideSize,
91 | slide_num: current++
92 | })
93 |
94 | // 逐个读取slide,并发处理,注意,需要传入顺序,保证幻灯片是正确的顺序插入的
95 | const slides = filesInfo['slides']
96 | const numOfSlides = slides.length
97 | for (let i = 0; i < numOfSlides; i++) {
98 | // 取得名字和下标
99 | const path = slides[i]
100 | const first = path.includes('/') ? path.lastIndexOf('/') + 1 : 0
101 | const last = path.includes('.') ? path.lastIndexOf('.') : path.length
102 | const filename = path.substring(first, last)
103 | const slideNumber =
104 | filename && filename.includes('slide') ? Number(filename.substr(5)) : 1
105 | // 最终渲染
106 | const slideHtml = await processSingleSlide(zip, path, i, slideSize)
107 | // 根据顺序发送,前面的没发送需要先等待,一旦前面发送完毕,后面的会立即触发
108 | const body = {
109 | type: 'slide',
110 | data: slideHtml,
111 | slide_num: slideNumber,
112 | file_name: filename
113 | }
114 | // 当前顺位,发送,并检测后面排队的,顺便发送了
115 | if (current === slideNumber) {
116 | postMessage(body)
117 | sendIfPossible(++current)
118 | } else {
119 | finished[slideNumber] = body
120 | }
121 | postMessage({
122 | type: 'progress-update',
123 | slide_num: numOfSlides + i + 1,
124 | data: ((i + 1) * 100) / numOfSlides
125 | })
126 | }
127 |
128 | postMessage({
129 | type: 'globalCSS',
130 | data: genGlobalCSS()
131 | })
132 |
133 | postMessage({
134 | type: 'ExecutionTime',
135 | data: new Date() - dateBefore
136 | })
137 | return finished
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/packages/vendors/pptx/styles/pptxjs.css:
--------------------------------------------------------------------------------
1 |
2 | .slide {
3 | position: relative;
4 | border: 1px solid #333;
5 | border-radius: 10px;
6 | overflow: hidden;
7 | margin-bottom: 50px;
8 | margin-left: auto;
9 | margin-right: auto;
10 | }
11 |
12 | .slide div.block {
13 | position: absolute;
14 | top: 0px;
15 | left: 0px;
16 | width: 100%;
17 | line-height: 1;
18 | }
19 |
20 | .slide div.content {
21 | display: flex;
22 | flex-direction: column;
23 | }
24 | .slide div.diagram-content{
25 | display: flex;
26 | flex-direction: column;
27 | }
28 |
29 | .slide div.content-rtl {
30 | display: flex;
31 | flex-direction: column;
32 | direction: rtl;
33 | }
34 | .slide .pregraph-rtl{
35 | direction: rtl;
36 | }
37 | .slide .pregraph-ltr{
38 | direction: ltr;
39 | }
40 | .slide .pregraph-inherit{
41 | direction: inherit;
42 | }
43 | .slide .slide-prgrph{
44 | width: 100%;
45 | /* overflow-wrap:break-word;
46 | word-wrap: break-word; */
47 |
48 | /* word-break: break-word; */
49 | /* unicode-bidi: bidi-override; */
50 | /* hyphens: auto;
51 | overflow-wrap: break-word; */
52 |
53 | }
54 |
55 | .slide .line-break-br::before{
56 | content: "\A";
57 | white-space: pre;
58 | }
59 | .slide div.v-up {
60 | justify-content: flex-start;
61 | }
62 | .slide div.v-mid {
63 | justify-content: center;
64 | }
65 | .slide div.v-down {
66 | justify-content: flex-end;
67 | }
68 |
69 | .slide div.h-left {
70 | justify-content: flex-start;
71 | align-items: flex-start;
72 | text-align: left;
73 | }
74 | .slide div.h-left-rtl {
75 | justify-content: flex-end;
76 | align-items: flex-end;
77 | text-align: left;
78 | }
79 | .slide div.h-mid {
80 | justify-content: center;
81 | align-items: center;
82 | text-align: center;
83 | }
84 | .slide div.h-right {
85 | justify-content: flex-end;
86 | align-items: flex-end;
87 | text-align: right;
88 | }
89 | .slide div.h-right-rtl {
90 | justify-content: flex-start;
91 | align-items: flex-start;
92 | text-align: right;
93 | }
94 |
95 | .slide div.h-just,
96 | .slide div.h-dist {
97 | text-align: justify;
98 | }
99 |
100 |
101 | .slide div.up-left {
102 | justify-content: flex-start;
103 | align-items: flex-start;
104 | text-align: left;
105 | }
106 | .slide div.up-center {
107 | justify-content: flex-start;
108 | align-items: center;
109 | }
110 | .slide div.up-right {
111 | justify-content: flex-start;
112 | align-items: flex-end;
113 | }
114 | .slide div.center-left {
115 | justify-content: center;
116 | align-items: flex-start;
117 | text-align: left;
118 | }
119 | .slide div.center-center {
120 | justify-content: center;
121 | align-items: center;
122 | }
123 | .slide div.center-right {
124 | justify-content: center;
125 | align-items: flex-end;
126 | }
127 | .slide div.down-left {
128 | justify-content: flex-end;
129 | align-items: flex-start;
130 | text-align: left;
131 | }
132 | .slide div.down-center {
133 | justify-content: flex-end;
134 | align-items: center;
135 | }
136 | .slide div.down-right {
137 | justify-content: flex-end;
138 | align-items: flex-end;
139 | }
140 |
141 |
142 | .slide li.slide {
143 | margin: 10px 0px;
144 | font-size: 18px;
145 | }
146 |
147 | .slide table {
148 | position: absolute;
149 | }
150 |
151 | .slide svg.drawing {
152 | position: absolute;
153 | overflow: visible;
154 | }
155 |
156 | /*
157 | #pptx-thumb {
158 | min-width: 240px;
159 | height: 180px;
160 | }
161 | */
162 |
--------------------------------------------------------------------------------
/packages/vendors/pptx/worker/pptx.worker.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import processPptx from '../process'
4 |
5 | processPptx(
6 | (func) => {
7 | self.onmessage = (e) => func(e.data)
8 | },
9 | (msg) => self.postMessage(msg)
10 | )
11 |
12 | export default processPptx
13 |
--------------------------------------------------------------------------------
/packages/vendors/text/CodeViewer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ value }}
4 |
5 |
6 |
7 |
18 |
19 |
32 |
--------------------------------------------------------------------------------
/packages/vendors/text/index.js:
--------------------------------------------------------------------------------
1 | import { readText } from '../../util'
2 | import EventBus from '../../util/EventBus'
3 | import Vue from 'vue'
4 | import CodeViewer from './CodeViewer'
5 |
6 | /**
7 | * 渲染文本
8 | * @param buffer 文本二进制内容
9 | * @param target 目标
10 | */
11 | export default async function renderText(buffer, target) {
12 | const text = await readText(buffer)
13 | return new Vue({
14 | render: (h) => h(CodeViewer, { props: { value: text } })
15 | }).$mount(target).$nextTick(() => {
16 | EventBus.$emit('fileLoaded', { fileType: 'text', success: true });
17 | })
18 | }
19 |
--------------------------------------------------------------------------------
/packages/vendors/xlsx/Table.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
16 |
17 |
18 |
19 |
20 |
332 |
333 |
339 |
340 |
357 |
--------------------------------------------------------------------------------
/packages/vendors/xlsx/color.js:
--------------------------------------------------------------------------------
1 | export const indexedColors = [
2 | '000000',
3 | 'FFFFFF',
4 | 'FF0000',
5 | '00FF00',
6 | '0000FF',
7 | 'FFFF00',
8 | 'FF00FF',
9 | '00FFFF',
10 | '000000',
11 | 'FFFFFF',
12 | 'FF0000',
13 | '00FF00',
14 | '0000FF',
15 | 'FFFF00',
16 | 'FF00FF',
17 | '00FFFF',
18 | '800000',
19 | '008000',
20 | '000080',
21 | '808000',
22 | '800080',
23 | '008080',
24 | 'C0C0C0',
25 | '808080',
26 | '9999FF',
27 | '993366',
28 | 'FFFFCC',
29 | 'CCFFFF',
30 | '660066',
31 | 'FF8080',
32 | '0066CC',
33 | 'CCCCFF',
34 | '000080',
35 | 'FF00FF',
36 | 'FFFF00',
37 | '00FFFF',
38 | '800080',
39 | '800000',
40 | '008080',
41 | '0000FF',
42 | '00CCFF',
43 | 'CCFFFF',
44 | 'CCFFCC',
45 | 'FFFF99',
46 | '99CCFF',
47 | 'FF99CC',
48 | 'CC99FF',
49 | 'FFCC99',
50 | '3366FF',
51 | '33CCCC',
52 | '99CC00',
53 | 'FFCC00',
54 | 'FF9900',
55 | 'FF6600',
56 | '666699',
57 | '969696',
58 | '003366',
59 | '339966',
60 | '003300',
61 | '333300',
62 | '993300',
63 | '993366',
64 | '333399',
65 | '333333',
66 | 'b7e0ff',
67 | '00CCFF'
68 | ]
69 |
--------------------------------------------------------------------------------
/packages/vendors/xlsx/index.js:
--------------------------------------------------------------------------------
1 | import ExcelJS from 'exceljs'
2 | import Vue from 'vue'
3 | import EventBus from '../../util/EventBus'
4 | import Table from './Table'
5 | import 'handsontable/dist/handsontable.full.min.css'
6 |
7 | /**
8 | * 渲染excel
9 | */
10 | export default async function render(buffer, target) {
11 | const workbook = await new ExcelJS.Workbook().xlsx.load(buffer)
12 | return new Vue({
13 | render: (h) =>
14 | h(Table, {
15 | props: {
16 | workbook
17 | }
18 | })
19 | }).$mount(target).$nextTick(() => {
20 | EventBus.$emit('fileLoaded', { fileType: 'excel', success: true });
21 | })
22 | }
23 |
--------------------------------------------------------------------------------
/packages/vendors/xlsx/util.js:
--------------------------------------------------------------------------------
1 | // 深度扁平化routes
2 | export function flatten(routes) {
3 | return routes.flatMap((route) =>
4 | route.children ? [route, ...flatten(route.children)] : [route]
5 | )
6 | }
7 |
8 | // 转化style对象为style字符串
9 | export function toStyleString(style) {
10 | return [...style].map((key) => `${key}: ${style[key]}`).join(';')
11 | }
12 |
13 | // 修复矩阵的宽度
14 | export function fixMatrix(data, colLen) {
15 | for (const row of data) {
16 | for (let j = 0; j < colLen; j++) {
17 | if (!row[j]) {
18 | row[j] = ''
19 | }
20 | }
21 | }
22 | return data
23 | }
24 |
25 | // 首字母大写
26 | export function captain(str) {
27 | return `${str.charAt(0).toUpperCase()}${str.slice(1)}`
28 | }
29 |
30 | // 连字符转驼峰
31 | export function camelCase(str) {
32 | return str
33 | .split('-')
34 | .map((part, index) => {
35 | if (index !== 0) {
36 | return captain(part)
37 | } else {
38 | return part
39 | }
40 | })
41 | .join('')
42 | }
43 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2022-11-22 16:39:30
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-09 19:59:32
6 | * @Descripttion: prettier 配置
7 | */
8 | module.exports = {
9 | // tab缩进大小,默认为2
10 | tabWidth: 2,
11 | useTabs: false,
12 | // 使用分号, 默认true
13 | semi: false,
14 | // 使用单引号,
15 | singleQuote: true,
16 | // 行尾逗号,
17 | TrailingCooma: 'none',
18 | // 空格
19 | bracketSpacing: true,
20 | htmlWhitespaceSensitivity: 'ignore', // 不启用空格敏感格式化
21 | // 箭头函数参数括号 默认avoid 可选 avoid| always
22 | // avoid 能省略括号的时候就省略 例如x => x
23 | // always 总是有括号
24 | arrowParens: 'always'
25 | }
26 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/public/favicon.ico
--------------------------------------------------------------------------------
/public/file-viewer/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/public/file-viewer/favicon.ico
--------------------------------------------------------------------------------
/public/file-viewer/fileTest/mp3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/public/file-viewer/fileTest/mp3.mp3
--------------------------------------------------------------------------------
/public/file-viewer/fileTest/mp4.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/public/file-viewer/fileTest/mp4.mp4
--------------------------------------------------------------------------------
/public/file-viewer/fileTest/pdf.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/public/file-viewer/fileTest/pdf.pdf
--------------------------------------------------------------------------------
/public/file-viewer/fileTest/pic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/public/file-viewer/fileTest/pic.png
--------------------------------------------------------------------------------
/public/file-viewer/fileTest/ppt.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/public/file-viewer/fileTest/ppt.pptx
--------------------------------------------------------------------------------
/public/file-viewer/fileTest/word.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/public/file-viewer/fileTest/word.docx
--------------------------------------------------------------------------------
/public/file-viewer/index.html:
--------------------------------------------------------------------------------
1 | @zuiyouliao/vue-file-viewer
--------------------------------------------------------------------------------
/public/fileTest/mp3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/public/fileTest/mp3.mp3
--------------------------------------------------------------------------------
/public/fileTest/mp4.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/public/fileTest/mp4.mp4
--------------------------------------------------------------------------------
/public/fileTest/pdf.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/public/fileTest/pdf.pdf
--------------------------------------------------------------------------------
/public/fileTest/pic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/public/fileTest/pic.png
--------------------------------------------------------------------------------
/public/fileTest/ppt.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/public/fileTest/ppt.pptx
--------------------------------------------------------------------------------
/public/fileTest/word.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyl-ui/vue-file-viewer/09aaddda08d51ad27ff889c5181a1e8ed248e32c/public/fileTest/word.docx
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | <%= htmlWebpackPlugin.options.title %>
18 |
19 |
29 |
30 |
31 |
32 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: zhanghan
3 | * @Date: 2023-01-09 21:03:01
4 | * @LastEditors: zhanghan
5 | * @LastEditTime: 2023-01-10 14:45:44
6 | * @Descripttion:
7 | */
8 |
9 | // 组件开发环境(编译构建的是examples文件夹内的东西,用于组件库开发调试)
10 | const devConfig = require('./config/config.dev')
11 |
12 | // 组件打包环境(编译构建的是packages文件夹内的东西,用于打包组件发布npm)
13 | const buildConfig = require('./config/config.build')
14 |
15 | // 组件打包环境(编译构建的是doc文件夹内的东西,用于文档演示调试和发布)
16 | const docConfig = require('./config/config.doc')
17 |
18 | // iframe(编译构建的是examples文件夹内的东西,用于打包项目对外作为iframe提供api使用)
19 | const iframeConfig = require('./config/config.build.iframe')
20 |
21 | let nowConf = {}
22 |
23 | // 根据环境判断使用哪个配置
24 | switch (process.env.VUE_APP_ENV) {
25 | case 'production':
26 | nowConf = buildConfig
27 | break
28 | case 'development':
29 | nowConf = devConfig
30 | break
31 | case 'doc':
32 | nowConf = docConfig
33 | break
34 | case 'iframe':
35 | nowConf = iframeConfig
36 | break
37 | }
38 |
39 | module.exports = nowConf
40 |
--------------------------------------------------------------------------------