上传图片获取宽高 表格拖拽 上传图片截取

507 阅读1分钟

上传图片获取宽高

在项目中上传图片需要同时把图片原始宽高传过去, 就用到了下面的方法

getWidthHeight (file) { // 获取附件图片宽高
  let that = this
  let fileReader = new FileReader()
  fileReader.readAsDataURL(file) // 根据图片路径读取图片
  fileReader.onload = function (e) {
    let base64 = this.result
    let img = new Image()
    img.src = base64
    img.onload = function () {
    	console.log('宽' + img.naturalWidth + ' 高:' + img.naturalHeight)
    }
  }
}

element表格拖拽 右击显示菜单

需要表格每行能上下拖拽,并且每行右击能显示一个操作栏,如下图,我点击了第一行 这里使用的是element的表格,拖拽需要安装插件 sortablejs,引入后在mounted中调用rowDrop()方法,** 注意表格的数据时tableData,而在rowDrop()中改变的数据时tableData1, 因为表格拖拽默认不会改变原数据,只是改变了页面二姨而已,如果你在rowDrop()中改变的数据是tableData 这样拖拽后就会发现拖拽后不是你想要的结果**

<template>
  <div class="sortTable">
    <el-table ref="contentTable"
            :data="tableData"
            @row-contextmenu="rihgtClick"
            :header-cell-style="{'background': '#f0f1f4'}"
            :row-class-name="tableRowClassName"
            border>
      <el-table-column
        prop="id"
        label="ID"
        width="180">
      </el-table-column>
      <el-table-column
        prop="date"
        label="日期"
        width="180">
      </el-table-column>
      <el-table-column
        prop="name"
        label="姓名"
        width="180">
      </el-table-column>
      <el-table-column
        prop="address"
        label="地址">
      </el-table-column>
    </el-table>
    <!--鼠标右键点击出现页面-->
    <div v-show="menuVisible">
      <el-menu
        id = "rightClickMenu"
        class="el-menu-vertical"
        @select="handleRightSelect"
        active-text-color="#fff"
        text-color="#fff">
        <el-menu-item index="1" v-show="!ifTop" class="menuItem">
          <span slot="title">置顶</span>
        </el-menu-item>
        <el-menu-item index="2" v-show="ifTop" class="menuItem">
          <span slot="title">取消置顶</span>
        </el-menu-item>
        <el-menu-item index="3" class="menuItem">
          <span slot="title">复制</span>
        </el-menu-item>
        <el-menu-item index="4" class="menuItem">
          <span slot="title">移动</span>
        </el-menu-item>
      </el-menu>
    </div>
  </div>
</template>

<script>
import Sortable from 'sortablejs'
export default {
  name: 'sortTable',
  props: {
    
  },
  data () {
      return {
        tableData: [],
        tableData1: [], // 排序后的数据
        menuVisible: false, // 右击菜单
        ifTop: false, // 当前行是否置顶
        rightRow: 0 // 右击行数据
      }
  },
  created () {
    this.getListData()
  },
  mounted () {
    this.rowDrop()
  },
  methods: {
    getListData () {
      setTimeout(() => {
        this.tableData = [
          {
              id: 1,
              date: '2016-05-02',
              name: '王小虎',
              address: '上海市普陀区金沙江路 1518 弄'
          }, {
              id: 2,
              date: '2016-05-04',
              name: '王小虎',
              address: '上海市普陀区金沙江路 1517 弄'
          }
        ]
        this.tableData1 = JSON.parse(JSON.stringify(this.tableData))
      }, 500)
    },
    rowDrop () { // 拖拽排序
      let _this = this
      const tbody = document.querySelector('.el-table__body-wrapper tbody')
      Sortable.create(tbody, {
        onEnd ({ newIndex, oldIndex }) {
          console.log(_this.tableData)
          const currRow = _this.tableData1.splice(oldIndex, 1)[0]
          _this.tableData1.splice(newIndex, 0, currRow)
        }
      })
    },
    rihgtClick (row, column, event) { // 表格右击
      this.$refs.contentTable.clearSelection()
      this.$refs.contentTable.toggleRowSelection(row, true)
      if (row.top === 1) {
        this.ifTop = true
      } else {
        this.ifTop = false
      }
      this.rightRow = row
      this.menuVisible = true
      document.addEventListener('click', (e) => {
        console.log(e)
        this.menuVisible = false
      })
      let menu = document.querySelector('#rightClickMenu')
      /* 菜单定位基于鼠标点击位置 */
      menu.style.left = event.clientX + 'px'
      menu.style.top = event.clientY + 'px'
      menu.style.position = 'absolute' // 为新创建的DIV指定绝对定位
      menu.style.width = 160 + 'px'
      // 阻止右键默认行为
      event.preventDefault()
    },
    handleRightSelect (key) { // 右击弹框选择
      if (key === '1') { // 置顶
        this.top()
        this.menuVisible = false
      } else if (key === '2') { // 取消置顶
        this.cancelTop()
        this.menuVisible = false
      } else if (key === '3') { // 复制
        this.$refs.copy.clickCopy([this.rightRow])
        this.menuVisible = false
      } else if (key === '4') { // 移动
        this.$refs.remove.clickRemove([this.rightRow])
        this.menuVisible = false
      }
    },
    tableRowClassName ({ rowIndex }) {
      if (rowIndex % 2 === 1) {
        return 'b_f8f8f8'
      } else {
        return 'b_fcfcfd'
      }
    }
  }
}
</script>
<style scoped>
.sortTable {
    background-color: #fff;
}
h3 {
    height: 30px;
}
/*************************标签鼠标右击页面样式******************************/
.el-menu-vertical{
  border: 3px solid rgb(84, 92, 100);
  border-radius: 10px;
  z-index: 100;
}
.el-menu-vertical i{
  color: #777777;
}
.menuItem{
  height: 40px;
  line-height: 40px;
  background-color: #545c64;
  font-size: 1.2rem;
}
.menuItem:hover{
  background-color: #409EFF;
}
</style>

