使用Vue3实现一个Upload组件的示例代码

通用上传组件开发

开发上传组件前我们需要了解:

首先实现一个最基本的上传流程:

基本上传流程,点击按钮选择,完成上传

代码如下:

<template>
  <div class="app-container">
      <!--使用change事件-->
      <input type="file" @change="handleFileChange">
  </div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import axios from 'axios'
export default defineComponent({
  name: 'App',
  setup() {
    const handleFileChange = (e: Event) => {
      // 断言为HTMLInputElement
      const target = e.target as HTMLInputElement
      const files = target.files
      if(files) {
        const uploadedFile = files[0]
        const formData = new FormData()
        formData.append('file', uploadedFile)
        // 使用node模拟上传接口
        axios.post('http://localhost:3001/upload', formData, {
          headers: {
            "Content-Type": 'multipart/form-data'
          }
        }).then(resp=> {
          console.log('resp', resp)
        }).catch(error=> {

        })
      }
    }

    return {
      handleFileChange
    }
  }
})
</script>

<style>
.page-title {
  color: #fff;
}
</style>

结果如下:

到这里我们基本的上传已经处理完成了,相对来说还是比较简单的,接下来我们创建Uploader.vue文件用来封装Upload组件。

我们需要实现如下功能

自定义模版

我们知道使用<input type="file">系统自带样式比较难看所用我们需要如下处理:

  • 对样式进行优化,隐藏input
  • 点击<div class="upload-area" @click="triggerUpload"></div>使用js出发input的click事件
  • 处理上传状态

代码如下:

<template>
  <div class="file-upload">
    <div class="upload-area" @click="triggerUpload"></div>
    <span v-if="fileStatus==='loading'">正在上传</span>
    <span v-else-if="fileStatus==='success'">上传成功</span>
    <span v-else-if="fileStatus==='error'">上传失败</span>
    <span v-else>点击上传</span>
    <input ref="fileInput" type="file" name="file" style="display: none" />
  </div>
</template>
<script lang="ts">
    import { computed, defineComponent, PropType, reactive, ref } from 'vue'
    import axios from 'axios'
    type UploadStatus = 'ready' | 'loading' | 'success' | 'error'
    export default defineComponent({
        props: {
            action: { // url地址
                type: String,
                required: true
            }
        },
        setup(props) {
            // input实例对象,通过与ref="fileInput"的关联获取到input实例对象
            const fileInput = ref<null | HTMLInputElement>(null)
            const fileStatus = ref<UploadStatus>('ready')

            // 1.div点击事件
            const triggerUpload = () => {
                if(fileInput.value) {
                    fileInput.value.click()
                }
            }
            // 通过div来触发input的change事件
            const handleFileChange = (e: Event) => {
              const target = e.target as HTMLInputElement
              const files = target.files
              if(files) {
                const uploadedFile = files[0]
                const formData = new FormData()
                formData.append('file', uploadedFile)
                readyFile.status = 'loading' // 上传之前将状态设置为loading
                axios.post(props.actions, formData, {
                  headers: {
                    "Content-Type": 'multipart/form-data'
                  }
                }).then(resp=> {
                  console.log('resp', resp)
                  readyFile.status = 'success' // 上传成功将状态设置为success
                }).catch(error=> {
                readyFile.status = 'error' // // 上传失败将状态设置为error
                })
              }
            }
            return {
                fileInput,
                triggerUpload,
                handleFileChange,
                fileStatus
            }

        }
    })
</script>

现在我们已经完成了对上传组件样式的优化,和上传状态的处理,接下来我们需要处理文件上传列表

支持文件上传列表

处理文件上传列表我们需要有如下实现:

  • 显示文件名称
  • 状态
  • 可删除
  • 显示上传进度
  • 有可能有更丰富的显示

在上一步代码的基础上做一些修改:

