记录用vue用原生input写图片上传

如果项目中,因为各种因素导致无法用UI组件完成文件上传,需要用原生input标签完成可以采用如下方式

功能如下

  • 1.input介绍
  • 2.修改上传input样式
  • 3.多个文件上传(批量上传+多次累计上传)
  • 4.图片删除
  • 5.图片大图查看

一.input上传介绍

type:设置为file才能有上传功能
accept:允许上传的文件类型
multiple:批量上传(一次上传多张)想要上传多个文件需要在选择文件时,按住Ctrl进行多选

贴出html代码:

 <input id="upload-input" type="file" class="file" multiple accept="image/jpg,image/png" />

二.修改input样式

文件上传input的默认样式如图


文件上传input的默认样式

针对input标签设置一些样式不会起作用(比如去掉黑色边框之类的),所以我们采用如下方式:

  1. 先隐藏type = "file"的元素,添加一个type="button"的元素
  2. 把上传按钮的样式写到type="button"的元素上
  3. 在type="button"的元素上绑定点击事件
    代码如下:
    1.html 部分
 <div class="demo-upload-btn">
     <!--上传input-->
     <input id="upload-input" type="file" class="file" multiple accept="image/jpg,image/png" />
      <!--加一个相机图标-->
     <div  class="camera"></div>
     <!--点击上传的按钮-->
     <input type="button" class="btn" @click="clickFile" />
  </div>

2.JS部分

<script>
  clickFile (type) {
      const input = document.querySelector('#file' + type)
      input.click()
   }
</script>

3.css部分

<style lang="less">
.demo-upload-btn {
    position: relative;
    width: 60px;
    height: 60px;
    background: #fff;
    border: 1px dashed #d7dde4;
    border-radius: 4px;
    text-align: center;

    overflow: hidden;
    transition: border-color 0.2s ease;
    .file { // 将type="file"隐藏
      display: none; 
    }
    .btn { //type="button"设置样式
      position: absolute;
      top: 0;
      left: 0;
      width: 60px;
      height: 60px;
      background: rgba(0, 0, 0, 0);
      z-index: 10;
      border: none;
      cursor: pointer;
    }
    .camera {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      font-size: 28px;
      cursor: pointer;
      background-image: '你的icon';
    }
  }
</style>

然后上传的样式变成了


image.png

三.多个文件上传

1.获取文件上传的数据

给input="file"的标签加上input的change事件,获取上传后的文件数据

html 部分

 <div class="demo-upload-btn">
     <!--上传input-->
     <input id="upload-input" type="file" class="file" multiple accept="image/jpg,image/png" @change="handleSuccess" />
      <!--加一个相机图标-->
     <div  class="camera"></div>
     <!--点击上传的按钮-->
     <input type="button" class="btn" @click="clickFile" />
  </div>

JS部分

<script>
   handleSuccess1 (e) {
      // 因为上传过程为实例,这里模拟添加 url
      console.log(e.target.files) //拿到了上传的文件,是个数组
    }
</script>

打印如下

image.png

如果是批量上传多张,FileList会是多条数据
image.png

2.实现在本地显示上传的文件/图片

HTML部分

<div class="demo-upload-box">
    <div class="demo-upload-image-box" v-if="imageUrlArr && imageUrlArr.length">
    <!--循环展示上传后的图片-->
        <div class="demo-upload-image" v-for="(item,index) in imageUrlArr" :key="index">
            <img :src="item">
            <div class="demo-upload-box-cover">
                <!--图片查看大图-->
                <div class="ios-eye-outline" @click.native="handleView(item)"></div>
                <!--图片删除-->
                <div class="ios-trash-outline" @click.native="handleRemove(item)"></div>
            </div>
        </div>
     </div>
      <!--图片上传-->
     <div class="demo-upload-btn">
         <input ref="uploadInput1" type="file" multiple id="file" accept="image/jpg,image/png" class="file" @change="handleSuccess">
         <div type="camera"></div>
         <input type="button" class="btn" @click="clickFile" />
    </div>
</div>

JS部分

// urlList作为公共变量,可以实现批量上传+多次累计上传
var urlList = []
handleSuccess1 (e) {
      // 因为上传过程为实例,这里模拟添加 url
      let file = e.target
      for (let i = 0; i < file.files.length; i++) {
        urlList.push(window.URL.createObjectURL(file.files.item(i)))
      }
      this.imageUrlArr = urlList
}

如图:


image.png

三.图片删除和查看大图

因为input中的files是只读,不能直接删除某一项,所以需要用一个变量接收上传的文件信息,所以上传方法和删除方法改造如下:

