Vue3 导入 element-ui mybatis 将多个xlxs文件导入页面

文章描述了在Vue前端项目中,如何使用ElementPlus库实现主页面的导入弹窗以及子页面的文件上传功能,包括axios接口调用、文件上传处理、数据验证和后端SpringBoot框架的MultipartFile接收Excel文件的处理。
摘要由CSDN通过智能技术生成

 前端:

主页面----引用弹窗:

</template>
    <view>
    <view>
        <el-button type="primary" plain @click="handleImport">
            <Icon icon="ep:plus" />导入
        </el-button>
    </view>
    <ImportModel ref="importFormRef" @success="handleInit" />
    <view>
</template>
<script>
    const handleImport = () => {
      importFormRef.value.open() // 打开引入文件弹窗
    }
    const handleInit = () => {
          init() //刷新页面数据
    }
</script>

子页面----上传文件弹窗:

<template>
  <Dialog v-model="dialogVisible" title="信息导入" width="400">
    <el-upload
      ref="uploadRef"
      v-model:file-list="fileList"
      :auto-upload="false"
      :multiple="true"
      :disabled="formLoading"
      :headers="uploadHeaders"
      :on-error="submitFormError"
      :on-exceed="handleExceed"
      :on-change="handleChange"
      :on-success="submitFormSuccess"
      :before-upload="beforeColorUpload"
      :http-request="uploadFiles"
      accept=".xlsx, .xls"
      drag
    >
      <Icon icon="ep:upload" />
      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
    </el-upload>
    <template #footer>
      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
      <el-button @click="cancelUpload">取 消</el-button>
    </template>
  </Dialog>
</template>
<script lang="ts" name="ImportForm" setup>
import { getAccessToken, getTenantId } from '@/utils/auth'
import importInfo from '@/api/cloud/checkTem/index'

const message = useMessage() // 消息弹窗

const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中
const uploadRef = ref()
const uploadHeaders = ref() // 上传 Header 头
const fileList = ref([]) // 文件列表
const state = reactive({
  uploadEle: null as Element | null,
  uploadList: []
})
const handleChange = async (file, fileList) => {
  fileList.value = file
}