<template>
  <div class="file-upload">
    <div class="upload-area" @click="triggerUpload"></div>
    <!-- 点击上传态 -->
      <slot v-if="isUploading" name='loading'>
        <button disabled>正在上传</button>
      </slot>
      <!-- 上传完毕态 -->
      <slot name="uploaded" v-else-if="lastFileData && lastFileData.loaded" :uploadData="lastFileData.data">
        <button disabled>点击上传</button>
      </slot>
      <!-- 默认态 -->
      <slot v-else name='default'>
        <!-- <button disabled>点击上传</button> -->
        <span>点击上传</span>
      </slot>
    <input ref="fileInput" type="file" name="file" style="display: none" />
    <!--展示fileList-->
    <ul>
      <li
      v-for="file in filesList"
      :key="file.uid"
      :class="`uploaded-file upload-${file.status}`"
      >
        <span class="filname">
          {{ file.name }}
        </span>
        <button class="delete-icon" @click="reomveFile(file.uid)">Del</button>
      </li>
    </ul>
  </div>
</template>
<script lang="ts">
    import { last } from 'lodash-es'
    import { v4 as uuidv4 } from 'uuid';
    // 定义上传状态
    type UploadStatus = 'ready' | 'loading' | 'success' | 'error'
    // step1 定义上传文件对象接口类
    export interface UploadFile {
      uid: string; // 文件唯一id
      size: number; // 文件大小
      name: string; // 文件名称
      status: UploadStatus; // 上传状态
      raw: File;            // 文件
      progress?: string;    // 文件上传进度
      resp?: any;           // 服务端返回数据
      url?: string          // 对应展示的url
    }
    export default defineComponent({
        props: {
            action: { // url地址
                type: String,
                required: true
            }
        },
        setup(props) {
            // input实例对象,通过与ref="fileInput"的关联获取到input实例对象
            const fileInput = ref<null | HTMLInputElement>(null)
            // step2 上传文件列表
            const filesList = ref<UploadFile[]>([])
            // step4-1 判断是否正在上传中
            const isUploading = computed(()=> {
                return filesList.value.some((file)=>file.status==='loading')
            })
            //step4-2 获取上传文件的最后一项
            const lastFileData = computed(()=>{
              const lastFile = last(filesList.value)
              if(!lastFile) return false
              return {
                loaded: lastFile?.status === 'success',
                data: lastFile?.resp
              }
            })
            // 1.div点击事件
            const triggerUpload = () => {
                if(fileInput.value) {
                    fileInput.value.click()
                }
            }
            // 通过div来触发input的change事件
            const handleFileChange = (e: Event) => {
              const target = e.target as HTMLInputElement
              const files = target.files
              if(files) {
                const uploadedFile = files[0]
                const formData = new FormData()
                formData.append('file', uploadedFile)
                // step3 设置响应式对象,存储到filesList中,以便在页面中展示
                const fileObj = reactive<UploadFile>({
                  uid: uuid(); // 文件唯一id
                  size: uploadedFile.size,
                  name: uploadedFile.name,
                  status: 'loading',
                  raw: uploadedFile
                })
                filesList.value.push(fileObj)

                axios.post(props.actions, formData, {
                  headers: {
                    "Content-Type": 'multipart/form-data'
                  },
                  //step6 处理上传进度
                  onUploadProgress: (progressEvent)=> {
                      const complete = (progressEvent.loaded / progressEvent.total * 100 | 0) + '%'
                      fileObj.progress = complete
                  }
                }).then(resp=> {
                  console.log('resp', resp)
                  fileObj.status = 'success'

                }).catch(error=> {
                    fileObj.status = 'error'
                }).finally(()=> {
                    // 一张图片重复上传时无法继续上传bug
                    if(fileInput.value) {
                        fileInput.value.value = ''
                    }
                })
              }
            }
            // step7 处理删除
            const reomveFile = (uid: string) => {
                 filesList.value = filesList.value.filter(file=>file.uid!==uid)
            }
            return {
                fileInput,
                triggerUpload,
                handleFileChange,
                fileStatus,
                isUploading,
                filesList,
                lastFileData
            }
        }
    })