上传图片截取

上传图片可能需要对图片宽高做个限制, 这里使用插件 vue-cropperjs, 直接贴代码

index.vue

<template>
  <div>
    <el-upload class="avatar-uploader"
               action="#"
               :show-file-list="false"
               :before-upload="beforeAvatarUpload">
      <img v-if="uploadUrl"
           :src="uploadUrl"
           class="avatar">
      <i v-else
         class="el-icon-plus avatar-uploader-icon"></i>
    </el-upload>
    <Cropper title="设置封面"
             :visible="cropperVisible"
             @close="cropperClose"
             :cropImgSrc="imageUrl"
             @upCover="uploadFile"
             @upCoverImgShow="handleupCoverImgShow" />
  </div>
</template>

<script>
import Cropper from './cropper'
export default {
  props: ['src'],
  data () {
    return {
      uploadUrl: this.src,
      imageUrl: '',
      cropperVisible: false,
      imgShow: ''
    }
  },
  watch: {
    src () {
      this.uploadUrl = this.src
    }
  },
  components: {
    Cropper
  },
  methods: {
    beforeAvatarUpload (file) {
      if (typeof FileReader === 'function') {
        const reader = new FileReader()

        reader.onload = (event) => {
          this.imageUrl = event.target.result
        }

        reader.readAsDataURL(file)
        this.cropperOpen()
      } else {
        alert('Sorry, FileReader API not supported')
      }
    },
    cropperOpen () {
      this.cropperVisible = true
    },
    cropperClose () {
      this.cropperVisible = false
    },
    // 封面上传
    uploadFile (file) {
      console.log('调用接口传递的文件:', file)
    },
    handleupCoverImgShow (img) {
      this.imgShow = img
    }
  }
}
</script>

<style>
.avatar-uploader {
  display: flex;
  justify-content: center;
}
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.avatar-uploader .el-upload:hover {
  border-color: #409eff;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 320px;
  height: 178px;
  line-height: 178px;
  text-align: center;
}
.avatar {
  width: 320px;
  height: 178px;
  display: block;
}
</style>

cropper.vue

<template>
  <el-dialog :title="title"
             :visible.sync="dialogVisible"
             width="1000px"
             :before-close="handleClose"
             :close-on-click-modal="false">
    <div class="uploadFile">
      <el-upload action="#"
                 multiple
                 accept=".jpg,.jpeg,.png,.gif,.bmp,.JPG,.JPEG,.PBG,.GIF,.BMP"
                 :show-file-list="false"
                 :before-upload="beforeAvatarUpload">
        <el-button size="small"
                   type="primary">点击上传</el-button>
      </el-upload>
    </div>
    <div class="content">
      <section class="cropper-area">
        <div class="img-cropper">
          <vue-cropper v-if="isHead"
                       ref="cropper"
                       :aspect-ratio=1
                       :src="imgSrc"
                       preview=".preview" />
          <vue-cropper v-else
                       ref="cropper"
                       :src="imgSrc"
                       preview=".preview" />
        </div>
        <div class="actions">
          <el-button size="small"
                     type="primary"
                     icon="el-icon-zoom-in"
                     @click.prevent="zoom(0.2)"></el-button>
          <el-button size="small"
                     type="primary"
                     icon="el-icon-zoom-out"
                     @click.prevent="zoom(-0.2)"></el-button>
          <el-button size="small"
                     type="primary"
                     icon="el-icon-refresh-left"
                     @click.prevent="rotate(-90)"></el-button>
          <el-button size="small"
                     type="primary"
                     icon="el-icon-refresh-right"
                     @click.prevent="rotate(90)"></el-button>
          <el-button size="small"
                     type="primary"
                     icon="el-icon-refresh"
                     @click.prevent="reset"></el-button>
        </div>
      </section>
      <section class="preview-area">
        <p>预览效果</p>
        <div class="preview" />
      </section>
    </div>
    <span slot="footer"
          class="dialog-footer">
      <el-button @click="submitNatural">使用原图</el-button>
      <el-button type="primary"
                 @click="submitCroped">确 定</el-button>
    </span>
  </el-dialog>
