vue2使用v-viewer实现自动预览
前面写了一篇《vue2使用v-viewer图片预览…》,发现v-viewer.static虽然不会一直初始化插件了,但是遇到新增/删除图片后 DOM 不刷新的问题,是因为静态绑定 (static) 只会在组件初始化时创建一次 Viewer 实例。要解决这个问题,需要手动管理 Viewer 的生命周期。
我用的是1.7.4的版本
npm install v-viewer@1.7.4--save
有很多无用代码,仅做备份以便查找。后面有一份demo
<template><div id="uploadfiles" v-loading="loading"><div class="navbar" @click.stop><i class="el-icon-arrow-left"></i><span>用心软件</span><i class="el-icon-more" @click="bottomDrawer"></i></div><!-- viewer.static --><!-- 移除 v-viewer 指令,改用 ref --><!-- v-viewer="viewerOptions" --><div class="viewer" ref="imgContainer"><div class="nodata" v-if="imgList.length==0">暂无图片,请点击右上角【<i class="el-icon-more"></i>】选择上传!</div><!-- @viewed="onViewed" --><!-- @load="imageLoaded" --><span v-for="(item, index) in imgList" :key="index" style="display: none;"><img class="image" :src="item.resourceURL" :id="item.resId" @error="imgOnError($event)" /></span></div><input v-show="false" ref="imgRef" type="file" @change="fileChange($event)" accept="image/*,"><el-drawer :with-header="false" :visible.sync="drawer" direction="btt" custom-class="btt-custom-drawer" :before-close="handleClose"><el-button plain class="el-drawer-item" @click="setDef()" :disabled="imgList.length==0 || mItemInfo.defFlag">设为默认</el-button><el-button plain class="el-drawer-item" @click="delete_files()" :disabled="imgList.length==0 || mItemInfo.defFlag">删除图片</el-button><el-button plain class="el-drawer-item" @click="upload_files(1)">手机拍照</el-button><el-button plain class="el-drawer-item" @click="upload_files(2)">从手机相册选择</el-button><el-button plain class="el-drawer-item" @click="handleClose">取消</el-button></el-drawer></div>
</template>
<script>
import 'viewerjs/dist/viewer.css';
import Viewer from 'viewerjs'; // 直接导入 viewerjsimport uploadFile from '@/utils/uploadFile.js';
import CryptoJS from 'crypto-js';
export default {components: { Viewer, uploadFile },data() {return {loading: false,pubid: '',fileMd5: '',ossConfig: null,imgWidth: '',imgHeight: '',uoloadform: {name: "",size: "",type: "",downurl: "",},dataList: [],imgList: [],mIndex: null,mItemInfo: {},viewerOptions: {className: 'mobile-custom-viewer',zIndex: 999,inline: false,//false默认值,点击图片弹出全屏预览; true内联模式,直接在当前容器中显示预览toolbar: false,navbar: true,title: false,button: false,backdrop: 'static', // false:完全禁用遮罩层hide: function () {// if (this.fulled || this.played || this.viewed) return false // 只有通过工具栏关闭按钮才能关闭return true},viewed: (e) => {// this.mIndex = e.detail.index// console.log('当前显示的是第', viewer.index, '张图片')// this.mItemInfo = this.imgList[this.mIndex];},// dblclickToZoom: false, // 禁止双击放大},// loadedImages: 0,viewerInstance: null,drawer: false,}},created() {this.pubid = this.getQueryParams('id');//this.$route.query.idthis.getOss();},mounted() {this.get_UploadFile(1);},beforeDestroy() {console.log('销毁实例')// 销毁实例if (this.viewerInstance) {this.viewerInstance.destroy();this.viewerInstance = null;}},methods: {imgOnError(e) {let img = e.srcElement;img.src = '/src/assets/img/nopic.png'img.onerror = null; //防止闪图},// onViewed(e) {// // const viewer = this.$refs.imgContainer.$viewer;// // if (viewer) viewer.update();// // this.mIndex = e.detail.index// // console.log('当前显示的是第', viewer.index, '张图片')// // this.mItemInfo = this.imgList[this.mIndex];// },// imageLoaded() {// this.loadedImages++// // 所有图片加载完成后自动显示// if (this.loadedImages === this.imgList.length) {// const viewer = that.$refs.imgContainer.$viewer;// // console.log(viewer)// if (viewer) {// viewer.show();// }// }// },bottomDrawer() {this.drawer = true;console.log(this.imgList)if (this.imgList.length > 0) this.getCurrentIndex()},handleClose() {this.drawer = false;},getCurrentIndex() {this.$nextTick(() => {// const viewer = this.$refs.imgContainer.$viewer;// if (viewer) {// console.log('当前显示的是第', viewer.index, '张图片')// this.mIndex = viewer.index;// this.mItemInfo = this.imgList[this.mIndex];// console.log(this.mItemInfo)// }console.log(this.viewerInstance)if (this.viewerInstance) {console.log('当前显示的是第', this.viewerInstance.index, '张图片')this.mIndex = this.viewerInstance.index;this.mItemInfo = this.imgList[this.mIndex];console.log(this.mItemInfo)}});},update_mindex(index) {// const viewer = this.$refs.imgContainer.$viewer;// if (viewer) viewer.view(index);console.log(this.viewerInstance)if (this.viewerInstance) this.viewerInstance.view(index);},initViewer(update) {this.$nextTick(() => {// 动态更新if (update) {console.log('---动态更新---')// 1. 先销毁现有实例if (this.viewerInstance) {this.viewerInstance.destroy();this.viewerInstance = null;}}// 初始化console.log('---初始化---')// const firstImg = this.$refs.imgContainer.querySelector('img');// if (firstImg) firstImg.click();// const viewer = this.$refs.imgContainer.$viewer;// // if (viewer) viewer.show();// if (viewer) viewer.view(this.mIndex);// // 获取图片容器// const container = this.$refs.imgContainer;// console.log(container)// // 初始化Viewer实例// this.viewerInstance = new Viewer(container, {// ...this.viewerOptions,// // 初始化完成后自动显示// ready: () => {// this.viewerInstance.show();// // this.viewerInstance.view(this.mIndex);// },// });// 使用 viewerjs 的原生 API 创建实例this.viewerInstance = new Viewer(this.$refs.imgContainer, this.viewerOptions)// // if (this.imgList.length > 0) this.viewerInstance.show();if (this.imgList.length > 0) this.viewerInstance.view(this.mIndex);})},get_UploadFile(isload) {this.imgList = [];this.loading = true;this.$http({url: this.$http.adornUrl(`/doc/v1/open/picture.getUploadFile`),method: 'post',data: {timestamp: new Date().getTime(),pubid: this.pubid,textStyle: {color: "C82E8F",g: "center",rotate: 0,shadow: 0,size: 50,text: "广州用心汽配演示版",},textStyle2: '',}}).then(async ({ data }) => {this.loading = false;if (data && data.code != 0) {this.loading = false;let message = data.message;if (data.msg) message = data.msg;this.msg(message, 1);return;}this.dataList = data.data.list;console.log(this.dataList)this.$nextTick(() => {if (this.dataList.length > 0) {for (var i = 0; i < this.dataList.length; i++) {if (this.dataList[i].fileType.indexOf('图片') > -1) this.imgList.push(this.dataList[i]);}console.log(this.imgList)if (this.imgList.length > 0) {this.mItemInfo = this.imgList[0];for (var i = 0; i < this.imgList.length; i++) {if (this.imgList[i].defFlag) this.mIndex = i;}console.log(this.mIndex)let that = this;setTimeout(() => {that.initViewer(!isload);// that.update_mindex(that.mIndex);that.loading = false;}, 200);}} else {this.loading = false;}});}).catch(err => {this.loading = false;});},//上传图片getOss() {this.$http({url: this.$http.adornUrl(`/doc/v1/open/picture.ossSet`),method: 'post',data: this.$http.adornData({timestamp: new Date().getTime(),pubid: this.pubid,resId: this.mItemInfo.resId,})}).then(async ({ data }) => {if (data.code !== 0) {alert(data.msg);return}if (data.data) {let content = this.setConfig(data.data);this.ossConfig = content;// console.log(this.ossConfig)}})},setConfig(content) {let Base64 = require('js-base64').Base64let str1 = Base64.decode(content.substr(1, 3) + content.substr(5, 3) + content.substr(9, content.length - 9));let contentN = Base64.decode(str1.substr(1, 3) + str1.substr(5, 3) + str1.substr(9, str1.length - 9));return JSON.parse(contentN);},validRatio(file) {return new Promise(resolve => {const reader = new FileReader();reader.readAsDataURL(file);reader.onload = function () {if (reader.readyState == 2) {const img = new Image();img.src = reader.result;img.onload = function () {let wh = {imgWidth: this.width,imgHeight: this.height,}resolve(wh);};}};});},readFile(file) {return new Promise((resolve, reject) => {const reader = new FileReader();reader.onload = () => resolve(reader.result);reader.onerror = reject;reader.readAsArrayBuffer(file);});},async calculateMD5(file) {const content = await this.readFile(file);const wordArray = CryptoJS.lib.WordArray.create(content);this.fileMd5 = (CryptoJS.MD5(wordArray).toString()).toUpperCase();// console.log('111:' + this.fileMd5)},upload_files(type) {if (type == 1) {this.$refs.imgRef.setAttribute('capture', 'camera');this.$refs.imgRef.click();}if (type == 2) {this.$refs.imgRef.removeAttribute('capture');this.$refs.imgRef.click();}this.drawer = false;},async fileChange(val) {let target = val.target;let file = target.files[0];this.$refs.imgRef.value = "";if (file.type.indexOf("image") < 0) {this.$message({ type: 'warning', message: "只能选择图片!" });return;}this.uoloadform.name = file.name;this.uoloadform.size = file.size;this.uoloadform.type = file.type;// console.log(file);this.calculateMD5(file);this.set_UploadFile(file);},async set_UploadFile(file) {// console.log(file)if (file.type.indexOf("image") > -1) {let isRatioValid = await this.validRatio(file);console.log(isRatioValid)this.imgWidth = isRatioValid.imgWidth;this.imgHeight = isRatioValid.imgHeight;}let that = this;if (!that.ossConfig) {alert("无效配置");return;}await uploadFile.ossNew(that, file, (result) => {if (!result || !result.url) {console.error(file.name + "上传失败");return;}let fileName = file.name;fileName = (fileName != "image.png" && fileName != "image/png" ? fileName : ('图片_' + new Date().getTime() + '.png'));// console.log(result);this.loading = true;this.$http({url: this.$http.adornUrl(`/doc/v1/open/picture.setUploadFile`),method: 'post',data: {timestamp: new Date().getTime(),pubid: this.pubid,fileName: fileName,fileSize: file.size,bucketPath: result.filedir,bucketEtag: result.bucketEtag,downPath: result.url,fileMd5: this.fileMd5,version: '',localPath: '',imgWidth: this.imgWidth,imgHeight: this.imgHeight,}}).then(async ({ data }) => {this.loading = false;if (data && data.code != 0) {alert(data.msg);return}this.msg(`【${file.name}】上传成功!`);this.get_UploadFile();}).catch(err => {console.error(err);this.loading = false;});//调试日志console.error("======================调试时使用======================");console.error("上传OK");this.uoloadform.downurl = result.url;console.error(result.url); //图片地址console.error("======================调试时使用======================");});},//设置默认setDef() {this.drawer = false;this.loading = true;this.$http({url: this.$http.adornUrl(`/doc/v1/open/picture.defUploadFile`),method: 'post',data: {timestamp: new Date().getTime(),pubid: this.pubid,resId: this.mItemInfo.resId,}}).then(async ({ data }) => {this.loading = false;if (data && data.code != 0) {let message = data.message;if (data.msg) {message = data.msg;}this.msg(message, 1);return;}this.msg("设置成功");for (const item of this.imgList) {item.defFlag = false;if (item.resId === this.mItemInfo.resId) item.defFlag = true;}}).catch(err => {this.loading = false;});},// 删除图片delete_files() {this.drawer = false;if (this.mItemInfo.defFlag) {this.msg("默认文件不允许删除", 1);return;}this.$confirm('确认要删除所选文件吗?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning',roundButton: true,customClass: 'yyd-el-message',}).then(() => {this.del_UploadFile();});},del_UploadFile() {this.loading = true;this.$http({url: this.$http.adornUrl(`/doc/v1/open/picture.delUploadFile`),method: 'post',data: this.$http.adornData({timestamp: new Date().getTime(),pubid: this.pubid,resId: this.mItemInfo.resId,})}).then(async ({ data }) => {this.loading = false;if (data && data.code != 0) {this.$message.error(data.msg ? data.msg : '删除失败!')return}this.msg('删除成功!');this.get_UploadFile();})},},
}
</script>
<style lang="">
.yyd-el-message {width: 90%;
}
html,
body {width: 100%;height: 100%;padding: 0;margin: 0;overflow: hidden !important;background: #000;
}
#uploadfiles {width: 100%;height: 100%;padding: 0;margin: 0;overflow: hidden;background: #000;display: flex;flex-direction: column;
}
.navbar {color: #fff;display: flex;align-items: center;justify-content: space-between;height: 50px;font-size: 18px;border-bottom: 1px solid #5c5c5c;position: relative;z-index: 1002;background: #000;
}
.navbar > i {padding: 10px 15px;
}
.viewer {flex: 1 1 auto;
}
.viewer .nodata {padding: 30px 10px;color: #fff;text-align: center;font-size: 16px;
}
.viewer .nodata > i {padding: 0 5px;
}/* 阻止 Viewer 遮罩层的点击事件 */
.mobile-custom-viewer {/* pointer-events: none; *//* top: 50px; */
}/* 允许工具栏和导航栏的交互 */
.mobile-custom-viewer .viewer-canvas > img {/* pointer-events: auto; */
}
.mobile-custom-viewer .viewer-navbar {/* pointer-events: auto !important; */background: #fff;border-top-left-radius: 20px;border-top-right-radius: 20px;
}/* 阻止图片容器的双击事件 */
.mobile-custom-viewer .viewer-container .viewer-canvas {/* pointer-events: none; */
}/* 但允许拖动操作 */
.mobile-custom-viewer .viewer-container .viewer-canvas img {/* pointer-events: auto;cursor: move; */
}.btt-custom-drawer {border-top-left-radius: 20px;border-top-right-radius: 20px;height: auto !important;
}
.btt-custom-drawer .el-drawer__body .el-drawer-item {display: block;width: 100%;font-size: 16px;color: #000;height: 50px;line-height: 50px;text-align: center;margin: 0;padding: 0;outline: 0;border: 0;border-top: 1px solid #f7f7f7;font-family: 微软雅黑;background: #fff;
}
.btt-custom-drawer .el-drawer__body .el-drawer-item:hover {background: #fff;
}
.btt-custom-drawer .el-drawer__body .el-drawer-item:disabled {color: #999;
}
.btt-custom-drawer .el-drawer__body .el-drawer-item:first-child {border-top: 0;
}
.btt-custom-drawer .el-drawer__body .el-drawer-item:last-child {border-width: 4px;
}
</style>
demo:
<template><div><!-- 移除 v-viewer 指令,改用 ref --><div ref="viewerContainer"><img v-for="(img, index) in images" :key="index" :src="img.src" width="100"></div><button @click="addImage">添加图片</button><button @click="removeImage">删除图片</button></div>
</template><script>
import 'viewerjs/dist/viewer.css';
import Viewer from 'viewerjs'; // 直接导入 viewerjsexport default {data() {return {images: [{ src: 'https://picsum.photos/200/300?image=1' },{ src: 'https://picsum.photos/200/300?image=2' }],viewerOptions: {inline: false,toolbar: {zoomIn: 1,zoomOut: 1,reset: 1,prev: 1,next: 1,}},viewerInstance: null};},mounted() {this.initViewer();},methods: {// 初始化 ViewerinitViewer() {if (this.viewerInstance) {this.viewerInstance.destroy();}// 使用 viewerjs 的原生 API 创建实例this.viewerInstance = new Viewer(this.$refs.viewerContainer, this.viewerOptions);},// 添加图片addImage() {const newId = Math.floor(Math.random() * 1000);this.images.push({ src: `https://picsum.photos/200/300?image=${newId}` });this.refreshViewer();},// 删除图片removeImage() {if (this.images.length > 0) {this.images.pop();this.refreshViewer();}},// 刷新 ViewerrefreshViewer() {this.$nextTick(() => {this.initViewer();});}},beforeDestroy() {if (this.viewerInstance) {this.viewerInstance.destroy();}}
};
</script><style>
/* 可选:添加图片间距 */
img {margin: 5px;
}
</style>