Vue component encapsulates sample code for uploading pictures and videos

Vue component encapsulates sample code for uploading pictures and videos

First download the dependencies:

cnpm i -S vue-uuid ali-oss

The image and video fields are both array types, ensuring that multiple files can be uploaded.

UploadImageVideo:

<!--UploadImageVideo upload in pieces-->
<template>
  <div class="UploadImageVideo">
    <el-upload
      action
      :on-change="handleChange"
      :on-remove="handleRemove"
      :limit="limitFileNumber"
      :on-exceed="handleExceed"
      :file-list="_fileList"
      :http-request="handleHttpRequest"
      :before-upload="handleBeforeUpload"
      :multiple="isMultiple"
    >
      <el-button slot="trigger" size="small" type="primary">Select file</el-button>
      <div slot="tip" class="el-upload__tip">{{ tip }}</div>
    </el-upload>
 
    <el-dialog
      title="Upload progress"
      :visible.sync="dialogTableVisible"
      :close-on-click-modal="false"
      :modal-append-to-body="false"
    >
      <el-progress :text-inside="true" :stroke-width="26" :percentage="percentage"></el-progress>
    </el-dialog>
  </div>
</template>
 
<script>
import { uuid } from "vue-uuid";
const OSS = require("ali-oss");
 
export default {
  name: "",
  components: {},
  props: {
    region:
      type: String,
      default: "oss-cn-chengdu"
    },
    accessKeyId: {
      type: String,
      default: "xxx"
    },
    accessKeySecret: {
      type: String,
      default: "xxx"
    },
    //Storage location bucket: {
      type: String,
      required: true
    },
    currentUrls: {
      type: Array,
      default: () => [],
      required: true
    },
    //Limit the number of uploaded files limitFileNumber: {
      type: Number,
      default: 1
    },
    //Whether to support multiple selection isMultiple: {
      type: Boolean,
      default: false
    },
    //File format fileType: {
      type: String,
      default: ""
    },
    //tip: {
      type: String
    }
  },
  data() {
    return {
      client: new OSS({
        region: this.region,
        accessKeyId: this.accessKeyId,
        accessKeySecret: this.accessKeySecret,
        bucket: this.bucket
      }),
      percentage: 0,
      dialogTableVisible: false,
      fileList: []
    };
  },
  computed: {
    //Note: Be careful when using console.log() to print in calculated properties, because it is possible that the printed variable depends on a certain property and the calculated property is called repeatedly! ! ! ! ! !
    _fileList() {
      const arr = [];
      // Make sure this.currentUrls is not empty, otherwise an error will be reported if (this.currentUrls.length !== 0) {
        for (const item of this.currentUrls) {
          let { pathname } = new URL(item);
          arr.push({ name: decodeURIComponent(pathname), url: item });
        }
      }
 
      this.fileList = arr; //This line of code is very important! !
      return arr;
    }
  },
  created() {},
  mounted() {},
  methods: {
    handleChange(file, fileList) {
      this.fileList = fileList;
    },
    handleRemove(file, fileList) {
      this.fileList = fileList;
    },
    handleExceed(files, fileList) {
      this.$message.warning(
        `Currently limit selection to ${this.limitFileNumber} files, this time selected ${
          files.length
        } files, ${files.length + fileList.length} files selected`
      );
    },
 
    //Note: In order for the custom upload handleHttpRequest to take effect, the following conditions must be met:
    // 1. Set: auto-upload='true' or do not write this attribute, because it defaults to true 2. Set action='#' or write action directly
    handleHttpRequest(file) {
      //Although there is no content, this function is indispensable!
    },
    //Note: The custom upload handleHttpRequest must be effective to trigger the before-upload hook function handleBeforeUpload(file) {
      if (this.fileType == "image") {
        let { type, size, name } = file;
        let isJPEG = type === "image/jpeg";
        let isJPG = type === "image/jpg";
        let isPNG = type === "image/png";
        let isLt5M = size / 1024 / 1024 < 5;
        if (!isJPEG && !isJPG && !isPNG) {
          this.$message.error("Uploaded images can only be in JPEG/JPG/PNG format!");
          return false;
        }
        if (!isLt5M) {
          this.$message.error("The size of a single image cannot exceed 5MB!");
          return false;
        }
      }
      if (this.fileType == "video") {
        let { type, size, name } = file;
        let isMP4 = type === "video/mp4";
        let isLt50M = size / 1024 / 1024 < 50;
        if (!isMP4) {
          this.$message.error("Uploaded videos can only be in MP4 format!");
          return false;
        }
        if (!isLt50M) {
          this.$message.error("The size of a single video cannot exceed 50MB!");
          return false;
        }
      }
    },
    // Upload data in pieces and display a progress bar. Upload the renamed file to alioss and return a single file url string. Supports Chinese file names async UploadImageVideo(filename, file) {
      let newFileName =
        filename.split(".")[0] + "-" + uuid.v1() + "." + filename.split(".")[1];
      let that = this;
      that.dialogTableVisible = true;
 
      let {
        res: { requestUrls }
      } = await this.client.multipartUpload(newFileName, file, {
        progress: function(p, checkpoint) {
          that.percentage = parseFloat((p * 100).toFixed(2));
        }
      });
      if (that.percentage == 100) {
        that.dialogTableVisible = false;
      }
      let { origin, pathname } = new URL(requestUrls[0]);
      return origin + decodeURIComponent(pathname);
    },
    //Batch upload files. Returns an array of successfully uploaded urls async addFiles() {
      let urls = [];
      if (this.fileList.length !== 0) {
        for (const item of this.fileList) {
          let { name, raw } = item;
          let pathname = await this.UploadImageVideo(name, raw);
          urls.push(pathname);
        }
      }
      return urls;
    },
    //Update file data. Upload new data to the server, delete old data in the server, and return the updated url array async UpdateFiles() {
      let arr_newUploaded = []; //Newly uploaded image url.
      let arr_original = []; //Original image url. No need to delete let arr_delete = []; //The URL of the image to be deleted.
      if (this.fileList.length !== 0) {
        for (const { raw, name, url } of this.fileList) {
          //Note: It is important to determine whether raw exists here. If it exists, it means it is newly uploaded; if it does not exist, it means it is the original if (raw) {
            let pathname = await this.UploadImageVideo(name, raw);
            arr_newUploaded.push(pathname);
          }
          if (this.currentUrls.includes(url)) {
            arr_original.push(url);
          }
        }
      }
 
      for (const element of this.currentUrls) {
        if (!arr_original.includes(element)) {
          arr_delete.push(element);
        }
      }
      await this.deleteMultiFiles(arr_delete);
 
      return [...arr_original, ...arr_newUploaded];
    },
    //Batch delete files in the server. Parameters: Array of URLs of files to be deleted from the server.
    async deleteMultiFiles(urls = []) {
      let arr_pathname = [];
      if (urls.length !== 0) {
        for (const item of urls) {
          //Don't use let url=require("url");url.parse(); it is invalid. To use new URL()
          let { pathname } = new URL(item);
          // decodeURIComponent() function converts Chinese garbled characters into Chinese arr_pathname.push(decodeURIComponent(pathname));
        }
        //Delete the pictures in the server await this.client.deleteMulti(arr_pathname);
      }
    }
  },
  watch: {}
};
</script>
 
