mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-10-31 22:57:17 +08:00 
			
		
		
		
	新增:新增系统管理/部门管理/导出功能(引入 Easy Excel 依赖用于导出 Excel,详情可见 README 介绍。另请注意:测试导出功能时,前端需要关闭 mockjs,否则 responseType 会被 mockjs 设置为 '',导致导出的文件无法打开)
This commit is contained in:
		| @@ -85,6 +85,7 @@ yarn dev | ||||
| | [Liquibase](https://github.com/liquibase/liquibase)          | 4.9.1        | 用于管理数据库版本,跟踪、管理和应用数据库变化。             | | ||||
| | [Redis](https://redis.io/)                                   | 6.2.7        | 高性能的 key-value 数据库。                                  | | ||||
| | [Redisson](https://github.com/redisson/redisson/wiki/Redisson%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D) | 3.19.0       | 不仅仅是一个 Redis Java 客户端,同其他 Redis Java 客户端有着很大的区别,相比之下其他客户端提供的功能还仅仅停留在作为数据库驱动层面上,比如仅针对 Redis 提供连接方式,发送命令和处理返回结果等。而 Redisson 充分的利用了 Redis 键值数据库提供的一系列优势,基于 Java 实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。使得原本作为协调单机多线程并发程序的工具包获得了协调分布式多机多线程并发系统的能力,大大降低了设计和研发大规模分布式系统的难度。同时结合各富特色的分布式服务,更进一步简化了分布式环境中程序相互之间的协作。 | | ||||
| | [Easy Excel](https://easyexcel.opensource.alibaba.com/)      | 3.2.0        | 一个基于 Java 的、快速、简洁、解决大文件内存溢出的 Excel 处理工具。 | | ||||
| | Easy Captcha                                                 | 1.6.2        | Java 图形验证码,支持 gif、中文、算术等类型,可用于 Java Web、JavaSE 等项目。 | | ||||
| | [Knife4j](https://doc.xiaominfo.com/)                        | 4.0.0        | 前身是 swagger-bootstrap-ui,集 Swagger2 和 OpenAPI3 为一体的增强解决方案。本项目使用的是 [knife4j-openapi3-spring-boot-starter](https://gitee.com/xiaoym/swagger-bootstrap-ui-demo/tree/master/knife4j-springdoc-openapi-demo) 基于 OpenAPI3 规范,在 Spring Boot < 3.0.0-M1 的单体架构下可以直接引用此 starter,该模块包含了 UI 部分,底层基于 springdoc-openapi 项目。 | | ||||
| | [Hutool](https://www.hutool.cn/)                             | 5.8.11       | 小而全的 Java 工具类库,通过静态方法封装,降低相关 API 的学习成本,提高工作效率,使 Java 拥有函数式语言般的优雅,让 Java 语言也可以“甜甜的”。 | | ||||
| @@ -214,6 +215,7 @@ continew-admin  # 全局通用项目配置及依赖版本管理 | ||||
|   │                ├─ annotation   # 公共注解 | ||||
|   │                ├─ base         # 公共基类 | ||||
|   │                ├─ config       # 公共配置 | ||||
|   │                │  ├─ easyexcel    # Easy Excel 配置 | ||||
|   │                │  ├─ jackson      # Jackson 配置 | ||||
|   │                │  ├─ mybatis      # MyBatis Plus 配置 | ||||
|   │                │  ├─ threadpool   # 线程池配置 | ||||
|   | ||||
| @@ -115,6 +115,12 @@ limitations under the License. | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- ################ 工具库相关 ################ --> | ||||
|         <!-- Easy Excel(一个基于 Java 的、快速、简洁、解决大文件内存溢出的 Excel 处理工具) --> | ||||
|         <dependency> | ||||
|             <groupId>com.alibaba</groupId> | ||||
|             <artifactId>easyexcel</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- 第三方封装 Ip2region(离线 IP 数据管理框架和定位库,支持亿级别的数据段,10 微秒级别的查询性能,提供了许多主流编程语言的 xdb 数据管理引擎的实现) --> | ||||
|         <dependency> | ||||
|             <groupId>net.dreamlu</groupId> | ||||
|   | ||||
| @@ -22,6 +22,7 @@ import lombok.Data; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
|  | ||||
| import com.alibaba.excel.annotation.ExcelProperty; | ||||
| import com.fasterxml.jackson.annotation.JsonIgnore; | ||||
|  | ||||
| /** | ||||
| @@ -45,11 +46,13 @@ public class BaseDetailVO extends BaseVO { | ||||
|      * 修改人 | ||||
|      */ | ||||
|     @Schema(description = "修改人") | ||||
|     @ExcelProperty(value = "修改人") | ||||
|     private String updateUserString; | ||||
|  | ||||
|     /** | ||||
|      * 修改时间 | ||||
|      */ | ||||
|     @Schema(description = "修改时间") | ||||
|     @ExcelProperty(value = "修改时间") | ||||
|     private LocalDateTime updateTime; | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,39 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.cnadmin.common.base; | ||||
|  | ||||
| import java.io.Serializable; | ||||
|  | ||||
| import com.baomidou.mybatisplus.annotation.IEnum; | ||||
|  | ||||
| /** | ||||
|  * 枚举基类 | ||||
|  * | ||||
|  * @param <V> | ||||
|  *            value 类型 | ||||
|  * @param <D> | ||||
|  *            description 类型 | ||||
|  * @author Charles7c | ||||
|  * @since 2023/2/5 20:44 | ||||
|  */ | ||||
| public interface BaseEnum<V extends Serializable, D extends Serializable> extends IEnum<V> { | ||||
|  | ||||
|     /** | ||||
|      * 枚举描述 | ||||
|      */ | ||||
|     D getDescription(); | ||||
| } | ||||
| @@ -23,6 +23,7 @@ import lombok.Data; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
|  | ||||
| import com.alibaba.excel.annotation.ExcelProperty; | ||||
| import com.fasterxml.jackson.annotation.JsonIgnore; | ||||
|  | ||||
| /** | ||||
| @@ -46,11 +47,13 @@ public class BaseVO implements Serializable { | ||||
|      * 创建人 | ||||
|      */ | ||||
|     @Schema(description = "创建人") | ||||
|     @ExcelProperty(value = "创建人") | ||||
|     private String createUserString; | ||||
|  | ||||
|     /** | ||||
|      * 创建时间 | ||||
|      */ | ||||
|     @Schema(description = "创建时间") | ||||
|     @ExcelProperty(value = "创建时间") | ||||
|     private LocalDateTime createTime; | ||||
| } | ||||
|   | ||||
| @@ -86,6 +86,8 @@ public class WebMvcConfiguration implements WebMvcConfigurer { | ||||
|         corsProperties.getAllowedMethods().forEach(config::addAllowedMethod); | ||||
|         // 配置允许跨域的请求头 | ||||
|         corsProperties.getAllowedHeaders().forEach(config::addAllowedHeader); | ||||
|         // 配置允许跨域的响应头 | ||||
|         corsProperties.getExposedHeaders().forEach(config::addExposedHeader); | ||||
|  | ||||
|         // 添加映射路径,拦截一切请求 | ||||
|         UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); | ||||
|   | ||||
| @@ -0,0 +1,92 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.cnadmin.common.config.easyexcel; | ||||
|  | ||||
| import com.alibaba.excel.converters.Converter; | ||||
| import com.alibaba.excel.enums.CellDataTypeEnum; | ||||
| import com.alibaba.excel.metadata.GlobalConfiguration; | ||||
| import com.alibaba.excel.metadata.data.ReadCellData; | ||||
| import com.alibaba.excel.metadata.data.WriteCellData; | ||||
| import com.alibaba.excel.metadata.property.ExcelContentProperty; | ||||
|  | ||||
| import cn.hutool.core.convert.Convert; | ||||
| import cn.hutool.core.util.ClassUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
|  | ||||
| import top.charles7c.cnadmin.common.base.BaseEnum; | ||||
|  | ||||
| /** | ||||
|  * Easy Excel 枚举基类转换器 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/2/5 19:29 | ||||
|  */ | ||||
| public class ExcelBaseEnumConverter implements Converter<BaseEnum<Integer, String>> { | ||||
|  | ||||
|     @Override | ||||
|     public Class<BaseEnum> supportJavaTypeKey() { | ||||
|         return BaseEnum.class; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public CellDataTypeEnum supportExcelTypeKey() { | ||||
|         return CellDataTypeEnum.STRING; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 转换为 Java 数据(读取 Excel) | ||||
|      */ | ||||
|     @Override | ||||
|     public BaseEnum convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, | ||||
|         GlobalConfiguration globalConfiguration) { | ||||
|         return this.getEnum(BaseEnum.class, Convert.toStr(cellData.getData())); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 转换为 Excel 数据(写入 Excel) | ||||
|      */ | ||||
|     @Override | ||||
|     public WriteCellData<String> convertToExcelData(BaseEnum<Integer, String> value, | ||||
|         ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { | ||||
|         if (ObjectUtil.isNull(value)) { | ||||
|             return new WriteCellData<>(""); | ||||
|         } | ||||
|         return new WriteCellData<>(value.getDescription()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 通过 value 获取枚举对象,获取不到时为 {@code null} | ||||
|      * | ||||
|      * @param enumType | ||||
|      *            枚举类型 | ||||
|      * @param description | ||||
|      *            描述 | ||||
|      * @return 对应枚举 ,获取不到时为 {@code null} | ||||
|      */ | ||||
|     private BaseEnum<Integer, String> getEnum(Class<?> enumType, String description) { | ||||
|         Object[] enumConstants = enumType.getEnumConstants(); | ||||
|         for (Object enumConstant : enumConstants) { | ||||
|             if (ClassUtil.isAssignable(BaseEnum.class, enumType)) { | ||||
|                 BaseEnum<Integer, String> baseEnum = (BaseEnum<Integer, String>)enumConstant; | ||||
|                 if (baseEnum.getDescription().equals(description)) { | ||||
|                     return baseEnum; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,79 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.cnadmin.common.config.easyexcel; | ||||
|  | ||||
| import java.math.BigDecimal; | ||||
|  | ||||
| import com.alibaba.excel.converters.Converter; | ||||
| import com.alibaba.excel.enums.CellDataTypeEnum; | ||||
| import com.alibaba.excel.metadata.GlobalConfiguration; | ||||
| import com.alibaba.excel.metadata.data.ReadCellData; | ||||
| import com.alibaba.excel.metadata.data.WriteCellData; | ||||
| import com.alibaba.excel.metadata.property.ExcelContentProperty; | ||||
|  | ||||
| import cn.hutool.core.convert.Convert; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
|  | ||||
| /** | ||||
|  * Easy Excel 大数值转换器(Excel 中对长度超过 15 位的数值输入是有限制的,从 16 位开始无论录入什么数字均会变为 0,因此输入时只能以文本的形式进行录入) | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/2/5 19:29 | ||||
|  */ | ||||
| public class ExcelBigNumberConverter implements Converter<Long> { | ||||
|  | ||||
|     /** | ||||
|      * Excel 输入数值长度限制 | ||||
|      */ | ||||
|     private static final int MAX_LENGTH = 15; | ||||
|  | ||||
|     @Override | ||||
|     public Class<Long> supportJavaTypeKey() { | ||||
|         return Long.class; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public CellDataTypeEnum supportExcelTypeKey() { | ||||
|         return CellDataTypeEnum.STRING; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 转换为 Java 数据(读取 Excel) | ||||
|      */ | ||||
|     @Override | ||||
|     public Long convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, | ||||
|         GlobalConfiguration globalConfiguration) { | ||||
|         return Convert.toLong(cellData.getData()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 转换为 Excel 数据(写入 Excel) | ||||
|      */ | ||||
|     @Override | ||||
|     public WriteCellData<Object> convertToExcelData(Long value, ExcelContentProperty contentProperty, | ||||
|         GlobalConfiguration globalConfiguration) { | ||||
|         if (ObjectUtil.isNotNull(value)) { | ||||
|             String str = Long.toString(value); | ||||
|             if (str.length() > MAX_LENGTH) { | ||||
|                 return new WriteCellData<>(str); | ||||
|             } | ||||
|         } | ||||
|         WriteCellData<Object> writeCellData = new WriteCellData<>(new BigDecimal(value)); | ||||
|         writeCellData.setType(CellDataTypeEnum.NUMBER); | ||||
|         return writeCellData; | ||||
|     } | ||||
| } | ||||
| @@ -19,7 +19,6 @@ package top.charles7c.cnadmin.common.config.jackson; | ||||
| import java.io.IOException; | ||||
| import java.lang.reflect.Field; | ||||
| 
 | ||||
| import com.baomidou.mybatisplus.annotation.IEnum; | ||||
| import com.fasterxml.jackson.core.*; | ||||
| import com.fasterxml.jackson.databind.DeserializationContext; | ||||
| import com.fasterxml.jackson.databind.JsonDeserializer; | ||||
| @@ -28,20 +27,23 @@ import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; | ||||
| import cn.hutool.core.util.ClassUtil; | ||||
| import cn.hutool.core.util.ReflectUtil; | ||||
| 
 | ||||
| import top.charles7c.cnadmin.common.base.BaseEnum; | ||||
| 
 | ||||
| /** | ||||
|  * 通用枚举接口 IEnum 反序列化器 | ||||
|  * 通用枚举基类 BaseEnum 反序列化器 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/1/8 13:56 | ||||
|  */ | ||||
| @JacksonStdImpl | ||||
| public class IEnumDeserializer extends JsonDeserializer<IEnum> { | ||||
| public class BaseEnumDeserializer extends JsonDeserializer<BaseEnum> { | ||||
| 
 | ||||
|     /** 静态实例 */ | ||||
|     public static final IEnumDeserializer SERIALIZER_INSTANCE = new IEnumDeserializer(); | ||||
|     public static final BaseEnumDeserializer SERIALIZER_INSTANCE = new BaseEnumDeserializer(); | ||||
| 
 | ||||
|     @Override | ||||
|     public IEnum deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { | ||||
|     public BaseEnum deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) | ||||
|         throws IOException { | ||||
|         Class<?> targetClass = jsonParser.getCurrentValue().getClass(); | ||||
|         String fieldName = jsonParser.getCurrentName(); | ||||
|         String value = jsonParser.getText(); | ||||
| @@ -49,7 +51,7 @@ public class IEnumDeserializer extends JsonDeserializer<IEnum> { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 通过某字段对应值获取枚举,获取不到时为 {@code null} | ||||
|      * 通过某字段对应值获取枚举实例,获取不到时为 {@code null} | ||||
|      * | ||||
|      * @param targetClass | ||||
|      *            目标类型 | ||||
| @@ -57,17 +59,17 @@ public class IEnumDeserializer extends JsonDeserializer<IEnum> { | ||||
|      *            字段值 | ||||
|      * @param fieldName | ||||
|      *            字段名 | ||||
|      * @return 对应枚举 ,获取不到时为 {@code null} | ||||
|      * @return 对应枚举实例 ,获取不到时为 {@code null} | ||||
|      */ | ||||
|     public IEnum getEnum(Class<?> targetClass, String value, String fieldName) { | ||||
|     private BaseEnum getEnum(Class<?> targetClass, String value, String fieldName) { | ||||
|         Field field = ReflectUtil.getField(targetClass, fieldName); | ||||
|         Class<?> fieldTypeClass = field.getType(); | ||||
|         Object[] enumConstants = fieldTypeClass.getEnumConstants(); | ||||
|         for (Object enumConstant : enumConstants) { | ||||
|             if (ClassUtil.isAssignable(IEnum.class, fieldTypeClass)) { | ||||
|                 IEnum iEnum = (IEnum)enumConstant; | ||||
|                 if (iEnum.getValue().equals(Integer.valueOf(value))) { | ||||
|                     return iEnum; | ||||
|             if (ClassUtil.isAssignable(BaseEnum.class, fieldTypeClass)) { | ||||
|                 BaseEnum baseEnum = (BaseEnum)enumConstant; | ||||
|                 if (baseEnum.getValue().equals(Integer.valueOf(value))) { | ||||
|                     return baseEnum; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -18,26 +18,27 @@ package top.charles7c.cnadmin.common.config.jackson; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| import com.baomidou.mybatisplus.annotation.IEnum; | ||||
| import com.fasterxml.jackson.core.JsonGenerator; | ||||
| import com.fasterxml.jackson.databind.JsonSerializer; | ||||
| import com.fasterxml.jackson.databind.SerializerProvider; | ||||
| import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; | ||||
| 
 | ||||
| import top.charles7c.cnadmin.common.base.BaseEnum; | ||||
| 
 | ||||
| /** | ||||
|  * 通用枚举接口 IEnum 序列化器 | ||||
|  * 通用枚举接口 BaseEnum 序列化器 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/1/8 13:56 | ||||
|  */ | ||||
| @JacksonStdImpl | ||||
| public class IEnumSerializer extends JsonSerializer<IEnum> { | ||||
| public class BaseEnumSerializer extends JsonSerializer<BaseEnum> { | ||||
| 
 | ||||
|     /** 静态实例 */ | ||||
|     public static final IEnumSerializer SERIALIZER_INSTANCE = new IEnumSerializer(); | ||||
|     public static final BaseEnumSerializer SERIALIZER_INSTANCE = new BaseEnumSerializer(); | ||||
| 
 | ||||
|     @Override | ||||
|     public void serialize(IEnum value, JsonGenerator generator, SerializerProvider serializers) throws IOException { | ||||
|     public void serialize(BaseEnum value, JsonGenerator generator, SerializerProvider serializers) throws IOException { | ||||
|         generator.writeObject(value.getValue()); | ||||
|     } | ||||
| } | ||||
| @@ -31,7 +31,6 @@ import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; | ||||
|  | ||||
| import com.baomidou.mybatisplus.annotation.IEnum; | ||||
| import com.fasterxml.jackson.databind.*; | ||||
| import com.fasterxml.jackson.databind.module.SimpleModule; | ||||
| import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; | ||||
| @@ -43,6 +42,8 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; | ||||
| import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; | ||||
| import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; | ||||
|  | ||||
| import top.charles7c.cnadmin.common.base.BaseEnum; | ||||
|  | ||||
| /** | ||||
|  * Jackson 配置 | ||||
|  * | ||||
| @@ -89,15 +90,15 @@ public class JacksonConfiguration { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 针对通用枚举接口 IEnum 的序列化和反序列化 | ||||
|      * 针对枚举基类 BaseEnum 的序列化和反序列化 | ||||
|      */ | ||||
|     @Bean | ||||
|     public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) { | ||||
|         SimpleModule simpleModule = new SimpleModule(); | ||||
|         simpleModule.addSerializer(IEnum.class, IEnumSerializer.SERIALIZER_INSTANCE); | ||||
|         simpleModule.addSerializer(BaseEnum.class, BaseEnumSerializer.SERIALIZER_INSTANCE); | ||||
|  | ||||
|         SimpleDeserializersWrapper deserializers = new SimpleDeserializersWrapper(); | ||||
|         deserializers.addDeserializer(IEnum.class, IEnumDeserializer.SERIALIZER_INSTANCE); | ||||
|         deserializers.addDeserializer(BaseEnum.class, BaseEnumDeserializer.SERIALIZER_INSTANCE); | ||||
|         simpleModule.setDeserializers(deserializers); | ||||
|  | ||||
|         ObjectMapper objectMapper = builder.createXmlMapper(false).build(); | ||||
|   | ||||
| @@ -38,7 +38,7 @@ import com.fasterxml.jackson.databind.type.ClassKey; | ||||
|  * 重写增强后:<br> | ||||
|  * 1. 同默认 1;<br> | ||||
|  * 2. 同默认 2;<br> | ||||
|  * 3. 如果也找不到 Enum 类型(所有枚举父类)的反序列化器,开始查找指定枚举类型的接口的反序列化器(例如:GenderEnum 枚举类型,则是找它的接口 IEnum 的反序列化器);<br> | ||||
|  * 3. 如果也找不到 Enum 类型(所有枚举父类)的反序列化器,开始查找指定枚举类型的接口的反序列化器(例如:GenderEnum 枚举类型,则是找它的接口 BaseEnum 的反序列化器);<br> | ||||
|  * 4. 同默认 3。 | ||||
|  * </p> | ||||
|  * | ||||
| @@ -56,7 +56,7 @@ public class SimpleDeserializersWrapper extends SimpleDeserializers { | ||||
|             return deser; | ||||
|         } | ||||
|  | ||||
|         // 重写增强:开始查找指定枚举类型的接口的反序列化器(例如:GenderEnum 枚举类型,则是找它的接口 IEnum 的反序列化器) | ||||
|         // 重写增强:开始查找指定枚举类型的接口的反序列化器(例如:GenderEnum 枚举类型,则是找它的接口 BaseEnum 的反序列化器) | ||||
|         for (Class<?> typeInterface : type.getInterfaces()) { | ||||
|             deser = this._classMappings.get(new ClassKey(typeInterface)); | ||||
|             if (deser != null) { | ||||
|   | ||||
| @@ -49,4 +49,9 @@ public class CorsProperties { | ||||
|      * 允许跨域的请求头 | ||||
|      */ | ||||
|     private List<String> allowedHeaders = new ArrayList<>(); | ||||
|  | ||||
|     /** | ||||
|      * 允许跨域的响应头 | ||||
|      */ | ||||
|     private List<String> exposedHeaders = new ArrayList<>(); | ||||
| } | ||||
|   | ||||
| @@ -19,7 +19,7 @@ package top.charles7c.cnadmin.common.enums; | ||||
| import lombok.Getter; | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| import com.baomidou.mybatisplus.annotation.IEnum; | ||||
| import top.charles7c.cnadmin.common.base.BaseEnum; | ||||
|  | ||||
| /** | ||||
|  * 启用/禁用状态枚举 | ||||
| @@ -29,7 +29,7 @@ import com.baomidou.mybatisplus.annotation.IEnum; | ||||
|  */ | ||||
| @Getter | ||||
| @RequiredArgsConstructor | ||||
| public enum DisEnableStatusEnum implements IEnum<Integer> { | ||||
| public enum DisEnableStatusEnum implements BaseEnum<Integer, String> { | ||||
|  | ||||
|     /** 启用 */ | ||||
|     ENABLE(1, "启用"), | ||||
|   | ||||
| @@ -19,7 +19,7 @@ package top.charles7c.cnadmin.common.enums; | ||||
| import lombok.Getter; | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| import com.baomidou.mybatisplus.annotation.IEnum; | ||||
| import top.charles7c.cnadmin.common.base.BaseEnum; | ||||
|  | ||||
| /** | ||||
|  * 性别枚举 | ||||
| @@ -29,7 +29,7 @@ import com.baomidou.mybatisplus.annotation.IEnum; | ||||
|  */ | ||||
| @Getter | ||||
| @RequiredArgsConstructor | ||||
| public enum GenderEnum implements IEnum<Integer> { | ||||
| public enum GenderEnum implements BaseEnum<Integer, String> { | ||||
|  | ||||
|     /** 未知 */ | ||||
|     UNKNOWN(0, "未知"), | ||||
|   | ||||
| @@ -0,0 +1,95 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.cnadmin.common.util; | ||||
|  | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
|  | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
|  | ||||
| import com.alibaba.excel.EasyExcel; | ||||
| import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; | ||||
| import com.esotericsoftware.minlog.Log; | ||||
|  | ||||
| import cn.hutool.core.date.DateUtil; | ||||
| import cn.hutool.core.util.URLUtil; | ||||
|  | ||||
| import top.charles7c.cnadmin.common.config.easyexcel.ExcelBigNumberConverter; | ||||
| import top.charles7c.cnadmin.common.exception.ServiceException; | ||||
|  | ||||
| /** | ||||
|  * Excel 工具类 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/2/5 18:00 | ||||
|  */ | ||||
| @Slf4j | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class ExcelUtils { | ||||
|  | ||||
|     /** | ||||
|      * 导出 | ||||
|      * | ||||
|      * @param list | ||||
|      *            导出数据集合 | ||||
|      * @param fileName | ||||
|      *            文件名 | ||||
|      * @param clazz | ||||
|      *            导出数据类型 | ||||
|      * @param response | ||||
|      *            响应对象 | ||||
|      */ | ||||
|     public static <V> void export(List<V> list, String fileName, Class<V> clazz, HttpServletResponse response) { | ||||
|         export(list, fileName, "Sheet1", clazz, response); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 导出 | ||||
|      * | ||||
|      * @param list | ||||
|      *            导出数据集合 | ||||
|      * @param fileName | ||||
|      *            文件名 | ||||
|      * @param sheetName | ||||
|      *            工作表名称 | ||||
|      * @param clazz | ||||
|      *            导出数据类型 | ||||
|      * @param response | ||||
|      *            响应对象 | ||||
|      */ | ||||
|     public static <V> void export(List<V> list, String fileName, String sheetName, Class<V> clazz, | ||||
|         HttpServletResponse response) { | ||||
|         try { | ||||
|             fileName = String.format("%s_%s.xlsx", fileName, DateUtil.format(new Date(), "yyyyMMddHHmmss")); | ||||
|             fileName = URLUtil.encode(fileName); | ||||
|             response.setHeader("Content-disposition", "attachment;filename=" + fileName); | ||||
|             response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"); | ||||
|             EasyExcel.write(response.getOutputStream(), clazz).autoCloseStream(false) | ||||
|                 // 自动适配宽度 | ||||
|                 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) | ||||
|                 // 自动转换大数值 | ||||
|                 .registerConverter(new ExcelBigNumberConverter()).sheet(sheetName).doWrite(list); | ||||
|         } catch (Exception e) { | ||||
|             Log.error("Export excel occurred an error.", e); | ||||
|             throw new ServiceException("导出 Excel 出现错误"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -91,6 +91,17 @@ public class ExceptionUtils { | ||||
|         return exToDefault(supplier, null, exConsumer); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 如果有异常,返回空字符串 | ||||
|      * | ||||
|      * @param exSupplier | ||||
|      *            可能会出现异常的方法执行 | ||||
|      * @return / | ||||
|      */ | ||||
|     public static String exToBlank(ExSupplier<String> exSupplier) { | ||||
|         return exToDefault(exSupplier, ""); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 如果有异常,返回默认值 | ||||
|      * | ||||
|   | ||||
| @@ -19,7 +19,7 @@ package top.charles7c.cnadmin.monitor.enums; | ||||
| import lombok.Getter; | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| import com.baomidou.mybatisplus.annotation.IEnum; | ||||
| import top.charles7c.cnadmin.common.base.BaseEnum; | ||||
|  | ||||
| /** | ||||
|  * 操作状态枚举 | ||||
| @@ -29,7 +29,7 @@ import com.baomidou.mybatisplus.annotation.IEnum; | ||||
|  */ | ||||
| @Getter | ||||
| @RequiredArgsConstructor | ||||
| public enum LogStatusEnum implements IEnum<Integer> { | ||||
| public enum LogStatusEnum implements BaseEnum<Integer, String> { | ||||
|  | ||||
|     /** 成功 */ | ||||
|     SUCCESS(1, "成功"), | ||||
|   | ||||
| @@ -17,11 +17,14 @@ | ||||
| package top.charles7c.cnadmin.system.model.vo; | ||||
|  | ||||
| import lombok.Data; | ||||
| import lombok.experimental.Accessors; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
|  | ||||
| import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | ||||
| import com.alibaba.excel.annotation.ExcelProperty; | ||||
|  | ||||
| import top.charles7c.cnadmin.common.base.BaseDetailVO; | ||||
| import top.charles7c.cnadmin.common.config.easyexcel.ExcelBaseEnumConverter; | ||||
| import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum; | ||||
|  | ||||
| /** | ||||
| @@ -31,7 +34,7 @@ import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum; | ||||
|  * @since 2023/2/1 22:19 | ||||
|  */ | ||||
| @Data | ||||
| @Accessors(chain = true) | ||||
| @ExcelIgnoreUnannotated | ||||
| @Schema(description = "部门详情信息") | ||||
| public class DeptDetailVO extends BaseDetailVO { | ||||
|  | ||||
| @@ -41,12 +44,14 @@ public class DeptDetailVO extends BaseDetailVO { | ||||
|      * 部门 ID | ||||
|      */ | ||||
|     @Schema(description = "部门 ID") | ||||
|     @ExcelProperty(value = "部门ID") | ||||
|     private Long deptId; | ||||
|  | ||||
|     /** | ||||
|      * 部门名称 | ||||
|      */ | ||||
|     @Schema(description = "部门名称") | ||||
|     @ExcelProperty(value = "部门名称") | ||||
|     private String deptName; | ||||
|  | ||||
|     /** | ||||
| @@ -65,11 +70,13 @@ public class DeptDetailVO extends BaseDetailVO { | ||||
|      * 描述 | ||||
|      */ | ||||
|     @Schema(description = "描述") | ||||
|     @ExcelProperty(value = "描述") | ||||
|     private String description; | ||||
|  | ||||
|     /** | ||||
|      * 状态(1启用 2禁用) | ||||
|      */ | ||||
|     @Schema(description = "状态(1启用 2禁用)") | ||||
|     @ExcelProperty(value = "状态", converter = ExcelBaseEnumConverter.class) | ||||
|     private DisEnableStatusEnum status; | ||||
| } | ||||
|   | ||||
| @@ -18,6 +18,8 @@ package top.charles7c.cnadmin.system.service; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
|  | ||||
| import cn.hutool.core.lang.tree.Tree; | ||||
|  | ||||
| import top.charles7c.cnadmin.common.base.BaseService; | ||||
| @@ -64,4 +66,14 @@ public interface DeptService extends BaseService<DeptVO, DeptDetailVO, DeptQuery | ||||
|      * @return 是否存在 | ||||
|      */ | ||||
|     boolean checkDeptNameExist(String deptName, Long parentId, Long deptId); | ||||
|  | ||||
|     /** | ||||
|      * 导出 | ||||
|      * | ||||
|      * @param query | ||||
|      *            查询条件 | ||||
|      * @param response | ||||
|      *            响应对象 | ||||
|      */ | ||||
|     void export(DeptQuery query, HttpServletResponse response); | ||||
| } | ||||
|   | ||||
| @@ -17,10 +17,13 @@ | ||||
| package top.charles7c.cnadmin.system.service.impl; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
|  | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| import org.springframework.stereotype.Service; | ||||
| @@ -37,6 +40,7 @@ import top.charles7c.cnadmin.common.base.BaseDetailVO; | ||||
| import top.charles7c.cnadmin.common.base.BaseServiceImpl; | ||||
| import top.charles7c.cnadmin.common.base.BaseVO; | ||||
| import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum; | ||||
| import top.charles7c.cnadmin.common.util.ExcelUtils; | ||||
| import top.charles7c.cnadmin.common.util.ExceptionUtils; | ||||
| import top.charles7c.cnadmin.common.util.TreeUtils; | ||||
| import top.charles7c.cnadmin.common.util.helper.QueryHelper; | ||||
| @@ -65,13 +69,25 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptVO, | ||||
|  | ||||
|     @Override | ||||
|     public List<DeptVO> list(DeptQuery query) { | ||||
|         List<DeptDO> deptList = this.listDept(query); | ||||
|         List<DeptVO> list = BeanUtil.copyToList(deptList, DeptVO.class); | ||||
|         list.forEach(this::fill); | ||||
|         return list; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 查询列表 | ||||
|      * | ||||
|      * @param query | ||||
|      *            查询条件 | ||||
|      * @return 列表信息 | ||||
|      */ | ||||
|     private List<DeptDO> listDept(DeptQuery query) { | ||||
|         QueryWrapper<DeptDO> queryWrapper = QueryHelper.build(query); | ||||
|         queryWrapper.lambda().orderByAsc(DeptDO::getParentId).orderByAsc(DeptDO::getDeptSort) | ||||
|             .orderByDesc(DeptDO::getCreateTime); | ||||
|         List<DeptDO> deptList = baseMapper.selectList(queryWrapper); | ||||
|         List<DeptVO> list = BeanUtil.copyToList(deptList, DeptVO.class); | ||||
|         list.forEach(this::fill); | ||||
|         return list; | ||||
|         return CollUtil.isNotEmpty(deptList) ? deptList : Collections.emptyList(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -170,6 +186,14 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptVO, | ||||
|             .eq(DeptDO::getParentId, parentId).ne(deptId != null, DeptDO::getDeptId, deptId)); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void export(DeptQuery query, HttpServletResponse response) { | ||||
|         List<DeptDO> deptList = this.listDept(query); | ||||
|         List<DeptDetailVO> list = BeanUtil.copyToList(deptList, DeptDetailVO.class); | ||||
|         list.forEach(this::fillDetail); | ||||
|         ExcelUtils.export(list, "部门数据", DeptDetailVO.class, response); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 填充数据 | ||||
|      * | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import axios from 'axios'; | ||||
| import qs from 'query-string'; | ||||
| import { DeptParams } from '@/api/system/dept'; | ||||
| import { DeptParam } from '@/api/system/dept'; | ||||
| import { TreeNodeData } from '@arco-design/web-vue'; | ||||
|  | ||||
| export default function listDeptTree(params: DeptParams) { | ||||
| export default function listDeptTree(params: DeptParam) { | ||||
|   return axios.get<TreeNodeData[]>('/common/tree/dept', { | ||||
|     params, | ||||
|     paramsSerializer: (obj) => { | ||||
|   | ||||
| @@ -17,12 +17,12 @@ export interface DeptRecord { | ||||
|   children?: Array<DeptRecord>, | ||||
| } | ||||
|  | ||||
| export interface DeptParams { | ||||
| export interface DeptParam { | ||||
|   deptName?: string; | ||||
|   status?: number; | ||||
| } | ||||
|  | ||||
| export function listDept(params: DeptParams) { | ||||
| export function listDept(params: DeptParam) { | ||||
|   return axios.get<DeptRecord[]>(`${BASE_URL}/all`, { | ||||
|     params, | ||||
|     paramsSerializer: (obj) => { | ||||
| @@ -46,3 +46,13 @@ export function updateDept(req: DeptRecord) { | ||||
| export function deleteDept(ids: number | Array<number>) { | ||||
|   return axios.delete(`${BASE_URL}/${ids}`); | ||||
| } | ||||
|  | ||||
| export function exportDept(params: DeptParam) { | ||||
|   return axios.get(`${BASE_URL}/export`, { | ||||
|     params, | ||||
|     paramsSerializer: (obj) => { | ||||
|       return qs.stringify(obj); | ||||
|     }, | ||||
|     responseType: 'blob', | ||||
|   }); | ||||
| } | ||||
| @@ -36,11 +36,17 @@ export interface HttpResponse<T = unknown> { | ||||
|  | ||||
| // response interceptors | ||||
| axios.interceptors.response.use((response: AxiosResponse<HttpResponse>) => { | ||||
|     // 二进制数据则直接返回 | ||||
|     if(response.request.responseType ===  'blob' || response.request.responseType ===  'arraybuffer'){ | ||||
|       return response; | ||||
|     } | ||||
|  | ||||
|     // 操作成功则直接返回 | ||||
|     const res = response.data; | ||||
|     if (res.success) { | ||||
|       return res; | ||||
|     } | ||||
|  | ||||
|     // 操作失败,弹出错误提示 | ||||
|     Message.error({ | ||||
|       content: res.msg, | ||||
|       duration: 3000 | ||||
|   | ||||
| @@ -51,12 +51,12 @@ | ||||
|                 <a-button type="primary" status="danger" :disabled="multiple" :title="multiple ? '请选择要删除的数据' : ''" @click="handleBatchDelete"> | ||||
|                   <template #icon><icon-delete /></template>删除 | ||||
|                 </a-button> | ||||
|                 <a-button :loading="exportLoading" type="primary" status="warning" @click="handleExport"> | ||||
|                   <template #icon><icon-download /></template>导出 | ||||
|                 </a-button> | ||||
|               </a-space> | ||||
|             </a-col> | ||||
|             <a-col :span="12" style="display: flex; align-items: center; justify-content: end"> | ||||
|               <a-button type="primary" status="warning" disabled title="尚未开放"> | ||||
|                 <template #icon><icon-download /></template>导出 | ||||
|               </a-button> | ||||
|             </a-col> | ||||
|           </a-row> | ||||
|         </div> | ||||
| @@ -239,12 +239,13 @@ | ||||
|   import { SelectOptionData, TreeNodeData } from '@arco-design/web-vue'; | ||||
|   import { | ||||
|     DeptRecord, | ||||
|     DeptParams, | ||||
|     DeptParam, | ||||
|     listDept, | ||||
|     getDept, | ||||
|     createDept, | ||||
|     updateDept, | ||||
|     deleteDept, | ||||
|     exportDept, | ||||
|   } from '@/api/system/dept'; | ||||
|   import listDeptTree from '@/api/common'; | ||||
|  | ||||
| @@ -267,6 +268,7 @@ | ||||
|   const multiple = ref(true); | ||||
|   const loading = ref(false); | ||||
|   const detailLoading = ref(false); | ||||
|   const exportLoading = ref(false); | ||||
|   const visible = ref(false); | ||||
|   const detailVisible = ref(false); | ||||
|   const statusOptions = ref<SelectOptionData[]>([ | ||||
| @@ -296,7 +298,7 @@ | ||||
|    * | ||||
|    * @param params 查询参数 | ||||
|    */ | ||||
|   const getList = (params: DeptParams = { ...queryParams.value }) => { | ||||
|   const getList = (params: DeptParam = { ...queryParams.value }) => { | ||||
|     loading.value = true; | ||||
|     listDept(params).then((res) => { | ||||
|       deptList.value = res.data; | ||||
| @@ -309,29 +311,6 @@ | ||||
|   }; | ||||
|   getList(); | ||||
|  | ||||
|   /** | ||||
|    * 确定 | ||||
|    */ | ||||
|   const handleOk = () => { | ||||
|     proxy.$refs.formRef.validate((valid: any) => { | ||||
|       if (!valid) { | ||||
|         if (form.value.deptId !== undefined) { | ||||
|           updateDept(form.value).then((res) => { | ||||
|             handleCancel(); | ||||
|             getList(); | ||||
|             proxy.$message.success(res.msg); | ||||
|           }); | ||||
|         } else { | ||||
|           createDept(form.value).then((res) => { | ||||
|             handleCancel(); | ||||
|             getList(); | ||||
|             proxy.$message.success(res.msg); | ||||
|           }); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * 打开新增对话框 | ||||
|    */ | ||||
| @@ -384,6 +363,29 @@ | ||||
|     proxy.$refs.formRef.resetFields(); | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * 确定 | ||||
|    */ | ||||
|   const handleOk = () => { | ||||
|     proxy.$refs.formRef.validate((valid: any) => { | ||||
|       if (!valid) { | ||||
|         if (form.value.deptId !== undefined) { | ||||
|           updateDept(form.value).then((res) => { | ||||
|             handleCancel(); | ||||
|             getList(); | ||||
|             proxy.$message.success(res.msg); | ||||
|           }); | ||||
|         } else { | ||||
|           createDept(form.value).then((res) => { | ||||
|             handleCancel(); | ||||
|             getList(); | ||||
|             proxy.$message.success(res.msg); | ||||
|           }); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * 查看详情 | ||||
|    * | ||||
| @@ -407,20 +409,6 @@ | ||||
|     detailVisible.value = false; | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * 修改状态 | ||||
|    * | ||||
|    * @param record 记录信息 | ||||
|    */ | ||||
|   const handleChangeStatus = (record: DeptRecord) => { | ||||
|     const tip = record.status === 1 ? '启用' : '禁用'; | ||||
|     updateDept(record).then((res) => { | ||||
|       proxy.$message.success(`${tip}成功`); | ||||
|     }).catch(() => { | ||||
|       record.status = (record.status === 1) ? 2 : 1; | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * 批量删除 | ||||
|    */ | ||||
| @@ -463,6 +451,52 @@ | ||||
|     multiple.value = !rowKeys.length; | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * 导出 | ||||
|    */ | ||||
|   const handleExport = () => { | ||||
|     if (exportLoading.value) return; | ||||
|     exportLoading.value = true; | ||||
|     exportDept({ ...queryParams.value }).then(async(res) => { | ||||
|       const blob = new Blob([res.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8'}); | ||||
|       const contentDisposition = res.headers['content-disposition'] | ||||
|       const pattern = new RegExp('filename=([^;]+\\.[^\\.;]+);*') | ||||
|       const result = pattern.exec(contentDisposition) || ''; | ||||
|       // 对名字进行解码 | ||||
|       const fileName = window.decodeURI(result[1]) | ||||
|       // 创建下载的链接 | ||||
|       const downloadElement = document.createElement('a'); | ||||
|       const href = window.URL.createObjectURL(blob); | ||||
|       downloadElement.style.display = 'none'; | ||||
|       downloadElement.href = href; | ||||
|       // 下载后文件名 | ||||
|       downloadElement.download = fileName; | ||||
|       document.body.appendChild(downloadElement); | ||||
|       // 点击下载 | ||||
|       downloadElement.click(); | ||||
|       // 下载完成,移除元素 | ||||
|       document.body.removeChild(downloadElement); | ||||
|       // 释放掉 blob 对象 | ||||
|       window.URL.revokeObjectURL(href); | ||||
|     }).finally(() => { | ||||
|       exportLoading.value = false; | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * 修改状态 | ||||
|    * | ||||
|    * @param record 记录信息 | ||||
|    */ | ||||
|   const handleChangeStatus = (record: DeptRecord) => { | ||||
|     const tip = record.status === 1 ? '启用' : '禁用'; | ||||
|     updateDept(record).then((res) => { | ||||
|       proxy.$message.success(`${tip}成功`); | ||||
|     }).catch(() => { | ||||
|       record.status = (record.status === 1) ? 2 : 1; | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * 过滤部门树 | ||||
|    * | ||||
|   | ||||
| @@ -148,7 +148,7 @@ | ||||
|           captchaLoading.value = false; | ||||
|           captchaDisable.value = true; | ||||
|           captchaBtnNameKey.value = `${t('userCenter.securitySettings.updateEmail.form.reSendCaptcha')}(${captchaTime.value -= 1}s)`; | ||||
|           captchaTimer.value = window.setInterval(function() { | ||||
|           captchaTimer.value = window.setInterval(() => { | ||||
|             captchaTime.value -= 1; | ||||
|             captchaBtnNameKey.value = `${t('userCenter.securitySettings.updateEmail.form.reSendCaptcha')}(${captchaTime.value}s)`; | ||||
|             if (captchaTime.value < 0) { | ||||
|   | ||||
| @@ -20,6 +20,8 @@ import static top.charles7c.cnadmin.common.annotation.CrudRequestMapping.Api; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.Operation; | ||||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||||
|  | ||||
| @@ -52,4 +54,10 @@ public class DeptController extends BaseController<DeptService, DeptVO, DeptDeta | ||||
|         List<DeptVO> list = baseService.list(query); | ||||
|         return R.ok(baseService.buildListTree(list)); | ||||
|     } | ||||
|  | ||||
|     @Operation(summary = "导出部门数据") | ||||
|     @GetMapping("/export") | ||||
|     public void export(@Validated DeptQuery query, HttpServletResponse response) { | ||||
|         baseService.export(query, response); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -187,3 +187,5 @@ cors: | ||||
|   allowedMethods: '*' | ||||
|   # 配置允许跨域的请求头 | ||||
|   allowedHeaders: '*' | ||||
|   # 配置允许跨域的响应头 | ||||
|   exposedHeaders: '*' | ||||
| @@ -178,3 +178,5 @@ cors: | ||||
|   allowedMethods: '*' | ||||
|   # 配置允许跨域的请求头 | ||||
|   allowedHeaders: '*' | ||||
|   # 配置允许跨域的响应头 | ||||
|   exposedHeaders: '*' | ||||
|   | ||||
							
								
								
									
										8
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -51,6 +51,7 @@ limitations under the License. | ||||
|         <p6spy.version>3.9.1</p6spy.version> | ||||
|  | ||||
|         <!-- ### 工具库相关 ### --> | ||||
|         <easyexcel.version>3.2.0</easyexcel.version> | ||||
|         <ip2region.version>2.7.6</ip2region.version> | ||||
|         <knife4j.version>4.0.0</knife4j.version> | ||||
|         <redisson.version>3.19.0</redisson.version> | ||||
| @@ -113,6 +114,13 @@ limitations under the License. | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- ################ 工具库相关 ################ --> | ||||
|             <!-- Easy Excel(一个基于 Java 的、快速、简洁、解决大文件内存溢出的 Excel 处理工具) --> | ||||
|             <dependency> | ||||
|                 <groupId>com.alibaba</groupId> | ||||
|                 <artifactId>easyexcel</artifactId> | ||||
|                 <version>${easyexcel.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 第三方封装 Ip2region(离线 IP 数据管理框架和定位库,支持亿级别的数据段,10 微秒级别的查询性能,提供了许多主流编程语言的 xdb 数据管理引擎的实现) --> | ||||
|             <dependency> | ||||
|                 <groupId>net.dreamlu</groupId> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user