vue移动端文件预览

3 篇文章 0 订阅
1 篇文章 0 订阅

前言:

最近遇到一个移动端需求,要求在移动端预览上传的附件,在网上找了很多资料,最后参考CSDN博主「蛋蛋fighting哈湫」的原创文章实现了功能,并结合自己的项目情况,整理了这份文档。

原文链接:https://blog.csdn.net/u012439689/article/details/106570080/

整体思路:

  1. 实现在不下载的情况下预览附件内容功能;
  2. 使用不同组件分别实现不同文件类型预览功能。目前实现功能:图片、pdf文件、docx、excel表格(xls、xlsx格式表格)、txt文件预览;
  3. 样式使用less;

具体实现:

一、外壳(vViews.vue):

将各个组件进行整合,在页面中引用时更加方便,项目使用了vant组件库,在之前的文章中描述了如何安装,这里不做赘述。

<template>
	<div class="viewBox" v-if="isShow">
		<!-- 关闭按钮 -->
        <van-icon class="closeBtn" name="cross" @click="isShow = false" />
	</div>
</template>
export default {
	props: {
        datas: {},
        type: {}
    },
	data() {
        return {
            isShow: false
        };
    },
    methods: {
    	/**
    	 * 处理展示数据
    	*/
    	showFile(newVal) {
		}
    }
};
<style lang="less" scoped>
.viewBox {
    width: 100vw;
    height: 100vh;
    overflow: hidden;
    background: #ffffff;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 2000;
    .closeBtn {
    	position: absolute;
        top: 10px;
        right: 10px;
        z-index: 10;
        font-size: 24px;
    }
}
</style>

在父组件中引用:

<template>
	<div>
		<!-- 附件列表 -->
		<div v-for="(item, index) in fileList"
			:key="index"
			@click="showFile(item)">
			{{item.file_name}}
		</div>
		<v-views ref="vViews" :datas="src" :type="type"></v-views>
	</div>
</template>
import vViews from '@/components/vViews';
export default {
	components: { vViews },
	data() {
		return {
			fileList: [], // 附件列表
			src: '', // 传参
            type: '', // 附件类型
            fileObj: {} // 存放已获取的数据流
		};
	},
	methods: {
		/**
         * 点击查看附件
         */
        showFile(item) {
            // 获取允许预览文件类型
            const typeObj = {
            	'.docx': 'word',
            	'.pdf': 'pdf',
            	'.jpg': 'img',
            	'.png': 'img',
            	'.xls': 'excel',
            	'.xlsx': 'excel',
            	'.txt': 'txt',
           	};
            this.type = typeObj[item.file_type] || '';
            // 已经调用过接口获取到了文件流数据
            if (this.fileObj[item.file_name]) {
                this.openFile(this.fileObj[item.file_name]);
            } else {
                this.downloadFile(item);
            }
        },
        /**
         * 打开文件预览组件
         */
        openFile(datas) {
            this.src = datas.fileBinary;
            this.$nextTick(()=> {
                this.$refs.vViews.isShow = true;
                this.$refs.vViews.showFile(this.src);
            });
        }/**
         * 下载接口
         */
        downloadFile(item) {
            // 判断是否允许预览
            if (this.type !== '') {
            	const res = {...}; // 这里指从后台接口获取的数据 
                this.fileObj[res.data.fileName] = res.data;
                this.openFile(res.data);
            } else {
                const types = item.file_type.substr(1);
                this.$toast(`${types}文件类型暂不支持预览`);
            }
        },
	}
};

二、文件预览:

1.图片预览:

因为我的项目安装了vant,所以这里使用了vant的ImagePreview图片预览组件,在外壳(vViews.vue)页面里进行导入使用。

import { ImagePreview } from 'vant';
export default {
	methods: {
		showFile(newVal) {
            if (this.type === 'img') {
                // 图片预览,这里是后台返回了base64的数据,处理成了blob格式
                const val = this.$base64ToBlob(newVal, 'image/jpeg');
                this.newSrc = window.URL.createObjectURL(val);
                // vant自带图片预览组件
                const that = this;
                ImagePreview({
                    images: [this.newSrc],
                    showIndex: false,
                    onClose() {
                        that.isShow = false;
                    },
                });
			}
		}
	}
};