<style lang="scss" scoped>
.UploadImageVideo {
  /*Remove the upload component transition effect*/
  ::v-deep .el-upload-list__item {
    transition: none !important;
  }
}
</style>

use:

<UploadImageVideo
  ref="ref_UploadImageVideo"
  bucket="xxx"
  :currentUrls="formData.imgurl"
  :limitFileNumber="3"
  tip="1. Upload up to 3 photos; 2. Uploaded pictures can only be in JPEG/JPG/PNG format; 3. The size of a single picture cannot exceed 5MB!"
  fileType="image"
  :isMultiple="true"
></UploadImageVideo>
  • fileType is optional. By default, it is not set, which means that both pictures and videos can be uploaded. fileType="image" means only images can be uploaded. fileType="video" means only videos can be uploaded
  • bucket is required.
  • isMultipleOptional. Defaults to false
  • currentUrls is required. The current array of file server URLs. Usually when adding a file, the currentUrls passed in is an empty array []; when updating a file, the currentUrls passed in is a non-empty array
  • tip is optional. Tips

Provided methods: (All uploads in the current component are batch uploads and are uploaded in pieces to display the upload progress bar)

  1. UpdateFiles(). Update file data. Upload new data to the server, delete old data in the server, and return the updated url array
  2. addFiles(). Upload files in batches. Returns an array of successfully uploaded URLs
  3. deleteMultiFiles(urls = []). Batch delete files from the server. Parameters: Array of URLs of files to be deleted from the server.
  4. UploadImageVideo(filename, file). Upload data in pieces and display a progress bar. Upload the renamed file to alioss and return a single file url string. Support Chinese file names

