属性如下:
<template>
<div class="yh-photoWall">
<!-- 图片渲染包括删除区域 -->
<div v-for="(img,index) in list" :key="img.id" class="yh-photoWall-img"
:style="{height:height+'px',width:width+'px',marginRight:right+'px',marginBottom:bottom+'px'}"
@mouseover ="showDelIcon(img.id)">
<img :src="img.url" style="width:100%;height:100%" @mouseleave="delImgId=null" @dblclick="showPreviewImg(img.url,index)"/>
<i class="el-icon-close yh-photoWall-x" v-if="delImgId === img.id || showAllDel" @mouseenter ="showDelIcon(img.id)" @mouseleave="delImgId=null" @click="delImg(img.id)"></i>
</div>
<!-- 上传图片区域 -->
<div class="yh-photoWall-add"
:style="{height:height+'px',width:width+'px',marginRight:right+'px',marginBottom:bottom+'px',lineHeight:height+'px',color:color,fontSize:size+'px',border:borderStyle, background:uploadBgc}"
@click="clickFile"
>
<i v-if="showIcon" class="el-icon-plus"></i>
<span v-else>{{uploadText}}</span>
</div>
<!-- <input type="file" class="yh-photoWall-file"> -->
<!-- 预览图片区域 -->
<div v-if="showFlag" class="showFlag" >
<img :src="previewImgUrl" class="previewImg" />
<i class="el-icon-close yh-close-perview" @click="closeperview"></i>
<i class="el-icon-zoom-in yh-big-perview" @click="perImgBig"></i>
<i class="el-icon-zoom-out yh-samll-perview" @click="perImgSamll"></i>
<i class="el-icon-arrow-left yh-left-perview" @click="leftPerImg"></i>
<i class="el-icon-arrow-right yh-right-perview" @click="rightPerImg"></i>
<div class="yh-current-box" :style="{width:list.length*30+'px'}" v-if="showCurrentBox">
<span :class="['yh-current-perview',{'yh-current-perview-active':(i-1)===currentImg}]" v-for="i in list.length" :key="i" @click="jumpPreImg(i)"></span>
</div>
</div>
</div>
</template>
<script>
/* 组件介绍:该组件为照片墙组件,包括上传图片,删除图片,图片预览功能。
* 属性如下:
* imgList:必须值、初始的图片渲染列表,Array
* width:单元图片的宽度值默认为100px,height:单元图片的宽度值默认为100px;Number
* right:照片墙右侧距离默认值10px,bottom:照片墙下侧距离默认值10px;Number
* color:上传区域的文本及边框颜色,默认值#66b1ff,支持rgb\rgba\十六进制(3位或者6位)\hsl\hsla
* size:上传区域的文本字体大小,默认值20px;Number
* uploadText:上传区域的文本,默认值:点击上传。String
* borderStyle:上传区域的边框样式默认值直线,支持CSS一切边框样式。包括直线,曲线.... String[dashed \solid\dotted\double\groove\ridge\inset\outset]
* showIcon:上传区域是否显示图标,默认值不显示,如果设置则显示图标不显示文字。Boolean
* showAllDel:是否直接显示所有图片的删除按钮,默认值不显示,不显示的情况是由鼠标进入图片显示。Boolean
* base64:上传图片时返回给父组件的格式,默认是浏览器创建url,使用base64可以通过这个属性设置。Boolean
* showCurrentBox:是否展示当前图片位置。Boolean
* perviewMethods:触发预览图片的方式,默认为双击。可以选择click或者dblclick
* uploadBgc:修改上传图片区域的背景色,默认#fff,支持rgb\rgba\十六进制(3位或者6位)\hsl\hsla
* 方法:
* getDelImgIdList(delImgIdList)传回去的delImgIdList是需要删除图片的id
* addImg(addImgList)传回去的addImgList是新增图片的信息,包括随机生成的Id与url。url参考base64属性
*/
function CheckIsColor(bgVal) {
let type='';
if(/^rgb\(/.test(bgVal)){
//如果是rgb开头,200-249,250-255,0-199
type = "^[rR][gG][Bb][\(]([\\s]*(2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?)[\\s]*,){2}[\\s]*(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)[\\s]*[\)]{1}$";
}else if(/^rgba\(/.test(bgVal)){
//如果是rgba开头,判断0-255:200-249,250-255,0-199 判断0-1:0 1 1.0 0.0-0.9
type = "^[rR][gG][Bb][Aa][\(]([\\s]*(2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?)[\\s]*,){3}[\\s]*(1|1.0|0|0.[0-9])[\\s]*[\)]{1}$";
}else if(/^#/.test(bgVal)){
//六位或者三位
type = "^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$"
}else if(/^hsl\(/.test(bgVal)){
//判断0-360 判断0-100%(0可以没有百分号)
type = "^[hH][Ss][Ll][\(]([\\s]*(2[0-9][0-9]|360|3[0-5][0-9]|[01]?[0-9][0-9]?)[\\s]*,)([\\s]*((100|[0-9][0-9]?)%|0)[\\s]*,)([\\s]*((100|[0-9][0-9]?)%|0)[\\s]*)[\)]$";
}else if(/^hsla\(/.test(bgVal)){
type = "^[hH][Ss][Ll][Aa][\(]([\\s]*(2[0-9][0-9]|360|3[0-5][0-9]|[01]?[0-9][0-9]?)[\\s]*,)([\\s]*((100|[0-9][0-9]?)%|0)[\\s]*,){2}([\\s]*(1|1.0|0|0.[0-9])[\\s]*)[\)]$";
}
let re = new RegExp(type);
if (bgVal.match(re) == null){
return false
}else{
return true
}
}
export default {
name:'photoWall',
props:{
imgList:{
require:true,
type:Array,
},
width:{
type:Number,
default:100,
},
height:{
type:Number,
default:100,
},
right:{
type:Number,
default:10,
},
bottom:{
type:Number,
default:10,
},
color:{
default:function(){
return '#66b1ff'
},
validator:function(value){
return CheckIsColor(value)
}
},
size:{
default:20,
type:Number
},
uploadText:{
default:'点击上传',
type:String
},
borderStyle:{
default:function(){
return 'solid'
},
type:String,
validator:function(value){
return ["dashed","solid","dotted","double","groove","ridge","inset","outset"].indexOf(value) !== -1
}
},
showIcon:{
default:false,
type:Boolean
},
showAllDel:{
default:false,
type:Boolean
},
base64:{
default:false,
type:Boolean
},
showCurrentBox:{
default:true,
type:Boolean
},
uploadBgc:{
default:function(){
return '#fff'
},
validator:function(value){
return CheckIsColor(value)
}
}
},
data(){
return {
delImgId:null,
list:this.imgList,
delImgIdList:[],
addImgList:[],
showFlag:false,
previewImgUrl:null,
currentImg:null,
dbWidth:'',
dbHeight:'',
}
},
mounted(){
this.getWindowHW()
window.addEventListener('resize',this.getWindowHW)
},
destroyed(){
window.removeEventListener('resize',this.getWindowHW())
},
methods:{
// 获取浏览器宽高
getWindowHW(){
this.dbWidth=window.innerWidth;
this.dbHeight=window.innerHeight;
},
// 让删除图标展示
showDelIcon(imgId){
this.delImgId=imgId
},
// 删除图片
delImg(imgId){
this.delImgIdList.push(imgId);
this.$emit('getDelImgIdList',this.delImgIdList) //将删除图片的id给父组件
this.list=this.list.filter(img=>img.id!=imgId)
},
// 上传图片
clickFile(){
let yhphotoBox = document.querySelector('.yh-photoWall')
let uploadBtn = document.createElement('input')
yhphotoBox.appendChild(uploadBtn)
uploadBtn.setAttribute('type','file')
uploadBtn.setAttribute('class','yh-photoWall-file')
uploadBtn.click();
let _that=this
uploadBtn.onchange = function(){
let that=this
let imageList=["image/png","image/jpg","image/jpeg","image/gif"] //上传的图片类型白名单
// console.log(that.files[0].type);
if(!this.type||!imageList.some(item=>item===that.files[0].type)){
return _that.$message.error('只支持png、jpeg、jpg、gif模式')
}
if(_that.base64 && that.files[0].size>128*1024){
yhphotoBox.removeChild(uploadBtn)
return _that.$message.warning('base64格式数据最大为128字节')
}
let url=''
function uploadImg(url){
_that.list.push({url,id:Math.random()*10000})
_that.addImgList.push({url,id:Math.random()*10000})
_that.$emit('addImg', _that.addImgList)
}
if(_that.base64){
let reader =new FileReader();
reader.readAsDataURL(that.files[0])
reader.onload = function(){
url=reader.result
uploadImg(url)
yhphotoBox.removeChild(uploadBtn)
}
}else{
url=URL.createObjectURL(that.files[0]);
uploadImg(url)
yhphotoBox.removeChild(uploadBtn)
}
}
},
// 展示预览图片
showPreviewImg(val,index){
this.previewImgUrl=val;
this.showFlag=true;
this.currentImg=index;
},
// 关闭预览区域
closeperview(){
this.showFlag=false;
},
// 放大图片
perImgBig(){
// 放大预览图片
const perImg=document.querySelector('.previewImg')
const width=perImg.width
const height=perImg.height
const nowWidth=width+50
const computeRatio=nowWidth*height/width //得到等比列的大小
if(nowWidth>=(this.dbWidth-200)||computeRatio>=(this.dbHeight-200)){
return
}else{
perImg.style.width=nowWidth+'px'
}
},
// 缩小预览图片
perImgSamll(){
// 缩小图片
const perImg=document.querySelector('.previewImg')
const width=perImg.width
const height=perImg.height
if(width<=50||height<=50){
return
}else{
const nowWidth=width-50
perImg.style.width=nowWidth<=50?'50px':nowWidth+'px'
}
},
// 像上一张
leftPerImg(){
document.querySelector('.previewImg').style.width='auto'
this.currentImg=this.currentImg>0?--this.currentImg:this.list.length-1;
this.previewImgUrl=this.list[this.currentImg].url;
},
// 预览下一张
rightPerImg(){
document.querySelector('.previewImg').style.width='auto'
this.currentImg=this.currentImg<this.list.length-1?++this.currentImg:0;
this.previewImgUrl=this.list[this.currentImg].url;
},
// 预览指定图片
jumpPreImg(i){
document.querySelector('.previewImg').style.width='auto'
this.currentImg=i-1
this.previewImgUrl=this.list[this.currentImg].url;
}
}
}
</script>
<style lang="less">
.yh-photoWall{
.yh-photoWall-file{
display: none;
}
.yh-photoWall-img{
position: relative;
display: inline-block;
img{
display: inline-block;
}
.yh-photoWall-x{
position: absolute;
top: 0;
right: 0;
transform: translate(50%,-50%);
background-color: #333;
color: #fff;
border-radius: 50%;
}
}
.yh-photoWall-add{
text-align: center;
display: inline-block;
vertical-align: bottom;
box-sizing: border-box;
}
.showFlag{
width: 100%;
height: 100%;
background-color:rgba(0, 0, 0, 0.3);
position: fixed;
top: 0;
left: 0;
.previewImg{
max-height: calc(100% - 200px);
max-width: calc(100% - 200px);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
z-index: 9999;
}
.yh-close-perview{
position: absolute;
top: 20px;
right: 20px;
font-size: 30px;
color: #fff;
}
.yh-big-perview,.yh-samll-perview{
position: absolute;
bottom: 30px;
color: #fff;
font-size: 30px;
left: 50%;
}
.yh-big-perview{
transform: translate(calc(-50% - 55px));
}
.yh-samll-perview{
transform: translate(calc(-50% + 55px));
}
.yh-left-perview,.yh-right-perview{
position: absolute;
top: 50%;
color: #fff;
background-color: rgb(92, 86, 86);
border-radius: 50%;
font-size: 30px;
width: 50px;
height: 50px;
text-align: center;
line-height: 50px;
}
.yh-left-perview{
left: 50px;
}
.yh-right-perview{
right: 50px;
}
.yh-current-perview{
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #666;
}
.yh-current-perview-active{
background-color: #fff;
}
.yh-current-box{
display: flex;
position: absolute;
bottom: 70px;
left: 50%;
justify-content: space-around;
transform: translate(-50%);
}
}
}
</style>
测试用例
<template>
<photo-wall :imgList="imgList" :width="200" :height="100" :right="20" @getDelImgIdList="getDelImgIdList" showIcon perviewMethods="click" :size="50" borderStyle="dashed" @addImg="addImg" style="margin-left:20px;"/>
</template>
<script>
import photoWall from '@/components/photoWall.vue'
export default {
components:{
photoWall,
},
data(){
return {
imgList:[
{id:1,url:'https://fuss10.elemecdn.com/8/27/f01c15bb73e1ef3793e64e6b7bbccjpeg.jpeg'},
// {id:2,url:'https://fuss10.elemecdn.com/1/8e/aeffeb4de74e2fde4bd74fc7b4486jpeg.jpeg'}
]
}
},
methods:{
getDelImgIdList(val){
console.log(val);
},
addImg(val){
console.log(val);
}
}
}
</script>
<style lang="less">
</style>