</script>
  • 首先我们定义上传文件对象接口类UploadFile
  • 创建了一个filesList响应式对象
  • 去掉了fileStatus,因为在UploadFile接口中我们已经有定义status
  • 修改模版中的状态,利用computed来判读是否是处于上传状态,并增加slot进行自定义
  • 展示上传的图片
  • 处理上传进度
  • 处理删除

注意点:如果选择相同的图片不会进行上传操作,因为我们使用的是input的change事件,所以我们需要在上传后将input的value设置为null

支持一系列生命周期钩子事件,上传事件

beforeUpload

<template>
 ...
</template>
<script lang="ts">
    import { last } from 'lodash-es'
    import { v4 as uuidv4 } from 'uuid';
    // 定义上传状态
    type UploadStatus = 'ready' | 'loading' | 'success' | 'error'
    // 定义上传文件为boolean或promsie且接受一个File
    type CheckUpload = ()=> boolean | Promise<File>
    export interface UploadFile {
      ...
    }
    export default defineComponent({
        props: {
            action: { // url地址
                type: String,
                required: true
            },
            beforeUpload: {
                type: Function as PropType<CheckUpload>
            }
        },
        setup(props) {
            const fileInput = ref<null | HTMLInputElement>(null)
            const filesList = ref<UploadFile[]>([])
            // step4-1 判断是否正在上传中
            const isUploading = computed(()=> {
                return filesList.value.some((file)=>file.status==='loading')
            })
            const lastFileData = computed(()=>{
              const lastFile = last(filesList.value)
              if(!lastFile) return false
              return {
                loaded: lastFile?.status === 'success',
                data: lastFile?.resp
              }
            })
            const triggerUpload = () => {
                if(fileInput.value) {
                    fileInput.value.click()
                }
            }
            const handleFileChange = (e: Event) => {
              const target = e.target as HTMLInputElement
              const files = target.files
              const uploadedFile = files[0]
              if(props.beforeUpload) {
                  const result = props.beforeUpload(uploadedFile)
                  if(result && result instanceof Promise) {
                      result.then((processFile)=> {
                          if(uploadedFile instanceof File) {
                              postFile(uploadedFile)
                          }
                      }).catch(error=>{
                          console.error(error)
                      })
                  } esle if(result===true) {
                  postFile(uploadedFile)
                  }
              }else{
                  postFile(uploadedFile)
              }

            }
            const reomveFile = (uid: string) => {
                 filesList.value = filesList.value.filter(file=>file.uid!==uid)
            }
            // 处理文件上传
            const postFile = (readyFile:UploadFile)=> {
                const formData = new FormData()
                formData.append('file', readyFile.raw)
                readyFile.status = 'loading'
                axios.post(props.action, formData, {
          headers: {
            "Content-Type": 'multipart/form-data'
          },
          onUploadProgress: (progressEvent)=> {
              const complete = (progressEvent.loaded / progressEvent.total * 100 | 0) + '%'
            //   console.log('上传 ' + complete)
            readyFile.progress = complete
          }
        }, ).then(resp=> {
          console.log('resp', resp)
          // fileStatus.value = 'success'
          readyFile.status = 'success'
          readyFile.resp = resp.data
        }).catch(error=> {
            // fileStatus.value = 'error'
            readyFile.status = 'error'
        })
        .finally(()=> {
          // 一张图片重复上传时无法继续上传bug
          if(fileInput.value) {
            fileInput.value.value = ''
          }
        })
            }
            return {
                fileInput,
                triggerUpload,
                handleFileChange,
                fileStatus,
                isUploading,
                filesList,
                lastFileData
            }
        }
    })
</script>

实现步骤:

  • 在poops处定义属性beforeUpload,同时定义上传文件为boolean或promsie且接受一个File
  • 将原上传方法进行封装为postFile
  • 根据beforeUpload返回结果,进行接下来的流程处理