Call methods in components: For example, you can call methods for batch uploading images or videos through let urls = await this.$refs["ref_UploadImageVideo"].addFiles();

Example 1:

<!--userManage-->
<template>
  <div class="userManage">
    <el-card>
      <div style="margin-bottom: 10px">
        <el-input
          v-model="searchName"
          clearable
          placeholder="Enter user name to search"
          style="width: 200px; margin-right: 10px"
        />
        <el-button
          size="mini"
          type="success"
          icon="el-icon-search"
          @click="searchUser(searchName)"
        >Search</el-button>
        <el-button
          size="mini"
          type="warning"
          icon="el-icon-refresh-left"
          @click="searchName = ''"
        >Reset</el-button>
        <el-button sizi="mini" @click="handleAdd()" type="primary" icon="el-icon-plus">Add</el-button>
        <el-button @click="getUserList()" sizi="mini" icon="el-icon-refresh" style="float: right">Refresh</el-button>
      </div>
      <el-table :data="tableData" border v-loading="isLoading">
        <el-table-column label="username" prop="username" align="center" width="150px"></el-table-column>
        <el-table-column label="password" prop="password" align="center"></el-table-column>
        <el-table-column label="picture" align="center">
          <template slot-scope="scope">
            <div
              style="
                display: flex;
                justify-content: space-around;
                flex-flow: row wrap;
              "
            >
              <el-image
                style="width: 50px; height: 50px"
                v-for="(item, index) in scope.row.imgurl"
                :key="index"
                :src="item"
                :preview-src-list="scope.row.imgurl"
              ></el-image>
              <!-- <a :href="scope.row.imgurl" rel="external nofollow" target="_blank">{{scope.row.imgurl}}</a> -->
            </div>
          </template>
        </el-table-column>
        <el-table-column label="operation" align="center">
          <template slot-scope="scope">
            <el-button size="mini" @click="showEditDialog(scope.row)">
              <i class="el-icon-edit" /> Edit</el-button>
            <el-button size="mini" type="danger" @click="handleDelete(scope.row)">
              <i class="el-icon-delete" /> Delete</el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-card>
    <UserManageDialog :dialog="dialog" :formData="formData" @addUser="addUser" @editUser="editUser"></UserManageDialog>
  </div>
</template>
 
<script>
import UserManageDialog from "./userManageDialog.vue";
import { client_alioss, deleteMultiFiles } from "@/utils/alioss.js";
 
import {
  addUser,
  getUserList,
  editUser,
  deleteUser,
  searchUser
} from "@/api/userManage/index";
export default {
  name: "userManage",
  components: { UserManageDialog },
  data() {
    return {
      searchName: "",
      isLoading: false,
      dialog: {
        show: false,
        title: ""
      },
      formData: {},
      tableData: [
        {
          _id: "",
          username: "admin",
          password: "123",
          imgurl: []
        }
      ],
      currentImgs: []
    };
  },
  props: {},
  created() {},
  mounted() {
    this.getUserList();
  },
  computed: {},
  methods: {
    //Get the user list async getUserList() {
      this.isLoading = true;
      let { data } = await getUserList();
      this.tableData = data.data;
      this.isLoading = false;
    },
 
    //Open the new user window handleAdd() {
      this.dialog = {
        show: true,
        title: "New User",
        option: "add"
      };
      this.formData = {
        username: "",
        password: "",
        imgurl: []
      };
    },
    //Open the edit user window showEditDialog(row) {
      this.currentImgs = row.imgurl;
 
      this.dialog = {
        show: true,
        title: "Edit User",
        option: "edit"
      };
      this.formData = {
        _id: row._id,
        username: row.username,
        password: row.password,
        imgurl: row.imgurl
      };
    },
    //Add new user async addUser(urls) {
      this.formData.imgurl = urls;
 
      await addUser(this.formData);
      this.dialog.show = false;
      this.$notify({
        title: "Success",
        message: "Added user successfully!",
        type: "success"
      });
      this.getUserList();
    },
    //Edit user async editUser(urls) {
      this.formData.imgurl = urls;
 
      await editUser(this.formData, this.formData._id); //Update the database, especially the image url
 
      this.dialog.show = false;
      this.$notify({
        title: "Success",
        message: "Edit user successfully!",
        type: "success"
      });
      this.getUserList();
    },
    //Delete user handleDelete({ _id }) {
      this.$confirm("This operation will permanently delete the file, do you want to continue?", "Prompt", {
        confirmButtonText: "Confirm",
        cancelButtonText: "Cancel",
        type: "warning"
      })
        .then(async () => {
          this.$message({
            type: "success",
            message: "Deleted successfully!",
            showClose: true
          });
          let {
            data: { imgurl }
          } = await deleteUser(_id);
 
          //Delete the file in the server. Pass in the url array to be deleted await deleteMultiFiles(imgurl);
 
          this.getUserList();
        })
        .catch(() => {
          this.$message({
            type: "info",
            message: "Deleted",
            showClose: true
          });
        });
    },
    //Query by user name async searchUser(searchName) {
      this.isLoading = true;
      let { data } = await searchUser({
        searchName
      });
      this.tableData = data.data;
      this.isLoading = false;
    }
  },
  watch: {}
};
</script>
 
