yangfeng
2023-11-08 3af52f6a984c5fe6671640dbfb1c40540416ea7d
新建产品增加上传图片功能,增加vue-cropper插件
1个文件已添加
3个文件已修改
362 ■■■■ 已修改文件
package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productManage/product/AddProductDialog.vue 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productManage/product/IconCropper.vue 288 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json
@@ -17,6 +17,7 @@
    "element-ui": "^2.15.13",
    "js-cookie": "^3.0.5",
    "vue": "^2.6.14",
    "vue-cropper": "^0.6.4",
    "vue-router": "^3.5.1",
    "vuex": "^3.6.2"
  },
src/main.js
@@ -15,6 +15,9 @@
ElementUI.Dialog.props.closeOnClickModal.default = false
Vue.config.productionTip = false
// 图片裁剪
import VueCropper from "vue-cropper"
Vue.use(VueCropper)
new Vue({
  router,
src/views/productManage/product/AddProductDialog.vue
@@ -122,6 +122,14 @@
                      :disabled="!showFooter"
                    ></el-input>
                  </el-form-item>
                  <el-form-item label="图片上传:" prop="imageUrl">
                    <IconCropper
                      :isView="isView"
                      :image-url="editConfig.infomation.imageSrc"
                      @getImageData="getImageData"
                      style="width: 85%"
                    />
                  </el-form-item>
                </div>
                <div class="right">
                  <el-form-item label="销售价格" prop="salePrice">
@@ -462,11 +470,10 @@
</template>
<script>
// import CommonFormTableView from "@/components/makepager/CommonFormTableView"
import IconCropper from "./IconCropper"
import { getProductCategoryList } from "@/api/product/productCategory"
import { getProductList, addProduct, updateProduct } from "@/api/product/product"
let inputElement = null
export default {
  name: "AddProductDialog",
  props: {
@@ -482,7 +489,7 @@
      }
    }
  },
  components: {},
  components: { IconCropper },
  computed: {
    modalTitle() {
      if (this.editConfig.title === "编辑" && this.editConfig.autoEdit) {
@@ -542,10 +549,19 @@
      ],
      statisticsMap: {
        inLibrary: 0 // 在库
      }
      },
      isView: false
    }
  },
  created() {
    if (this.editConfig.title === "编辑" && !this.editConfig.autoEdit) {
      this.isView = true
    }
    // if (this.editConfig.title === "新建") {
    //   this.editConfig.infomation.imageSrc = ""
    // } else {
    //   this.editConfig.infomation.imageSrc = "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
    // }
    this.getProductCategoryList()
    this.getProductList()
    this.setTableForm()
@@ -712,50 +728,8 @@
      this.tableData.splice(scope.$index, 1)
    },
    // 上传图片
    toGetImg() {
      if (this.showFooter) {
        if (inputElement === null) {
          // 生成文件上传的控件
          inputElement = document.createElement("input")
          inputElement.setAttribute("type", "file")
          inputElement.style.display = "none"
          if (window.addEventListener) {
            inputElement.addEventListener("change", this.uploadFile, false)
          } else {
            inputElement.attachEvent("onchange", this.uploadFile)
          }
          document.body.appendChild(inputElement)
        }
        inputElement.click()
      }
    },
    uploadFile(el) {
      if (el && el.target && el.target.files && el.target.files.length > 0) {
        console.log(el)
        const files = el.target.files[0]
        const isLt2M = files.size / 1024 / 1024 < 2
        const size = files.size / 1024 / 1024
        console.log(size)
        // 判断上传文件的大小
        if (!isLt2M) {
          this.$message.error("上传头像图片大小不能超过 2MB!")
        } else if (files.type.indexOf("image") === -1) {
          //如果不是图片格式
          this.$message.error("请选择图片文件")
        } else {
          const that = this
          const reader = new FileReader() // 创建读取文件对象
          reader.readAsDataURL(el.target.files[0]) // 发起异步请求,读取文件
          reader.onload = function () {
            // 读取完成后,将结果赋值给img的src
            that.imageSrc = this.result
            console.log(this.result)
            // 数据传到后台
            //const formData = new FormData()
            //formData.append('file', files); // 可以传到后台的数据
          }
        }
      }
    getImageData(data) {
      console.log(data, "图片数据")
    },
    // 进出点击
    inOutBoundClick() {
src/views/productManage/product/IconCropper.vue
New file
@@ -0,0 +1,288 @@
<template>
  <div class="cropper-wrapper">
    <div v-if="isView">
      <el-image :src="imageUrl" class="view-image">
        <div slot="error" class="image-slot">
          <i class="el-icon-picture-outline"></i>
        </div>
      </el-image>
    </div>
    <div v-else>
      <template v-if="!isPreview">
        <!-- element 上传图片按钮 -->
        <el-upload
          class="avatar-uploader"
          action=""
          drag
          :auto-upload="false"
          :show-file-list="false"
          :on-change="handleChangeUpload"
        >
          <i class="el-icon-plus avatar-uploader-icon"></i>
        </el-upload>
      </template>
      <div class="pre-box" v-else>
        <el-image :src="previewImg" alt="" class="view-image" />
        <el-upload
          class="upload-demo"
          action=""
          :auto-upload="false"
          :show-file-list="false"
          :on-change="handleChangeUpload"
        >
          <el-button v-if="!isView" type="primary" plain>更换图片</el-button>
        </el-upload>
      </div>
    </div>
    <!-- vueCropper 剪裁图片实现-->
    <el-dialog title="图片剪裁" :visible.sync="dialogVisible" class="crop-dialog" append-to-body>
      <div class="cropper-content">
        <div class="cropper" style="text-align: center">
          <vueCropper
            ref="cropper"
            :img="option.img"
            :outputSize="option.size"
            :outputType="option.outputType"
            :info="true"
            :full="option.full"
            :canMove="option.canMove"
            :canMoveBox="option.canMoveBox"
            :original="option.original"
            :autoCrop="option.autoCrop"
            :fixed="option.fixed"
            :fixedNumber="option.fixedNumber"
            :centerBox="option.centerBox"
            :infoTrue="option.infoTrue"
            :fixedBox="option.fixedBox"
            :autoCropWidth="option.autoCropWidth"
            :autoCropHeight="option.autoCropHeight"
            @cropMoving="cropMoving"
          />
        </div>
      </div>
      <div class="action-box">
        <el-upload
          class="upload-demo"
          action=""
          :auto-upload="false"
          :show-file-list="false"
          :on-change="handleChangeUpload"
        >
          <el-button type="primary" plain>更换图片</el-button>
        </el-upload>
        <el-button type="primary" plain @click="clearImgHandle">清除图片</el-button>
        <el-button type="primary" plain @click="rotateLeftHandle">左旋转</el-button>
        <el-button type="primary" plain @click="rotateRightHandle">右旋转</el-button>
        <el-button type="primary" plain @click="changeScaleHandle(1)">放大</el-button>
        <el-button type="primary" plain @click="changeScaleHandle(-1)">缩小</el-button>
        <el-button type="primary" plain @click="downloadHandle('blob')">下载</el-button>
      </div>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="finish" :loading="loading">确认</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
export default {
  name: "CropperV",
  props: {
    isView: {
      type: Boolean,
      default: false
    },
    imageUrl: {
      type: String,
      default: ""
    }
  },
  data() {
    return {
      isPreview: false,
      dialogVisible: false,
      previewImg: "", // 预览图片地址
      // 裁剪组件的基础配置option
      option: {
        img: "https://pic1.zhimg.com/80/v2-366c0aeae2b4050fa2fcbfc09c74aad4_720w.jpg", // 裁剪图片的地址
        info: true, // 裁剪框的大小信息
        outputSize: 1, // 裁剪生成图片的质量
        outputType: "png", // 裁剪生成图片的格式
        canScale: true, // 图片是否允许滚轮缩放
        autoCrop: true, // 是否默认生成截图框
        canMoveBox: true, // 截图框能否拖动
        autoCropWidth: 200, // 默认生成截图框宽度
        autoCropHeight: 200, // 默认生成截图框高度
        fixedBox: false, // 固定截图框大小 不允许改变
        fixed: true, // 是否开启截图框宽高固定比例
        fixedNumber: [1, 1], // 截图框的宽高比例
        full: false, // 是否输出原图比例的截图
        original: false, // 上传图片按照原始比例渲染
        centerBox: false, // 截图框是否被限制在图片里面
        infoTrue: true // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
      },
      // 防止重复提交
      loading: false
    }
  },
  mounted() {
    console.log(this.imageUrl)
    if (!this.isView && this.imageUrl?.length > 0) {
      this.isPreview = true
      this.previewImg = this.imageUrl
    } else {
      this.isPreview = false
    }
  },
  methods: {
    // 上传按钮 限制图片大小和类型
    handleChangeUpload(file, fileList) {
      const isJPG = file.raw.type === "image/jpeg" || file.raw.type === "image/png"
      const isLt2M = file.size / 1024 / 1024 < 2
      if (!isJPG) {
        this.$message.error("上传头像图片只能是 JPG/PNG 格式!")
        return false
      }
      if (!isLt2M) {
        this.$message.error("上传头像图片大小不能超过 2MB!")
        return false
      }
      console.log(file, fileList)
      // 上传成功后将图片地址赋值给裁剪框显示图片
      this.$nextTick(async () => {
        // base64方式
        // this.option.img = await fileByBase64(file.raw)
        this.option.img = URL.createObjectURL(file.raw)
        this.loading = false
        this.dialogVisible = true
      })
    },
    // 放大/缩小
    changeScaleHandle(num) {
      num = num || 1
      this.$refs.cropper.changeScale(num)
    },
    // 左旋转
    rotateLeftHandle() {
      this.$refs.cropper.rotateLeft()
    },
    // 右旋转
    rotateRightHandle() {
      this.$refs.cropper.rotateRight()
    },
    // 下载
    downloadHandle(type) {
      let aLink = document.createElement("a")
      aLink.download = "author-img"
      if (type === "blob") {
        this.$refs.cropper.getCropBlob((data) => {
          aLink.href = URL.createObjectURL(data)
          aLink.click()
        })
      } else {
        this.$refs.cropper.getCropData((data) => {
          aLink.href = data
          aLink.click()
        })
      }
    },
    // 清理图片
    clearImgHandle() {
      this.option.img = ""
    },
    // 截图框移动回调函数
    cropMoving(data) {
      console.log(data)
      // 截图框的左上角 x,y和右下角坐标x,y
      // let cropAxis = [data.axis.x1, data.axis.y1, data.axis.x2, data.axis.y2]
      // console.log(cropAxis)
    },
    finish() {
      // 获取截图的 blob 数据
      this.$refs.cropper.getCropBlob((blob) => {
        this.loading = true
        this.dialogVisible = false
        this.previewImg = URL.createObjectURL(blob)
        this.isPreview = true
        console.log(blob)
        this.$emit("getImageData", blob)
      })
      // 获取截图的 base64 数据
      // this.$refs.cropper.getCropData(data => {
      //     console.log(data)
      // })
    }
  }
}
</script>
<style lang="scss" scoped>
.cropper-wrapper {
  .avatar-uploader .el-upload:hover {
    border-color: #409eff;
  }
  .avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 178px;
    height: 178px;
    line-height: 178px;
    text-align: center;
  }
  .avatar {
    width: 178px;
    height: 178px;
    display: block;
  }
  .view-image {
    width: 180px;
    height: 180px;
    background: #f5f7fa;
    font-size: 30px;
    text-align: center;
    line-height: 180px;
  }
  .image-slot {
    color: #909399;
    text-align: center;
  }
}
::v-deep {
  .el-upload-dragger {
    width: 180px;
    height: 180px;
  }
}
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  width: 178px;
  height: 178px;
}
.crop-dialog {
  .cropper-content {
    padding: 0 40px;
    .cropper {
      width: auto;
      height: 350px;
    }
  }
  .action-box {
    padding: 25px 40px 10px;
    display: flex;
    justify-content: center;
    button {
      width: 80px;
      margin-right: 15px;
    }
  }
}
</style>