Browse Source

demo上传

master
zyzm 3 years ago
parent
commit
9141740a0a
  1. 14
      xd-file-preview/.editorconfig
  2. 21
      xd-file-preview/.gitignore
  3. 42
      xd-file-preview/.npmignore
  4. 201
      xd-file-preview/LICENSE
  5. 207
      xd-file-preview/README.md
  6. 10
      xd-file-preview/alias.config.js
  7. 5
      xd-file-preview/babel.config.js
  8. 103
      xd-file-preview/build/autoload.js
  9. 40
      xd-file-preview/build/clog.js
  10. 70
      xd-file-preview/build/commands/copy.js
  11. 35
      xd-file-preview/build/entries.create.page.js
  12. 182
      xd-file-preview/build/fileHepler.js
  13. 9
      xd-file-preview/build/path.js
  14. 19
      xd-file-preview/build/plugins/CreatedComponentsPlugin.js
  15. 53
      xd-file-preview/build/template/api.js
  16. 5
      xd-file-preview/build/template/autoload.txt
  17. 11
      xd-file-preview/build/template/entry.js
  18. 60
      xd-file-preview/build/template/module.js
  19. 7
      xd-file-preview/build/template/sub-component.txt
  20. 20
      xd-file-preview/build/template/test-data.js
  21. 39
      xd-file-preview/devServer.js
  22. 2
      xd-file-preview/index.js
  23. 8
      xd-file-preview/lib/demo.html
  24. 74080
      xd-file-preview/lib/gxd.common.js
  25. 74090
      xd-file-preview/lib/gxd.umd.js
  26. 27
      xd-file-preview/lib/gxd.umd.min.js
  27. 27096
      xd-file-preview/package-lock.json
  28. 101
      xd-file-preview/package.json
  29. BIN
      xd-file-preview/public/favicon.ico
  30. 18
      xd-file-preview/public/index.html
  31. BIN
      xd-file-preview/public/static/999.ofd
  32. BIN
      xd-file-preview/public/static/PlayerAPI_v1.0.6.pdf
  33. 8
      xd-file-preview/public/static/helper.css
  34. 321
      xd-file-preview/public/static/helper.js
  35. 17
      xd-file-preview/public/static/index.html
  36. 17
      xd-file-preview/public/static/index.txt
  37. 44
      xd-file-preview/settings.js
  38. 182
      xd-file-preview/src/App.vue
  39. BIN
      xd-file-preview/src/assets/water-bg.png
  40. 250
      xd-file-preview/src/components/XdFileListPreview.vue
  41. 60
      xd-file-preview/src/components/XdLoadPdfScript.vue
  42. 131
      xd-file-preview/src/components/XdPdf.vue
  43. 48
      xd-file-preview/src/components/contact.js
  44. 350
      xd-file-preview/src/components/preview/helper.js
  45. 18
      xd-file-preview/src/components/preview/image.js
  46. 554
      xd-file-preview/src/components/preview/imageView.vue
  47. 143
      xd-file-preview/src/components/preview/index.js
  48. 17
      xd-file-preview/src/components/preview/json.js
  49. 309
      xd-file-preview/src/components/preview/jsonView.vue
  50. 55
      xd-file-preview/src/components/preview/loading.vue
  51. 17
      xd-file-preview/src/components/preview/ofd.js
  52. 18
      xd-file-preview/src/components/preview/office.js
  53. 198
      xd-file-preview/src/components/preview/officeView.vue
  54. 17
      xd-file-preview/src/components/preview/pdf.js
  55. 305
      xd-file-preview/src/components/preview/pdfView.vue
  56. 88
      xd-file-preview/src/components/preview/style.css
  57. 17
      xd-file-preview/src/components/preview/text.js
  58. 397
      xd-file-preview/src/components/preview/textView.vue
  59. 5
      xd-file-preview/src/directives/index.js
  60. 56
      xd-file-preview/src/directives/waterMark.js
  61. 31
      xd-file-preview/src/install.js
  62. 18
      xd-file-preview/src/main.js
  63. 89
      xd-file-preview/vue.config.js

14
xd-file-preview/.editorconfig

@ -0,0 +1,14 @@
# https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

21
xd-file-preview/.gitignore

@ -0,0 +1,21 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

42
xd-file-preview/.npmignore

@ -0,0 +1,42 @@
.DS_Store
node_modules
/dist
/src/assets
/src/data
/src/styles
/src/utils
/src/view
/src/App.vue
/src/main.js
/build
/public
/alias.config.js
/babel.config.js
/devServer.js
/settings.js
/vue.config.js
/lib/dome.html
/lib/gxd.umd.js
/lib/gxd.common.js
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

201
xd-file-preview/LICENSE

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

207
xd-file-preview/README.md

@ -0,0 +1,207 @@
# Gxd-vue-file-preview
#### 介绍
```text
vue 文件在线预览展示功能,支持文件(PDF,PNG,JPEG,JPG,GIF,DOC,DOCX,PPT,PPTX,ELXS,ELX)
```
#### 预览
```markdown
![demo](http://static.e56buy.com/1610238353869.jpg "demo")
```
#### 安装
```bash
npm i gxd-file-preview --save --registry https://registry.npm.taobao.org
```
#### 插件全局引用
``` javascript
import vueFilePreview from 'gxd-file-preview';
//初始化自定义插件,(pdf.js,worker.js文件建议放在本地服务器),cdn有可能不稳定
Vue.use(vueFilePreview,{
pdf: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.0.288/build/pdf.min.js', //pdf插件
worker:'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.0.288/build/pdf.worker.min.js',//pdf.work插件
});
```
#### 插件使用
```vue
<template>
<div id="app"></div>
</template>
<script>
export default {
name: 'app',
components: {},
data(){
return {}
},
created() {
setTimeout(()=>{
this.$preview({
//url: 'https://testimg.tiangongy.com/100601/a024b86760bb1ff3b38f25ae2e0b9bdf', //图片
//url: 'https://testimg.tiangongy.com/100601/9958ff80d202f91b347b14b5c56f14e8', // xlsx
//url: 'https://testimg.tiangongy.com/100601/12d7e6a9b0b9169b800fbb29061212c2', //pptx
//url: 'https://testimg.tiangongy.com/100601/ce44c69f3075334e6c624b8180a42804', //doc,
//url: 'https://testimg.tiangongy.com/100601/3b85b4f1c3accdb4bb9f7e42e1f9070e',
url:'http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf',
fid: '12121212'
})
}, 2000);
},
methods:{
}
}
</script>
<style>
</style>
```
```vue
<template>
<div id="app">
<h1>列表展示</h1>
<xd-file-list-preview
:show-close="showClose"
:list="list" @remove="handleRemoveClick"
:is-pagination="isPagination"
></xd-file-list-preview>
<hr>
<h1>文件预览模式</h1>
<a @click="handleClick" style="color: #4285f4">9958ff80d202f91b347b14b5c56f14e811</a>
</div>
</template>
<script>
interface FileItemFormat {
url:string; //文件路径(绝对路径)
name?: string; //文件名称
fid?: string; //文件ID
download?: string; //预览页面是否显示下载按钮
}
export default {
name: 'app',
components: {},
data() {
return {
showClose: true, //是否开启删除功能
isPagination: true, //列表启动翻页功能
/**@type FileItemFormat **/
list: [
{url: 'http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf'},
{url: 'https://jfb-public-images.oss-cn-qingdao.aliyuncs.com/admin-upload/202111081034429231.png?x-oss-process=style/common'},
{url: 'http://static.e56buy.com/XdgfsqR2INp7uFxTuLQtnMstYLY4K8rr.蛋糕缺少内容.docx', name: 'aaaa'},
{
url: 'http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf',
name: 'aaaa',
fid: 'aadadads',
download: false ,//是否展示下载按钮
},
]
}
},
created() {
},
methods: {
/**
* @description 删除图片事件
* @param item {Object} 当前被删除的文件对象
* @param done {function} 删除文件完成回调函数
*/
handleRemoveClick(item, done) {
setTimeout(() => {
console.log('handleRemoveClick', item);
done()
}, 2000);
},
/**
* @description 点击查看预览功能
*/
handleClick() {
this.$preview({
url: 'http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf',
fid: 'aadadads',
download: false ,//是否展示下载按钮
})
},
}
}
</script>
<style>
</style>
```
##### 项目开发下载与初始化
```bash
# 克隆项目
git clone git@gitee.com:e56buy/xd-file-preview.git
# 进入项目目录
cd xd-file-preview
# 安装依赖
npm install
# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。
建议通过npm按照,通过如下操作解决 npm 下载速度慢的问题
npm install --save --registry=https://registry.npm.taobao.org
```
##### Nginx配置静态资源可以跨域访问(注意访问静态资源需要做跨域处理)
```text
#全局模式
server {
listen 80;
add_header 'Access-Control-Allow-Origin' '*';
location /Roboto/ {
root /home/images;
autoindex on;
}
}
#文件路径配置访问
#访问路径拼接img访问本地绝对路径下的某图片
location /img/ {
#跨域配置,如果不生效请先清除浏览器缓存数据
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
expires 30d;
#当访问https://server_name/img/路径时,就会访问本的/Users/chokshen/Desktop/img/文件夹
root /Users/chokshen/Desktop/;
error_log off;
access_log /dev/null;
autoindex on;
}
```
```text
版本日志
1.2.0
1、新增JSON,TXT,JS,HTML,CSS预览功能
2、列表展示功能新增 预览模式时候 左右翻页功能
1.1.18
优化文档显示
1.1.17
修复预览offic系列访问不成功问题
```

10
xd-file-preview/alias.config.js

@ -0,0 +1,10 @@
'use strict';
const resolve = dir => require('path').join(__dirname, dir);
module.exports = {
resolve: {
alias: {
'@': resolve('src'),
}
}
};

5
xd-file-preview/babel.config.js

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/app'
]
}

103
xd-file-preview/build/autoload.js

@ -0,0 +1,103 @@
'use strict';
const path = require('path');
const basePath = require('./path');
const fileHandle = require('./fileHepler');
const fs = require("fs");
const clog = require('./clog');
/**
* 递归创建目录
* @param dirname
* @returns {boolean}
*/
const mkdirsSync = dirname => {
if (fs.existsSync(dirname)) {
return true;
} else {
if (mkdirsSync(path.dirname(dirname))) {
fs.mkdirSync(dirname);
return true;
}
}
};
let autoload = {
/**
* @description 自动加载模块
* @param dir 加载路径
* @param desc 描述
* @param flieType 加载文件类型
* @param type 加载返回数据类型Object|Array
*/
createAutoload: function (dir, flieType=['js'], type = "object", desc='Utils',) {
let autoladFile = fileHandle.getDirFiles(dir, flieType, ['.bak','autoload']),
importStr = '',
exportStr = '',
count = 0;
Object.keys(autoladFile).forEach((file) => {
/**多层关系加载**/
if (file.indexOf('bak') === -1) {
let moduleName = this.handleFile(file);
let modulePath = file;
let str = '';
if (count === 0) {
str = `import ${moduleName} from './${modulePath}';`;
exportStr += `${moduleName}`;
} else {
str = `
import ${moduleName} from './${modulePath}';`;
exportStr += `,
${moduleName}`;
}
importStr += str;
count++;
}
});
/**读取文件,并替换文件内容**/
let buffer = fs.readFileSync(path.join(basePath.buildTemplateDirectory, 'autoload.txt'));
let content = String(buffer);
if(type === 'object') {
exportStr = `{
${exportStr}
}`;
}
if (type === 'array') {
exportStr = `[
${exportStr}
]`;
}
content = content.replace(/@import_modules@/g, importStr);
content = content.replace(/@modules_name@/g, exportStr);
/**写文件**/
let fd = fs.openSync(path.join(dir, 'autoload.js'), 'w');
fs.writeFileSync(fd, content);
fs.closeSync(fd);
clog(`\n\n\n${desc} 加载完成,path:${path.join(dir, 'autoload.js')}`,'green');
},
handleFile(path) {
if (path.indexOf('/') !== -1) {
let temp = '';
let arr = path.split('/');
let len = arr.length;
for (let i = 0; i < len; i++) {
if (i === 0) temp = arr[i];
else temp += arr[i].replace(arr[i][0], arr[i][0].toLocaleUpperCase());
}
return temp;
} else {
return path;
}
},
};
module.exports = autoload;

40
xd-file-preview/build/clog.js

@ -0,0 +1,40 @@
'use strict';
let styles = {
'nomarl':['',''],
'bold': ['\x1B[1m', '\x1B[22m'],
'italic': ['\x1B[3m', '\x1B[23m'],
'underline': ['\x1B[4m', '\x1B[24m'],
'inverse': ['\x1B[7m', '\x1B[27m'],
'strikethrough': ['\x1B[9m', '\x1B[29m'],
'white': ['\x1B[37m', '\x1B[39m'],
'grey': ['\x1B[90m', '\x1B[39m'],
'black': ['\x1B[30m', '\x1B[39m'],
'blue': ['\x1B[34m', '\x1B[39m'],
'cyan': ['\x1B[36m', '\x1B[39m'],
'green': ['\x1B[32m', '\x1B[39m'],
'magenta': ['\x1B[35m', '\x1B[39m'],
'red': ['\x1B[31m', '\x1B[39m'],
'yellow': ['\x1B[33m', '\x1B[39m'],
'whiteBG': ['\x1B[47m', '\x1B[49m'],
'greyBG': ['\x1B[49;5;8m', '\x1B[49m'],
'blackBG': ['\x1B[40m', '\x1B[49m'],
'blueBG': ['\x1B[44m', '\x1B[49m'],
'cyanBG': ['\x1B[46m', '\x1B[49m'],
'greenBG': ['\x1B[42m', '\x1B[49m'],
'magentaBG': ['\x1B[45m', '\x1B[49m'],
'redBG': ['\x1B[41m', '\x1B[49m'],
'yellowBG': ['\x1B[43m', '\x1B[49m']
};
function clog(obj, key='black') {
if (typeof obj === 'string') {
console.log(styles[key][0] + '%s' + styles[key][1], obj)
} else if (typeof obj === 'object') {
console.log(styles[key][0] + '%o' + styles[key][1], obj)
} else {
console.log(styles[key][0] + '%s' + styles[key][1], obj)
}
}
module.exports = clog;

70
xd-file-preview/build/commands/copy.js