<style lang="scss" scoped>
.userManage {
}
</style> 

<!--userManageDialog -->
<template>
  <div class="userManageDialog">
    <el-dialog :title="dialog.title" width="45%" :visible.sync="dialog.show" v-if="dialog.show">
      <el-form ref="ref_form_userManage" :model="formData" :rules="rules" label-width="100px">
        <el-form-item label="username" prop="username">
          <el-input v-model="formData.username" autocomplete="off" style="width: 90%"></el-input>
        </el-form-item>
        <el-form-item label="password" prop="password">
          <el-input v-model="formData.password" autocomplete="off" style="width: 90%"></el-input>
        </el-form-item>
        <el-form-item label="picture" prop="imgurl">
          <!-- If the fileType attribute is not written, it means that both pictures and videos can be uploaded. fileType="image" means only images can be uploaded. fileType="video" means you can only upload videos -->
          <UploadImageVideo
            ref="ref_UploadImageVideo"
            bucket="bucket-lijiang-test"
            :currentUrls="formData.imgurl"
            :limitFileNumber="3"
            tip="1. Upload up to 3 photos; 2. Uploaded pictures can only be in JPEG/JPG/PNG format; 3. The size of a single picture cannot exceed 5MB!"
            fileType="image"
            :isMultiple="true"
          ></UploadImageVideo>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialog.show = false">Cancel</el-button>
        <el-button
          v-if="dialog.option == 'add'"
          @click="addUser('ref_form_userManage')"
          type="primary"
        >OK</el-button>
        <el-button
          v-if="dialog.option == 'edit'"
          @click="editUser('ref_form_userManage')"
          type="primary"
        >OK</el-button>
      </div>
    </el-dialog>
  </div>
</template>
 
<script>
import UploadImageVideo from "@/components/UploadImageVideo";
 
export default {
  name: "userManageDialog",
  components: { UploadImageVideo },
  props: ["dialog", "formData"],
  data() {
    return {
      fileList: [],
      rules:
        username: [
          { required: true, message: "Please enter your username", trigger: "blur" }
        ]
      }
    };
  },
  created() {},
  mounted() {},
  computed: {},
  methods: {
    addUser(formName) {
      this.$refs[formName].validate(async valid => {
        if (valid) {
          let urls = await this.$refs["ref_UploadImageVideo"].addFiles();
          this.$emit("addUser", urls);
        } else {
          console.log("error submit!!");
          return false;
        }
      });
    },
    editUser(formName) {
      this.$refs[formName].validate(async valid => {
        if (valid) {
          let urls = await this.$refs["ref_UploadImageVideo"].UpdateFiles();
 
          this.$emit("editUser", urls);
        } else {
          console.log("error submit!!");
          return false;
        }
      });
    }
  },
  watch: {}
};
</script>
<style lang="scss" scoped>
</style> 

import { uuid } from 'vue-uuid';
const OSS = require("ali-oss");
 
let client = new OSS({
    region: "oss-cn-chengdu",
    accessKeyId: "LTAI5tQPHvixV8aakp1vg8Jr",
    accessKeySecret: "xYyToToPe8UFQMdt4hpTUS4PNxzl9S",
    bucket: "bucket-lijiang-test",
 
});
 