onProgress,onSuccess,onError,onChange与之类似

拖拽支持

大致流程如下图:

  • dragover和dragleave添加和删除对应的class
  • drop拿到正在被拖拽的文件,删除class,并且触发上传
  • 只有在属性drag为true才能触发

需要注意v-on的使用具体可参考vue官方文档

上代码:

<template>
  <div class="file-upload">
    <div
      :class="['upload-area',  drag && isDragOver ? 'is-dragover': '' ]"
      v-on="events"
      >
      <!-- 点击上传态 -->
      <slot v-if="isUploading" name='loading'>
        <button disabled>正在上传</button>
      </slot>
      <!-- 上传完毕态 -->
      <slot name="uploaded" v-else-if="lastFileData && lastFileData.loaded" :uploadData="lastFileData.data">
        <button disabled>点击上传</button>
      </slot>
      <!-- 默认态 -->
      <slot v-else name='default'>
        <!-- <button disabled>点击上传</button> -->
        <span>点击上传</span>
      </slot>
    </div>

    <input ref="fileInput" type="file" name="file" style="display: none" @change="handleFileChange" />

    <ul>
      <li
      v-for="file in filesList"
      :key="file.uid"
      :class="`uploaded-file upload-${file.status}`"
      >
      <img
          v-if="file.url && listType === 'picture'"
          class="upload-list-thumbnail"
          :src="file.url"
          :alt="file.name"
        >
        <span class="filname">
          {{ file.name }}
        </span>
        <span class="progress">
          {{ file.progress }}
        </span>
        <button class="delete-icon" @click="reomveFile(file.uid)">Del</button>
      </li>
    </ul>

  </div>
</template>

<script lang="ts">
import { computed, defineComponent, PropType, reactive, ref } from 'vue'
import axios from 'axios'
import { v4 as uuidv4 } from 'uuid';
import { last } from 'lodash-es'
type UploadStatus = 'ready' | 'loading' | 'success' | 'error'
type fileListType = 'text' | 'picture'
type CheckUpload = ()=> boolean | Promise<File>

export interface UploadFile  {
  uid: string; // 文件唯一id
  size: number; // 文件大小
  name: string; // 文件名称
  status: UploadStatus; // 上传状态
  raw: File;            // 文件
  progress?: string;
  resp?: any;           // 服务端返回数据
  url?: string          // 对应展示的url

}
// dragOver  文件拖拽到区域时触发
// dragLeave  文件离开拖动区域
// drop 事件拿到正在被拖拽的文件删除class并且触发上传
// 只有设置drag的时候才有效