2.pdf文件预览:

预览pdf文件,并实现展示当前、总页数及前后翻页功能。
安装vue-pdf依赖

npm install --save vue-pdf
<template>
	<div class="viewBox" v-if="isShow">
		<!-- 关闭按钮 -->
        <van-icon class="closeBtn" name="cross" @click="isShow = false" />
        <!-- pdf组件 -->
        <div class="pdf-box" v-if="type === 'pdf'">
            <div class="pdf_btn" v-if="pageTotalNum > 1">
                <van-button plain
                    color="#07AC7F"
                    @click="prePage"
                >上一页</van-button>
                {{pageNum}}/{{pageTotalNum}}
                <van-button plain
                    color="#07AC7F"
                    @click="nextPage"
                >下一页</van-button>
            </div>
            <pdf
                :page="pageNum"
                :src="newSrc"
                @progress="loadedRatio = $event"
                @num-pages="pageTotalNum=$event" 
            ></pdf>
        </div>
	</div>
</template>
import pdf from 'vue-pdf'; // pdf
export default {
	components: { pdf },
	data() {
        return {
			pageNum: 1,
            pageTotalNum: 1, // 总页数
            newSrc: '', // 路径
		};
    },
	methods: {
		showFile(newVal) {
            if (this.type === 'pdf') {
                this.pageNum = 1;
                this.pageTotalNum = 1;
                // pdf预览
                const val = this.$base64ToBlob(newVal, 'application/pdf');
                const href = window.URL.createObjectURL(val);
                this.newSrc = pdf.createLoadingTask(href);
            }
		},
		/**
         * 上一页
         */
        prePage() {
            let page = this.pageNum;
            page = page > 1 ? page - 1 : this.pageTotalNum
            this.pageNum = page;
        },
        /**
         * 下一页
         */
        nextPage() {
            let page = this.pageNum;
            page = page < this.pageTotalNum ? page + 1 : 1
            this.pageNum = page;
        },
	}
};

3.docx文件预览:

安装mammoth依赖

npm install --save mammoth
<template>
	<div class="viewBox" v-if="isShow">
		<!-- 关闭按钮 -->
        <van-icon class="closeBtn" name="cross" @click="isShow = false" />
        <!-- docx组件 -->
		<div class="word-box" v-if="type === 'word'" ref="docPreview"></div>
	</div>
</template>
import mammoth from 'mammoth'; // word文档
export default {
	methods: {
		showFile(newVal) {
            if (this.type === 'word') {
                // word文档预览,只能预览docx
                const val = this.$base64ToBlob(newVal, 'application/msword');
                mammoth
                    .convertToHtml({ arrayBuffer: val })
                    .then(this.displayResult)
                    .done();
            }
		},
		/**
         * word----docx预览
         */
        displayResult(result) {
            this.$refs.docPreview.innerHTML = result.value || '';
        },
	}
};

4.excel表格预览(xls、xlsx格式表格):

实现表格预览,实现多表格切换显示。
安装xlsx依赖

npm install xlsx --save
<template>
	<div class="viewBox" v-if="isShow">
		<!-- 关闭按钮 -->
        <van-icon class="closeBtn" name="cross" @click="isShow = false" />
        <!-- 表格组件 -->
        <div class="table-box" v-if="type === 'excel'">
            <van-tabs class="table-tab"
                v-if="sheetNames.length"
                title-active-color="#07AC7F"
                color="#07AC7F"
                @click="clickTab">
                <van-tab
                    v-for="(item, index) in sheetNames" 
                    :key="index"
                    :name="item"
                    :title="item"></van-tab>
            </van-tabs>
            <div class="tableBox" ref="excPreview"></div>
        </div>
	</div>