/** 打开弹窗 */
const open = () => {
  dialogVisible.value = true
  formLoading.value = false // 每次允许上传
  nextTick(() => {
    state.uploadEle = document.querySelector('.el-upload__input')
    state.uploadEle.webkitdirectory = true
  })
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗

const uploadFiles = async () => {
  console.log('进入uploadFiles')
}
/** 提交表单 */
const submitForm = async () => {
  if (fileList.value.length == 0) {
    message.error('请上传文件')
    return
  }
  // // 提交请求
  uploadHeaders.value = {
    Authorization: 'Bearer ' + getAccessToken(),
    'tenant-id': getTenantId()
  }
  submitFormSuccess({ code: 0 })
}

/** 文件上传成功 */
const emits = defineEmits(['success'])
const submitFormSuccess = async (response: any) => {
  console.log('submitFormSuccess')
  if (response.code !== 0) {
    message.error(response.msg)
    formLoading.value = false
    return
  }
  let formData = new FormData()
  formData.append('name', 'myk')
  fileList.value.forEach((item) => {
    formData.append('file', item.raw)
  })

  formLoading.value = true
  await importInfo(formData)
    .then(() => {
      message.success('导入成功')
      formLoading.value = false
      // uploadRef.value.clearFiles()
    })
    .catch((res) => {
      formLoading.value = false
    })
  dialogVisible.value = false
  unref(uploadRef)?.clearFiles()
  emits('success')
}
const beforeColorUpload = async () => {
  unref(uploadRef)?.clearFiles()
}
/** 上传错误提示 */
const submitFormError = (): void => {
  message.error('上传失败,请您重新上传!')
  formLoading.value = false
}

/** 文件数超出提示 */
const handleExceed = (): void => {
  message.error('最多只能上传一个文件!')
}

const cancelUpload = async () => {
  unref(uploadRef)?.clearFiles()
  dialogVisible.value = false
}
</script>

axios接口

import request from '@/config/axios'
    // 导入设备模板维护
    export const importEquTemObject = (formData) => {
      return request.upload({
        url: '/url/url/import',
        data: formData
      })
    }

获取权限js{看框架如何调用权限来更改}

/**
 * 配置浏览器本地存储的方式,可直接存储对象数组。
 */

import WebStorageCache from 'web-storage-cache'

type CacheType = 'localStorage' | 'sessionStorage'

export const CACHE_KEY = {
  IS_DARK: 'isDark',
  USER: 'user',
  LANG: 'lang',
  THEME: 'theme',
  LAYOUT: 'layout',
  ROLE_ROUTERS: 'roleRouters',
  DICT_CACHE: 'dictCache'
}

export const useCache = (type: CacheType = 'localStorage') => {
  const wsCache: WebStorageCache = new WebStorageCache({
    storage: type
  })

  return {
    wsCache
  }
}


// 第二个js引用
import { useCache } from '@/hooks/web/useCache'
const { wsCache } = useCache()


// 获取token
export const getAccessToken = () => {
  // 此处与TokenKey相同,此写法解决初始化时Cookies中不存在TokenKey报错
  return wsCache.get(AccessTokenKey) ? wsCache.get(AccessTokenKey) : wsCache.get('ACCESS_TOKEN')
}

// 刷新token
export const getRefreshToken = () => {
  return wsCache.get(RefreshTokenKey)
}


// ========== 租户相关 ==========
const TenantIdKey = 'TENANT_ID'
const TenantNameKey = 'TENANT_NAME'

export const getTenantId = () => {
  return wsCache.get(TenantIdKey)
}

export const setTenantId = (username: string) => {
  wsCache.set(TenantIdKey, username)
}

后端 :

Spring Boot框架 | 建立MUltpartFile类用于接收excel文件

package org.springframework.web.multipart;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;

import org.springframework.core.io.InputStreamSource;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
import org.springframework.util.FileCopyUtils;

public interface MultipartFile extends InputStreamSource {

	String getName();

    @Nullable
	String getOriginalFilename();
    
    @Nullable
	String getContentType();

    boolean isEmpty();

    long getSize();

    byte[] getBytes() throws IOException;

    @Override
	InputStream getInputStream() throws IOException;

    default Resource getResource() {
		return new MultipartFileResource(this);
	}

    void transferTo(File dest) throws IOException, IllegalStateException;

    default void transferTo(Path dest) throws IOException, IllegalStateException {
		FileCopyUtils.copy(getInputStream(), Files.newOutputStream(dest));
	}

controller 

@PostMapping("import")
@Operation(summary = "导入")
@Parameters(
      @Parameter(name = "file", description = "Excel 文件", required = true)
            )
public CommonResult<CheckBaseProjectImportRespVO> importEXCEL(
    @RequestParam("file") MultipartFile[] files) throws Exception {
        return success(importService.importBaseProject(files));
}




// service:
CheckBaseProjectImportRespVO importBaseProject(MultipartFile[] files) throws Exception;

导入VO类

// 传入进来的importVo类

package cn.iocoder.yudao.base.vo;

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import javax.validation.constraints.NotNull;
import java.math.BigDecimal;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题
public class BaseProjectImportVO {

    @ExcelProperty("学生姓名") // 导入的列名
    @NotNull(message = "学生姓名不可为空!") 
    private String name;


    @ExcelProperty("学生年龄") // 导入的列名
    @NotNull(message = "学生年龄不可为空!") 
    private String age;

    @ExcelProperty("成绩") // 导入的列名
    @NotNull(message = "成绩不可为空!") 
    private String score;

    @ExcelProperty("选项内容")
    private String options;

}

 返回的结果集

package cn.iocoder.yudao.base.BaseVo;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.util.List;
import java.util.Map;

@Data // 继承需要返回的基础数据
public class importResVO extends BaseVo{

    @Schema(description = "创建成功的数组", requiredMode =  Schema.RequiredMode.REQUIRED)
    private List<String> names;

    @Schema(description = "导入失败的用户集合,key 为用户名,value 为失败原因", 
            requiredMode = Schema.RequiredMode.REQUIRED)
    private Map<String, String> failNames;
}

实现方法

@Override
@Transactional(rollbackFor = Exception.class) // 必须添加事务!!
public ImportResVo  importBaseProject(MultipartFile[] files) throws Exception {
        // 筛选出文件名包含 "学生信息导入" 的文件
        List<MultipartFile> ftFiles = Arrays.stream(files).filter(ft ->         
        ft.getOriginalFilename().contains("学生信息导入")).collect(Collectors.toList());
        if(ftFiles.isEmpty()){
            throw exception(ALL,"请上传文件名包含【学生信息导入】的文件" );
        }
        // 读取 Excel 文件并将其内容转换为指定类型的对象列表
        List<ImportVO>  importExcelVOList = ExcelUtils.read(ftFiles.get(0),ImportVO.class);
        // 判断导入的数据是否重复
        for (int i = 0; i < importExcelVOList.size(); i++) {
            ImportVO importVo1 = importExcelVOList.get(i);
            for (int j = i + 1; j < importExcelVOList.size(); j++) {
                ImportVO importVo2 = importExcelVOList.get(j);
                if (importVo1.getName().equals(importVo2.getName()) &&
                    importVo1.getAge().equals(importVo2.getAge())) {
                    throw exception(ALL, "第"+(i + 1)+"行和第"+(j + 1)+"行数据重复!");
                }
            }
        }

        // 验证导入数据是否已经存在于表中:存在--修改更新该条数据;不存在--直接添加
        for (ImportVO importExcelVO : importExcelVOList) {
            // 验证点检类别是否存在于基础类别表中
            QueryWrapper<studentDO> qw3 = new QueryWrapper<>();
            qw3.in("name", importExcelVO.getName());
            StudentDO  studentDO = studentMapper.selectOne(qw3);
            if (studentDO == null) {
                // todo 添加数据 mapper.inseert
// 若传入A;B;C列,想存到数据库是[{index:0,value:A},{index:1,value:B},{index:2,value:C}] 
            String[] optionsArray = importExcelVO.getOptions().split("[;;]");
            JSONArray jsonArray = new JSONArray();
            for (int i = 0; i < optionsArray.length; i++) {
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("index", i);
                    jsonObject.put("value", optionsArray[i]);
                    jsonArray.add(jsonObject);
            }
            String jsonArrayString = jsonArray.toJSONString();
            projectDO1.setOptions(jsonArrayString);
            // Json类型数据存储结束——end———
            }else {
                // todo 添加数据 mapper.update 
            }   

        }

        for (MultipartFile ft : files) {
            String name = ft.getOriginalFilename();
        }
        return null;
    }

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值