export default defineComponent({
  name: 'Uploader',
  props: {
    action: { // url地址
        type: String,
        required: true
    },
    beofreUpload:{ // 上传之前的处理
      type: Function as PropType<CheckUpload>
    },
    drag: { // 是否拖拽
      type: Boolean,
      default: false
    },
    autoUpload: { // 自动上传
      type: Boolean,
      default: true
    },
    listType: {
      type: String as PropType<fileListType>,
      default: 'text'
    }
  },

  setup(props) {
    const fileInput = ref<null | HTMLInputElement>(null)
    const fileStatus = ref<UploadStatus>('ready')
    // 存储上传的文件
    const filesList = ref<UploadFile[]>([])
    // 定义了一个isDragOver标识,处理拖动后样式.upload-area显示
    const isDragOver = ref<boolean>(false)
    const triggerUpload = () => {
        if(fileInput.value) {
            fileInput.value.click()
        }
    }

    let events: {[key:string]: (e: any)=>void} = {
      'click': triggerUpload,

    }
    // 只要有一个文件状态是loading态就说明是正在上传
    const isUploading = computed(()=>{
      return filesList.value.some((file)=> file.status==='loading')
    })

    // 获取上传文件的最后一项
    const lastFileData = computed(()=>{
      const lastFile = last(filesList.value)
      if(!lastFile) return false
      return {
        loaded: lastFile?.status === 'success',
        data: lastFile?.resp
      }
    })

    // 处理dragover,dragleave
    const handleDrag = (e: DragEvent,over: boolean)=> {
    // 阻止默认事件
      e.preventDefault()
      // dragover为true,dragleave为fasle
      isDragOver.value = over
    }
    // 处理drop
    const handleDrop = (e: DragEvent) => {
      e.preventDefault()
      isDragOver.value = false
      if(e.dataTransfer) {
        beforeUploadCheck(e.dataTransfer.files)
      }
    }

    if(props.drag){
      events =  {
        ...events,
         'dragover': (e: DragEvent) => { handleDrag(e, true)},
        'dragleave': (e: DragEvent) => { handleDrag(e, false)},
        'drop': handleDrop
      }
      // console.log(events)
    }
    // 删除文件
    const reomveFile = (uid: string)=> {
      filesList.value = filesList.value.filter(file=>file.uid!==uid)
    }

    const postFile = (readyFile:UploadFile) => {
       const formData = new FormData()
        formData.append('file', readyFile.raw)
        readyFile.status = 'loading'
        // 选择文件后push到存储对象里面
        axios.post(props.action, formData, {
          headers: {
            "Content-Type": 'multipart/form-data'
          },
          onUploadProgress: (progressEvent)=> {
              const complete = (progressEvent.loaded / progressEvent.total * 100 | 0) + '%'
            //   console.log('上传 ' + complete)
            readyFile.progress = complete
          }
        }, ).then(resp=> {
          console.log('resp', resp)
          // fileStatus.value = 'success'
          readyFile.status = 'success'
          readyFile.resp = resp.data
        }).catch(error=> {
            // fileStatus.value = 'error'
            readyFile.status = 'error'
        })
        .finally(()=> {
          // 一张图片重复上传时无法继续上传bug
          if(fileInput.value) {
            fileInput.value.value = ''
          }
        })
    }
    const addFileToList = (uploadedFile: File) => {
      const fileObj = reactive<UploadFile>({
        uid: uuidv4(),
        size: uploadedFile.size,
        name: uploadedFile.name,
        status: 'ready',
        raw: uploadedFile
      })
       // 处理图片格式并显示
      if(props.listType==='picture') {
        // try {
        //   fileObj.url = URL.createObjectURL(uploadedFile)
        // }catch(err) {
        //   console.error('upload file err', err)
        // }
        const fileReader = new FileReader()
        fileReader.readAsDataURL(uploadedFile)
        fileReader.addEventListener('load', ()=> {
          fileObj.url = fileReader.result as string
        })
      }

      filesList.value.push(fileObj)
      if(props.autoUpload) {
        postFile(fileObj)
      }
    }

    const uploadFiles = ()=> {
      // filesList.value.filter(file => file.status === 'ready').forEach(readyFile => postFile(readyFile))
      filesList.value.filter(file => file.status === 'ready').forEach(readyFile=>postFile(readyFile))
    }

    const beforeUploadCheck = (files: null | FileList ) => {
      if(files) {
          fileStatus.value = 'loading'
        const uploadedFile = files[0]
        if(props.beofreUpload) {
          const result = props.beofreUpload()
          // 如果有返回值
          if(result && result instanceof Promise) {
            result.then(processedFile=> {
              if(processedFile instanceof File) {
                addFileToList(processedFile)
              } else {
                throw new Error('beforeUpload promise should return file object')
              }
            }).catch(err=> {
              console.log(err)
            })
          } else if(result === true) {
              addFileToList(uploadedFile)
          }
        } else {
          addFileToList(uploadedFile)
        }
      }
    }

    const handleFileChange = (e: Event) => {
      const target = e.target as HTMLInputElement
      const files = target.files
      beforeUploadCheck(files)
    }
    return {
        fileInput,
        triggerUpload,
        handleFileChange,
        isUploading,
        filesList,
        reomveFile,
        lastFileData,
        beforeUploadCheck,
        isDragOver,
        events
    }
  }
})
</script>