</template>
import XLSX from 'xlsx'; // excel表格
export default {
	data() {
		return {
			sheetNames: [],
			wsObj: {}
		};
	},
	methods: {
		showFile(newVal) {
            if (this.type === 'excel') {
                // excel预览
                this.sheetNames = []; // 重置
                const wb = XLSX.read(newVal, { type: 'base64' });
                this.sheetNames = [...wb.SheetNames]; // 数组
                this.wsObj = { ...wb.Sheets };
                this.changeExcel(this.sheetNames[0]);
            }
		},
		/**
         * 切换表格
         */
        clickTab(name) {
            this.changeExcel(name);
        },
		/**
         * 处理excel表格
         */
        changeExcel(item) {
            // 获取当前选中表格对象
            const ws = this.wsObj[item];
            const keyArr = Object.keys(ws) || [];
            const HTML = keyArr.length > 1 ? XLSX.utils.sheet_to_html(ws)
                    : '<html><head><meta charset="utf-8"/>' +
                    '<title>SheetJS Table Export</title></head><body><div class="myTable">暂无数据</div></body>' +
                    '</html>';
            this.$nextTick(()=> {
                this.$refs.excPreview.innerHTML = HTML;
                // 获取表格dom元素
                const tables = this.$refs.excPreview.children[2];
                // 添加完毕后 通过空格将数组组装为字符串
                tables.className = 'myTable';
            })
        },
	}
};

5.txt文件预览:

<template>
	<div class="viewBox" v-if="isShow">
		<!-- 关闭按钮 -->
        <van-icon class="closeBtn" name="cross" @click="isShow = false" />
        <div class="txt-box" v-if="type === 'txt'" ref="txtPreview"></div>
	</div>
</template>
export default {
	methods: {
		showFile(newVal) {
            if (this.type === 'txt') {
                // txt
                const val = this.$base64ToBlob(newVal, 'text/plain');
                const reader = new FileReader();
                const that = this;
                reader.readAsText(val);
                reader.onload = function () {
                    that.$refs.txtPreview.innerHTML = reader.result || '';
                }
            }
		}
	}
};

三、补充外壳文件css样式:

<style lang="less" scoped>
.pdf-box, .word-box, .table-box, .txt-box {
    width: 100vw;
    height: 100vh;
}
.pdf-box {
    .pdf_btn {
        padding: 0 10px;
        height: 44px;
        box-sizing: border-box;
        display: flex;
        justify-content: center;
        align-items: center;
        /deep/.van-button {
            margin: 0 2px;
            height: 24px;
            &:nth-child(1) {
                border-radius: 15px 0 0 15px;
            }
            &:nth-child(2) {
                border-radius: 0 15px 15px 0;
            }
        }
    }
    >span {
        width: 100%;
        height: calc(100% - 44px);
    }
}
.word-box {
    padding: 40px 15px 15px;
    box-sizing: border-box;
    overflow: auto;
}
.table-tab {
    width: calc(100% - 44px);
}
// 考核模块table样式
.tableBox {
    width: 100vw;
    height: calc(100vh - 44px);
    overflow: auto;
    // 表格边框
    @table-border: 1px solid #929292;
    // 表格
    /deep/ table.myTable {
        width: auto;
        height: auto;
        color: #333333;
        // 合并边框
        border-collapse: collapse;
        border: @table-border;
        // th,td
        th, td {
            // 禁止换行
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            background: #ffffff;
            padding: 10px;
            border: @table-border;
        }
        th {
            // 正常粗细
            font-weight: normal;
            // 表头信息居左显示
            &.th-info {
                text-align: left;
                span {
                    // 表头信息样式优化
                    margin-right: 30px;
                }
            }
        }
        // 表格内容居中
        td {
            text-align: center;
        }
    }
    /deep/ div.myTable {
        text-align: center;
        margin: 30% auto;
    }
}
.txt-box {
    padding: 40px 15px 15px;
    box-sizing: border-box;
    overflow: auto;
}
</style>

四、js文件补充:

function base64ToBlob(base64, mimetype, slicesize) {
    let num = 0;
    if (!window.atob || !window.Uint8Array) {
        return null;
    }
    mimetype = mimetype || '';
    slicesize = slicesize || 512;
    const bytechars = atob(base64);
    const bytearrays = [];
    for (let a = 0; a < bytechars.length; a += slicesize) {
        const slice = bytechars.slice(a, a + slicesize);
        const bytenums = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
            bytenums[i] = slice.charCodeAt(i);
        }
        const bytearray = new Uint8Array(bytenums);
        bytearrays[bytearrays.length] = bytearray;
    }
    return new Blob(bytearrays, {type: mimetype});
}
  • 17
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值