@ -0,0 +1,70 @@
'use strict';
const fileHepler = require('./../fileHepler');
const basePath = require('./../path');
const clog = require('./../clog');
// const path = require('path');
let cssContentArray = [];
clog('\n\n\n');
clog('---开始拷贝--------------', 'red');
/**
* 替换文件index
* @param content
* @param item
* @returns {*}
*/
function replaceHash(content, item){
let cssReg = /((\..*?)\.css)$/;
let contentarr = item.match(cssReg);
let version = `.css?version=${Math.floor(new Date().getTime() / 1000)}`;
let replce = {
oldPath: item,
newPath : item.replace(new RegExp(contentarr[2]),''),
};
cssContentArray.push(replce);
return content.replace(new RegExp(contentarr[1]), version);
}
function init(){
fileHepler.readFile(basePath.indexHtmlFilePath)
.then((res) => {
let cssReg = /\/static\/.*?(\..*?)\.css/ig;
let contentarr = res.match(cssReg);
if (!contentarr) {
clog('文件无更新', 'black');
return;
}
for (let i = 0; i < contentarr.length; i++) {
res = replaceHash(res, contentarr[i]);
}
fileHepler.writeFile(basePath.indexHtmlFilePath, res)
.then(() => {
clog('更新首页样式路径成功!', 'green');
})
.catch();
for (let k = 0; k < cssContentArray.length; k++) {
let olpPath = basePath.cssOutputRoot + cssContentArray[k]['oldPath'];
let newPath = basePath.cssOutputRoot + cssContentArray[k]['newPath'];
fileHepler.renameFile(olpPath, newPath)
.then(() => {
clog(cssContentArray[k], 'green');
})
.catch()
}
});
}
setTimeout(() => {
init()
}, 2000);

35
xd-file-preview/build/entries.create.page.js

@ -0,0 +1,35 @@
'use strict';
const path = require('path');
module.exports = {
init(conf) {
return this.pages(conf);
},
commonParamter() {
let time = new Date();
let version = String(time.getTime() / 1000);
return {
version: version,
};
},
pages(conf) {
console.log('\n');
let entries = conf.getDirJsFile(conf.entryDirectory);
let pages = {};
Object.keys(entries).forEach((entry) => {
console.log(`cretae entry, path:${entry} file:${entry}.html`);
pages[entry] = {};
pages[entry]['entry'] = path.resolve(conf['entryDirectory'], `${entry}.js`);
pages[entry]['template'] = path.resolve(conf['packingTemplatesPath'], 'index.html');
pages[entry]['filename'] = `${entry}.html`;
pages[entry]['title'] = entry;
pages[entry]['paramters'] = this.commonParamter();
pages[entry]['chunks'] = ['chunk-vendors', 'chunk-common', entry];
});
return pages;
}
};

182
xd-file-preview/build/fileHepler.js

@ -0,0 +1,182 @@
'use strict';
const requireContext = require('require-context');
const fs = require('fs');
class FileHepler {
checkVarType(obj) {
let toString = Object.prototype.toString;
let map = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regExp',
'[object Undefined]': 'undefined',
'[object Null]': 'null',
'[object Object]': 'object'
}
return map[toString.call(obj)];
}
inArray(sourceArray = [], findArray = []) {
if (this.checkVarType(sourceArray) === 'array' && this.checkVarType(findArray) === 'array') {
let sourceArraylen = sourceArray.length;
let find = JSON.parse(JSON.stringify(findArray));
let temp = [];
for (let i = 0; i < sourceArraylen; i++) {
let sourceVal = sourceArray[i];
for (let k = 0; k < find.length; k++) {
if (find[k] === sourceVal) {
temp.push(true);
find.splice(k, 1);
break;
}
}
}
return findArray.length === temp.length;
} else {
return false;
}
}
getDirFiles(directory, fileType = [], ignoreFileName = []) {
/**
* @description
* @type {RegExp}
*/
let reg = new RegExp(`\.(${fileType.join('|')})$`);
let regFile = new RegExp(`^(.*)\.(${fileType.join('|')})$`);
let modulesFiles = requireContext(directory, true, reg);
let modules = modulesFiles.keys().reduce((modules, modulePath) => {
const moduleName = modulePath.replace(regFile, '$1');
const extName = modulePath.replace(regFile, '$2');
const moduleNameArr = moduleName.split('/');
modules[moduleName] = {
path: moduleName,
fileName: moduleNameArr[moduleNameArr.length - 1],
ext: extName,
fullName: `${moduleName}.${extName}`
};
return modules;
}, {});
//过滤忽略文件
let temp = {};
Object.keys(modules).map((key) => {
let item = modules[key];
if (!this.inArray(ignoreFileName, [item['fileName']])) {
temp[key] = modules[key];
}
});
return temp;
}
/***
* @description 复制文件到新位置
* @param currentFilePath
* @param targetFliePath
*/
copyFile(currentFilePath, targetFliePath) {
return new Promise((resolve,reject)=>{
if (!fs.existsSync(currentFilePath)) {
reject(`复制文件路径不存在:${currentFilePath}`);
}
let readStream = fs.createReadStream(currentFilePath);
readStream.once('error', (err) => {
reject(err);
});
readStream.once('end', () => {
resolve();
});
readStream.pipe(fs.createWriteStream(targetFliePath));
})
}
/***
* @description 删除文件
* @param FilePath
* @returns {Promise<unknown>}
*/
removeFile(FilePath){
return new Promise((resolve, reject)=>{
fs.unlink(FilePath, (err) => {
if (err) {
reject(err);
return;
}
resolve('ok');
});
})
}
/**
* @description 修改文件名字
* @param FilePath
* @param newPath
* @returns {Promise<unknown>}
*/
renameFile(FilePath,newPath){
return new Promise((resolve, reject) => {
fs.rename(FilePath, newPath ,(err) => {
if (err) {
reject(err);
return;
}
resolve('ok');
});
})
}
/**
* @description 判断文件是否存在
* @param FilePath
* @returns {Promise<unknown>}
*/
existFile(FilePath){
return new Promise((resolve, reject) => {
fs.access(FilePath, fs.constants.F_OK, (err) => {
if (err) {
reject(err);
return;
}
resolve('ok');
});
})
}
readFile(FilePath){
return new Promise((resolve, reject) => {
fs.readFile(FilePath,(err, data) => {
if (err) {
reject(err);
return;
}
resolve(String(data));
});
});
}
writeFile(FilePath,data){
return new Promise((resolve, reject) => {
fs.writeFile(FilePath, data, (err) => {
if (err) {
reject(err);
return;
}
resolve('ok');
});
});
}
}
module.exports = new FileHepler();

9
xd-file-preview/build/path.js

@ -0,0 +1,9 @@
'use strict';
const resolve = dir => require('path').join(__dirname, dir);
module.exports = {
buildTemplateDirectory: resolve( './template'),
buildComponentsDirectory: resolve('../src/components'),
};

19
xd-file-preview/build/plugins/CreatedComponentsPlugin.js

@ -0,0 +1,19 @@
'use strict';
const autoload = require('./../autoload');
const basePath = require('./../path');
let time = null;
class CreatedComponentsPlugin {
constructor() {}
apply(compiler) {
if (time) {
clearTimeout(time);
time = null;
}
time = setTimeout(() => {
autoload.createAutoload(basePath.buildComponentsDirectory, ['vue'], 'array', 'Components');
}, 30);
}
}
module.exports = CreatedComponentsPlugin;

53
xd-file-preview/build/template/api.js

@ -0,0 +1,53 @@
'use strict';
import request from '@/utils/request'
const {apiUrl, isTestData } = require('@/setting');
import { getTestDataList } from "@/test-data/@pathname@";
/**
* @description 描述
* @param params { Object }
* @returns {AxiosPromise}
*/
export function getTestList(params) {
/**测试数据**/
if (isTestData) {
return getTestDataList(data)
}
let url = apiUrl.demoUrl;
return request({
url,
method: 'get',
params
})
}
/**
* @description 描述
* @param data { Object }
* @returns {AxiosPromise}
*/
export function postTestList(data) {
/**测试数据**/
if (isTestData) {
return getTestDataList(data)
}
let url = apiUrl.demoUrl;
data = request.formDataSendParam(data);
return request({
url,
method: 'post',
data
})
}

5
xd-file-preview/build/template/autoload.txt

@ -0,0 +1,5 @@
'use strict';
@import_modules@
export default @modules_name@

11
xd-file-preview/build/template/entry.js

@ -0,0 +1,11 @@
'use strict';
import app from '@/main';
import page from '@/views/@entryname@';
import store from "@/store";
store.commit('changePage', '@pathname@');
store['dispatch']('appInit', function () {
app.mount(page);
});

60
xd-file-preview/build/template/module.js

@ -0,0 +1,60 @@
'use strict';
import {
getTestList,
postTestList
} from '@/api/@pathname@';
const state = {};
const actions = {
/**
* @description postTestList
* @param commit
* @param params
* @returns {Promise<unknown>}
*/
getTestList({commit}, params) {
return new Promise((resolve, reject) => {
getTestList(params)
.then((res) => {
console.log('getTestList.res', res.data);
resolve(res.data);
})
.catch((error) => {
reject(error);
})
})
},
/**
* @description 描述
* @param commit
* @param params
* @returns {Promise<unknown>}
*/
postTestList({commit}, params) {
return new Promise((resolve, reject) => {
postTestList(params)
.then((res) => {
console.log('postTestList.res', res.data);
resolve(res.data);
})
.catch((error) => {
reject(error);
})
})
},
};
const getters = {};
const mutations = {};
export default {
namespaced: true,
state,
getters,
actions,
mutations
};

7
xd-file-preview/build/template/sub-component.txt

@ -0,0 +1,7 @@
'use strict';
import Vue from 'vue';
@import_modules@
@use_sub_components@

20
xd-file-preview/build/template/test-data.js

@ -0,0 +1,20 @@
'use strict';
/**
* @description 测试数可以使用faker
* @description https://github.com/Marak/faker.js
*/
import helper from "@/utils/helper";
/**
* @description 描述
* @param data
* @returns {Promise<unknown>}
*/
export function getTestDataList(data) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(helper.callback([]));
}, 1000)
});
}

39
xd-file-preview/devServer.js

@ -0,0 +1,39 @@
'use strict';
const proxy = {
'/api/admin': {
target: 'https://sandbox-c.jufubao.cn',
pathRewrite: {"^/api/admin": "/api/admin"},
changeOrigin: true,
secure: false,
},
'/oauth': {
target: 'https://sandbox-c.jufubao.cn',
pathRewrite: {"^/oauth": "/oauth"},
changeOrigin: true,
secure: false,
}
};
const defaultSettings = require('./settings.js');
// If your port is set to 80,
// use administrator privileges to execute the command line.
// For example, Mac: sudo npm run
// You can change the port by the following method:
// port = 9527 npm run dev OR npm run dev --port = 9527
const port = process.env.port || process.env.npm_config_port || defaultSettings.port || 9527; // dev port
const devServer = {
port: defaultSettings.port,
open: true,
//host:'192.168.0.4',
overlay: {
warnings: true,
errors: true
},
//proxy, //proxy与before不能共用,before优先于proxy
//before: ()=> {}
};
module.exports = devServer;

2
xd-file-preview/index.js

@ -0,0 +1,2 @@
module.exports = require('./lib/gxd.umd.min');

8
xd-file-preview/lib/demo.html

@ -0,0 +1,8 @@
<meta charset="utf-8">
<title>gxd demo</title>
<script src="./gxd.umd.js"></script>
<script>
console.log(gxd)
</script>

74080
xd-file-preview/lib/gxd.common.js
File diff suppressed because it is too large
View File

74090
xd-file-preview/lib/gxd.umd.js
File diff suppressed because it is too large
View File

27
xd-file-preview/lib/gxd.umd.min.js
File diff suppressed because it is too large
View File

27096
xd-file-preview/package-lock.json
File diff suppressed because it is too large
View File

101
xd-file-preview/package.json

@ -0,0 +1,101 @@
{
"name": "gxd-file-preview",
"version": "1.2.1",
"private": false,
"description": "vue 文件在线预览展示功能,支持文件(PDF,PNG,JPEG,JPG,GIF,DOC,DOCX,PPT,PPTX,XLS,XLSX,JSON,TXT,JS,HTML,CSS)",
"scripts": {
"library": "vue-cli-service build --no-clean --target lib --name gxd --dest lib --entry src/install.js",
"library:dev": "vue-cli-service build --no-clean --target lib --name gxdSource --dest lib --entry src/install.js",
"remove": " rm ./lib/gxd.common.js && rm ./lib/gxd.umd.js && rm ./lib/demo.html",
"build": "npm run library && npm run remove",
"dev": "vue-cli-service serve",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^2.6.10",
"dl-watermark": "^1.0.0",
"downloadjs": "^1.4.7",
"md5.js": "^1.3.5",
"vue": "^2.6.10",
"vue-highlightjs": "1.3.3",
"vue-json-viewer": "2.2.22",
"vue-pdf": "^4.2.0"
},
"repository": {
"type": "git",
"url": "git@gitee.com:e56buy/xd-file-preview.git"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.12.0",
"@vue/cli-plugin-eslint": "^3.12.0",
"@vue/cli-service": "^3.12.0",
"babel-eslint": "^10.0.1",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
"less": "^3.12.2",
"less-loader": "^7.0.1",
"require-context": "^1.1.0",
"sass": "^1.28.0",
"sass-loader": "^10.0.5",
"script-ext-html-webpack-plugin": "^2.1.5",
"uglifyjs-webpack-plugin": "^1.1.1",
"vue-template-compiler": "^2.6.10"
},
"keywords": [
"gxd",
"gxd-file-preview",
"vue-pdf",
"vue pdf",
"pdf",
"pdf-vue",
"pdf-vue-preview",
"pdf",
"pdf vue",
"pdf vue preview",
"pdf在线预览",
"file",
"file-preview",
"file-vue-preview",
"docx",
"doc",
"vue-docx",
"vue-doc",
"ppt",
"pptx",
"vue-ppt",
"vue-pptx",
"xls",
"xlsx",
"vue-xls",
"vue-xlsx",
"doc在线预览",
"docx在线预览",
"ppt在线预览",
"pptx在线预览",
"xls在线预览",
"xlsx在线预览"
],
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"rules": {},
"parserOptions": {
"parser": "babel-eslint"
}
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions"
]
}

BIN
xd-file-preview/public/favicon.ico

18
xd-file-preview/public/index.html

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="stylesheet" href="//unpkg.com/@highlightjs/cdn-assets@11.6.0/styles/atom-one-dark.css">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>文件预览功能</title>
</head>
<body>
<noscript>
<strong>We're sorry but xd-editer doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

BIN
xd-file-preview/public/static/999.ofd

BIN
xd-file-preview/public/static/PlayerAPI_v1.0.6.pdf

8
xd-file-preview/public/static/helper.css

@ -0,0 +1,8 @@
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: left;
color: #2c3e50;
margin-top: 60px;
}

321
xd-file-preview/public/static/helper.js