以上就是基本的基于vue3实现的上传通用组件
另附上上传接口代码:

// 引入模块
const Koa = require('koa');
const fs = require('fs');
const path = require('path');
const router = require('koa-router')();
const koaBody = require('koa-body');
const static = require('koa-static');
const cors = require('koa2-cors')

// 实例化
const app = new Koa();

app.use(koaBody({
  multipart: true, // 支持文件上传
  formidable: {
    maxFieldsSize: 2 * 1024 * 1024, // 最大文件为2兆
    multipart: true // 是否支持 multipart-formdate 的表单
  }
}));

const uploadUrl = "http://localhost:3001/static/upload";

// 配置路由
router.get('/', (ctx) => {
  // 设置头类型, 如果不设置,会直接下载该页面
  ctx.type = 'html';
  // 读取文件
  const pathUrl = path.join(__dirname, '/static/upload.html');
  ctx.body = fs.createReadStream(pathUrl);
});

// 上传文件
router.post('/upload', (ctx) => {
  // 获取上传文件
  const file = ctx.request.files.file;
  console.log(file);
  // 读取文件流
  const fileReader = fs.createReadStream(file.path);
  // 设置文件保存路径
  const filePath = path.join(__dirname, '/static/upload/');
  // 组装成绝对路径
  const fileResource = filePath + `/${file.name}`;

  /**
   * 使用 createWriteStream 写入数据,然后使用管道流pipe拼接
   */
  const writeStream = fs.createWriteStream(fileResource);
  // 判断 /static/upload 文件夹是否存在,如果不在的话就创建一个
  if (!fs.existsSync(filePath)) {
    fs.mkdir(filePath, (err) => {
      if (err) {
        throw new Error(err);
      } else {
        fileReader.pipe(writeStream);
        ctx.body = {
          url: uploadUrl + `/${file.name}`,
          code: 0,
          message: '上传成功1'
        };
      }
    });
  } else {
    fileReader.pipe(writeStream);
    ctx.body = {
      url: uploadUrl + `/${file.name}`,
      code: 0,
      message: '上传成功1'
    };
  }
});

// 配置静态资源路径
app.use(static(path.join(__dirname)));
// 开启跨域
app.use(cors())
// 启动路由
app.use(router.routes()).use(router.allowedMethods());

// 监听端口号
app.listen(3001, () => {
  console.log('server is listen in 3001');
});

写在最后

已上只是一个简单的实现,当然了我们也可以添加自定义headers,自定义传递数据data,accecpt等等