export const client_alioss = client;
 
 
//delete file array export async function deleteMultiFiles(urls = []) {
    let arr_pathname = [];
    if (urls.length !== 0) {
        for (const item of urls) {
            //Don't use let url=require("url");url.parse(); it is invalid. To use new URL()
            let { pathname } = new URL(item);
            // decodeURIComponent() function converts Chinese garbled characters into Chinese arr_pathname.push(decodeURIComponent(pathname));
        }
        await client.deleteMulti(arr_pathname);
    }
} 

import request from '@/utils/request'
// Get the user list export function getUserList() {
    return request({
        url: '/api/userManage',
        method: 'get'
    })
}
 
// Add a new user export function addUser(data) {
    return request({
        url: '/api/userManage',
        method: 'post',
        data
    })
}
 
// Edit user export function editUser(data, _id) {
    return request({
        url: `/api/userManage/${_id}`,
        method: 'put',
        data
    })
}
 
// Delete user export function deleteUser(_id) {
    return request({
        url: `/api/userManage/${_id}`,
        method: 'delete'
    })
}
 
// Query by keyword export function searchUser(data) {
    return request({
        url: `/api/userManage/search`,
        method: 'get',
        params: data
    })
} 

const router = require('koa-router')()
 
const User = require("../models/User"); //Introduce module model router.prefix('/userManage')
 
//Get user list router.get('/', async (ctx, next) => {
    let data = await User.find({})
    ctx.body = {
        code: 200,
        message: "Request successful",
        data,
    }
})
//Add new user router.post('/', async (ctx, next) => {
    let { username, password, imgurl } = ctx.request.body;
    await User.create({ username, password, imgurl })
    ctx.body = { code: 200, message: "Added successfully" }
})
//Edit user router.put('/:_id', async (ctx, next) => {
    let { username, password, imgurl } = ctx.request.body;
    let { _id } = ctx.params
 
    await User.findByIdAndUpdate(_id, { username, password, imgurl })
    ctx.body = { code: 200, message: "Edited successfully" }
})
//Delete user router.delete('/:_id', async (ctx, next) => {
    let { _id } = ctx.params;
    let { imgurl } = await User.findByIdAndDelete(_id)
    ctx.body = { code: 200, message: "Deleted successfully", imgurl }
 
})
 
//Query users based on keywords. Fuzzy query router.get('/search', async (ctx, next) => {
    let { searchName } = ctx.request.query;
 
    let data = await User.find({ username: { $regex: searchName } })
    ctx.body = { code: 200, message: "Query successful", data }
})
module.exports = router

This is the end of this article about Vue encapsulation components for uploading pictures and videos. For more relevant vue component encapsulation content, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • vue-cropper plug-in realizes the encapsulation of image capture and upload component
  • Vue development package upload file component and usage examples
  • An example of encapsulating an image upload component based on vue-upload-component
  • An example of Vue encapsulating a simple and lightweight file upload component

<<:  Detailed explanation of Linux DMA interface knowledge points

>>:  Instance method for mysql string concatenation and setting null value

Recommend

Basic statements of MySQL data definition language DDL

MySQL DDL statements What is DDL, DML. DDL is dat...

Briefly describe the use and description of MySQL primary key and foreign key

Table of contents 1. Foreign key constraints What...

HTML Tutorial: Collection of commonly used HTML tags (4)

These introduced HTML tags do not necessarily ful...

How to connect to MySQL visualization tool Navicat

After installing Navicat The following error may ...

Docker View the Mount Directory Operation of the Container

Only display Docker container mount directory inf...

Linux disk sequential writing and random writing methods

1. Introduction ● Random writing will cause the h...

A very detailed summary of communication between Vue components

Table of contents Preface 1. Props, $emit one-way...

Configure nginx to redirect to the system maintenance page

Last weekend, a brother project was preparing to ...

Summary of common tool examples in MySQL (recommended)

Preface This article mainly introduces the releva...

Solution to the error when calling yum in docker container

When executing yum in dockerfile or in the contai...

Vue3 + TypeScript Development Summary

Table of contents Vue3 + TypeScript Learning 1. E...

A great collection of web standards learning resources

These specifications are designed to allow for bac...

Bugs encountered when using mybatis-generator with mysql8.0.3 in IDEA

1. Add the plug-in and add the following configur...