@ -0,0 +1,321 @@
'use strict';
const MD5 = require('md5.js');
import {iconData, typeHeader } from "./../contact";
class Helper {
hideScroll(type){
let body = document.getElementsByTagName('body')[0];
if (type === 1) {
body.style.overflowY = 'hidden';
body.style.height = '100%';
} else {
body.style.overflowY = '';
body.style.height = '';
}
};
/**
* @description 字符串截取
* @param val
* @param len
*/
cutStringLen(val, len = 10) {
let fix = '...';
let newLength = 0;
let newStr = "";
let chineseRegex = /[^\x00-\xff]/g;
let singleChar = "";
let strLength = val.replace(chineseRegex, "**").length;
for (let i = 0; i < strLength; i++) {
singleChar = val.charAt(i).toString();
if (singleChar.match(chineseRegex) != null) {
newLength += 2;
} else {
newLength++;
}
if (newLength > len) {
break;
}
newStr += singleChar;
}
if (strLength > len) {
newStr += fix;
}
return newStr;
}
checkVarType(obj) {
let toString = Object.prototype.toString;
let map = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regExp',
'[object Undefined]': 'undefined',
'[object Null]': 'null',
'[object Object]': 'object',
'[object Blob]' : 'blob'
};
return map[toString.call(obj)];
}
getID() {
return this.random(1111111, 9999999);
}
md5Fn(str, hex = 'hex'){
return new MD5()['update'](str)['digest'](hex);
}
random(min, max) {
let Range = max - min;
let Rand = Math.random();
return (min + Math.round(Rand * Range));
}
parseURL(url) {
if (!url) {
url = window.location.href;
}
let a = document.createElement('a');
a.href = url;
return {
source: url,
protocol: a.protocol.replace(':', ''),
host: a.hostname,
port: a.port,
query: a.search,
params: (function() {
let ret = {},
seg = a.search.replace(/^\?/, '').split('&'),
len = seg.length, i = 0, s;
for (; i < len; i++) {
if (!seg[i]) {
continue;
}
s = seg[i].split('=');
if (s[1]) {
ret[s[0]] = s[1];
}
}
return ret;
})(),
file: (a.pathname.match(/\/([^\/?#]+)$/i) || [, ''])[1],
hash: a.hash.replace('#', ''),
path: a.pathname.replace(/^([^\/])/, '/$1'),
relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [, ''])[1],
segments: a.pathname.replace(/^\//, '').split('/')
};
}
/***
* @description 获取文件类型
* @param blob
*/
checkFileType(blob){
let type = typeHeader['unkown'];
if(typeHeader[blob.type]) {
type = typeHeader[blob.type];
}
return type;
}
/**
* @description 获取文件base64流
* @param blob
* @param file
* @returns {Promise<unknown>}
*/
fileReaderBase64(blob, file){
return new Promise((resolve, reject)=>{
let reader = new FileReader();
reader.onload = (e)=> {
resolve(e.target['result']);
};
reader.onerror = ()=>{
reject('读取文件错误')
};
reader.readAsDataURL(blob);
});
}
/**
* @description 获取文件相关信息
* @param url
* @param name
* @returns {Promise<unknown>}
*/
getFileBase64(url='',name = '') {
return new Promise((resolve, reject) => {
let getName = (type)=> {
let str = url;
if(str.indexOf('?')) str = url.split('?')[0];
let arr = str.split('/');
return arr[arr.length - 1];
};
let x = new XMLHttpRequest();
x.open("GET", url, true);
x.responseType = "blob";
x.onload = (e) => {
console.log('XMLHttpRequest',e);
if (e.target['status'] === 200) {
//console.log('this.checkFileType(e.target[\'response\'])',url,this.checkFileType(e.target['response']));
let temp = {
type: this.checkFileType(e.target['response']),
size: e.target['response']['size'],
name: name ? name: getName(this.checkFileType(e.target['response'])),
};
this.fileReaderBase64(e.target['response'], temp)
.then(res=>{
temp['url'] = res;
temp['icon'] = this.getIcon(temp['type']);
temp['response'] = e.target['response'];
resolve(temp);
})
.catch(res=>{
reject(res)
})
} else if (e.target['status'] === 404) {
console.error('error', e);
reject('您下载的文件不存在!');
} else {
console.error('error', e);
reject('网络错误!');
}
};
x.onerror = (e) => {
console.log('onerror')
console.log('error', e);
reject('网络错误!');
}
x.send();
})
}
/**
* @description 获取图片相应的图片
* @param type
* @returns {string}
*/
getIcon(type='') {
let temp = '';
if(iconData[type.toLocaleLowerCase()]) {
temp = iconData[type.toLocaleLowerCase()];
}
return temp;
}
/**
* @description 判断俩个需要处理的数字谁的小数点后位数多
* 以多的为准值乘以10的小数位的幂数相加以后再除以10的小数位的幂数
* @param currentNum
* @param targetNum
*/
checkFloatMore(currentNum, targetNum){
let sq1, sq2;
try {sq1 = currentNum.toString().split(".")[1].length;}
catch (e) {sq1 = 0;}
try {sq2 = targetNum.toString().split(".")[1].length;}
catch (e) {sq2 = 0;}
return Math.pow(10, Math.max(sq1, sq2));
}
/**
* @description 两个小数相加
* @param currentNum
* @param targetNum
* @return number
*/
addFloatNumber(currentNum, targetNum){
let power = this.checkFloatMore(currentNum, targetNum);
return (currentNum * power + targetNum * power) / power;
}
/**
* @description 两个小数减
* @param currentNum
* @param targetNum
* @return number
*/
cutFloatNumber(currentNum, targetNum) {
let power = this.checkFloatMore(currentNum, targetNum);
return (currentNum * power - targetNum * power) / power;
}
/**
* @description 计算两个小数相乘
* @param currentNum
* @param targetNum
* @returns {number}
*/
multiplyFloatNumber(currentNum, targetNum){
let m = 0, s1 = currentNum.toString(), s2 = targetNum.toString();
try {m += s1.split(".")[1].length;} catch (e) {}
try {m += s2.split(".")[1].length;} catch (e) {}
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
}
/**
* @description 计算两个小数相除
* @param currentNum
* @param targetNum
* @returns {number}
*/
divisionFloatNumber(currentNum, targetNum){
let t1 = 0, t2 = 0, r1, r2;
try {t1 = currentNum.toString().split(".")[1].length} catch (e) {}
try {t2 = targetNum.toString().split(".")[1].length} catch (e) {}
r1 = Number(currentNum.toString().replace(".", ""))
r2 = Number(targetNum.toString().replace(".", ""))
return (r1 / r2) * Math.pow(10, t2 - t1);
}
/**
* @description 创建node节点
* @param options {object}
* @param options.fid 文件ID唯一id必填
* @param options.name 文件名称选填
* @param options.url //文件地址(必填)
* @param Vue Vue类 必填
* @param view vue文件必填
*/
createElement(options, Vue, view){
console.log('PDF预览功能', options);
let str = `${options.name}${options['fid']}`;
let elId = `img-${this.md5Fn(str)}`;
let ele = document.getElementById(elId);
if (ele) {
ele.style.display = "block";
this.hideScroll(1);
return ele;
}
const View = Vue.extend(view);
let $view = new View({
el: document.createElement('div')
});
options['ele'] = elId;
$view.options = options;
$view.close = (id) => {
let ele = document.getElementById(id);
ele.style.display = "none";
this.hideScroll(-1);
};
document.body.appendChild($view.$el);
this.hideScroll(1);
}
}
export default new Helper();

17
xd-file-preview/public/static/index.html

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>文件预览功能</title>
</head>
<body>
<noscript>
<strong>We're sorry but xd-editer doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

17
xd-file-preview/public/static/index.txt

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>文件预览功能</title>
</head>
<body>
<noscript>
<strong>We're sorry but xd-editer doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

44
xd-file-preview/settings.js

@ -0,0 +1,44 @@
'use strict';
const settings = {
/**
* @type {string}
* @description 项目名称
*/
title: '',
/**
* @type {string}
* @description 系统版本
*/
version: 'v1.0.0',
/**
* @type {boolean}
* @description 是否关闭eslint语法检测
*/
isCloseEslint: false,
/**
* @type {boolean} true | false
* @description 开发环境与生产环境
*/
isDebug: false,
/**
* @type {boolean} true | false
* @description 是否加载模拟数据
*/
isTestData: false,
/**
* @type {number}
* @description 开发环境端口号
*/
port: 8057,
}
module.exports = settings;

182
xd-file-preview/src/App.vue

@ -0,0 +1,182 @@
<template>
<div id="app">
<h1>列表展示</h1>
<xd-file-list-preview :show-close="true" :list="list" @remove="handleRemoveClick" is-pagination></xd-file-list-preview>
<hr>
<h1>文件预览模式</h1>
<a @click="handleClick" style="color: #4285f4">9958ff80d202f91b347b14b5c56f14e811</a>
<h1>基于 Canvas 实现水印效果前端加水印</h1>
<!-- <a @click="handleClick2" style="color: #4285f4">9958ff80d202f91b347b14b5c56f14e811</a> -->
<div>
<img v-watermark="1+1" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6f9190aa805144858810da50b4aca13b~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp?" alt="上海鲜花港 - 郁金香">
</div>
</div>
</template>
<script>
import XdFileListPreview from "@/components/XdFileListPreview";
export default {
name: 'app',
components: { XdFileListPreview },
data() {
return {
list: [
{
url: 'http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf',
name: 'test.pdf',
fid: 'test.pdf',
download: false
},
{
url: 'https://jfb-public-images.oss-cn-qingdao.aliyuncs.com/admin-upload/202111081034429231.png?x-oss-process=style/common',
name: 'test.png',
fid: 'test.png',
download: false
},
{
url: 'https://jfb-public-images.oss-cn-qingdao.aliyuncs.com/uploads/20220407/7e11e47b25c1048edb6eb82c2445aa13.pconline.com.cn_images_upload_upc_tx_photoblog_1511_15_c4_15264230_15264230_1447554198988.jpg&refer=http___img.pconline.com.jpeg',
name: 'test.jpg',
fid: 'test.jpg',
download: false
},
{
url: 'http://static.e56buy.com/Xd9EfP1YFgdaYbI6H3RBiG6E3HAF6udj.PlayerAPI_v1.0.6.pdf',
name: 'Xd9EfP1YFgdaYbI6H3RBiG6E3HAF6udj.pdf',
fid: 'Xd9EfP1YFgdaYbI6H3RBiG6E3HAF6udj.pdf',
download: false
},
{
url: 'http://static.e56buy.com/XdgfsqR2INp7uFxTuLQtnMstYLY4K8rr.蛋糕缺少内容.docx',
name: 'Xd9EfP1YFgdaYbI6H3RBiG6E3HAF6udj.docx',
fid: 'Xd9EfP1YFgdaYbI6H3RBiG6E3HAF6udj.docx',
download: false
},
{
url: 'http://static.e56buy.com/XdU9Tmy2x6OGydRUQpWmThRaZKL1gkbd.ceshiyo.xlsx',
name: 'ka.xlsx',
fid: 'ka.xlsx',
download: false
},
{
url: 'https://sandbox-editx-website.oss-cn-qingdao.aliyuncs.com/sandbox-website-01.jufubao.cn/store/20221009153933/config/pages/pg_cn4THNcGvif-SDvN9A.json',
name: 'PlayerAPI_v1.0.6.json',
fid: 'PlayerAPI_v1.0.6.json',
download: true
},
{
url: 'http://localhost:8057/static/helper.js',
name: 'PlayerAPI_v1.0.6.js',
fid: 'PlayerAPI_v1.0.6.js',
download: false
},
{
url: 'http://localhost:8057/static/index.html',
name: 'PlayerAPI_v1.0.6.html',
fid: 'PlayerAPI_v1.0.6.html',
download: false
},
{
url: 'http://localhost:8057/static/index.txt',
name: 'PlayerAPI_v1.0.6.txt',
fid: 'PlayerAPI_v1.0.6.txt',
download: false
},
{
url: 'http://localhost:8057/static/helper.css',
name: 'PlayerAPI_v1.0.6.css',
fid: 'PlayerAPI_v1.0.6.css',
download: false
},
{
url: 'http://localhost:8057/static/999.ofd',
name: '999.ofd',
fid: '999.ofd',
download: false
},
]
}
},
created() {
},
methods: {
/**
* @description 删除图片事件
* @param item {Object} 当前被删除的文件对象
* @param done {function} 删除文件完成回调函数
*/
handleRemoveClick(item, done) {
setTimeout(() => {
console.log('handleRemoveClick', item);
done()
}, 100);
},
/**
* @description 点击查看预览功能
*/
handleClick() {
this.$preview({
//url: 'http://static.e56buy.com/XdU9Tmy2x6OGydRUQpWmThRaZKL1gkbd.ceshiyo.xlsx', // xlsx
//url: 'http://static.e56buy.com/XdgfsqR2INp7uFxTuLQtnMstYLY4K8rr..docx', //doc,
url: 'http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf',
fid: 'aadadads',
download: false
})
},
// handleClick1() {
// const dlWaterMark = new DlWaterMark({
// el: showImage, //
// width: 500, //
// height: 300, //
// toggleShow: false, //
// rotate: -25, //
// fillStyle: '#000', //
// fontSize: '20px', //
// context: '' //
// });
// dlWaterMark.draw('https://jfb-public-images.oss-cn-qingdao.aliyuncs.com/admin-upload/202111081034429231.png?x-oss-process=style/common')
// }
}
}
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: left;
color: #2c3e50;
margin-top: 60px;
}
/********* css 部分代码如下 ***********/
/* .water-mark {
display: inline-block;
overflow: hidden;
position: relative;
} */
/* .water-mark::after {
pointer-events: none;
position: absolute;
content: " ";
width: 100%;
height: 100%;
opacity: 0.2;
background-image: url("../assets/water-bg.png");
background-repeat: repeat;
} */
.water-mark {
display: inline-block;
overflow: hidden;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
pointer-events: none;
background-repeat: repeat;
}
</style>

BIN
xd-file-preview/src/assets/water-bg.png

After

Width: 45  |  Height: 80  |  Size: 1.4 KiB

250
xd-file-preview/src/components/XdFileListPreview.vue

@ -0,0 +1,250 @@
<template>
<div class="xd-file-list__body">
<div class="xd-file-list__item" v-if="item" v-for="(item,index) in dataList" :key="item.fid">
<div class="xd-file-list__item-icon">
<img v-if="item['icon']" :src="item['icon']" alt="icon">
<div v-else><i class="fileIconfont iconwenjian"></i></div>
</div>
<div class="xd-file-list__item-text">
<div class="xd-file-list__item-text-title" :title="getName(item)">{{getFileName(item)}}</div>
<div class="xd-file-list__item-text-link" @click="handleClick(item,index)" :style="`color: ${linkColor}`">点击查看</div>
</div>
<i class="fileIconfont iconyduicuowushixin" v-if="showClose" @click.stop="handleRemoveClick(item,index)"></i>
</div>
</div>
</template>
<script>
import helper from "@/components/preview/helper";
import {iconData} from "@/components/contact";
export default {
name: "XdFileListPreview",
props: {
list: {
type: Object | Array,
default() {
return []
}
},
showClose: {
type: Boolean,
default: false,
},
linkColor: {
type: String,
default: '#4285F4',
},
isPagination: {
type: Boolean,
default: false,
}
},
data() {
return {
dataList: [],
}
},
watch: {
list(val) {
this.setDataValue(val)
}
},
created() {
this.setDataValue(this.list);
},
methods: {
setDataValue(val) {
if (val.length > 0) {
//
let temp = [];
val.map((item, index) => {
if (item[status]) {
temp[index] = item;
} else {
helper.getFileBase64(item.url, item.name ? item.name : '')
.then(res => {
res['status'] = true;
temp[index] = Object.assign({source: item['url']}, item, res, );
console.log(temp[index])
})
.catch(res => {
temp[index] = {
src: iconData.loadicon,
status: false,
url: item['url'],
source: item['url'],
}
});
}
});
let timeer = setInterval(() => {
if (temp.length === val.length) {
this.dataList = temp;
this.$emit('change', this.dataList, 'add');
clearInterval(timeer);
console.log('setInterval', temp)
}
}, 50);
}
},
getName(item) {
if (!item.url) return '文件不存在';
if (item.name) {
return item.name;
}
let arr = item.url.split('/');
return arr[arr.length - 1];
},
getFileName(item) {
if (!item.url) return '文件不存在';
if (item.name) {
return helper.cutStringLen(item.name, 23);
}
let arr = item.url.split('/');
return helper.cutStringLen(arr[arr.length - 1], 23);
},
/**
* @description 启动自动翻页功能
* @param index
*/
callback(index){
return (status)=>{
let idx = 0;
if (status === 'prev') {
idx = (index - 1) < 0 ? (this.dataList.length -1) : (index - 1);
this.handleClick(this.dataList[idx], idx)
}
if (status === 'next') {
idx = (index + 1) === this.dataList.length ? 0 :(index + 1);
this.handleClick(this.dataList[idx], idx)
}
}
},
handleClick(item,index) {
let calback = null;
if(this.isPagination) {
calback = this.callback(index);
}
this.$preview(item, calback)
},
handleRemoveClick(item, index) {
this.$emit('remove', item, () => {
this.dataList.splice(index, 1);
this.$emit('change', this.dataList,'remove');
});
}
}
}
</script>
<style scoped lang="less">
@name: xd-file-list;
.@{name} {
&__body {
padding-top: 20px;
color: #333;
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: wrap;
}
&__item {
position: relative;
padding: 10px;
box-sizing: border-box;
height: 68px;
background: #f6f7fa;
border: 1px solid #cacad1;
border-radius: 5px;
width: 294px;
margin: 0 20px 20px 0;
display: flex;
justify-content: center;
align-items: center;
align-content: flex-start;
text-align: left;
flex-wrap: nowrap;
&-icon {
width: 50px;
height: 50px;
margin-right: 10px;
background: #fff;
img {
width: 100%;
height: 100%;
}
div {
width: 50px;
height: 50px;
text-align: center;
vertical-align: middle;
}
i {
margin-top: 3px;
font-size: 40px !important;
width: 100%;
height: 100%;
display: inline-block;
}
}
&-text {
flex: 1;
&-title {
}
&-link {
height: 20px;
font-size: 14px;
font-family: PingFangSC, PingFangSC-Regular;
color: #4285f4;
line-height: 20px;
cursor: pointer;
}
&-link:hover {
text-decoration: underline;
}
}
& i.iconyduicuowushixin {
cursor: pointer;
display: none;
position: absolute;
top: 2px;
right: -7px;
width: 30px;
height: 30px;
font-size: 21px;
color: #999;
}
&:hover i.iconyduicuowushixin {
display: block;
}
}
}
</style>

60
xd-file-preview/src/components/XdLoadPdfScript.vue

@ -0,0 +1,60 @@
<template></template>
<script>
export default {
name: 'XdLoadPdfScript',
props: {
pdf: {
type: String,
default: 'https://cdn.bootcdn.net/ajax/libs/pdf.js/2.0.288/pdf.min.js',
},
worker: {
type: String,
default: 'https://cdn.bootcdn.net/ajax/libs/pdf.js/2.0.288/pdf.worker.min.js',
}
},
watch:{
pdfStatus(val) {
if (val && this.workerStatus) {
this.$emit('load')
}
},
workerStatus(val) {
if (val && this.pdfStatus) {
this.$emit('load')
}
}
},
data(){
return {
pdfStatus : false,
workerStatus: false,
}
},
created() {
this.$loadGetApi();
this.$loadGetscript();
},
methods:{
$loadGetApi() {
const s = document.createElement('script');
s.type = 'text/javascript';
s.src = this.pdf;
document.body.appendChild(s);
s.onload = () => {
this.pdfStatus = true;
}
},
$loadGetscript() {
const s = document.createElement('script');
s.type = 'text/javascript';
s.src = this.worker;
document.body.appendChild(s);
s.onload = () => {
this.workerStatus = true;
}
},
}
}
</script>

131
xd-file-preview/src/components/XdPdf.vue

@ -0,0 +1,131 @@
<template>
<div>
<div ref="parent"></div>
<xd-load-pdf-script :pdf="$xdOptions['pdf']" :worker="$xdOptions['worker']" @load="handleLoad"></xd-load-pdf-script>
</div>
</template>
<script>
import XdLoadPdfScript from "./XdLoadPdfScript";
export default {
name: 'XdPdf',
components: {XdLoadPdfScript},
props: {
fileUrl: {
type: Blob,
default: ''
},
currentPages: {
type:Number,
default: 1,
}
},
data() {
return {
pdfFile: null, //pdf
context: null, //canvas
}
},
watch:{
currentPages(val){
this.openPage(val);
},
},
created(){
console.log('created', this.$xdOptions);
},
methods: {
/**
* @description pdf workerjs加载完毕事件
*/
handleLoad(){
console.log('handleLoad')
this.createCanvas();
this.fileReader(this.fileUrl)
.then(pdfArrayBuffer => {
this.renderFile(pdfArrayBuffer);
this.$emit('onLoad', pdfArrayBuffer);
})
.catch();
},
/**
* @description 创建Canvas节点
*/
createCanvas(){
let canvas = document.createElement('canvas');
if (this.$refs.parent) {
this.$refs.parent.appendChild(canvas)
}
this.context = canvas.getContext('2d')
},
/**
* @description 文件流转化 Blob to ArrayBuffer
* @param responseBlob {Blob}
* @returns {Promise<unknown>}
*/
fileReader(responseBlob){
return new Promise((resolve, reject)=>{
const reader = new FileReader();
reader.onload = (e) =>{
console.log('fileReader', e.target['result']);
let pdfArrayBuffer = new Uint8Array(e.target['result']);
resolve(pdfArrayBuffer);
};
reader.readAsArrayBuffer(responseBlob);
})
},
/***
* @description 生产pdf对象
* @param pdfArrayBuffer
*/
renderFile(pdfArrayBuffer) {
console.log('renderFile',pdfArrayBuffer);
if(PDFJS !== undefined) {
//
PDFJS.cMapUrl = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.0.288/cmaps/';
PDFJS.cMapPacked = true;
PDFJS['getDocument'](pdfArrayBuffer).then(pdfFile => {
console.log('getDocument', pdfFile);
if (pdfFile) {
this.pdfFile = pdfFile;
let pageNum = this.pdfFile.numPages;
this.$emit('num-pages', pageNum);
this.openPage(this.currentPages);
}
})
}
},
openPage(pageNumber) {
let scale = 8;
this.pdfFile.getPage(pageNumber).then(page => {
let viewport = page.getViewport(scale);
let canvas = this.context.canvas;
canvas.width = viewport.width;
canvas.height = viewport.height;
canvas.style.width = '100%';
canvas.style.height = '100%';
let renderContext = {
canvasContext: this.context,
viewport: viewport
}
page.render(renderContext).then(() => {
//
this.$emit('onRender')
})
})
}
}
}
</script>
<style>
</style>

48
xd-file-preview/src/components/contact.js
File diff suppressed because it is too large
View File

350
xd-file-preview/src/components/preview/helper.js

@ -0,0 +1,350 @@
'use strict';
const MD5 = require('md5.js');
import {iconData, textType, typeHeader} from "./../contact";
class Helper {
hideScroll(type){
let body = document.getElementsByTagName('body')[0];
if (type === 1) {
body.style.overflowY = 'hidden';
body.style.height = '100%';
} else {
body.style.overflowY = '';
body.style.height = '';
}
};
/**
* @description 字符串截取
* @param val
* @param len
*/
cutStringLen(val, len = 10) {
let fix = '...';
let newLength = 0;
let newStr = "";
let chineseRegex = /[^\x00-\xff]/g;
let singleChar = "";
let strLength = val.replace(chineseRegex, "**").length;
for (let i = 0; i < strLength; i++) {
singleChar = val.charAt(i).toString();
if (singleChar.match(chineseRegex) != null) {
newLength += 2;
} else {
newLength++;
}
if (newLength > len) {
break;
}
newStr += singleChar;
}
if (strLength > len) {
newStr += fix;
}
return newStr;
}
checkVarType(obj) {
let toString = Object.prototype.toString;
let map = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regExp',
'[object Undefined]': 'undefined',
'[object Null]': 'null',
'[object Object]': 'object',
'[object Blob]' : 'blob'
};
return map[toString.call(obj)];
}
getID() {
return this.random(1111111, 9999999);
}
md5Fn(str, hex = 'hex'){
return new MD5()['update'](str)['digest'](hex);
}
random(min, max) {
let Range = max - min;
let Rand = Math.random();
return (min + Math.round(Rand * Range));
}
parseURL(url) {
if (!url) {
url = window.location.href;
}
let a = document.createElement('a');
a.href = url;
return {
source: url,
protocol: a.protocol.replace(':', ''),
host: a.hostname,
port: a.port,
query: a.search,
params: (function() {
let ret = {},
seg = a.search.replace(/^\?/, '').split('&'),
len = seg.length, i = 0, s;
for (; i < len; i++) {
if (!seg[i]) {
continue;
}
s = seg[i].split('=');
if (s[1]) {
ret[s[0]] = s[1];
}
}
return ret;
})(),
file: (a.pathname.match(/\/([^\/?#]+)$/i) || [, ''])[1],
hash: a.hash.replace('#', ''),
path: a.pathname.replace(/^([^\/])/, '/$1'),
relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [, ''])[1],
segments: a.pathname.replace(/^\//, '').split('/')
};
}
/***
* @description 获取文件类型
* @param blob
*/
checkFileType(blob){
let type = typeHeader['unkown'];
if(typeHeader[blob.type]) {
type = typeHeader[blob.type];
}
return type;
}
checkType(arr, item) {
let temp = arr.filter(it => {
return it === item;
});
return temp.length > 0;
}
/**
* @description 获取文件base64流
* @param blob
* @param file
* @returns {Promise<unknown>}
*/
fileReaderBase64(blob, file){
return new Promise((resolve, reject)=>{
let reader = new FileReader();
reader.onload = (e)=> {
if(file.type === 'JSON') {
resolve(JSON.parse(e.target['result']));
}
else {
resolve(e.target['result']);
}
};
reader.onerror = ()=>{
reject('读取文件错误')
};
if (file.type === 'JSON'
|| this.checkType(textType, file.type)
) reader.readAsText(blob, 'utf-8');
else reader.readAsDataURL(blob);
});
}
/**
* @description 获取文件相关信息
* @param url
* @param name
* @returns {Promise<unknown>}
*/
getFileBase64(url='',name = '') {
return new Promise((resolve, reject) => {
let getName = (type)=> {
let str = url;
if(str.indexOf('?')) str = url.split('?')[0];
let arr = str.split('/');
return arr[arr.length - 1];
};
let x = new XMLHttpRequest();
x.open("GET", url, true);
x.responseType = "blob";
x.onload = (e) => {
console.log('XMLHttpRequest',e);
if (e.target['status'] === 200) {
//console.log('this.checkFileType(e.target[\'response\'])',url,this.checkFileType(e.target['response']));
let temp = {
type: this.checkFileType(e.target['response']),
size: e.target['response']['size'],
name: name ? name: getName(this.checkFileType(e.target['response'])),
};
this.fileReaderBase64(e.target['response'], temp)
.then(res=>{
temp['url'] = res;
temp['icon'] = this.getIcon(temp['type']);
temp['response'] = e.target['response'];
resolve(temp);
})
.catch(res=>{
reject(res)
})
} else if (e.target['status'] === 404) {
console.error('error', e);
reject('您下载的文件不存在!');
} else {
console.error('error', e);
reject('网络错误!');
}
};
x.onerror = (e) => {
console.log('onerror')
console.log('error', e);
reject('网络错误!');
}
x.send();
})
}
/**
* @description 获取图片相应的图片
* @param type
* @returns {string}
*/
getIcon(type='') {
let temp = '';
if(this.checkType(textType, type)) {
type = 'text';
}
if(iconData[type.toLocaleLowerCase()]) {
temp = iconData[type.toLocaleLowerCase()];
}
return temp;
}
/**
* @description 判断俩个需要处理的数字谁的小数点后位数多
* 以多的为准值乘以10的小数位的幂数相加以后再除以10的小数位的幂数
* @param currentNum
* @param targetNum
*/
checkFloatMore(currentNum, targetNum){
let sq1, sq2;
try {sq1 = currentNum.toString().split(".")[1].length;}
catch (e) {sq1 = 0;}
try {sq2 = targetNum.toString().split(".")[1].length;}
catch (e) {sq2 = 0;}
return Math.pow(10, Math.max(sq1, sq2));
}
/**
* @description 两个小数相加
* @param currentNum
* @param targetNum
* @return number
*/
addFloatNumber(currentNum, targetNum){
let power = this.checkFloatMore(currentNum, targetNum);
return (currentNum * power + targetNum * power) / power;
}
/**
* @description 两个小数减
* @param currentNum
* @param targetNum
* @return number
*/
cutFloatNumber(currentNum, targetNum) {
let power = this.checkFloatMore(currentNum, targetNum);
return (currentNum * power - targetNum * power) / power;
}
/**
* @description 计算两个小数相乘
* @param currentNum
* @param targetNum
* @returns {number}
*/
multiplyFloatNumber(currentNum, targetNum){
let m = 0, s1 = currentNum.toString(), s2 = targetNum.toString();
try {m += s1.split(".")[1].length;} catch (e) {}
try {m += s2.split(".")[1].length;} catch (e) {}
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
}
/**
* @description 计算两个小数相除
* @param currentNum
* @param targetNum
* @returns {number}
*/
divisionFloatNumber(currentNum, targetNum){
let t1 = 0, t2 = 0, r1, r2;
try {t1 = currentNum.toString().split(".")[1].length} catch (e) {}
try {t2 = targetNum.toString().split(".")[1].length} catch (e) {}
r1 = Number(currentNum.toString().replace(".", ""))
r2 = Number(targetNum.toString().replace(".", ""))
return (r1 / r2) * Math.pow(10, t2 - t1);
}
/**
* @description 创建node节点
* @param options {object}
* @param options.fid 文件ID唯一id必填
* @param options.name 文件名称选填
* @param options.url //文件地址(必填)
* @param Vue Vue类 必填
* @param view vue文件必填
*/
createElement(options, Vue, view){
console.log('PDF预览功能', options);
let str = `${options.name}${options['fid']}`;
let elId = `img-${this.md5Fn(str)}`;
let ele = document.getElementById(elId);
if (ele) {
ele.style.display = "block";
this.hideScroll(1);
if(typeof options['callback'] === 'function') {
options['callback']('show')
}
return ele;
}
const View = Vue.extend(view);
let $view = new View({
el: document.createElement('div')
});
options['ele'] = elId;
$view.options = options;
$view.close = (id) => {
let ele = document.getElementById(id);
ele.style.display = "none";
this.hideScroll(-1);
if (typeof options['callback'] === 'function') {
options['callback']('close')
}
};
document.body.appendChild($view.$el);
this.hideScroll(1);
if (typeof options['callback'] === 'function') {
options['callback']('show')
}
}
}
export default new Helper();

18
xd-file-preview/src/components/preview/image.js

@ -0,0 +1,18 @@
'use strict';
import imgPreview from "./imageView";
import helper from "./helper";
/**
* @description 图片预览功能
* @param options {object}
* @param options.fid 文件ID唯一id必填
* @param options.name 文件名称
* @param options.type 文件类型
* @param options.url //图片地址
* @param $vue Vue
*/
export default function (options, $vue) {
console.log('111,PDF预览功能');
helper.createElement(options, $vue, imgPreview);
}

554
xd-file-preview/src/components/preview/imageView.vue

@ -0,0 +1,554 @@
<template>
<div class="img-preview" :id="options.ele" v-if="info && options && options.ele">
<div class="img-preview-title" ref="imgPreviewHeader">
<div class="img-preview-l" v-if="options.status">
<img :src="options.icon" width="30" height="30">
<span>{{info.name|getFileName(options['type'].toLocaleLowerCase())}}</span>
<button v-if="isDownLoad" class="btn" @click="download">下载</button>
</div>
<div class="img-preview-close" @click="closeHandle(options.ele)"><i class="fileIconfont iconwrong"></i></div>
</div>
<div class="img-preview-content">
<div v-show="options.status" class="img-preview-content-box" @click="closeHandle(options.ele)"
:style="`top:${top}px;left: ${left}px`">
<img
@mousedown="interHandle"
@mousemove="moveHandle"
@mouseup="leaveHandle"
@mouseleave="leaveHandle"
@mouseout="leaveHandle"
@click.stop="()=> {return false}"
ref="imgPreview"
:style="`cursor: ${pointerHandle()}; height: ${height}px; width:${width}px;transform:scale(${scale}) translate(${translatex}px,${translatey}px) rotate(${angle}deg)`"
@load="loadhandle()"
@error="errorHandle"
:src="options.url" alt="preview"
/>
</div>
<div class="img-preview-scale-percentage" v-if="showScale"><span>{{scalePercentage}}%</span></div>
<div class="img-preview-error" v-if="!options.status" @click="closeHandle(options.ele)">
<div class="img-preview-error-content">
<div class="img-preview-error-ico"><i class="iconfont iconwenjian"></i></div>
<div class="img-preview-error-tips">哎哟我们无法加载该文件</div>
<div class="img-preview-error-url" v-html="options.url"></div>
</div>
</div>
</div>
<div class="img-preview-toolbar" v-if="options.status && isShowToolbar">
<span>
<i class="fileIconfont iconfangda" @click="zoomHandle(1)"></i>
<i class="fileIconfont iconsuoxiao" @click="zoomHandle(-1)"></i>
<i class="fileIconfont iconzuozhuan" @click="rotateHandle(-1)"></i>
<i class="fileIconfont iconyouzhuan" @click="rotateHandle(1)"></i>
<i class="fileIconfont iconhuanyuan" @click="zoomOriginalSize()"></i>
</span>
</div>
<div class="img-preview-bottom"></div>
<div class="change change-prev" v-if="isBtn" @click="handeCilck('prev')">
<i class="fileIconfont iconpageup"></i>
</div>
<div class="change change-next" v-if="isBtn" @click="handeCilck('next')">
<i class="fileIconfont iconpagedown"></i>
</div>
</div>
</template>
<script>
import lodash from "lodash";
import {iconData} from './../contact'
import download from 'downloadjs';
window.timeer = null;
window.zoomTimeer = null;
window.boxyEleStop = () => {
return false;
};
export default {
name: "img-preview",
props: {
options: Object,
},
watch: {
options(val) {
this.info = val;
if(val.download === false) this.isDownLoad = val.download;
console.log('options',val)
}
},
filters: {
getFileName(name, type) {
console.log('getFileName',name, type)
if (name.indexOf(`.${type}`) === -1) {
return `${name}.${type}`
}
return name;
}
},
computed:{
isBtn() {
let isBtn = false;
if (this.info && this.info.callback && typeof this.info.callback === 'function') isBtn = true;
return isBtn
}
},
data() {
return {
/**image**/
showScale: false,
bodyEle: null,
eleStop: null,
isShowToolbar: false,
scalePercentage: 100,
scaleZoomTime: 500,
isError: false,
show: false,
top: -9999,
left: -9999,
width: 0,
height: 0,
angle: 0,
scale: 1,
moveStatus: false,
translatex: 0,
translatey: 0,
offsetX: 0,
offsetY: 0,
scaleLen: 0.2,
scaleMax: 4, /**最大放大4倍**/
scaleMin: 0.2, /**最小缩小0.2倍**/
originalWidth: 0,
originalHeight: 0,
windowWidth: 0,
windowHeight: 0,
loadingEle: '',
loadingUrl: iconData.loadicon,
/**image**/
info: null,
isDownLoad: true,
}
},
mounted() {
this.windowWidth = document.body.clientWidth;
this.windowHeight = window.innerHeight - 60;
let that = this;
this.$nextTick(() => {
window.onresize = lodash.debounce(() => {
that.windowWidth = window.innerWidth;
//console.log(that.$refs.imgPreviewHeader, that.$refs.imgPreviewHeader.clientHeight);
that.windowHeight = window.innerHeight - that.$refs.imgPreviewHeader.clientHeight;
that.loadhandle();
}, 400);
});
/**屏蔽图片拖拽功能**/
window.bodyEle = document.getElementsByTagName('body')[0];
window.bodyEle.ondragstart = () => {
return false;
}
window.bodyEle.onselect = () => {
return false;
}
//window.bodyEle.addEventListener('dragstart', window.boxyEleStop,false);
//window.bodyEle.addEventListener('select', window.boxyEleStop, false);
},
methods: {
handeCilck(status) {
if (this.info && this.info.callback && typeof this.info.callback === 'function') {
this.closeHandle(this.info.ele);
this.info.callback(status)
}
},
download() {
console.log(this.info)
download(this.info['response'], this.info['name'])
},
loadhandle() {
let img = this.$refs.imgPreview;
this.originalWidth = img.naturalWidth;
this.originalHeight = img.naturalHeight;
/**
* imgw <= ww and imgh <= wh
*/
if (this.originalWidth <= this.windowWidth && this.originalHeight <= this.windowHeight) {
this.width = this.originalWidth;
this.height = this.originalHeight;
}
// else if(this.originalWidth > this.windowWidth && this.originalHeight < this.windowHeight){
// this.width = this.windowWidth;
// this.height = this.width * this.originalHeight/ this.originalWidth;
// }else if(this.originalHeight > this.windowHeight && this.originalWidth < this.windowWidth){
// this.height = this.windowHeight;
// this.width = this.height * this.originalWidth / this.originalHeight;
// }
/**
* imgh > wh and imgw > ww
* 实际高度/窗口高度的倍数 实际宽度/窗口宽度的倍数 比较 谁的倍数越大 就使用谁为基准参数
*/
else {
let wb = this.originalWidth / this.windowWidth;
let Hb = this.originalHeight / this.windowHeight;
console.log('aaa', wb, Hb);
if (wb > Hb) {
this.width = this.windowWidth;
this.height = this.width * this.originalHeight / this.originalWidth;
} else {
this.height = this.windowHeight;
this.width = this.height * this.originalWidth / this.originalHeight;
}
}
console.log(this.width, this.height, this.originalWidth, this.originalHeight);
this.top = 0;
this.left = 0;
this.show = true;
this.isShowToolbar = true;
},
zoomHandle(type) {
let scale = this.scale + this.scaleLen * type;
if (scale < this.scaleMin) scale = this.scaleMin;
if (scale > this.scaleMax) scale = this.scaleMax;
this.scale = scale;
this.showScale = true;
this.scalePercentage = parseInt(this.scale * 100);
if (window.zoomTimeer) {
clearTimeout(window.zoomTimeer);
}
window.zoomTimeer = setTimeout(() => {
this.showScale = false;
}, this.scaleZoomTime);
},
zoomOriginalSize() {
/**原始尺寸不处理**/
// if (this.scale === 1) {
// return
// }
this.scale = 1;
this.angle = 0;
this.translatex = 0;
this.translatey = 0;
this.showScale = true;
this.scalePercentage = parseInt(this.scale * 100);
if (window.zoomTimeer) {
clearTimeout(window.zoomTimeer);
}
window.zoomTimeer = setTimeout(() => {
this.showScale = false;
}, this.scaleZoomTime);
},
rotateHandle(type) {
this.angle = this.angle + 90 * type;
if (this.angle % 180 === 0) {
this.loadhandle(1)
} else {
this.loadhandle(-1)
}
},
pointerHandle() {
let img = this.$refs.imgPreview;
/**旋转状态不支持拖拽**/
if (this.angle !== 0) {
return 'default';
}
if (!img) return 'default';
if (this.width * this.scale > this.windowWidth
|| this.height * this.scale > this.windowHeight) {
return 'pointer';
} else {
return "default"
}
},
moveHandle($vet) {
if (this.pointerHandle() === 'default' || !this.moveStatus) return;
this.translatex = $vet.offsetX - this.offsetX;
this.translatey = $vet.offsetY - this.offsetY;
},
interHandle($vet) {
if (this.pointerHandle() === 'default') return;
this.offsetX = $vet.offsetX - this.translatex;
this.offsetY = $vet.offsetY - this.translatey;
this.moveStatus = true;
},
leaveHandle($vet) {
if (this.pointerHandle() === 'default') return;
this.moveStatus = false;
},
errorHandle($vet) {
console.log('errorHandle=>', $vet);
this.isError = true;
this.show = true
},
closeHandle(id) {
//window.bodyEle.removeEventListener('dragstart', window.boxyEleStop);
//window.bodyEle.removeEventListener('select', window.boxyEleStop);
this.close(id);
},
},
};
</script>
<style type="text/css">
@import "style.css";
.img-preview {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
min-width: 1000px;
min-height: 300px;
background: rgba(0, 0, 0, 0.5);
z-index: 2098;
}
.img-preview-title {
width: 100%;
height: 48px;
background: #333;
color: #fff;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
}
.img-preview-l img {
margin-right: 15px;
}
.img-preview-l {
float: left;
height: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
padding-left:10px;
}
.img-preview-l .btn {
background: #4395ff;
margin-left: 20px;
font-size: 14px;
height: 30px;
max-height: 30px;
border-radius: 15px;
padding: 0 20px;
outline: none;
border: 1px solid #4395ff;
color: #fff;
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
transition: all .5s;
}
.img-preview-l .btn:hover {
box-shadow: 0 0 10px rgba(0, 0, 0, .3);
opacity: 0.9;
background: #539dfc;
cursor: pointer;
}
.img-preview-l .icontupian {
font-size: 30px;
margin-right: 10px;
}
.img-preview-bottom {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 0;
background: #333;
}
.img-preview-close {
cursor: pointer;
float: right;
font-size: 30px;
width: 30px;
height: 30px;
margin: 9px 7px 0 0;
line-height: 30px;
}
i.fileIconfont {
font-size: 30px;
font-weight: normal;
}
i.fileIconfont.iconzuozhuan, i.fileIconfont.iconyouzhuan, i.fileIconfont.iconhuanyuan {
font-size: 28px;
font-weight: normal !important;
}
.img-preview-toolbar {
position: absolute;
font-size: 0;
left: 50%;
bottom: 30px;
text-align: center;
transform: translateX(-50%);
z-index: 10000
}
.img-preview-toolbar span {
display: flex;
line-height: 24px;
border-radius: 8px;
padding: 8px 10px;
font-size: 0;
background: rgba(0, 0, 0, 0.5);
}
.img-preview-toolbar span * {
margin: 0 6px;
color: #fff;
font-size: 30px;
cursor: pointer;
font-weight: 400;
}
.img-preview-scale-percentage {
display: flex;
justify-content: center;
align-items: center;
align-content: center;
flex-wrap: nowrap;
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
}
.img-preview-scale-percentage span {
width: 80px;
height: 30px;
line-height: 30px;
background: rgba(0, 0, 0, 0.6);
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
color: #fff;
text-align: center;
margin-right: 5px;
}
.img-preview-content {
height: -moz-calc(100% - 60px);
height: -webkit-calc(100% - 60px);
height: calc(100% - 60px);
overflow: hidden;
position: relative;
}
.img-preview-content-box {
display: flex;
justify-content: center;
align-items: center;
align-content: center;
flex-wrap: nowrap;
height: 100%;
width: 100%;
position: absolute;
top: -9999px;
left: -9999px;
}
.img-preview-content-box img {
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}
.img-preview-mask {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
display: flex;
justify-content: center;
align-items: center;
flex-direction: row;
flex-wrap: nowrap;
}
.img-preview-mask span {
width: 80px;
height: 80px;
padding: 10px;
margin-right: 5px;
border-radius: 10px;
background: #171717;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}
.img-preview-mask img {
width: 100%;
height: 100%;
}
.img-preview-error {
display: flex;
justify-content: center;
align-items: center;
align-content: center;
flex-wrap: nowrap;
height: 100%;
width: 100%;
}
.img-preview-error-content {
width: 540px;
background: #000;
border-radius: 10px;
padding: 30px;
color: #fff;
text-align: center;
}
i.fileIconfont.iconwenjian {
font-size: 60px;
font-weight: normal;
}
.img-preview-error-tips {
font-size: 18px;
line-height: 30px;
padding: 10px 0 30px
}
.img-preview-error-url {
font-size: 13px;
line-height: 24px;
word-wrap: break-word;
}
.ant-tooltip.ant-tooltip-placement-top {
z-index: 1000;
}
</style>

143
xd-file-preview/src/components/preview/index.js

@ -0,0 +1,143 @@
'use strict';
import image from "./image";
import office from "./office";
import pdf from "./pdf";
import json from "./json";
import text from "./text";
import helper from "./helper";
import ofd from "./ofd";
import {iconData, imagesType , wordType, pdfType, jsonType, textType, ofdType} from './../contact';
import Loading from "./loading";
/**
* @description 检查文件类型
* @param arr {Array}
* @param item
*/
function checkType(arr, item ) {
let temp = arr.filter(it =>{
return it === item;
});
return temp.length > 0;
}
function check(options, $vue) {
console.log('options', options['type'], JSON.stringify(wordType))
//错误图片
if (!options.status) {
image(options, $vue);
return;
}
//图片类型
if (checkType(imagesType, options['type'])) {
console.log('image');
image(options, $vue);
}
//word文件类型
if (checkType(wordType, options['type'])) {
console.log('office');
office(options, $vue);
}
//Pdf文件类型
if (checkType(pdfType, options['type'])) {
console.log('pdf');
pdf(options, $vue);
}
//TEXT文件类型
if (checkType(textType, options['type'])) {
console.log('text');
text(options, $vue);
}
//JSON文件类型
if (checkType(jsonType, options['type'])) {
console.log('json');
json(options, $vue);
}
//OFD文件类型
if (checkType(ofdType, options['type'])) {
console.log('ofd');
ofd(options, $vue);
}
}
let __File_Save = {};
/**
* @description 文件预览功能
* @param options {object}
* @param options.fid 文件ID唯一id必填
* @param options.name 文件名称选填
* @param options.url //文件地址(必填)
* @param $vue
*/
export function preview(options={}, $vue) {
//已经加载过的文件
if(options['response'] && helper.checkVarType(options['response']) === 'blob') {
check(options, $vue);
return;
}
let keyMd5 = helper.md5Fn(`${options.url}${options.fid}`);
if(__File_Save[keyMd5]) {
check(__File_Save[keyMd5], $vue);
return
}
/**
* @description 创建loading实例
* @type {ExtendedVue<Vue, unknown, unknown, unknown, Record<never, any>>}
*/
const loadingVue = $vue.extend(Loading);
let $loading = new loadingVue({
el: document.createElement('div'),
props: {
show: {
type: Boolean,
default: true
}
}
});
document.body.appendChild($loading.$el);
/**
* @description 销毁loading实例
*/
const $destroy = () => {
$loading.$destroy();
$loading.$el.parentNode.removeChild($loading.$el);
};
let t = new Date().getTime();
options['src'] = iconData.loadicon;
options['source'] = options['url'];
helper.getFileBase64(options.url, options.name)
.then(res=>{
let now = new Date().getTime();
let deTime = 1000 - (now - t);
setTimeout(()=>{
$destroy();
options = Object.assign({}, options, res);
options['status'] = true;
__File_Save[keyMd5] = options;
check(options, $vue);
}, (deTime <=10? 10: deTime));
})
.catch(res=>{
console.error('res',options, res);
options['status'] = false;
$destroy();
__File_Save[keyMd5] = options;
console.log(options);
image(options, $vue);
});
}

17
xd-file-preview/src/components/preview/json.js

@ -0,0 +1,17 @@
'use strict';
import jsonView from "./jsonView";
import helper from "./helper";
/**
* @description PDF预览功能
* @param options {object}
* @param options.fid 文件ID唯一id必填
* @param options.name 文件名称
* @param options.type 文件类型
* @param options.url // PDF地址
* @param $vue Vue
*/
export default function (options, $vue) {
helper.createElement(options, $vue, jsonView);
}

309
xd-file-preview/src/components/preview/jsonView.vue

@ -0,0 +1,309 @@
<template>
<div class="pdf-preview" :id="options.ele" v-if="info">
<div class="pdf-preview__title">
<div class="pdf-preview__title-text">
<img :src="options.icon" height="30" width="30">
<input class="pdf-preview__input" v-model.number="page" type="number">
<span>/ {{numPages}}</span>
<button class="btn" v-if="isDownLoad" @click="download">下载</button>
<span>{{info.name|getFileName(options['type'].toLocaleLowerCase())}}</span>
</div>
<div class="pdf-preview__title-close" @click="closeHandle(options.ele)"><i class="fileIconfont iconwrong"></i></div>
</div>
<div class="pdf-preview__content" :style="`width: ${getWdith}`">
<json-viewer
:value="info.url"
:expand-depth='2'
>
</json-viewer>
</div>
<div class="img-preview__bottom"></div>
<div class="change change-prev" v-if="isBtn" @click="handeCilck('prev')">
<i class="fileIconfont iconpageup"></i>
</div>
<div class="change change-next" v-if="isBtn" @click="handeCilck('next')">
<i class="fileIconfont iconpagedown"></i>
</div>
</div>
</template>
<script>
import helper from "./helper";
import download from 'downloadjs';
import JsonViewer from 'vue-json-viewer'
export default {
name: "xdPdfPreview",
props: {
options: {
type: Object|null,
default(){
return null
}
},
},
components: {
JsonViewer
},
data() {
return {
src: '',
loadedRatio: 0,
page: 1,
numPages: 0,
rotate: 0,
width: 0.5,
info: null,
isDownLoad: true,
}
},
filters:{
getFileName(name, type){
if(name.indexOf(`.${type}`) === -1) {
return `${name}.${type}`
}
return name;
}
},
watch:{
options(val){
this.info = val;
if (val.download === false) this.isDownLoad = val.download;
if(this.info['url']) {
this.src= this.info['url']
}
},
},
created(){
this.info = this.options;
if (this.options && this.options.download === false) this.isDownLoad = this.options.download;
},
computed:{
getWdith() {
return (this.width * 100) + '%';
},
isBtn() {
let isBtn = false;
if (this.info && this.info.callback && typeof this.info.callback === 'function') isBtn = true;
return isBtn
}
},
methods: {
handeCilck(status) {
if (this.info && this.info.callback && typeof this.info.callback === 'function') {
this.closeHandle(this.info.ele);
this.info.callback(status)
}
},
/**
* @description 设置总页数
*/
setTotalNumPages(num){
this.numPages = num;
},
closeHandle(id) {
this.close(id);
},
download(){
download(this.info['response'], this.info['name'])
},
/**
* @description 翻页处理
* @param type
*/
overPage(type){
if (this.page + type === 0) {
this.page = 1;
}
else if(this.page + type >= this.numPages){
this.page = this.numPages;
}
else{
this.page = this.page + type;
}
},
zoomOriginalSize(){
this.width = 0.5
},
/**
* @description 放大页面30-100之间
* @param type
*/
zoomHandle(type){
if(type === 1) {
if (helper.addFloatNumber(this.width, 0.1) < 0.3) {
this.width = 0.3;
} else if (helper.addFloatNumber(this.width, 0.1) >= 1) {
this.width = 0.98;
} else {
this.width = helper.addFloatNumber(this.width, 0.1);
}
}else{
if (helper.cutFloatNumber(this.width, 0.1) < 0.3) {
this.width = 0.3;
} else if (helper.cutFloatNumber(this.width, 0.1) >= 1) {
this.width = 0.98;
} else {
this.width = helper.cutFloatNumber(this.width, 0.1);
}
}
},
password: function (updatePassword, reason) {
updatePassword(prompt('password is "test"'));
},
error: function (err) {
console.log(err);
}
}
};
</script>
<style type="text/css">
@import "style.css";
.pdf-preview i.fileIconfont {
font-size: 30px;
font-weight: normal;
}
.pdf-preview {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
min-width: 1000px;
min-height: 300px;
background: rgba(0, 0, 0, .6);
z-index: 10000;
padding-top: 48px;
box-sizing: border-box;
}
.pdf-preview__title {
position: absolute;
background: #333;
top: 0;
right: 0;
left:0;
width: 100%;
height: 48px;
padding: 0 0 0 10px;
box-sizing: border-box;
color: #fff;
z-index: 10;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
}
.pdf-preview__title-close {
cursor: pointer;
font-size: 18px;
height: 40px;
width: 40px;
line-height: 40px;
}
.pdf-preview__content {
height: -moz-calc(100% - 55px);
height: -webkit-calc(100% - 55px);
height: calc(100% - 55px);
height: 100%;
overflow-x: hidden;
overflow-y: auto;
position: relative;
z-index: 9;
margin: 0 auto;
}
.pdf-preview__content .jv-container {
min-height: 100% !important;
}
.img-preview__toolbar {
position: absolute;
font-size: 0;
left: 50%;
bottom: 30px;
text-align: center;
transform: translateX(-50%);
z-index: 10000
}
.img-preview__toolbar span {
display: flex;
line-height: 24px;
border-radius: 8px;
padding: 8px 10px;
font-size: 0;
background: rgba(0, 0, 0, 0.5);
}
.img-preview__toolbar span * {
margin: 0 6px;
color: #fff;
font-size: 24px;
cursor: pointer;
font-weight: 400;
}
.pdf-preview__title-text {
display: flex;
justify-content: flex-start;
align-items: center;
}
.pdf-preview__title-text img {
margin-right: 10px;
}
.pdf-preview__title-text span {
padding: 0 20px 0 10px;
}
.pdf-preview__title-text .btn {
background: #4395ff;
font-size: 14px;
height: 30px;
max-height: 30px;
border-radius: 15px;
padding: 0 20px;
outline: none;
border: 1px solid #4395ff;
color: #fff;
box-shadow: 0 0 0 rgba(0,0,0,0);
transition: all .5s;
}
.pdf-preview__title-text .btn:hover {
box-shadow: 0 0 10px rgba(0, 0, 0, .3);
opacity: 0.9;
background: #539dfc;
cursor: pointer;
}
.pdf-preview__title-text .pdf-preview__input {
font-size: 14px;
height: 30px;
max-height: 30px;
border-radius: 15px;
background-color: #fff;
padding: 0 20px;
width: 50px;
text-align: center;
outline: none;
}
.img-preview__bottom {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 5px;
background: #333;
z-index: 10;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
}
</style>

55
xd-file-preview/src/components/preview/loading.vue

@ -0,0 +1,55 @@
<template>
<div class="xd-loading" v-if="show">
<div class="xd-loading-box">
<img :src="iconData.loadicon" />
</div>
</div>
</template>
<script>
import {iconData} from './../contact'
export default {
name: "loading",
props: ['show'],
data(){
return {
iconData
}
},
created() {
console.log('loading',this.show);
},
watch:{
show(){
console.log('loading', this.show);
}
}
}
</script>
<style>
.xd-loading {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 10000;
}
.xd-loading-box {
height: 120px;
width: 120px;
border-radius: 15px;
overflow: hidden;
}
.xd-loading-box img {
height: 100%;
width: 100%;
}
</style>

17
xd-file-preview/src/components/preview/ofd.js

@ -0,0 +1,17 @@
'use strict';
import jsonView from "./jsonView";
import helper from "./helper";
/**
* @description PDF预览功能
* @param options {object}
* @param options.fid 文件ID唯一id必填
* @param options.name 文件名称
* @param options.type 文件类型
* @param options.url // PDF地址
* @param $vue Vue
*/
export default function (options, $vue) {
helper.createElement(options, $vue, jsonView);
}

18
xd-file-preview/src/components/preview/office.js

@ -0,0 +1,18 @@
'use strict';
import officePreview from "./officeView";
import helper from "./helper";
/**
* @description Office预览功能
* @param options {object}
* @param options.fid 文件ID唯一id必填
* @param options.name 文件名称
* @param options.type 文件类型
* @param options.url // Office地址
* @param $vue Vue
*/
export default function (options, $vue) {
helper.createElement(options, $vue, officePreview);
}

198
xd-file-preview/src/components/preview/officeView.vue

@ -0,0 +1,198 @@
<template>
<div class="office-preview" :id="options.ele" v-if="info && options && options.ele">
<div class="office-preview-title">
<div class="office-preview-text">
<img :src="options.icon" height="30" width="30">
<span>{{info.name|getFileName(options['type'].toLocaleLowerCase())}}</span>
<button v-if="isDownLoad" class="btn" @click="download">下载</button>
</div>
<div class="office-preview-close" @click="handleCloseClick(options.ele)"><i class="fileIconfont iconwrong"></i></div>
</div>
<div class="office-preview-content">
<div class="office-preview-content-box">
<iframe v-if="src" :src="src" height="100%" width="100%" frameborder="0"></iframe>
</div>
</div>
<div class="change office change-prev" v-if="isBtn" @click="handeCilck('prev')">
<i class="fileIconfont iconpageup"></i>
</div>
<div class="change office change-next" v-if="isBtn" @click="handeCilck('next')">
<i class="fileIconfont iconpagedown"></i>
</div>
</div>
</template>
<script>
window.pdfUrl = "";
window.pdfLang = "zh-CN";
import download from 'downloadjs';
export default {
name: "XdOfficePreview",
props: {
options:{
type: Object|null,
default(){
return null
},
}
},
created(){
if(this.options) {
this.getUrl(this.options['source'])
if (this.options.download === false) this.isDownLoad = this.options.download;
}
},
filters: {
getFileName(name, type) {
if (name.indexOf(`.${type}`) === -1) {
return `${name}.${type}`
}
return name;
}
},
watch: {
options(val) {
if (val) {
console.log('options',val)
this.getUrl(val['source']);
this.info = val;
if (val.download === false) this.isDownLoad = val.download;
}
}
},
computed:{
isBtn() {
let isBtn = false;
if (this.info && this.info.callback && typeof this.info.callback === 'function') isBtn = true;
return isBtn
}
},
data() {
return {
otherPreviewUrl: 'https://view.officeapps.live.com/op/view.aspx?src=',
src: '',
info: null,
isDownLoad: true,
}
},
methods: {
handeCilck(status) {
if (this.info && this.info.callback && typeof this.info.callback === 'function') {
this.handleCloseClick(this.info.ele);
this.info.callback(status)
}
},
download() {
console.log(this.info)
download(this.info['response'], this.info['name'])
},
getUrl(val) {
this.src = `${this.otherPreviewUrl}${val}&lang=${window.pdfLang}&llcc=${window.pdfLang}`;
},
handleCloseClick(el){
this.close(el);
}
}
};
</script>
<style>
@import "style.css";
.change.office > i.fileIconfont {
color: #999;
opacity: 0.7;
}
.office-preview {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 10000;
}
.office-preview-title {
position: absolute;
top:0;
left:0;
right:0;
height: 48px;
background: #333;
color: #fff;
z-index: 10;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 5px;
box-sizing: border-box;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
}
.office-preview-text {
display: flex;
justify-content: flex-start;
align-items: center;
}
.office-preview-text img {
margin-right: 15px;
}
.office-preview-text .btn {
background: #4395ff;
margin-left: 20px;
font-size: 14px;
height: 30px;
max-height: 30px;
border-radius: 15px;
padding: 0 20px;
outline: none;
border: 1px solid #4395ff;
color: #fff;
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
transition: all .5s;
}
.office-preview-text .btn:hover {
box-shadow: 0 0 10px rgba(0, 0, 0, .3);
opacity: 0.9;
background: #539dfc;
cursor: pointer;
}
.office-preview-close {
cursor: pointer;
font-size: 30px;
height: 30px;
width: 30px;
line-height: 30px;
}
.office-preview-content {
height: -moz-calc(100% - 0);
height: -webkit-calc(100% - 0);
height: calc(100% - 0);
height: 100%;
overflow: hidden;
position: relative;
z-index: 9;
}
.office-preview-content iframe {
background: rgba(0,0,0,.5);
height: 100%;
}
.office-preview-content-box {
height: 100%;
}
</style>

17
xd-file-preview/src/components/preview/pdf.js

@ -0,0 +1,17 @@
'use strict';
import pdfPreview from "./pdfView";
import helper from "./helper";
/**
* @description PDF预览功能
* @param options {object}
* @param options.fid 文件ID唯一id必填
* @param options.name 文件名称
* @param options.type 文件类型
* @param options.url // PDF地址
* @param $vue Vue
*/
export default function (options, $vue) {
helper.createElement(options, $vue, pdfPreview);
}

305
xd-file-preview/src/components/preview/pdfView.vue

@ -0,0 +1,305 @@
<template>
<div class="pdf-preview" :id="options.ele" v-if="info">
<div class="pdf-preview__title">
<div class="pdf-preview__title-text">
<img :src="options.icon" height="30" width="30">
<input class="pdf-preview__input" v-model.number="page" type="number">
<span>/ {{numPages}}</span>
<button class="btn" v-if="isDownLoad" @click="download">下载</button>
<span>{{info.name|getFileName(options['type'].toLocaleLowerCase())}}</span>
</div>
<div class="pdf-preview__title-close" @click="closeHandle(options.ele)"><i class="fileIconfont iconwrong"></i></div>
</div>
<div class="pdf-preview__content" :style="`width: ${getWdith}`">
<xd-pdf
:file-url="info['response']"
@num-pages="setTotalNumPages"
:current-pages="page"
></xd-pdf>
</div>
<div class="img-preview__toolbar">
<span>
<i class="fileIconfont iconfangda" @click="zoomHandle(1)"></i>
<i class="fileIconfont iconsuoxiao" @click="zoomHandle(-1)"></i>
<i class="fileIconfont iconpageup" @click="overPage(-1)"></i>
<i class="fileIconfont iconpagedown" @click="overPage(1)"></i>
<i class="fileIconfont iconhuanyuan" @click="zoomOriginalSize()"></i>
</span>
</div>
<div class="img-preview__bottom"></div>
<div class="change change-prev" v-if="isBtn" @click="handeCilck('prev')"><i class="fileIconfont iconpageup"></i></div>
<div class="change change-next" v-if="isBtn" @click="handeCilck('next')"><i class="fileIconfont iconpagedown"></i></div>
</div>
</template>
<script>
import helper from "./helper";
import download from 'downloadjs';
import XdPdf from "../XdPdf";
export default {
name: "xdPdfPreview",
props: {
options: {
type: Object|null,
default(){
return null
}
},
},
components: {XdPdf},
data() {
return {
src: '',
loadedRatio: 0,
page: 1,
numPages: 0,
rotate: 0,
width: 0.5,
info: null,
isDownLoad: true
}
},
filters:{
getFileName(name, type){
if(name.indexOf(`.${type}`) === -1) {
return `${name}.${type}`
}
return name;
}
},
watch:{
options(val){
this.info = val;
if (val.download === false) this.isDownLoad = val.download;
if(this.info['url']) {
this.src= this.info['url']
}
},
},
created(){
this.info = this.options;
if (this.options && this.options.download === false) this.isDownLoad = this.options.download;
},
computed:{
getWdith() {
return (this.width * 100) + '%';
},
isBtn() {
let isBtn = false;
if (this.info && this.info.callback && typeof this.info.callback === 'function') isBtn = true;
return isBtn
}
},
methods: {
handeCilck(status) {
if (this.info && this.info.callback && typeof this.info.callback === 'function') {
this.closeHandle(this.info.ele);
this.info.callback(status)
}
},
/**
* @description 设置总页数
*/
setTotalNumPages(num){
this.numPages = num;
},
closeHandle(id) {
this.close(id);
},
download(){
download(this.info['response'], this.info['name'])
},
/**
* @description 翻页处理
* @param type
*/
overPage(type){
if (this.page + type === 0) {
this.page = 1;
}
else if(this.page + type >= this.numPages){
this.page = this.numPages;
}
else{
this.page = this.page + type;
}
},
zoomOriginalSize(){
this.width = 0.5
},
/**
* @description 放大页面30-100之间
* @param type
*/
zoomHandle(type){
if(type === 1) {
if (helper.addFloatNumber(this.width, 0.1) < 0.3) {
this.width = 0.3;
} else if (helper.addFloatNumber(this.width, 0.1) >= 1) {
this.width = 0.98;
} else {
this.width = helper.addFloatNumber(this.width, 0.1);
}
}else{
if (helper.cutFloatNumber(this.width, 0.1) < 0.3) {
this.width = 0.3;
} else if (helper.cutFloatNumber(this.width, 0.1) >= 1) {
this.width = 0.98;
} else {
this.width = helper.cutFloatNumber(this.width, 0.1);
}
}
},
password: function (updatePassword, reason) {
updatePassword(prompt('password is "test"'));
},
error: function (err) {
console.log(err);
}
}
};
</script>
<style type="text/css">
@import "style.css";
.pdf-preview i.fileIconfont {
font-size: 30px;
font-weight: normal;
}
.pdf-preview {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
min-width: 1000px;
min-height: 300px;
background: rgba(0, 0, 0, .6);
z-index: 10000;
padding-top: 48px;
box-sizing: border-box;
}
.pdf-preview__title {
position: absolute;
background: #333;
top: 0;
right: 0;
left:0;
width: 100%;
height: 48px;
padding: 0 0 0 10px;
box-sizing: border-box;
color: #fff;
z-index: 10;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
}
.pdf-preview__title-close {
cursor: pointer;
font-size: 18px;
height: 40px;
width: 40px;
line-height: 40px;
}
.pdf-preview__content {
height: -moz-calc(100% - 55px);
height: -webkit-calc(100% - 55px);
height: calc(100% - 55px);
height: 100%;
overflow-x: hidden;
overflow-y: auto;
position: relative;
z-index: 9;
margin: 0 auto;
}
.img-preview__toolbar {
position: absolute;
font-size: 0;
left: 50%;
bottom: 30px;
text-align: center;
transform: translateX(-50%);
z-index: 10000
}
.img-preview__toolbar span {
display: flex;
line-height: 24px;
border-radius: 8px;
padding: 8px 10px;
font-size: 0;
background: rgba(0, 0, 0, 0.5);
}
.img-preview__toolbar span * {
margin: 0 6px;
color: #fff;
font-size: 24px;
cursor: pointer;
font-weight: 400;
}
.pdf-preview__title-text {
display: flex;
justify-content: flex-start;
align-items: center;
}
.pdf-preview__title-text img {
margin-right: 10px;
}
.pdf-preview__title-text span {
padding: 0 20px 0 10px;
}
.pdf-preview__title-text .btn {
background: #4395ff;
font-size: 14px;
height: 30px;
max-height: 30px;
border-radius: 15px;
padding: 0 20px;
outline: none;
border: 1px solid #4395ff;
color: #fff;
box-shadow: 0 0 0 rgba(0,0,0,0);
transition: all .5s;
}
.pdf-preview__title-text .btn:hover {
box-shadow: 0 0 10px rgba(0, 0, 0, .3);
opacity: 0.9;
background: #539dfc;
cursor: pointer;
}
.pdf-preview__title-text .pdf-preview__input {
font-size: 14px;
height: 30px;
max-height: 30px;
border-radius: 15px;
background-color: #fff;
padding: 0 20px;
width: 50px;
text-align: center;
outline: none;
}
.img-preview__bottom {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 5px;
background: #333;
z-index: 10;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
}
</style>

88
xd-file-preview/src/components/preview/style.css

@ -0,0 +1,88 @@
@font-face {
font-family: "fileIconfont";
src: url('//at.alicdn.com/t/font_2306061_i5jhgzel2u.eot?t=1610246045536'); /* IE9 */
src: url('//at.alicdn.com/t/font_2306061_i5jhgzel2u.eot?t=1610246045536#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAhIAAsAAAAAENgAAAf7AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCEQgqSWI8lATYCJAM0CxwABCAFhG0HgSYbPw5RlE9SmuwLbBv2ZIMWZhwbxgArIACOAgkkAADgA5khERlKSfA19tt3d98E0ySaGLpPJ9FoDEkrlUYiFDwVUiF7qPa/c5kdkBqwyvPHCtSIFPpc8pOmr81PAdXQOSCjJ+Qm3IanU/9d3qveJe1lQRWPSAb5UDHdN3O3OdkcFR6CCQhmpCPL/LwGCP7ar1X10EzbhcKQiUw/Svt7/+z9XWRxsUaIns8Na9CkNJluuQLNLY/ho6/JIgvbrBOonuG2EUx+OdcEmmZ1LewdXVyTlsLiAkNOtGKklctXWsJDPbtaY7IMx3xR6uk6HcdN+PXxbT1iSSqZdaf940NBdt7Wfmf+n5eV7wjqSvGQR8YaojyynmvLb8EY1kLcrBuNXCAGLRr2pbcR26YUlHp5//7/QjnTlgmYyyoNg3bsqnXbzhA1dQ1J1tLUVl08VMx/XpWB21jxmCt4W3Op2jfZAlUH2QbVAJmASiBTUBkEQLVAJKgmiAKrmiCoKkgKKkBekELh/Zu9OOV0fBOTiFUkvZJ/WFlHIc52a11RdQWpvCLEf0pURLw42pGP9FCKxJqO4tj4aKkVTmExp0RJalIir9au3T+BAB0MMuEwFwqx2Q7eY1WytN3qExI51in4bQqGc9m8vJxm8kN32fA9LniH4QNa+bfINU7iWI6h2wKyaDsKSQL/gKGgZJ1YbLUDgD0PpF/EO0ipzUVAwZnD4AIJyPcmRdgqpzGN4ox0xF8UQTKORDItHPlzz+7LXA28LbF19/Y9nPIWt36t2nR32JiWgJYOQZe0HOetcnmTzm/5hyvIOAK0PcQ6w4WuLWrkWG76276is5TALJ2RZsrB/1DO9SrpN4Tgs3rkANs9vJckbX4R2KgIn5LATvAX/Lz5er02fyJBuvyCD2OrBxKbF+FVANIBHjzR92tM9bmu0g1n1NEVp03UxrP/UEuTo93NpTHlhXZ+kqUlLmF0JSKH7U1Tmp1WO6/I3tCSwO2/A3SEsr1Cd/9iz4UDwhZH28JjGYfNL/y91WjD0bYz5P8gHiVIqjbrI66B59YXlG7ZlkqQnPifJZYTRNGZqCzB1rnm/1rUvQCAtRDMd2yJq2pjBmlbtXLhGLAygigexgFlsd0DgNm5vkJRjK3LVzQnq5Hk3qxzvadSwcx8Oueq9m2U06GbYxzNuap86+WnA/hastPsncm5Nngr5KcTXaCBVf5NCiZ8a1sobT26gdX+DYozQfIkywgEdLDfDvTxp1S6wwgzUxs2Dp+VbEhxz5w1a6Y72ZhcOmvGn1EKSmk8AnyxnI/S6aJGesE9zqMLUTotukpGbVkwf8vo6CmL18BIBKrn2sijtWgNwg64Fq6JRPRDly3zevr3f/TwwQPujFyI0vYqXLwWPooIIaReM2LA0P797z94+HAe5/EAuzzUOtTK19oRx+zaxcTtxj7c7xvsjwOtS7EiB7wrV4H9L/V//PHnLofT/zXCzztj9Obeh2ObPkZpecA+ZDvmK9lHp8y9DXmhPMP2PKVfTlGTF45sKGn0lOq8ZOHkZ9KFC+U2uf+zBXtHDP9rCn9qEMNCSlgjsaGwqaP9Qqme7L90BFkX+HRYkK6JGjY4hRsXPZH7LzC8iXqifJnuuf4YVA3sVdMZio1Od9GxE+7S08QJ42lQbKz4u07d1aZHwc99eYrRD04wm5/Seobilce/LhwuWdGsZvZAEy6tMdWU4tDe7ov+NM2PM8XNHz06Doy8XyJ/LC/JLjSbu8wzMi+0uTqIUlNREAVSVcGoQJQqFQVQUKWSTNy6BwEC7oZ7CLAbNgOA9qDdgAC9nvarrLw2aCnIQYdJ8lClyAjSQzDYMH6BcUdNjVczSNPt2RPemze9Coc3bpZ5b9xIv+ObNzqeHP7UqDVRwymT1ihpvGChBmqNCRaLRGb7IVMTZBHU6u4ta98Qb1Qb490SiVc1SF0KdudRDR0T+xuU0CknvssHry7JeHjELevUUVbFCpa/V103aiPpjpOfRvmAAR3MZwHd92we2TOSv0LRUMVfanxJqG5QVBZEOg/X7FC+2O8C0ulQ0fVnsOXr7eOGjbgadXxcw/Ky/sxq+PAhlHS1GW5HrejGYXBrk4p0fbUjrEZ0s6/lLQDAlyODfiE/n4C8Mz4hyH75+z9YJPrIFwsCda2zfMyX4Bdcgv+3r3QWThB1m4xEOvkC5HVKVeDvPZKzuQm9XsW2/nk2XdbNrAwRjbPQWU2iQgTAt1Bf5JCS6s/l6kdsFUjQ11wuos1bx/Es/8ufgnIJcGu7WATDQNPega5YPeiPkZFJsD7Uc8JHbFIzA7nOew4KvNdiK1o2oArvzdimVcfmW4aEqqI0sOIZsaHvPTbpeoEM7x8o8P6NrRj1L60SHDC26SBWbGxZiK86+yGwGASdF1RzNDJFcScaegkq8VjIxKamWwgjjYaO+sO/rmMwEKrQE6VqHMeSyhB9enTTAZ6H1IboAo/7Dp12NhjIl94+R5907AoBJvYTC9RcwatxyJBnxzqlz78ElISHCVvm/Oa9BUIRfXlqpG9IcD/GhjTnVEZHUspYzEol6k4OIR91xEqBZ7cjytYrcwEu1ucItFozA7aWpOL+8Fn+OcdO37zWO3UiRY4SlahGLerRiGa0oh2d6OpdikTzBLMkcnSuTcsyBQIz04hkJraZgXE1M434oKCk5SRA4bpYz0I0qhklmGuGDcmMEqzJ+oxaf2FSkupbZYJRHiE=') format('woff2'),
url('//at.alicdn.com/t/font_2306061_i5jhgzel2u.woff?t=1610246045536') format('woff'),
url('//at.alicdn.com/t/font_2306061_i5jhgzel2u.ttf?t=1610246045536') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('//at.alicdn.com/t/font_2306061_i5jhgzel2u.svg?t=1610246045536#iconfont') format('svg'); /* iOS 4.1- */
}
.fileIconfont {
font-family: "fileIconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconyduicuowushixin:before {
content: "\e676";
}
.iconpagedown:before {
content: "\e638";
}
.iconpageup:before {
content: "\e639";
}
.iconwenjian:before {
content: "\e6ac";
}
.icontupian:before {
content: "\e7b3";
}
.iconhuanyuan:before {
content: "\e66f";
}
.iconwrong:before {
content: "\e615";
}
.iconsuoxiao:before {
content: "\e619";
}
.iconfangda:before {
content: "\e61a";
}
.iconfangda1:before {
content: "\e666";
}
.iconyouzhuan:before {
content: "\e665";
}
.iconzuozhuan:before {
content: "\e667";
}
.change {
width: 30px;
height: 30px;
position: absolute;
top: 50%;
margin-top: -15px;
z-index: 100000;
}
.change > i.fileIconfont {
color: #fff;
font-size: 40px!important;
opacity: .8;
cursor: pointer;
}
.change.change-prev {
left: 20px;
}
.change.change-next {
right: 20px;
}

17
xd-file-preview/src/components/preview/text.js

@ -0,0 +1,17 @@
'use strict';
import textView from "./textView";
import helper from "./helper";
/**
* @description PDF预览功能
* @param options {object}
* @param options.fid 文件ID唯一id必填
* @param options.name 文件名称
* @param options.type 文件类型
* @param options.url // PDF地址
* @param $vue Vue
*/
export default function (options, $vue) {
helper.createElement(options, $vue, textView);
}

397
xd-file-preview/src/components/preview/textView.vue

@ -0,0 +1,397 @@
<template>
<div class="pdf-preview" :id="options.ele" v-if="info">
<div class="pdf-preview__title">
<div class="pdf-preview__title-text">
<img :src="options.icon" height="30" width="30">
<input class="pdf-preview__input" v-model.number="page" type="number">
<span>/ {{numPages}}</span>
<button class="btn" v-if="isDownLoad" @click="download">下载</button>
<span>{{info.name|getFileName(options['type'].toLocaleLowerCase())}}</span>
</div>
<div class="pdf-preview__title-close" @click="closeHandle(options.ele)"><i class="fileIconfont iconwrong"></i></div>
</div>
<div class="pdf-preview__content" :style="`width: ${getWdith}`">
<pre v-highlightjs="info.url"><code :class="getCss(info.type)"></code></pre>
</div>
<div class="img-preview__bottom"></div>
<div class="change change-prev" v-if="isBtn" @click="handeCilck('prev')"><i class="fileIconfont iconpageup"></i></div>
<div class="change change-next" v-if="isBtn" @click="handeCilck('next')"><i class="fileIconfont iconpagedown"></i></div>
</div>
</template>
<script>
import helper from "./helper";
import download from 'downloadjs';
export default {
name: "xdPdfPreview",
components:{
},
props: {
options: {
type: Object|null,
default(){
return null
}
},
},
data() {
return {
src: '',
loadedRatio: 0,
page: 1,
numPages: 0,
rotate: 0,
width: 0.5,
info: null,
isDownLoad: true,
}
},
filters:{
getFileName(name, type){
if(name.indexOf(`.${type}`) === -1) {
return `${name}.${type}`
}
return name;
}
},
watch:{
options(val){
this.info = val;
if (val.download === false) this.isDownLoad = val.download;
if(this.info['url']) {
this.src= this.info['url']
}
},
},
created(){
this.info = this.options;
if (this.options && this.options.download === false) this.isDownLoad = this.options.download;
},
computed:{
getWdith() {
return (this.width * 100) + '%';
},
isBtn(){
let isBtn = false;
if(this.info && this.info.callback && typeof this.info.callback === 'function') isBtn = true;
return isBtn
}
},
methods: {
handeCilck(status){
if (this.info && this.info.callback && typeof this.info.callback === 'function'){
this.closeHandle(this.info.ele);
this.info.callback(status)
}
},
getCss(type){
return type.toLocaleLowerCase()
},
/**
* @description 设置总页数
*/
setTotalNumPages(num){
this.numPages = num;
},
closeHandle(id) {
this.close(id);
},
download(){
download(this.info['response'], this.info['name'])
},
/**
* @description 翻页处理
* @param type
*/
overPage(type){
if (this.page + type === 0) {
this.page = 1;
}
else if(this.page + type >= this.numPages){
this.page = this.numPages;
}
else{
this.page = this.page + type;
}
},
zoomOriginalSize(){
this.width = 0.5
},
/**
* @description 放大页面30-100之间
* @param type
*/
zoomHandle(type){
if(type === 1) {
if (helper.addFloatNumber(this.width, 0.1) < 0.3) {
this.width = 0.3;
} else if (helper.addFloatNumber(this.width, 0.1) >= 1) {
this.width = 0.98;
} else {
this.width = helper.addFloatNumber(this.width, 0.1);
}
}else{
if (helper.cutFloatNumber(this.width, 0.1) < 0.3) {
this.width = 0.3;
} else if (helper.cutFloatNumber(this.width, 0.1) >= 1) {
this.width = 0.98;
} else {
this.width = helper.cutFloatNumber(this.width, 0.1);
}
}
},
password: function (updatePassword, reason) {
updatePassword(prompt('password is "test"'));
},
error: function (err) {
console.log(err);
}
}
};
</script>
<style type="text/css">
@import "style.css";
.pdf-preview i.fileIconfont {
font-size: 30px;
font-weight: normal;
}
.pdf-preview {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
min-width: 1000px;
min-height: 300px;
background: rgba(0, 0, 0, .6);
z-index: 10000;
padding-top: 48px;
box-sizing: border-box;
}
.pdf-preview__title {
position: absolute;
background: #333;
top: 0;
right: 0;
left:0;
width: 100%;
height: 48px;
padding: 0 0 0 10px;
box-sizing: border-box;
color: #fff;
z-index: 10;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
}
.pdf-preview__title-close {
cursor: pointer;
font-size: 18px;
height: 40px;
width: 40px;
line-height: 40px;
}
.pdf-preview__content {
height: -moz-calc(100% - 55px);
height: -webkit-calc(100% - 55px);
height: calc(100% - 55px);
height: 100%;
overflow-x: hidden;
overflow-y: auto;
position: relative;
z-index: 9;
margin: 0 auto;
background: #fff;
padding: 10px;
box-sizing: border-box;
}
.pdf-preview__content pre {
font-size: 14px;
margin: 0;
height: 100%;
}
.pdf-preview__content code {
min-height: 100% !important;
word-break: break-all;
word-wrap: break-word;
white-space: pre-wrap;
box-sizing: border-box;
border-radius: 6px;
font-size: 14px!important;
padding: 20px;
}
.img-preview__toolbar {
position: absolute;
font-size: 0;
left: 50%;
bottom: 30px;
text-align: center;
transform: translateX(-50%);
z-index: 10000
}
.img-preview__toolbar span {
display: flex;
line-height: 24px;
border-radius: 8px;
padding: 8px 10px;
font-size: 0;
background: rgba(0, 0, 0, 0.5);
}
.img-preview__toolbar span * {
margin: 0 6px;
color: #fff;
font-size: 24px;
cursor: pointer;
font-weight: 400;
}
.pdf-preview__title-text {
display: flex;
justify-content: flex-start;
align-items: center;
}
.pdf-preview__title-text img {
margin-right: 10px;
}
.pdf-preview__title-text span {
padding: 0 20px 0 10px;
}
.pdf-preview__title-text .btn {
background: #4395ff;
font-size: 14px;
height: 30px;
max-height: 30px;
border-radius: 15px;
padding: 0 20px;
outline: none;
border: 1px solid #4395ff;
color: #fff;
box-shadow: 0 0 0 rgba(0,0,0,0);
transition: all .5s;
}
.pdf-preview__title-text .btn:hover {
box-shadow: 0 0 10px rgba(0, 0, 0, .3);
opacity: 0.9;
background: #539dfc;
cursor: pointer;
}
.pdf-preview__title-text .pdf-preview__input {
font-size: 14px;
height: 30px;
max-height: 30px;
border-radius: 15px;
background-color: #fff;
padding: 0 20px;
width: 50px;
text-align: center;
outline: none;
}
.img-preview__bottom {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 5px;
background: #333;
z-index: 10;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
}
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #abb2bf;
background: #282c34;
}
.hljs-comment,
.hljs-quote {
color: #5c6370;
font-style: italic;
}
.hljs-doctag,
.hljs-keyword,
.hljs-formula {
color: #c678dd;
}
.hljs-section,
.hljs-name,
.hljs-selector-tag,
.hljs-deletion,
.hljs-subst {
color: #e06c75;
}
.hljs-literal {
color: #56b6c2;
}
.hljs-string,
.hljs-regexp,
.hljs-addition,
.hljs-attribute,
.hljs-meta-string {
color: #98c379;
}
.hljs-built_in,
.hljs-class .hljs-title {
color: #e6c07b;
}
.hljs-attr,
.hljs-variable,
.hljs-template-variable,
.hljs-type,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-number {
color: #d19a66;
}
.hljs-symbol,
.hljs-bullet,
.hljs-link,
.hljs-meta,
.hljs-selector-id,
.hljs-title {
color: #61aeee;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
.hljs-link {
text-decoration: underline;
}
</style>

5
xd-file-preview/src/directives/index.js

@ -0,0 +1,5 @@
import watermark from './waterMark'
export default function installDirective(app) {
app.directive(watermark.name, watermark.directives);
}

56
xd-file-preview/src/directives/waterMark.js

@ -0,0 +1,56 @@
// 全局保存 canvas 和 div ,避免重复创建(单例模式)
const globalCanvas = null;
const globalWaterMark = null;
// 获取 toDataURL 的结果
const getDataUrl = ({
font = "16px normal",
fillStyle = "rgba(180, 180, 180, 0.3)",
textAlign,
textBaseline,
text = "test1233",
}) => {
const rotate = -20;
const canvas = globalCanvas || document.createElement("canvas");
const ctx = canvas.getContext("2d"); // 获取画布上下文
ctx.rotate((rotate * Math.PI) / 180);
ctx.font = font;
ctx.fillStyle = fillStyle;
ctx.textAlign = textAlign || "left";
ctx.textBaseline = textBaseline || "middle";
ctx.fillText(text, canvas.width / 3, canvas.height / 2);
return canvas.toDataURL("image/png");
};
// 设置水印
const setWaterMark = (el, binding) => {
const { parentElement } = el;
// 获取对应的 canvas 画布相关的 base64 url
console.log(111111);
const url = getDataUrl(binding);
// 创建 waterMark 父元素
const waterMark = globalWaterMark || document.createElement("div");
waterMark.className = `water-mark`; // 方便自定义展示结果
waterMark.setAttribute("style", `background-image: url(${url});`);
// 将对应图片的父容器作为定位元素
parentElement.setAttribute("style", "position: relative;");
// 将图片元素移动到 waterMark 中
parentElement.appendChild(waterMark);
};
const directives = {
bind(el, binding) {
el.onload = setWaterMark.bind(null, el, binding.value);
},
};
export default {
name: "watermark",
directives,
};

31
xd-file-preview/src/install.js

@ -0,0 +1,31 @@
'use strict';
import {preview} from "./components/preview";
import helper from "./components/preview/helper";
import XdFileListPreview from "@/components/XdFileListPreview";
import VueHighlightJS from 'vue-highlightjs'
// 定义 install 方法
const install = function (Vue, options) {
if (install.installed) return;
install.installed = true;
Vue.use(VueHighlightJS);
Vue.prototype.$xdOptions = options;
Vue.prototype.$preview = (options, callback=null)=>{
options['callback'] = callback;
preview(options, Vue);
};
Vue.prototype.$fileHelper = helper;
console.log('Vue.use()=> options', options);
Vue.component(XdFileListPreview.name, XdFileListPreview)
};
if (typeof window !== 'undefined' && window['Vue']) {
install(window['Vue'])
}
export default {
install,
XdFileListPreview
}

18
xd-file-preview/src/main.js

@ -0,0 +1,18 @@
'use strict';
import Vue from 'vue'
import App from './App.vue'
import install from "@/install";
import directives from './directives'
Vue.use(install,{
pdf: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.0.288/build/pdf.min.js',
worker:'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.0.288/build/pdf.worker.min.js',
});
Vue.use(directives)
Vue.config.productionTip = false;
new Vue({
render: h => h(App),
}).$mount('#app');

89
xd-file-preview/vue.config.js

@ -0,0 +1,89 @@
'use strict';
const path = require('path');
const defaultSettings = require('./settings.js');
const devServer = require('./devServer'); //devServer相关配置
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
/**自动加载关系**/
const CreatedComponentsPlugin = require('./build/plugins/CreatedComponentsPlugin');
function resolve(dir) {
return path.join(__dirname, dir)
}
/***
* @description 根据环境入口使用的插件
* @type {CreatedComponentsPlugin[]}
*/
let plugins = [];
//console.log(process.env.npm_lifecycle_script.indexOf('gxdVue'), process.env);
if(process.env.npm_lifecycle_script.indexOf('gxdVue') !== -1) {
plugins.push(new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false,
drop_debugger: true, //去掉debugger
drop_console: true, // 去掉console
pure_funcs: ['console.log']// 移除console
}
},
sourceMap: false,
parallel: true
}));
}
const name = defaultSettings.title || 'XD Vue Package'; // page title
// All configuration item explanations can be find in https://cli.vuejs.org/config/
module.exports = {
/**
* You will need to set publicPath if you plan to deploy your site under a sub path,
* for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,
* then publicPath should be set to "/bar/".
* In most cases please use '/' !!!
* Detail: https://cli.vuejs.org/config/#publicpath
*/
publicPath: '/',
outputDir: 'dist',
assetsDir: 'static',
/**是否关闭eslint语法检测**/
lintOnSave: defaultSettings.isCloseEslint,
productionSourceMap: false,
devServer,
css: {
extract: false //设置不产生css样式模式
},
configureWebpack: {
// provide the app's title in webpack's name field, so that
// it can be accessed in index.html to inject the correct title.
name: name,
//关闭性能提示
performance: {
hints: false,
//入口起点的最大体积
maxEntrypointSize: 50000000,
//生成文件的最大体积
maxAssetSize: 30000000,
//只给出 js 文件的性能提示
assetFilter: function (assetFilename) {
return assetFilename.endsWith('.js');
}
},
resolve: {
alias: {
'@': resolve('src'),
}
},
plugins
},
chainWebpack: (config) => {
}
};
Loading…
Cancel
Save