//公共变量记录上传文件:filesData
 handleSuccess (e) {
      let file = e.target
      for (let i = 0; i < file.files.length; i++) {
        this.imageUrlArr.push(window.URL.createObjectURL(file.files.item(i)))
        this.filesData.push(file.files[i])
      }
},
// 删除上传的案例图
    handleRemove (index) {
      this.imageUrlArr.splice(index, 1)
      this.filesData.splice(index, 1)
      this.$forceUpdate()
}
 // 查看大图
    handleView (index) {
      this.bigPicSrc = this.imageUrlArr[index]
 }

三.完整代码

<template>
  <div class="updata-component">
    <div class="demo-upload-box">
      <div class="demo-upload-image-box" v-if="imageUrlArr && imageUrlArr.length">
        <div class="demo-upload-image" v-for="(item,index) in imageUrlArr" :key="index">
          <img :src="item">
          <div class="demo-upload-box-cover">
            <Icon type="ios-eye-outline" @click.native="handleView(index)"></Icon>
            <Icon type="ios-trash-outline" @click.native="handleRemove(index)"></Icon>
          </div>
        </div>
      </div>
      <div class="demo-upload-btn">
        <input :ref="name" type="file" :multiple="multiple" :accept="accept" class="file" @change="handleSuccess">
        <Icon type="camera"></Icon>
        <input type="button" class="btn" @click="clickFile" />
      </div>
    </div>
    <!-- 查看大图 -->
    <div class="big-pic" v-if="bigPicSrc" @click.self="bigPicSrc = ''">
      <img class="" :src="bigPicSrc" />
    </div>
  </div>
</template>
<script>
export default {
  props: {
    multiple: {
      type: Boolean,
      default: () => {
        return false
      }
    },
    accept: {
      type: String,
      default: () => {
        return 'image/jpg,image/png'
      }
    },
    name: {
      type: String,
      default: () => {
        return 'uploadInput'
      }
    }
  },
  data () {
    return {
      bigPicSrc: '',
      imageUrlArr: [],
      filesData: []
    }
  },
  methods: {
    // 文件上传接收
    handleSuccess (e) {
      let file = e.target
      for (let i = 0; i < file.files.length; i++) {
        this.imageUrlArr.push(window.URL.createObjectURL(file.files.item(i)))
        this.filesData.push(file.files[i])
      }
    },
    clickFile () {
      const input = this.$refs[this.name]
      input.click()
    },
    // 删除上传的案例图
    handleRemove (index) {
      this.imageUrlArr.splice(index, 1)
      this.filesData.splice(index, 1)
      this.$forceUpdate()
    },
    // 查看大图
    handleView (index) {
      this.bigPicSrc = this.imageUrlArr[index]
    }
  }
}
</script>
<style scoped lang="less">
.updata-component {
  .demo-upload-box {
    display: flex;
    .demo-upload-image-box {
      display: flex;
    }
    .demo-upload-image {
      width: 60px;
      height: 60px;
      text-align: center;
      line-height: 60px;
      border-radius: 4px;
      position: relative;
      box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
      margin-right: 4px;
      .demo-upload-box-cover {
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        background: rgba(0, 0, 0, 0);
      }
      &:hover {
        .demo-upload-box-cover {
          position: absolute;
          top: 0;
          bottom: 0;
          left: 0;
          right: 0;
          background: rgba(0, 0, 0, 0.3);
        }
        .demo-upload-box-cover .ivu-icon {
          color: #fff;
          font-size: 20px;
          cursor: pointer;
          margin: 0 2px;
        }
      }
    }
    .demo-upload-btn {
      position: relative;
      width: 60px;
      height: 60px;
      background: #fff;
      border: 1px dashed #d7dde4;
      border-radius: 4px;
      text-align: center;

      overflow: hidden;
      transition: border-color 0.2s ease;
      .file {
        display: none;
      }
      .btn {
        position: absolute;
        top: 0;
        left: 0;
        width: 60px;
        height: 60px;
        background: rgba(0, 0, 0, 0);
        z-index: 10;
        border: none;
        cursor: pointer;
      }
      .ivu-icon-camera {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        font-size: 28px;
        cursor: pointer;
      }
    }
  }
  .big-pic {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.6);
    z-index: 9999;
    img {
      position: absolute;
      width: 700px;
      height: auto;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
    }
  }
}
</style>
<style lang="less">
.ivu-form .ivu-form-item-label {
  font-size: 14px;
  color: #45484c;
}
</style>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,743评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,296评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,285评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,485评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,581评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,821评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,960评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,719评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,186评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,516评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,650评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,936评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,757评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,991评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,370评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,527评论 2 349

推荐阅读更多精彩内容