Jeecg-boot 开发挖坑日记02 用户头像上传 / Vue-AntD上传组件

项目正式开始,第一个功能模块的用户信息编辑就开始卡壳,特别是头像上传组件。

  • 弹出框 a-mdal 的表单值怎么获取到的
  • ant-design-vue 上传组件的头像上传

使用 ant-design-vue 上传组件的头像上传。

在 Jeecg-boot 中头像上传是 antD 的上传组件 listType="picture-card"
在教程里是直接使用 upload 组件的 defaultFileList API,
但他们 DEMO 是 listType="text",或 listType="picture-card" 但绑定的是 fileList
并不是 defaultFileList,就和 ant-design-vue 的文档是一摸一样的照着读了一遍,

然后我直接复制的官方用户头像 DEMO,就出现了问题。

AntD 官方 DEMO

点击上传用户头像,并使用 beforeUpload 限制用户上传的图片格式和大小。beforeUpload 的返回值可以是一个 Promise 以支持异步处理,如服务端校验等

<template>
  <a-upload
    name="avatar"
    listType="picture-card"
    class="avatar-uploader"
    :showUploadList="false"
    action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
    :beforeUpload="beforeUpload"
    @change="handleChange"
  >
    <img v-if="imageUrl" :src="imageUrl" alt="avatar" />
    <div v-else>
      <a-icon :type="loading ? 'loading' : 'plus'" />
      <div class="ant-upload-text">Upload</div>
    </div>
  </a-upload>
</template>
<script>
  function getBase64(img, callback) {
    const reader = new FileReader();
    reader.addEventListener("load", () => callback(reader.result));
    reader.readAsDataURL(img);
  }
  export default {
    data() {
      return {
        loading: false,
        imageUrl: ""
      };
    },
    methods: {
      handleChange(info) {
        if (info.file.status === "uploading") {
          this.loading = true;
          return;
        }
        if (info.file.status === "done") {
          // Get this url from response in real world.
          getBase64(info.file.originFileObj, imageUrl => {
            this.imageUrl = imageUrl;
            this.loading = false;
          });
        }
      },
      beforeUpload(file) {
        const isJPG = file.type === "image/jpeg";
        if (!isJPG) {
          this.$message.error("You can only upload JPG file!");
        }
        const isLt2M = file.size / 1024 / 1024 < 2;
        if (!isLt2M) {
          this.$message.error("Image must smaller than 2MB!");
        }
        return isJPG && isLt2M;
      }
    }
  };
</script>
<style>
  // css style...
</style>

第一天的时候,我直接复制的 用户头像 DEMO,然后直接使用了 defaultFileList 设置默认头像但是没效果,依然是空白。

找啊找啊找,发现 DEMO 上设置了 :showUploadList="false",删之…🙄 还是不行,继续查问题。

发现 defaultFileList 是不能异步设置的,要在组件初始化完成之前就设置好,或者使用 fileList 来设置默认头像 🙃,

emmm….怎么还有一个空白头像,原来还有一个 img 标签,删之。

现在的 upload 组件是这样的:

<a-upload
  action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
  listType="picture-card"
  :defaultFileList="fileList"
  @change="handleChange"
>
  <div v-if="fileList.length < 1">
    <a-icon :type="loading ? 'loading' : 'plus'" />
    <div class="ant-upload-text">Upload</div>
  </div>
</a-upload>

好,现在基本显示没问题了,但是先要删除原先的头像再上传

等等..删除了头像怎么没出现上传头像按钮?handleChange 事件要修改成:

handleChange({ fileList }) {
  this.fileList = fileList;
},

完事,基本完成。
但是…

需求:点击头像就能修改,而不是先要删除原先的头像再上传

所以结合两个官方 DEMO,最后修改如下:

<template>
  // 上下文...
  <a-upload
    listType="picture-card"
    class="avatar-uploader"
    :showUploadList="false"
    :action="uploadURL"
    :beforeUpload="beforeUpload"
    @change="handleChange"
  >
    <img v-if="imageUrl" :src="imageUrl" alt="avatar" style="width:100%;" />
    <div v-else>
      <a-icon :type="loading ? 'loading' : 'plus'" />
      <div class="ant-upload-text">Upload</div>
    </div>
  </a-upload>
  // 上下文...
</template>
<script>
export default {
  data() {
    return(){
      url:{
        upload: '/project/upload'
      },

      imageUrl: '',
      loading: false
    }
  },
  computed: {
    uploadURL() {
      return `${window._CONFIG['domianURL']}${this.url.upload}`
    }
  },
  methods:{
    handleChange(info) {
      if (info.file.status === 'uploading') {
        this.loading = true
        return
      }
      if (info.file.status === 'done') {
        getBase64(info.file.originFileObj, imageUrl => {
          this.imageUrl = imageUrl
          this.loading = false
        })
        // 这里可以放上传成功后台返回的图片url的表单赋值,使用 form.setFieldsValue 给表单赋值
      }
    },
    beforeUpload(file) {
      const isJPG = file.type === 'image/jpeg'
      if (!isJPG) {
        this.$message.error('You can only upload JPG file!')
      }
      const isLt2M = file.size / 1024 / 1024 < 2
      if (!isLt2M) {
        this.$message.error('Image must smaller than 2MB!')
      }
      return isJPG && isLt2M
    }
    // 初始图片赋值等方法
    ...
  }
</script>
<style>
// css style...
</style>

弹出框的表单值是怎么设置的?

edit() 方法中传入了当前对象 record

AntD 封装了表单域 <Form.Item /> ,需要使用 Form.create 来创建表单实例,
而且设置表单的值要用 form.setFieldsValue (有些时候需要确保子组件完成渲染 $nextTick() 后才执行赋值操作)

且使用 pick()[1] 函数从指定的对象中挑选出需要的任意 key 属性。

特殊控件(select,radio,checkbox)需要使用 v-decorator 来绑定数据 [2]

尾声

  • 1,官方 DEMO 的代码差异比较大,特别是 handleChange 部分,需要细看。
  • 2,对后台返回的数据有要求。
  • 3,表单修改需要使用 setFieldsValue 来动态设置其他控件的值。

[ 注 1 ]: pick() 的坑待填,用的 lodash.pick 函数

[ 注 2 ]: 这里的 v-decorator 挖坑,看后边什么时候填上