当前位置: 首页 > news >正文

关于使用EasyExcel、 Vue3实现导入导出功能

后端部分: 其中查询数据的服务省略

1、引用

 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.3</version></dependency>

2、controller

package com.rs.cphs.sys.controller;import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.rs.common.exception.BusinessException;
import com.rs.common.response.ResponseResult;
import com.rs.common.utils.ResponseUtil;
import com.rs.cphs.common.util.Pageful;
import com.rs.cphs.sys.dto.ThirdDictMappingDTO;
import com.rs.cphs.sys.dto.ThirdDictMappingUpdateDTO;
import com.rs.cphs.sys.service.ImportListener;
import com.rs.cphs.sys.service.RedunMdmCodeDetailService;
import com.rs.cphs.sys.service.ThirdDictMappingService;
import com.rs.cphs.sys.service.impl.ThirdDictMappingServiceImpl;
import com.rs.cphs.sys.vo.ThirdDictMappingVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;/*** @Description:* @CreateDate: 2025-06-04 13:45:06* @Version: 7.0* @Author: zhuchuang* @Copyright: 2022-2032 Cloud HIS, All rights reserved.*/
@Api(tags = "三方字典对照管理")
@RestController
@RequestMapping("/api/thirdDictMapping")
@Slf4j
@Validated
public class ThirdDictMappingController {@Resourceprivate ThirdDictMappingService dictMappingService;@Resourceprivate RedunMdmCodeDetailService redunMdmCodeDetailService;ApiOperation("根据输入字典分类导出模板")@GetMapping(value = "/exportTemplate")public void exportTemplate(@RequestParam String publicDictCategory, @RequestParam String publicDictCategoryName, HttpServletResponse response) throws IOException {log.info("下载字典对照模板, 入参inputDictCategory: {}", publicDictCategory);// 1. 设置响应头response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");String fileName = URLEncoder.encode("dictMapping.xlsx", StandardCharsets.UTF_8);response.setHeader("Content-Disposition", "attachment;filename=" + fileName);// 2. 查询数据List<ThirdDictMappingVO> dataList = dictMappingService.getThirdDictMappingInfo(publicDictCategory, publicDictCategoryName);if (dataList == null || dataList.isEmpty()) {log.warn("没有找到对应的字典对照数据,publicDictCategory: {}", publicDictCategory);// 如果没有数据,可以选择抛出异常或返回空文件response.getWriter().write("没有找到对应的字典对照数据");}// 3. 写入ExcelEasyExcel.write(response.getOutputStream(), ThirdDictMappingVO.class).sheet("三方字典对照模板").doWrite(dataList);}@ApiOperation("导入字典对照")@PostMapping(value = "/importTemplate")public ResponseResult<Boolean> importTemplate(@RequestParam("file") MultipartFile file) throws IOException {if (file.isEmpty()) {log.error("导入字典对照失败,文件不能为空");throw new BusinessException("文件不能为空");}EasyExcel.read(file.getInputStream(), ThirdDictMappingVO.class, new ImportListener((ThirdDictMappingServiceImpl) dictMappingService, redunMdmCodeDetailService)).sheet().doRead();return ResponseUtil.makeSuccess(true);}
}

3、导入监听service

package com.rs.cphs.sys.service;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.util.StringUtils;
import com.rs.common.exception.BusinessException;
import com.rs.cphs.sys.dao.entity.ThirdDictMappingEntity;
import com.rs.cphs.sys.dto.RedunMdmCodeDetailQueryDTO;
import com.rs.cphs.sys.service.impl.ThirdDictMappingServiceImpl;
import com.rs.cphs.sys.vo.RedunMdmCodeDetailVO;
import com.rs.cphs.sys.vo.ThirdDictMappingVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;import java.util.ArrayList;
import java.util.List;/*** @Description:* @CreateDate: 2025-06-04 16:15:14* @Version: 7.0* @Author: zhuchuang* @Copyright: 2022-2032 NeuSoft Cloud HIS, All rights reserved.*/
@Slf4jpublic class ImportListener extends AnalysisEventListener<ThirdDictMappingVO> {private List<ThirdDictMappingVO> cachedList = new ArrayList<>();private final ThirdDictMappingServiceImpl thirdDictMappingServiceImpl;private final RedunMdmCodeDetailService redunMdmCodeDetailService;public ImportListener(ThirdDictMappingServiceImpl thirdDictMappingServiceImpl,  RedunMdmCodeDetailService redunMdmCodeDetailService) {this.thirdDictMappingServiceImpl = thirdDictMappingServiceImpl;this.redunMdmCodeDetailService = redunMdmCodeDetailService;}@Overridepublic void invoke(ThirdDictMappingVO thirdDictMappingVO, AnalysisContext analysisContext) {// 单行校验if (StringUtils.isBlank(thirdDictMappingVO.getThirdDictCode()) || StringUtils.isBlank(thirdDictMappingVO.getThirdDictName()) ||StringUtils.isBlank(thirdDictMappingVO.getThirdPartyCode()) || StringUtils.isBlank(thirdDictMappingVO.getThirdPartyName())|| StringUtils.isBlank(thirdDictMappingVO.getPublicDictCode()) || StringUtils.isBlank(thirdDictMappingVO.getPublicDictName())|| StringUtils.isBlank(thirdDictMappingVO.getPublicDictCategory()) || StringUtils.isBlank(thirdDictMappingVO.getPublicDictCategoryName())) {throw new BusinessException("第" + analysisContext.readRowHolder().getRowIndex() + "行:数据有为空的情况");}// 判断三方厂商是否存在RedunMdmCodeDetailQueryDTO queryDTO = new RedunMdmCodeDetailQueryDTO();queryDTO.setConsInfoCode(thirdDictMappingVO.getThirdPartyCode());queryDTO.setConsInfoName(thirdDictMappingVO.getThirdPartyName());List<RedunMdmCodeDetailVO> redunMdmCodeDetailVOList = redunMdmCodeDetailService.getList(queryDTO);if (CollectionUtils.isEmpty(redunMdmCodeDetailVOList)) {throw new BusinessException("第" + analysisContext.readRowHolder().getRowIndex() + "行:三方厂商不存在");}cachedList.add(thirdDictMappingVO);try {// 每1000条批量入库if (cachedList.size() >= 1000) {// cachedList 转成 List<ThirdDictMappingEntity>List<ThirdDictMappingEntity> thirdDictMappingList = cachedList.stream().map(vo -> {ThirdDictMappingEntity entity = new ThirdDictMappingEntity();entity.setPublicDictCode(vo.getPublicDictCode());entity.setPublicDictName(vo.getPublicDictName());entity.setPublicDictCategory(vo.getPublicDictCategory());entity.setPublicDictCategoryName(vo.getPublicDictCategoryName());entity.setThirdPartyCode(vo.getThirdPartyCode());entity.setThirdPartyName(vo.getThirdPartyName());entity.setThirdDictCode(vo.getThirdDictCode());entity.setThirdDictName(vo.getThirdDictName());return entity;}).toList();thirdDictMappingServiceImpl.saveBatch(thirdDictMappingList);cachedList.clear();}} catch (BusinessException e) {log.error("导入三方对照数据入库失败", e);throw new BusinessException("导入三方对照数据入库失败:" + e.getMessage());}}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {try {if (!cachedList.isEmpty()) {List<ThirdDictMappingEntity> thirdDictMappingList = cachedList.stream().map(vo -> {ThirdDictMappingEntity entity = new ThirdDictMappingEntity();entity.setPublicDictCode(vo.getPublicDictCode());entity.setPublicDictName(vo.getPublicDictName());entity.setPublicDictCategory(vo.getPublicDictCategory());entity.setPublicDictCategoryName(vo.getPublicDictCategoryName());entity.setThirdPartyCode(vo.getThirdPartyCode());entity.setThirdPartyName(vo.getThirdPartyName());entity.setThirdDictCode(vo.getThirdDictCode());entity.setThirdDictName(vo.getThirdDictName());return entity;}).toList();thirdDictMappingServiceImpl.saveBatch(thirdDictMappingList);}} catch (BusinessException e) {log.error("导入三方对照数据入库失败", e);throw new BusinessException("导入三方对照数据入库失败:" + e.getMessage());}}
}

前端代码:前段给出导出导入页面及方法的代码,其他省略

1、前端样式

2、接口定义: 前端请求requestCphs方法直接解析了后端ResponseResult返回的接口

/*** 导出模板接口* @param publicDictCategory * @returns */
export const exportTemplate = async (publicDictCategory: string, publicDictCategoryName: string) => {return await requestCphs(`/api/thirdDictMapping/exportTemplate`, { params: { publicDictCategory: publicDictCategory, publicDictCategoryName: publicDictCategoryName }, method: 'get' , responseType: 'blob'})
}/*** 导入模板接口* @param file * @returns */
export const importTemplate = async (file: File) => {const formData = new FormData();formData.append('file', file);return await requestCphs(`/api/thirdDictMapping/importTemplate`, {method: 'post',data: formData,headers: { 'Content-Type': 'multipart/form-data' },});
}

3、导入页面控件

<template><el-dialogv-mod="dialogVisible":title="$t('pages.system.third-dict-mapping.import')"width="700px"top="10vh":before-close="handleClose"><div class="import-container"><!-- 文件选择按钮 --><inputtype="file"ref="fileInput"accept=".xlsx, .xls"@change="handleFileChange"style="display: none"/><el-buttonaction="upload"@click="openFileDialog">{{t('pages.system.third-dict-mapping.message.importTitle')}}</el-button><span v-if="sectedFile">{{t('pages.system.third-dict-mapping.message.importSected')}}:{{ sectedFile.name }}</span><!-- 导入按钮 --><el-buttonaction="import"@click="handleImport":disabled="!sectedFile">{{t('pages.system.third-dict-mapping.message.importData')}}</el-button></div><template #footer><el-button action="close" @click="handleClose" /></template></el-dialog>
</template>
<script lang="ts" setup>
import { useVMod } from '@vueuse/core'
const { t } = useI18n()
import { message } from '@hio/hio-biz/src/utils/message'const props = defineProps({visible: {type: Boolean,required: true,},
})// 状态管理
const fileInput = ref(null);
const sectedFile = ref(null);// 打开文件选择框
const openFileDialog = () => {fileInput.value.click();
};// 处理文件选择
const handleFileChange = (e: { target: { files: any[]; }; }) => {const file = e.target.files[0];if (file) {// 验证文件类型const isExc = file.type.includes('exc') || file.name.endsWith('.xls') || file.name.endsWith('.xlsx');if (!isExc) {message(t('pages.system.third-dict-mapping.message.importWarning'), { type: 'warning' });return;}sectedFile.value = file;}
};// 导入数据
const handleImport = async () => {if (!sectedFile.value) {message(t('pages.system.third-dict-mapping.message.importInfo'), { type: 'info' });return;}emit('handleThirdImportClick', sectedFile)
};const emit = defineEmits(['update:visible', 'handleThirdImportClick'])
const dialogVisible = useVMod(props, 'visible', emit)const handleClose = () => {dialogVisible.value = false
}</script><style lang="scss" scoped>.span-option {margin-left:5px;font-size: 15px;}
</style>

4、导出页面控件

<template><el-dialogv-model="dialogVisible":title="$t('pages.system.third-dict-mapping.export')"width="700px"top="10vh":before-close="handleClose"><el-form ref="formRef" :model="formData" :rules="rules"><el-form-item :label="$t('pages.system.third-dict-mapping.publicDictCategory')" prop="publicDictCategory"><el-select v-model="formData.publicDictCategory" :placeholder="$t('pages.system.third-dict-mapping.placeholder.publicDictCategory')" @change="changeThirdDict"><el-optionv-for="item in props.dict?.PUBLIC_DICT_CATEGORY || []":key="item.value":label="item.name":value="item.value":filterable="true"></el-option></el-select></el-form-item></el-form><template #footer><el-standard-button action="close" @click="handleClose" /><el-standard-button action="export" @click="handleExportClick" /></template></el-dialog>
</template>
<script lang="ts" setup>
import { useVModel } from '@vueuse/core'
import { GuaForm } from '@el/one-ui'
import {ThirdDictMapping} from "~/api/third-dict-mapping/data";
const { t } = useI18n()const props = defineProps({visible: {type: Boolean,required: true,},// business为undefined时,表示新增,否则表示编辑dict:{type: Object as PropType<Record<string, any>>,}
})const emit = defineEmits(['update:visible', 'handleThirdExportClick'])const formRef = ref<InstanceType<typeof GuaForm>>()const rules = ref({publicDictCategory: [{ required: true, message: t('pages.system.third-dict-mapping.placeholder.publicDictCategory'), trigger: 'blur' },]
})const formData: Partial<ThirdDictMapping> = reactive({})const dialogVisible = useVModel(props, 'visible', emit)const handleClose = () => {dialogVisible.value = false
}const handleExportClick = () => {formRef.value?.validate((valid: Boolean) => {if (valid) {emit('handleThirdExportClick', formData.publicDictCategory, formData.publicDictCategoryName)}})
}
const changeThirdDict = () => {const selected = props.dict?.PUBLIC_DICT_CATEGORY?.find((item: any) => item.value === formData.publicDictCategory)formData.publicDictCategoryName = selected ? selected.name : ''
}</script><style lang="scss" scoped>.span-option {margin-left:5px;font-size: 15px;}
</style>

5、主页导入导出控件引用

<third-dict-mapping-exportv-if="visibleExport"v-model:visible="visibleExport"@handleThirdExportClick="handleThirdExportClick":dict="dict"></third-dict-mapping-export><third-dict-mapping-importv-if="visibleImport"v-model:visible="visibleImport"@handleThirdImportClick="handleThirdImportClick"></third-dict-mapping-import>import thirdDictMappingExport from './widget/third-dict-mapping-export.vue'
import thirdDictMappingImport from './widget/third-dict-mapping-import.vue'

主页导入导出方法实现

const visibleExport = ref(false)
const visibleImport = ref(false)const thirdImportClick = () => {visibleImport.value = true
}/*** 导入*/
const handleThirdImportClick = async (selectedFile: { value: File; }) => {try {const response = await importTemplate(selectedFile.value)// 处理响应if (response) {message(t('pages.system.third-dict-mapping.message.importSuccess'), { type: 'success' });visibleImport.value = falsegetData()} else {message(t('pages.system.third-dict-mapping.message.importFail'), { type: 'error' });}} catch (error) {console.error(error)message(t('pages.system.third-dict-mapping.message.importFail'), { type: 'error' });}
}/*** 导出*/
const thirdExportClick = () => {visibleExport.value = true
}/*** 导出*/
const handleThirdExportClick = async (publicDictCategory: string, publicDictCategoryName: string) => {try {const result = await exportTemplate(publicDictCategory, publicDictCategoryName)if (result) {const blob = new Blob([result], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});// 创建下载链接const url = window.URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = `${publicDictCategory}_dict_mapping_${new Date().toISOString().slice(0, 10)}.xlsx`; // 设置下载文件名document.body.appendChild(a);a.click();// 清理资源document.body.removeChild(a);window.URL.revokeObjectURL(url);message(t('pages.system.third-dict-mapping.message.exportSuccess'), {type: 'success'})}else {message(t('pages.system.third-dict-mapping.message.exportFail'), {type: 'error'})}visibleExport.value = false} catch (error) {console.error(error)message(t('pages.system.third-dict-mapping.message.exportFail'), {type: 'error'})}
}

相关文章:

  • SpringCloud + MybatisPlus:多租户模式与实现
  • 《网络安全与防护》知识点复习
  • Linux安装C语言环境教程
  • C# net8生成excel,并设置列规则导出文件
  • 深入剖析 Spring @Bean 注解:灵活定义与掌控你的 Bean
  • ABAP 上传 excel 报表
  • get_attribute的使用方法
  • ThreadPoolTaskExecutor+CompletableFuture实现多线程异步数据同步和自定义线程池监控和动态调整实现
  • UE5 学习系列(九)光照系统介绍
  • stm32cubeide中编译非flash起始地址开始的程序
  • 【ARMv7-A】——CLZ 指令
  • Swift 解法详解:如何在二叉树中寻找最长连续序列
  • 怎么轻松实现报表跨库移植
  • 前端Vue3国际化开发 :使用vue-i18n库和Element Plus 组件实现
  • slam--高斯分布
  • 4、程序的固化和下载(一)
  • 基于 SpringBoot + Vue 在线点餐系统(前后端分离)
  • Eplan2022更改用户界面颜色
  • 文档测试发送
  • 目标检测我来惹2-SPPNet
  • 做百度竞价用什么网站/企业网站推广公司
  • 设计网站页面特效怎么做/宁波seo排名费用
  • 新沂网站制作/图片优化是什么意思
  • 数字化文化馆网站建设/全国疫情一览表
  • 在网上可以做宣传的有那些网站/提高百度搜索排名
  • 广告创意设计案例/贵阳seo网站推广