文章
问答
冒泡
如何写一个vue3组件

在vue3工程中,为了实现复用,我们需要将一些通用的部分封装成组件。通常来说,组件就是把一些页面元素基础操作封装。
但是考虑到一些特殊场景,比如适用Form表单的组件,不光要实现UI层面的效果,还需要实现数据的双向绑定。
这里以antd-vue的upload组件为例,写一个图片上传的组件。考虑到 v-model:value 不使用的时候,组件自身也能独立工作,我们定义了一个内部的值,然后通过监听这个值的变化来更新  v-model:value 。

<template>
  <div class="picture-upload">
    <div v-if="scopeValue" class="picture-upload-image">
      <a-image
          :width="200"
          :src="scopeValue"
      />
      <div class="picture-upload-image-tooltip">
        <a-upload  :multiple="false" :show-upload-list="false"
                   :customRequest="handleUpload">
          <a-button type="text" :style="{color: 'white'}">更换</a-button>
        </a-upload>
        <a-divider type="vertical" />
        <a-button type="text" :style="{color: 'white'}" @click="handleClear">删除</a-button>
      </div>
    </div>

    <a-upload v-else class="picture-upload" list-type="picture-card" :multiple="false" :show-upload-list="false"
              :customRequest="handleUpload">
      <UploadOutlined/>
    </a-upload>
  </div>
</template>

<script>
import {Button, Divider, Image, Tooltip, Upload} from "ant-design-vue";
import {UploadOutlined} from "@ant-design/icons-vue";
import {ref, watch} from "vue";

export default {
  name: "PictureUpload",
  components: {
    AUpload: Upload,
    AButton:Button,
    AImage: Image,
    ADivider:Divider,
    UploadOutlined,
  },
  props: {
    value: {
      type: String
    },
    height: {
      type: String,
      default(){
        return "auto"
      }
    },
    width:{
      type:String,
      default() {
        return "100%";
      }
    },
    uploadAction: {
      type: Function
    }
  },
  setup(props, context) {
    const scopeValue = ref();

    const handleUpload = (info) => {
      if (props.uploadAction) {
        let formData = new FormData();
        formData.append(info.filename, info.file);
        props.uploadAction(formData).then((res) => {
          if (res) {
             scopeValue.value = res.url;
          }
        });
      }
    }

    const handleClear = () => {
      scopeValue.value = ''
    }

    watch(() => props.value,(newValue, oldValue)=>{
      scopeValue.value = newValue
    });

    watch(scopeValue,(newValue,oldValue)=>{
      context.emit('update:value', newValue);
    })

    return {
      scopeValue,
      handleUpload,
      handleClear
    }
  }
}
</script>

<style scoped lang="less">
.picture-upload {
  .picture-upload-image{
    position: relative;
    display: inline-block;
    &:hover{
      .picture-upload-image-tooltip{
        display: flex;
      }
    }
    .picture-upload-image-tooltip{
      position: absolute;
      left: 50%;
      bottom: 8px;
      transform: translateX(-50%);
      background: rgba(18, 18, 18, 0.75) none repeat scroll 0% 0%;
      border-radius: 4px;
      display: none;
    }
  }
}
</style>


使用

<a-form-item label="照片" name="file">
  <PictureUpload v-model:value="formState.picture" :upload-action="handleUpload"/>
</a-form-item>


展示效果如下


选择图片之后效果

一个可以支持form表单的图片上传组件就完成了。


关于作者

落雁沙
非典型码农
获得点赞
文章被阅读