到此这篇关于使用Vue3实现一个Upload组件的示例代码的文章就介绍到这了,更多相关Vue3 Upload组件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue上传组件vue Simple Uploader的用法示例

    在日常开发中经常会遇到文件上传的需求,vue-simple-uploader 就是一个基于 simple-uploader.js 和 Vue 结合做的一个上传组件,自带 UI,可覆盖.自定义:先来张动图看看效果: 其主要特点就是: 支持文件.多文件.文件夹上传 支持拖拽文件.文件夹上传 统一对待文件和文件夹,方便操作管理 可暂停.继续上传 错误处理 支持"快传",通过文件判断服务端是否已存在从而实现"快传" 上传队列管理,支持最大并发上传 分块上传 支持进度.预估剩

  • vue-cli3.0+element-ui上传组件el-upload的使用

    最近项目中涉及很多文件上传的地方,然后文件上传又有很多限制.比如文件大小限制,文件个数限制,文件类型限制,文件上传后的列表样式自定义,包括上传进度条等问题.下面是我对element-ui的上传组件的一些改造, 点击查看源码. 我是自己维护了一个列表数据,再对这个列表数据进行一些操作,没用组件自带的.先看看我的组件模版 <template> <el-upload class="upload-demo" :limit="limit" :action=&

  • vue webuploader 文件上传组件开发

    最近项目中需要用到百度的webuploader大文件的分片上传,对接后端的fastdfs,于是着手写了这个文件上传的小插件,步骤很简单,但是其中猜到的坑也不少,详细如下: 一.封装组件 引入百度提供的webuploader.js.Uploader.swf css样式就直接写在组件里面了 <template> <div> <div id="list" class="uploader-list"></div> <di

  • vue中element 的upload组件发送请求给后端操作

    1.用到了before-upload属性, 用于在上传文件前的校验,并且发送请求给后端,传输格式进行文件流传输 什么都不用设置,action属性随便设置,不能为空即可! 在before-upload属性的方法中的代码如下: var _this = this; debugger; // var files=file.target.files[0]; debugger; const isJPG = file.type === "image/jpeg"; const isLt2M = fil

  • 基于vue-upload-component封装一个图片上传组件的示例

    需求分析 业务要求,需要一个图片上传控件,需满足 多图上传 点击预览 图片前端压缩 支持初始化数据 相关功能及资源分析 基本功能 先到https://www.npmjs.com/search?q=vue+upload上搜索有关上传的控件,没有完全满足需求的组件,过滤后找到 vue-upload-component 组件,功能基本都有,自定义也比较灵活,就以以此进行二次开发. 预览 因为项目是基于 vant 做的,本身就提供了 ImagePreview 的预览组件,使用起来也简单,如果业务需求需要

  • 在vue项目中使用element-ui的Upload上传组件的示例

    本文介绍了vue项目中使用element-ui的Upload上传组件的示例,分享给大家,具体如下: <el-upload v-else class='ensure ensureButt' :action="importFileUrl" :data="upLoadData" name="importfile" :onError="uploadError" :onSuccess="uploadSuccess&quo

  • vue2.0 使用element-ui里的upload组件实现图片预览效果方法

    1.首先我们在cli中引入element-ui 2.然后在具体的代码中放入uoload组件 <el-upload class="upload-demo" action="" :auto-upload='false' :on-change='changeUpload'> <el-button size="small" type="primary">点击上传</el-button> <di

  • vue element upload组件 file-list的动态绑定实现

    本文解决,upload组件 file-list的动态绑定list1,list2 ...,实现动态添加,相信很多电商后台管理系统都会遇到这个需求,例子如下 本例,我是使用的upload默认的上传地址(很多图片不能上传,你可以在本地截几张图片,进行测试),我可以上传多张活动图片,可以加相应的,名称,链接描述等,如果有多个活动,可以点击添加活动,在第二个活动又能添加相应的内容,保存完之后,可以实现回显,活动详情页可以看到添加的几个活动和相应的活动内容. 实现方法不为一,这是一种特别简单的.代码如下 <

  • 使用Vue3实现一个Upload组件的示例代码

    通用上传组件开发 开发上传组件前我们需要了解: FormData上传文件所需API dragOver文件拖拽到区域时触发 dragLeave文件离开拖动区域 drop文件移动到有效目标时 首先实现一个最基本的上传流程: 基本上传流程,点击按钮选择,完成上传 代码如下: <template> <div class="app-container"> <!--使用change事件--> <input type="file" @ch

  • vue 开发一个按钮组件的示例代码

    最近面试,被问到一个题目,vue做一个按钮组件: 当时只是说了一下思路,回来就附上代码. 解决思路: 通过父子组件通讯($refs 和 props) props接受参数, $refs调用子组件的方法 来达到点击提交改变按钮状态,如果不成功则取消按钮状态 在src/components/ 下建一个button.vue <template> <!-- use plane --> <!-- 传入bgColor改变按钮背景色 --> <!-- state切换button的

  • vue的一个分页组件的示例代码

    分页组件在项目中经常要用到之前一直都是在网上找些jq的控件来用(逃..),最近几个项目用上vue了项目又刚好需要一个分页的功能.具体如下: 文件page.vue为一个pc端的分页组件,基础的分页功能都有,基本的思路是,页面是用数据来展示的,那就直接操作相关数据来改变视图 Getting started import Page from './page.vue' 从目录引入该文件,在父组件注册使用 复制代码 代码如下: <page :total='total' :current-index="

  • 使用vue实现一个电子签名组件的示例代码

    在生活中我们使用到电子签名最多的地方可能就是银行了,每次都会让你留下大名.今天我们就要用vue实现一个电子签名的面板 想要绘制图形,第一步想到的就是使用canvas标签,在之前的文章里我们使用canvas实现了一个前端生成图形验证码的组件,被吐槽不够安全,那么这个电子签名组件想必不会被吐槽了吧~ canvas <canvas> 标签是 HTML 5 中的新标签. <canvas> 标签只是图形容器,您必须使用脚本来绘制图形. canvas标签本身是没有绘图能力的,所有的绘制工作必须

  • Vue3+TS实现语音播放组件的示例代码

    目录 第一步:点击拖拽进度条 第二步:操作媒体音频 第三步:进度条和播放进度关联 完整代码 该功能将使用vue3 + TS来实现语音播放组件,使用什么技术不重要,重要的是看懂了核心逻辑后,通过原生js.react.vue2等都可以轻松实现 所涉及到重要点有以下几个: (1)进度条的实现:拖拽进度条.点击进度条 (2)操作audio语音播放:通过js操作audio媒体 (3)播放进度与进度条紧密关联:播放的进度改变时,进度条也随之改变:进度条改变时,播放的进度也随之改变 效果图: 开始我们的设计吧

  • 使用Vue实现一个树组件的示例

    HTML代码: <!DOCTYPE html> <html> <head> <title>Vue Demo</title> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content

  • 基于Vue3实现数字华容道游戏的示例代码

    目录 前言 环境 思路 实现 GameCnt GameTool GamePass GameTip Menu 最后 前言 恰逢春之四月,天气忽热忽凉,遇游戏大赛,以笨拙之技,书一篇小文. 游戏规则:存在n*n的格子,需要将它们按数字顺序或图片顺序一一还原即可. 环境 主要环境: vue3 version:3.2.4 vite version:2.5.0 vue-router version:4.0.14 注:这个游戏的路由使用的是自动路由插件 主要插件: windicss version:3.5.

  • 开发Vue树形组件的示例代码

    本文介绍了Vue树形组件的示例代码,分享给大家,具体如下: 使用SemanticUI和vue做一个menubar组件,实现方法大概是这样的: <template> <div class="ui menu"> <template v-for="item in leftItems"> <a " v-if="!item.children" @click="item.click"&g

  • 基于vue的tab-list类目切换商品列表组件的示例代码

    在大多数电商场景中,页面都会有类目切换加上商品列表的部分,页面大概会长这样 每次写类似场景的时候,都需要去为类目商品列表写很多逻辑,为了提高开发效率我决定将这一部分抽离成组件. 实现 1.样式 所有tab栏的样式和商品列表的样式都提供插槽,供业务自己定制 2.变量 isTabFixed: false,//是否吸顶 tab: 1,//当前tab page: 1,//当前页数 listStatus: { finished: false,//是否已是最后一页 loading: false,//是否加载

  • 原生JavaScript实现弹幕组件的示例代码

    前言 如今几乎所有的视频网站都有弹幕功能,那么今天我们就自己用原生 JavaScript 封装一个弹幕类.这个类希望有如下属性和实例方法: 属性 el容器节点的选择器,容器节点应为绝对定位,设置好宽高 height 每条弹幕的高度 mode 弹幕模式,half则为一半容器高度,top为三分之一,full为占满 speed弹幕划过屏幕的时间 gapWidth后一条弹幕与前一条弹幕的距离 方法 pushData 添加弹幕元数据 addData持续加入弹幕 start开始调度弹幕 stop停止弹幕 r

随机推荐