vue3上传图片到lin-cms-koa后台

vue yekong

后台开发过程中,需要实现一个图片上传功能,后台api接口我们使用的是lin-cms-koa.

接口封装

// 上传文件
export function uploadImg(file) {
    const formData = new FormData();
    formData.append('file', file); // 将文件添加到 FormData 对象中

    return request({
        url: '/cms/file/upload',
        method: 'post',
        data: formData, // 使用 FormData 作为数据
        headers: {
            // 'Content-Type' 不需要在这里设置,axios 会自动处理
        }
    });
}

单个图片上传

<template>
  <div class="avatar-uploader" @click="handleUpload">
    <img v-if="computedAvatar" :src="computedAvatar" class="avatar" />
    <el-icon v-else class="avatar-uploader-icon">
      <Plus />
    </el-icon>
    <input
        ref="fileInput"
        type="file"
        class="file-input"
        @change="handleFileChange"
        style="display: none;"
    />
  </div>
</template>

<script setup>
import { defineProps, defineEmits, computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { apiBaseUrl } from '@/api/ipConfig.js'
import { uploadImg } from '@/api/api/cms.js'

const props = defineProps({
  avatar: {
    type: String,
    default: ''
  }
})

const emit = defineEmits(['update:avatar'])

const fileInput = ref(null)

// 计算属性,用于生成完整的头像 URL
const computedAvatar = computed(() => {
  return props.avatar ? apiBaseUrl + '/assets/' + props.avatar : ''
})

// 点击上传按钮时触发文件选择
const handleUpload = () => {
  fileInput.value.value = null; // 清空文件输入框的值
  fileInput.value.click()
}

// 文件选择后的处理函数
const handleFileChange = async (e) => {
  const file = e.target.files[0]
  if (!file) return; // 如果没有选择文件,直接返回

  const isJPG = file.type === 'image/jpeg'
  const isPNG = file.type === 'image/png'
  const isLt2M = file.size / 1024 / 1024 < 2

  if (!isJPG && !isPNG) {
    ElMessage.error('头像只能是 JPG 或 PNG 格式!')
    return
  }
  if (!isLt2M) {
    ElMessage.error('头像大小不能超过 2MB!')
    return
  }

  try {
    const response = await uploadImg(file); // 直接传递文件对象
    if (response && response.length > 0) {
      const newAvatarPath = response[0].path; // 更新头像路径
      emit('update:avatar', newAvatarPath); // 触发事件并传递新路径
      ElMessage.success('头像上传成功');
      fileInput.value.value = null; // 上传成功后清空文件输入框的值
    }
  } catch (error) {
    ElMessage.error('上传失败,请重试');
  }
}
</script>

<style scoped>
.avatar-uploader {
  text-align: center;
  width: 178px;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: nowrap;
  flex-direction: row;
  align-content: flex-start;
  height: 178px;
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}

.avatar {
  width: 178px;
  height: 178px;
  display: block;
}
</style>

使用

<AvatarUploader v-model:avatar="form.avatar"></AvatarUploader>

多个图片上传

多个图片上传,上传后可以删除,返回的图片路径,逗号分割。

<template>
  <div class="uploadImgList">
    <div v-for="(image, index) in computedImages" :key="index" class="avatar">
      <img :src="image" class="avatar"/>
      <el-icon class="delete-icon" @click.stop="removeImage(index)">
        <Delete/>
      </el-icon>
    </div>
    <div class="avatar-uploader" @click="handleUpload">
      <el-icon v-if="images.length < maxImages" class="avatar-uploader-icon">
        <Plus/>
      </el-icon>
      <input
          ref="fileInput"
          type="file"
          class="file-input"
          @change="handleFileChange"
          style="display: none;"
          multiple
      />
    </div>
  </div>
</template>

<script setup>
import {defineProps, defineEmits, computed, ref} from 'vue'
import {ElMessage} from 'element-plus'
import {apiBaseUrl} from '@/api/ipConfig.js'
import {uploadImg} from '@/api/api/cms.js'
import {Plus, Delete} from '@element-plus/icons-vue' // 确保引入删除图标

const props = defineProps({
  avatar: {
    type: String,
    default: ''
  },
  maxImages: {
    type: Number,
    default: 3 // 默认最多上传3张图片
  }
})

const emit = defineEmits(['update:avatar'])

const fileInput = ref(null)
const images = ref(props.avatar ? props.avatar.split(',') : []) // 用于存储上传的图片路径

// 计算属性,用于生成完整的图片 URL
const computedImages = computed(() => {
  return images.value.map(img => apiBaseUrl + '/assets/' + img)
})

// 点击上传按钮时触发文件选择
const handleUpload = () => {
  fileInput.value.value = null; // 清空文件输入框的值
  fileInput.value.click()
}

// 文件选择后的处理函数
const handleFileChange = async (e) => {
  const files = e.target.files
  if (!files.length) return; // 如果没有选择文件,直接返回

  for (const file of files) {
    const isJPG = file.type === 'image/jpeg'
    const isPNG = file.type === 'image/png'
    const isLt2M = file.size / 1024 / 1024 < 2

    if (!isJPG && !isPNG) {
      ElMessage.error('头像只能是 JPG 或 PNG 格式!')
      return
    }
    if (!isLt2M) {
      ElMessage.error('头像大小不能超过 2MB!')
      return
    }

    try {
      const response = await uploadImg(file); // 直接传递文件对象
      if (response && response.length > 0) {
        const newAvatarPath = response[0].path; // 更新头像路径
        images.value.push(newAvatarPath); // 添加新图片路径
        emit('update:avatar', images.value.join(',')); // 触发事件并传递新路径
        ElMessage.success('头像上传成功');
      }
    } catch (error) {
      ElMessage.error('上传失败,请重试');
    }
  }
}

// 移除图片
const removeImage = (index) => {
  images.value.splice(index, 1); // 从数组中移除图片
  emit('update:avatar', images.value.join(',')); // 更新父组件的图片路径
}
</script>

<style scoped>
.uploadImgList {
  display: flex;
  justify-content: flex-start;
  align-items: flex-start;
  flex-wrap: wrap;
  flex-direction: row;
  align-content: flex-start;
}

.avatar-uploader {
  text-align: center;
  width: 150px;
  height: 150px;
  align-items: center;
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: nowrap;
  flex-direction: row;
  align-content: flex-start;
}

.avatar {
  width: 150px;
  position: relative;
  height: 150px;
  display: block;
  margin-right: 10px;
  border: 1px solid #ddd;
}

.delete-icon {
  position: absolute;
  top: 0;
  right: 0;
  cursor: pointer;
  color: red; /* 删除图标颜色 */
}
</style>

使用组件

<AvatarUploader v-model:avatar="form.img"></AvatarUploader>
喜欢