</template>

<script>
import VueCropper from 'vue-cropperjs'
import 'cropperjs/dist/cropper.css'

export default {
  components: {
    VueCropper
  },
  props: ['title', 'visible', 'cropImgSrc', 'isHead'],
  data () {
    return {
      imgSrc: this.cropImgSrc,
      // cropImg: '',
      data: null,
      dialogVisible: this.visible,
      cropData: '',
      naturalData: ''
    }
  },
  watch: {
    visible () {
      this.dialogVisible = this.visible
    },
    cropImgSrc () {
      // if (this.$refs.cropper) {
      this.imgSrc = this.cropImgSrc
      this.$refs.cropper.replace(this.cropImgSrc)
      // }
    }
  },
  methods: {
    reset () {
      this.$refs.cropper.reset()
    },
    rotate (deg) {
      this.$refs.cropper.rotate(deg)
    },
    zoom (percent) {
      this.$refs.cropper.relativeZoom(percent)
    },
    handleClose (done) {
      this.$emit('close')
      // this.$confirm('确认关闭?')
      //   .then(_ => {
      //     this.$emit('close')
      //   })
      //   .catch(_ => { })
    },
    beforeAvatarUpload (file) {
      if (typeof FileReader === 'function') {
        const reader = new FileReader()

        reader.onload = (event) => {
          this.imgSrc = event.target.result
          // rebuild cropperjs with the updated source
          this.$refs.cropper.replace(event.target.result)
        }

        reader.readAsDataURL(file)
      } else {
        alert('Sorry, FileReader API not supported')
      }
    },
    submitNatural () {
      let imgFile = ''
      if (this.imgSrc.startsWith('data')) {
        imgFile = this.imgToFile(this.imgSrc)
        this.$emit('upCover', imgFile)
      } else {
        const getBase64Image = img => {
          var canvas = document.createElement('canvas')
          canvas.width = img.width
          canvas.height = img.height
          var ctx = canvas.getContext('2d')
          ctx.drawImage(img, 0, 0, img.width, img.height)
          var ext = img.src.substring(img.src.lastIndexOf('.') + 1).toLowerCase()
          var dataURL = canvas.toDataURL('image/' + ext)
          return dataURL
        }
        var tempImage = new Image()
        tempImage.crossOrigin = '*'
        tempImage.src = this.imgSrc
        tempImage.onload = () => {
          var base64 = getBase64Image(tempImage)
          imgFile = this.imgToFile(base64)
          this.$emit('upCover', imgFile)
        }
      }
    },
    imgToFile (cropImg) {
      this.$emit('upCoverImgShow', cropImg)
      const dataURLtoFile = (dataurl, filename) => { // 将base64转换为文件
        var arr = dataurl.split(','); var mime = arr[0].match(/:(.*?);/)[1]
        var bstr = atob(arr[1]); var n = bstr.length; var u8arr = new Uint8Array(n)
        while (n--) {
          u8arr[n] = bstr.charCodeAt(n)
        }
        return new File([u8arr], filename, { type: mime })
      }
      const file = dataURLtoFile(cropImg, 'jpg')
      return file
    },
    submitCroped () {
      const cropImg = this.$refs.cropper.getCroppedCanvas().toDataURL()
      this.$emit('upCover', this.imgToFile(cropImg))
      // this.$refs.cropper.getCroppedCanvas().toBlob((blob) => {
      //   this.$emit('upCoverblob', blob)
      // })
    }
  }
}
</script>

<style scoped>
.uploadFile {
  margin-bottom: 20px;
}
.content {
  display: flex;
  justify-content: space-between;
}
.cropper-area {
  width: 614px;
}
.actions {
  margin-top: 1rem;
}
.actions a {
  display: inline-block;
  padding: 5px 15px;
  background: #0062cc;
  color: white;
  text-decoration: none;
  border-radius: 3px;
  margin-right: 1rem;
  margin-bottom: 1rem;
}
textarea {
  width: 100%;
  height: 100px;
}
.preview-area {
  width: 307px;
}
.preview-area p {
  font-size: 1.25rem;
  margin: 0;
  margin-bottom: 1rem;
}
.preview-area p:last-of-type {
  margin-top: 1rem;
}
.preview {
  width: 100%;
  height: calc(372px * (9 / 16));
  overflow: hidden;
}
.crop-placeholder {
  width: 100%;
  height: 200px;
  background: #ccc;
}
.cropped-image img {
  max-width: 100%;
}
.el-button--small,.el-button--small.is-round {
  padding: 5px 12px !important;
  margin-right: 10px !important;
}
.el-button--mini,.el-button--small {
  font-size: 20px !important;
  border-radius: 3px !important;
}
</style>