commit 89c2e6c5c4d3aab258735ebfe012d566cfbea76a Author: zweiandlen Date: Wed Apr 23 11:03:01 2025 +0800 初始化 diff --git a/README.md b/README.md new file mode 100644 index 0000000..6009053 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# Spring Boot 3 项目模版 + +## 本模版项目使用方法 + +1. 安装 [cookiecutter](https://cookiecutter.readthedocs.io/en/stable/) 命令 +2. cookiecutter 本模版的文件夹 +3. 根据命令行提示,生成项目 + +## 项目模版 + +1. Spring Boot 3 + mybatis plus + mapstruct + mysql / postgis + redis 包含 RABC 权限模型 + +``` +cookiecutter https://www.llvy.ltd/llvy.ltd/sample-project.git --checkout main +``` \ No newline at end of file diff --git a/cookiecutter.json b/cookiecutter.json new file mode 100644 index 0000000..20e3e76 --- /dev/null +++ b/cookiecutter.json @@ -0,0 +1,15 @@ +{ + "project_hans": "项目名称", + "project_name": "Sample Project", + "project_slug": "{{ cookiecutter.project_name | lower | replace(' ', '_') | replace('-', '_') }}", + "mvn_group_id": "com.sample", + "mvn_artifact_id": "{{ cookiecutter.project_name | lower | replace(' ', '_') | replace('-', '_') }}", + "__mvn_package": "{{ cookiecutter.mvn_group_id }}.{{ cookiecutter.mvn_artifact_id }}", + "__package_path": "{{ cookiecutter.__mvn_package | replace('.', '/') }}", + "platform": ["mysql", "postgis"], + "author": "zweiandlen", + "email": "zweiandlen@outlook.com", + "_copy_without_render": [ + "*.vue" + ] +} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.gitignore b/{{cookiecutter.project_slug}}/.gitignore new file mode 100644 index 0000000..3ff6177 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.gitignore @@ -0,0 +1,50 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### Vue.js ### +.DS_Store +node_modules/ +dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +package-lock.json +tests/**/coverage/ + +# Editor directories and files +.vscode/ +.mvn/ +/src/main/resources/static/ +/files/ +*.suo +*.ntvs* +*.njsproj +*.sln \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/EasyCode/ColumnConfig/Default.json b/{{cookiecutter.project_slug}}/EasyCode/ColumnConfig/Default.json new file mode 100644 index 0000000..c2913c0 --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/ColumnConfig/Default.json @@ -0,0 +1,12 @@ +[ + { + "title": "disable", + "type": "BOOLEAN", + "selectValue": "" + }, + { + "title": "support", + "type": "SELECT", + "selectValue": "add,edit,query,del,ui" + } +] \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/EasyCode/GlobalConfig/MybatisCodeHelperPro/autoImport.vm b/{{cookiecutter.project_slug}}/EasyCode/GlobalConfig/MybatisCodeHelperPro/autoImport.vm new file mode 100644 index 0000000..a71211f --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/GlobalConfig/MybatisCodeHelperPro/autoImport.vm @@ -0,0 +1,4 @@ +##自动导入包(仅导入实体属性需要的包,通常用于实体类) +#foreach($import in $importList) +import $!import; +#end \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/EasyCode/GlobalConfig/MybatisCodeHelperPro/define.vm b/{{cookiecutter.project_slug}}/EasyCode/GlobalConfig/MybatisCodeHelperPro/define.vm new file mode 100644 index 0000000..62243e7 --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/GlobalConfig/MybatisCodeHelperPro/define.vm @@ -0,0 +1,39 @@ +##(Velocity宏定义) + +##定义设置表名后缀的宏定义,调用方式:#setTableSuffix("Test") +#macro(setTableSuffix $suffix) + #set($tableName = $!tool.append($tableInfo.name, $suffix)) +#end + +##定义设置包名后缀的宏定义,调用方式:#setPackageSuffix("Test") +#macro(setPackageSuffix $suffix) + #if($suffix!="")package #end#if($tableInfo.savePackageName!="")$!{tableInfo.savePackageName}.#{end}$!suffix; +#end + +##定义直接保存路径与文件名简化的宏定义,调用方式:#save("/entity", ".java") +#macro(save $path $fileName) + $!callback.setSavePath($tool.append($tableInfo.savePath, $path)) + $!callback.setFileName($tool.append($tableInfo.name, $fileName)) +#end + +##定义表注释的宏定义,调用方式:#tableComment("注释信息") +#macro(tableComment $desc) +/** +* $!{tableInfo.comment}($!{tableInfo.name})$desc +* +* @author $!author +* @since $!time.currTime() +*/ +#end + +##定义GET,SET方法的宏定义,调用方式:#getSetMethod($column) +#macro(getSetMethod $column) + +public $!{tool.getClsNameByFullName($column.type)} get$!{tool.firstUpperCase($column.name)}() { +return $!{column.name}; +} + +public void set$!{tool.firstUpperCase($column.name)}($!{tool.getClsNameByFullName($column.type)} $!{column.name}) { +this.$!{column.name} = $!{column.name}; +} +#end \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/EasyCode/GlobalConfig/MybatisCodeHelperPro/init.vm b/{{cookiecutter.project_slug}}/EasyCode/GlobalConfig/MybatisCodeHelperPro/init.vm new file mode 100644 index 0000000..498fc44 --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/GlobalConfig/MybatisCodeHelperPro/init.vm @@ -0,0 +1,41 @@ +##初始化区域 + +##去掉表的t_前缀 +$!tableInfo.setName($tool.getClassName($tableInfo.obj.name.replaceFirst("t_",""))) + +##参考阿里巴巴开发手册,POJO 类中布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误 +## when use actual columns name, should set this. +###foreach($column in $tableInfo.fullColumn) +## $!column.setName($column.obj.name) +###end + +#foreach($column in $tableInfo.fullColumn) + #if($column.name.startsWith("is") && $column.type.equals("java.lang.Boolean")) + $!column.setName($tool.firstLowerCase($column.name.substring(2))) + #end +#end + +##实现动态排除列 +#set($temp = $tool.newHashSet("testCreateTime", "otherColumn")) +#foreach($item in $temp) + #set($newList = $tool.newArrayList()) + #foreach($column in $tableInfo.fullColumn) + #if($column.name!=$item) + ##带有反回值的方法调用时使用$tool.call来消除返回值 + $tool.call($newList.add($column)) + #end + #end + ##重新保存 + $tableInfo.setFullColumn($newList) +#end + +##对importList进行篡改 +#set($temp = $tool.newHashSet()) +#foreach($column in $tableInfo.fullColumn) + #if(!$column.type.startsWith("java.lang.")) + ##带有反回值的方法调用时使用$tool.call来消除返回值 + $tool.call($temp.add($column.type)) + #end +#end +##覆盖 +#set($importList = $temp) diff --git a/{{cookiecutter.project_slug}}/EasyCode/GlobalConfig/MybatisCodeHelperPro/mybatisCodehelper.vm b/{{cookiecutter.project_slug}}/EasyCode/GlobalConfig/MybatisCodeHelperPro/mybatisCodehelper.vm new file mode 100644 index 0000000..e98aaa8 --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/GlobalConfig/MybatisCodeHelperPro/mybatisCodehelper.vm @@ -0,0 +1,27 @@ +##following code can be generated use MybatisCodeHelperPro plugin mybatis generator mingrate to template generate. +##copy group for different project. +#set($javamodelSrcFolder="${projectPath}/src/main/java") +#set($modelPackageName="{{ cookiecutter.__mvn_package }}.rest.entity") +#set($mapperSrcFolder="${projectPath}/src/main/java") +#set($mapperPackageName="{{ cookiecutter.__mvn_package }}.rest.dao") +#set($mapperXmlFolder="${projectPath}/src/main/resources") +#set($mapperXmlPackage="mapper") +#set($serviceSrcFolder="${projectPath}/src/main/java") +#set($servicePackageName="{{ cookiecutter.__mvn_package }}.rest.service") +#set($serviceImplSrcFolder="${projectPath}/src/main/java") +#set($serviceImplPackageName="{{ cookiecutter.__mvn_package }}.rest.service.impl") +#set($controllerSrcFolder="${projectPath}/src/main/java") +#set($controllerPackageName="{{ cookiecutter.__mvn_package }}.rest.controller") +#set($useLombok=true) +#set($useSwagger=false) +#set($useOpenApi=false) +#set($addSchemaName=false) +#set($mapperSuffix="Mapper") +#set($daoSuffix="Dao") +#set($useActualColumName=false) + +#if($useActualColumName) + #foreach($column in $tableInfo.fullColumn) + $!column.setName($column.obj.name) + #end +#end diff --git a/{{cookiecutter.project_slug}}/EasyCode/GlobalConfig/MybatisCodeHelperPro/mybatisSupport.vm b/{{cookiecutter.project_slug}}/EasyCode/GlobalConfig/MybatisCodeHelperPro/mybatisSupport.vm new file mode 100644 index 0000000..58d2456 --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/GlobalConfig/MybatisCodeHelperPro/mybatisSupport.vm @@ -0,0 +1,31 @@ +##针对Mybatis 进行支持,主要用于生成xml文件 +#foreach($column in $tableInfo.fullColumn) + ##储存列类型 + $tool.call($column.ext.put("sqlType", $tool.getField($column.obj.dataType, "typeName"))) + #if($tool.newHashSet("java.lang.String").contains($column.type)) + #set($jdbcType="VARCHAR") + #elseif($tool.newHashSet("java.lang.Boolean", "boolean").contains($column.type)) + #set($jdbcType="BOOLEAN") + #elseif($tool.newHashSet("java.lang.Byte", "byte").contains($column.type)) + #set($jdbcType="BYTE") + #elseif($tool.newHashSet("java.lang.Integer", "int", "java.lang.Short", "short").contains($column.type)) + #set($jdbcType="INTEGER") + #elseif($tool.newHashSet("java.lang.Long", "long").contains($column.type)) + #set($jdbcType="BIGINT") + #elseif($tool.newHashSet("java.lang.Float", "float", "java.lang.Double", "double").contains($column.type)) + #set($jdbcType="NUMERIC") + #elseif($tool.newHashSet( + "java.util.Date", "java.sql.Timestamp", "java.time.Instant", "java.time.LocalDateTime", + "java.time.OffsetDateTime", "java.time.ZonedDateTime").contains($column.type)) + #set($jdbcType="TIMESTAMP") + #elseif($tool.newHashSet("java.sql.Date", "java.time.LocalDate", "java.time.LocalTime").contains($column.type)) + #set($jdbcType="TIMESTAMP") + #else + ##其他类型 + #set($jdbcType="VARCHAR") + #end + $tool.call($column.ext.put("jdbcType", $jdbcType)) +#end + +##定义宏,查询所有列 +#macro(allSqlColumn)#foreach($column in $tableInfo.fullColumn)$column.obj.name#if($velocityHasNext), #end#end#end diff --git a/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/controller.java.vm b/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/controller.java.vm new file mode 100644 index 0000000..56ac80c --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/controller.java.vm @@ -0,0 +1,182 @@ +##导入宏定义 +$!{define.vm} +$!{mybatisCodehelper.vm} + +##设置表后缀(宏定义) +#set($controllerName = $tool.append($tableInfo.name, "Controller")) +##设置回调 +#set($controllerSavePath = $tool.append(${controllerSrcFolder},"/",${controllerPackageName.replace(".","/")})) + +$!callback.setSavePath($controllerSavePath) +$!callback.setFileName($tool.append($controllerName, ".java")) + +##定义服务名 +#set($serviceName = $!tool.append($!tool.firstLowerCase($!tableInfo.name), "Service")) + +##定义实体对象名 +#set($entityName = $!tool.firstLowerCase($!tableInfo.name)) + +package ${controllerPackageName}; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO; +import {{ cookiecutter.__mvn_package }}.rest.api.ApiController; +import {{ cookiecutter.__mvn_package }}.rest.api.R; +import $!{modelPackageName}.$!{tableInfo.name}; +import ${servicePackageName}.$!{tableInfo.name}Service; +import java.io.Serializable; +import java.util.List; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.lang.Nullable; +import org.springframework.web.bind.annotation.*; + +##表注释(宏定义) +#tableComment("表控制层.") +###if(${tableInfo.comment}) +##@Tag(name = "${tableInfo.comment}") +###end +@Tag(name = "标准接口", description = "由表结构自动生成") +@RestController +@RequestMapping("db/$!tool.firstLowerCase($!tableInfo.name)") +public class $!{tableInfo.name}Controller extends ApiController { + /** 服务对象. */ + private final $!{tableInfo.name}Service $!{serviceName}; + + public $!{tableInfo.name}Controller($!{tableInfo.name}Service $!{serviceName}) { + this.$!{serviceName} = $!{serviceName}; + } + + /** + * 分页查询所有数据. + * + * @param page 分页对象 + * @param $!entityName 查询实体 + * @return 分页数据 + */ + @Operation( + summary = "${tableInfo.comment} - 分页查询所有数据", + parameters = { + @Parameter(name = "page", description = "分页对象"), + @Parameter(name = "$!entityName", description = "查询实体"), + }, + responses = @ApiResponse(description = "分页数据"), + description = "size=-1时查询所有数据,orders配合asc排序") + @GetMapping + @SaCheckPermission("db:rest:get") + public R selectAll(@Nullable PageDTO<$!{tableInfo.name}> page, @Nullable $!{tableInfo.name} $!entityName) { + return success(this.$!{serviceName}.page(page, new QueryWrapper<>($!entityName))); + } + + /** + * 通过主键查询单条数据. + * + * @param id 主键 + * @return 单条数据 + */ + @Operation( + summary = "${tableInfo.comment} - 通过主键查询单条数据", + parameters = { + @Parameter(name = "id", description = "主键", schema = @Schema(type = "string")), + }, + responses = @ApiResponse(description = "单条数据")) + @GetMapping("{id}") + @SaCheckPermission("db:rest:get") + public R selectOne(@PathVariable Serializable id) { + return success(this.$!{serviceName}.getById(id)); + } + + /** + * 新增数据. + * + * @param $!entityName 实体对象 + * @return 新增结果 + */ + @Operation( + summary = "${tableInfo.comment} - 新增数据", + parameters = { + @Parameter(name = "$!entityName", description = "实体对象"), + }, + responses = @ApiResponse(description = "新增结果")) + @PostMapping + @SaCheckPermission("db:rest:post") + public R insert(@RequestBody $!tableInfo.name $!entityName) { + return success(this.$!{serviceName}.save($!entityName)); + } + + /** + * 批量新增数据. + * + * @param $!{entityName}s 实体对象 + * @return 新增结果 + */ + @Operation( + summary = "${tableInfo.comment} - 批量新增数据", + parameters = { + @Parameter(name = "$!{entityName}s", description = "实体对象"), + }, + responses = @ApiResponse(description = "新增结果")) + @PostMapping("s") + @SaCheckPermission("db:rest:post") + public R inserts(@RequestBody List<$!tableInfo.name> $!{entityName}s) { + return success(this.$!{serviceName}.saveBatch($!{entityName}s)); + } + + /** + * 修改数据. + * + * @param $!entityName 实体对象 + * @return 修改结果 + */ + @Operation( + summary = "${tableInfo.comment} - 修改数据", + parameters = {@Parameter(name = "$!entityName", description = "实体对象")}, + responses = @ApiResponse(description = "修改结果"), + description = "不存在的对象会新增") + @PutMapping + @SaCheckPermission("db:rest:put") + public R update(@RequestBody $!tableInfo.name $!entityName) { + return success(this.$!{serviceName}.saveOrUpdate($!entityName)); + } + + /** + * 批量修改数据. + * + * @param $!{entityName}s 实体对象 + * @return 修改结果 + */ + @Operation( + summary = "${tableInfo.comment} - 批量修改数据", + parameters = {@Parameter(name = "$!{entityName}s", description = "实体对象")}, + responses = @ApiResponse(description = "修改结果"), + description = "不存在的对象会新增") + @PutMapping("s") + @SaCheckPermission("db:rest:put") + public R updates(@RequestBody List<$!tableInfo.name> $!{entityName}s) { + return success(this.$!{serviceName}.saveOrUpdateBatch($!{entityName}s)); + } + + /** + * 删除数据. + * + * @param idList 主键结合 + * @return 删除结果 + */ + @Operation( + summary = "${tableInfo.comment} - 删除数据", + parameters = { + @Parameter(name = "idList", description = "主键结合", schema = @Schema(type = "string")) + }, + responses = @ApiResponse(description = "删除结果"), + description = "主键用逗号拼接") + @DeleteMapping + @SaCheckPermission("db:rest:del") + public R delete(@RequestParam("idList") List idList) { + return success(this.$!{serviceName}.removeByIds(idList)); + } +} diff --git a/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/dao.java.vm b/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/dao.java.vm new file mode 100644 index 0000000..581eb9a --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/dao.java.vm @@ -0,0 +1,21 @@ +##导入宏定义 +$!{define.vm} +$!{mybatisCodehelper.vm} + +##设置表后缀(宏定义) +#set($daoName = $tool.append($tableInfo.name, ${daoSuffix})) +##设置回调 +#set($daoSavePath = $tool.append(${mapperSrcFolder},"/",${mapperPackageName.replace(".","/")})) + +$!callback.setSavePath($daoSavePath) +$!callback.setFileName($tool.append($daoName, ".java")) + +package ${mapperPackageName}; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import $!{modelPackageName}.$!{tableInfo.name}; + +##表注释(宏定义) +#tableComment("表数据库访问层.") +public interface $!{tableInfo.name}Dao extends BaseMapper<$!tableInfo.name> { +} diff --git a/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/entity.java.vm b/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/entity.java.vm new file mode 100644 index 0000000..24f0672 --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/entity.java.vm @@ -0,0 +1,105 @@ +##导入宏定义 +$!{define.vm} +$!{mybatisCodehelper.vm} + +#set($entitySavePath = $tool.append(${javamodelSrcFolder},"/",${modelPackageName.replace(".","/")})) + +$!callback.setSavePath($entitySavePath) +$!callback.setFileName($tool.append($tableInfo.name, ".java")) + +##自动导入包(全局变量) +package ${modelPackageName}; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.Version; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import org.locationtech.jts.geom.Geometry; +import ltd.llvy.postgis.handler.GeometryDeserializer; +import ltd.llvy.postgis.handler.GeometrySerializer; + +import java.io.Serializable; + $!autoImport +import java.time.OffsetDateTime; + +import org.apache.ibatis.type.JdbcType; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +##表注释(宏定义) +#tableComment("表实体类.") +#if(${tableInfo.comment}) +@Schema(description = "${tableInfo.comment}") +#end +@EqualsAndHashCode(callSuper = true) +@Data +public class $!{tableInfo.name} extends Model<$!{tableInfo.name}> { + #foreach($column in $tableInfo.fullColumn) + #if(${column.comment}) + //${column.comment} + #end + #if($!{column.name} == "id" || $!{column.name.indexOf("Id")} != -1) + @Schema(description = "${column.comment}", type = "string") + @JsonSerialize(using = ToStringSerializer.class) + #else + @Schema(description = "${column.comment}") + #end + #if("password" == $!{column.name}) + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + #end + #if("version" == $!{column.name}) + @Version + #end + #if("deleteTime" == $!{column.name}) + @TableLogic + #end + #if("createTime" == $!{column.name}) + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + {% if cookiecutter.platform == "postgis" -%} + @TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.OTHER) + {% else %} + @TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.DATETIMEOFFSET) + {% endif %} + #end + #if("updateTime" == $!{column.name}) + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + {% if cookiecutter.platform == "postgis" -%} + @TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.OTHER) + {% else %} + @TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.DATETIMEOFFSET) + {% endif %} + #end + #if("createBy" == $!{column.name}) + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @TableField(fill = FieldFill.INSERT) + #end + #if("updateBy" == $!{column.name}) + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @TableField(fill = FieldFill.INSERT_UPDATE) + #end + #if("geom" == $!{column.name}) + @JsonSerialize(using = GeometrySerializer.class) + @JsonDeserialize(using = GeometryDeserializer.class) + #end + private $!{tool.getClsNameByFullName($column.type)} $!{column.name}; + #end + #foreach($column in $tableInfo.pkColumn) + /** + * 获取主键值. + * + * @return 主键值 + */ + @Override + public Serializable pkVal () { + return this.$!column.name; + } + #break + #end +} diff --git a/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/mapper.xml.vm b/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/mapper.xml.vm new file mode 100644 index 0000000..ae63573 --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/mapper.xml.vm @@ -0,0 +1,22 @@ + ##引入mybatis支持 + $!{mybatisCodehelper.vm} + $!{mybatisSupport.vm} + ##设置保存名称与保存位置 + #set($XmlSavePath = $tool.append(${mapperXmlFolder},"/",${mapperXmlPackage.replace(".","/")})) + $!callback.setSavePath($XmlSavePath) + $!callback.setFileName($tool.append($!{tableInfo.name}, $!{mapperSuffix},".xml")) + #set($daoName = $tool.append($tableInfo.name, ${daoSuffix})) + ##拿到主键 + #if(!$tableInfo.pkColumn.isEmpty()) + #set($pk = $tableInfo.pkColumn.get(0)) + #end + + + + + + #foreach($column in $tableInfo.fullColumn) + + #end + + diff --git a/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/service.java.vm b/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/service.java.vm new file mode 100644 index 0000000..0bbc39e --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/service.java.vm @@ -0,0 +1,24 @@ +##导入宏定义 +$!{define.vm} + +##定义初始变量 +$!{mybatisCodehelper.vm} +#set($serviceName = $tool.append($tableInfo.name, "Service")) +##设置回调 +#set($serviceSavePath = $tool.append(${serviceSrcFolder},"/",${servicePackageName.replace(".","/")})) + +$!callback.setSavePath($serviceSavePath) +$!callback.setFileName($tool.append($serviceName, ".java")) + + + +package $!{servicePackageName}; +import com.baomidou.mybatisplus.extension.service.IService; +import $!{modelPackageName}.$!{tableInfo.name}; + +##表注释(宏定义) +#tableComment("表服务接口.") +public interface $!{serviceName} extends IService + +<$!tableInfo.name> { +} diff --git a/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/serviceImpl.java.vm b/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/serviceImpl.java.vm new file mode 100644 index 0000000..d81806c --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/Templates/MybatisPlus/serviceImpl.java.vm @@ -0,0 +1,29 @@ +##导入宏定义 +$!{define.vm} + +$!{mybatisCodehelper.vm} +#set($ServiceImplName = $tool.append($tableInfo.name, "ServiceImpl")) +##设置回调 +##$!callback.setFileName($tool.append($ServiceImplName, ".java")) +##$!callback.setSavePath($tool.append($tableInfo.savePath, "/service/impl")) +#set($serviceImplSavePath = $tool.append(${serviceImplSrcFolder},"/",${serviceImplPackageName.replace(".","/")})) + +$!callback.setSavePath($serviceImplSavePath) +$!callback.setFileName($tool.append($ServiceImplName, ".java")) + +#set($daoName = $tool.append($tableInfo.name, ${daoSuffix})) + +package $!{serviceImplPackageName}; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import ${mapperPackageName}.${daoName}; +import $!{modelPackageName}.$!{tableInfo.name}; +import ${servicePackageName}.$!{tableInfo.name}Service; +import org.springframework.stereotype.Service; + +##表注释(宏定义) +#tableComment("表服务实现类.") +@Service("$!tool.firstLowerCase($tableInfo.name)Service") +public class $!{ServiceImplName} extends ServiceImpl<$!{daoName}, $!{tableInfo.name}> implements + + $!{tableInfo.name}Service { +} diff --git a/{{cookiecutter.project_slug}}/EasyCode/TypeMapperConfig/Default.json b/{{cookiecutter.project_slug}}/EasyCode/TypeMapperConfig/Default.json new file mode 100644 index 0000000..11e3a69 --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/TypeMapperConfig/Default.json @@ -0,0 +1,97 @@ +[ + { + "matchType": "REGEX", + "columnType": "varchar(\\(\\d+\\))?", + "javaType": "java.lang.String" + }, + { + "matchType": "REGEX", + "columnType": "char(\\(\\d+\\))?", + "javaType": "java.lang.String" + }, + { + "matchType": "ORDINARY", + "columnType": "tinyint(1)", + "javaType": "java.lang.Boolean" + }, + { + "matchType": "ORDINARY", + "columnType": "bit(1)", + "javaType": "java.lang.Boolean" + }, + { + "matchType": "REGEX", + "columnType": "(tiny|medium|long)*text", + "javaType": "java.lang.String" + }, + { + "matchType": "REGEX", + "columnType": "decimal(\\(\\d+,\\d+\\))?", + "javaType": "java.math.BigDecimal" + }, + { + "matchType": "ORDINARY", + "columnType": "integer", + "javaType": "java.lang.Integer" + }, + { + "matchType": "REGEX", + "columnType": "(tiny|small|medium)*int(\\(\\d+\\))?", + "javaType": "java.lang.Integer" + }, + { + "matchType": "ORDINARY", + "columnType": "int4", + "javaType": "java.lang.Integer" + }, + { + "matchType": "ORDINARY", + "columnType": "int8", + "javaType": "java.lang.Long" + }, + { + "matchType": "REGEX", + "columnType": "bigint(\\(\\d+\\))?", + "javaType": "java.lang.Long" + }, + { + "matchType": "ORDINARY", + "columnType": "double", + "javaType": "java.lang.Double" + }, + { + "matchType": "ORDINARY", + "columnType": "date", + "javaType": "java.time.LocalDate" + }, + { + "matchType": "ORDINARY", + "columnType": "datetime", + "javaType": "java.time.OffsetDateTime" + }, + { + "matchType": "ORDINARY", + "columnType": "timestamp with time zone", + "javaType": "java.time.OffsetDateTime" + }, + { + "matchType": "ORDINARY", + "columnType": "timestamp", + "javaType": "java.lang.Long" + }, + { + "matchType": "ORDINARY", + "columnType": "time", + "javaType": "java.time.LocalTime" + }, + { + "matchType": "ORDINARY", + "columnType": "boolean", + "javaType": "java.lang.Boolean" + }, + { + "matchType": "ORDINARY", + "columnType": "geometry", + "javaType": "org.postgis.Geometry" + } +] diff --git a/{{cookiecutter.project_slug}}/EasyCode/TypeMapperConfig/java8LocalDate.json b/{{cookiecutter.project_slug}}/EasyCode/TypeMapperConfig/java8LocalDate.json new file mode 100644 index 0000000..9935d63 --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/TypeMapperConfig/java8LocalDate.json @@ -0,0 +1,72 @@ +[ + { + "matchType": "REGEX", + "columnType": "varchar(\\(\\d+\\))?", + "javaType": "java.lang.String" + }, + { + "matchType": "REGEX", + "columnType": "char(\\(\\d+\\))?", + "javaType": "java.lang.String" + }, + { + "matchType": "REGEX", + "columnType": "(tiny|medium|long)*text", + "javaType": "java.lang.String" + }, + { + "matchType": "REGEX", + "columnType": "decimal(\\(\\d+,\\d+\\))?", + "javaType": "java.lang.Double" + }, + { + "matchType": "ORDINARY", + "columnType": "integer", + "javaType": "java.lang.Integer" + }, + { + "matchType": "REGEX", + "columnType": "(tiny|small|medium)*int(\\(\\d+\\))?", + "javaType": "java.lang.Integer" + }, + { + "matchType": "ORDINARY", + "columnType": "int4", + "javaType": "java.lang.Integer" + }, + { + "matchType": "ORDINARY", + "columnType": "int8", + "javaType": "java.lang.Long" + }, + { + "matchType": "REGEX", + "columnType": "bigint(\\(\\d+\\))?", + "javaType": "java.lang.Long" + }, + { + "matchType": "ORDINARY", + "columnType": "date", + "javaType": "java.time.LocalDate" + }, + { + "matchType": "ORDINARY", + "columnType": "datetime", + "javaType": "java.time.LocalDate" + }, + { + "matchType": "ORDINARY", + "columnType": "timestamp", + "javaType": "java.time.LocalDate" + }, + { + "matchType": "ORDINARY", + "columnType": "time", + "javaType": "java.time.LocalTime" + }, + { + "matchType": "ORDINARY", + "columnType": "boolean", + "javaType": "java.lang.Boolean" + } +] \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/EasyCode/group.json b/{{cookiecutter.project_slug}}/EasyCode/group.json new file mode 100644 index 0000000..2d96152 --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/group.json @@ -0,0 +1,11 @@ +[ + { + "groupName": "MybatisPlus", + "templateName": "MybatisPlus", + "globalConfigName": "MybatisCodeHelperPro", + "columnConfigName": "Default.json", + "typeMapperName": "Default.json", + "tableNameRegex": ".*", + "schemaNameRegex": ".*" + } +] diff --git a/{{cookiecutter.project_slug}}/EasyCode/velocity_implicit.vm b/{{cookiecutter.project_slug}}/EasyCode/velocity_implicit.vm new file mode 100644 index 0000000..ec59448 --- /dev/null +++ b/{{cookiecutter.project_slug}}/EasyCode/velocity_implicit.vm @@ -0,0 +1,12 @@ +#* @implicitly included *# +#* @vtlvariable name="author" type="java.lang.String" *# +#* @vtlvariable name="encode" type="java.lang.String" *# +#* @vtlvariable name="modulePath" type="java.lang.String" *# +#* @vtlvariable name="projectPath" type="java.lang.String" *# +#* @vtlvariable name="importList" type="java.util.List" *# +#* @vtlvariable name="callback" type="com.bruce.plugin.entity.Callback" *# +#* @vtlvariable name="tool" type="com.bruce.plugin.tool.GlobalTool" *# +#* @vtlvariable name="time" type="com.bruce.plugin.tool.TimeUtils" *# +#* @vtlvariable name="tableInfo" type="com.bruce.plugin.entity.TableInfo" *# +#* @vtlvariable name="tableInfoList" type="java.util.List" *# +#* @vtlvariable name="generateService" type="com.bruce.plugin.tool.ExtraCodeGenerateUtils" *# diff --git a/{{cookiecutter.project_slug}}/ManagerUI/.editorconfig b/{{cookiecutter.project_slug}}/ManagerUI/.editorconfig new file mode 100644 index 0000000..5a5809d --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/.editorconfig @@ -0,0 +1,9 @@ +[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}] +charset = utf-8 +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +end_of_line = lf +max_line_length = 100 diff --git a/{{cookiecutter.project_slug}}/ManagerUI/.env.development b/{{cookiecutter.project_slug}}/ManagerUI/.env.development new file mode 100644 index 0000000..e7c14b6 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/.env.development @@ -0,0 +1,2 @@ +VITE_APP_TITLE=测试环境 +VITE_BASE_URL=api diff --git a/{{cookiecutter.project_slug}}/ManagerUI/.env.production b/{{cookiecutter.project_slug}}/ManagerUI/.env.production new file mode 100644 index 0000000..41a763d --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/.env.production @@ -0,0 +1,2 @@ +VITE_APP_TITLE={{ cookiecutter.project_hans }} +VITE_BASE_URL=/ diff --git a/{{cookiecutter.project_slug}}/ManagerUI/.gitattributes b/{{cookiecutter.project_slug}}/ManagerUI/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/{{cookiecutter.project_slug}}/ManagerUI/.gitignore b/{{cookiecutter.project_slug}}/ManagerUI/.gitignore new file mode 100644 index 0000000..8ee54e8 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/.gitignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +/cypress/videos/ +/cypress/screenshots/ + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo diff --git a/{{cookiecutter.project_slug}}/ManagerUI/.prettierrc.json b/{{cookiecutter.project_slug}}/ManagerUI/.prettierrc.json new file mode 100644 index 0000000..29a2402 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": false, + "singleQuote": true, + "printWidth": 100 +} diff --git a/{{cookiecutter.project_slug}}/ManagerUI/README.md b/{{cookiecutter.project_slug}}/ManagerUI/README.md new file mode 100644 index 0000000..786e028 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/README.md @@ -0,0 +1,36 @@ +# manager + +This template should help get you started developing with Vue 3 in Vite. + +## Recommended IDE Setup + +[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and +disable Vetur). + +## Customize configuration + +See [Vite Configuration Reference](https://vite.dev/config/). + +## Project Setup + +```sh +npm install +``` + +### Compile and Hot-Reload for Development + +```sh +npm run dev +``` + +### Compile and Minify for Production + +```sh +npm run build +``` + +### Lint with [ESLint](https://eslint.org/) + +```sh +npm run lint +``` diff --git a/{{cookiecutter.project_slug}}/ManagerUI/eslint.config.js b/{{cookiecutter.project_slug}}/ManagerUI/eslint.config.js new file mode 100644 index 0000000..aaf1136 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/eslint.config.js @@ -0,0 +1,28 @@ +import { defineConfig, globalIgnores } from 'eslint/config' +import globals from 'globals' +import js from '@eslint/js' +import pluginVue from 'eslint-plugin-vue' +import pluginOxlint from 'eslint-plugin-oxlint' +import skipFormatting from '@vue/eslint-config-prettier/skip-formatting' + +export default defineConfig([ + { + name: 'app/files-to-lint', + files: ['**/*.{js,mjs,jsx,vue}'], + }, + + globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']), + + { + languageOptions: { + globals: { + ...globals.browser, + }, + }, + }, + + js.configs.recommended, + ...pluginVue.configs['flat/essential'], + ...pluginOxlint.configs['flat/recommended'], + skipFormatting, +]) diff --git a/{{cookiecutter.project_slug}}/ManagerUI/index.html b/{{cookiecutter.project_slug}}/ManagerUI/index.html new file mode 100644 index 0000000..1af7496 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/index.html @@ -0,0 +1,13 @@ + + + + + + + <%= VITE_APP_TITLE %> + + +
+ + + diff --git a/{{cookiecutter.project_slug}}/ManagerUI/jsconfig.json b/{{cookiecutter.project_slug}}/ManagerUI/jsconfig.json new file mode 100644 index 0000000..e04cd5e --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/jsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "paths": { + "@/*": [ + "./src/*" + ] + } + }, + "exclude": [ + "node_modules", + "dist" + ] +} diff --git a/{{cookiecutter.project_slug}}/ManagerUI/package.json b/{{cookiecutter.project_slug}}/ManagerUI/package.json new file mode 100644 index 0000000..b73bade --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/package.json @@ -0,0 +1,51 @@ +{ + "name": "manager-ui", + "version": "0.0.1", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "lint:oxlint": "oxlint . --fix -D correctness --ignore-path .gitignore", + "lint:eslint": "eslint . --fix", + "lint": "run-s lint:*", + "format": "prettier --write src/" + }, + "dependencies": { + "@ant-design/icons-vue": "^7.0.1", + "ant-design-vue": "^4.2.6", + "axios": "^1.8.4", + "dayjs": "^1.11.13", + "js-cookie": "^3.0.5", + "jsencrypt": "^3.3.2", + "jwt-decode": "^4.0.0", + "lodash": "^4.17.21", + "pinia": "^3.0.2", + "pinia-plugin-persistedstate": "^4.2.0", + "qs": "^6.14.0", + "tree-lodash": "^0.4.0", + "vue": "^3.5.13", + "vue-request": "^2.0.4", + "vue-router": "^4.5.0" + }, + "devDependencies": { + "@eslint/js": "^9.25.1", + "@vitejs/plugin-vue": "^5.2.3", + "@vitejs/plugin-vue-jsx": "^4.1.2", + "@vue/eslint-config-prettier": "^10.2.0", + "eslint": "^9.25.1", + "eslint-plugin-oxlint": "^0.16.7", + "eslint-plugin-vue": "~10.0.0", + "globals": "^16.0.0", + "npm-run-all2": "^7.0.2", + "oxlint": "^0.16.7", + "prettier": "3.5.3", + "sass-embedded": "^1.87.0", + "unplugin-vue-components": "^28.5.0", + "vite": "^6.3.2", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-html": "^3.2.2", + "vite-plugin-vue-devtools": "^7.7.5" + } +} diff --git a/{{cookiecutter.project_slug}}/ManagerUI/public/favicon.ico b/{{cookiecutter.project_slug}}/ManagerUI/public/favicon.ico new file mode 100644 index 0000000..34c6ad5 Binary files /dev/null and b/{{cookiecutter.project_slug}}/ManagerUI/public/favicon.ico differ diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/App.vue b/{{cookiecutter.project_slug}}/ManagerUI/src/App.vue new file mode 100644 index 0000000..5a5f59e --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/App.vue @@ -0,0 +1,13 @@ + + + diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/assets/css/base.css b/{{cookiecutter.project_slug}}/ManagerUI/src/assets/css/base.css new file mode 100644 index 0000000..9ec913d --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/assets/css/base.css @@ -0,0 +1,8 @@ +:root { +} + +.dark { +} + +.light { +} diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/assets/css/main.css b/{{cookiecutter.project_slug}}/ManagerUI/src/assets/css/main.css new file mode 100644 index 0000000..a55dc69 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/assets/css/main.css @@ -0,0 +1,34 @@ +@import 'base.css'; + +html, +body, +#app { + height: 100%; + width: 100%; + margin: 0; +} + +/* 全局滚动条样式(内侧滚动条) */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: transparent; /* 滚动条轨道透明 */ +} + +::-webkit-scrollbar-thumb { + background: rgba(0, 0, 0, 0.2); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: rgba(0, 0, 0, 0.3); +} + +.text-single { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/assets/images/login.webp b/{{cookiecutter.project_slug}}/ManagerUI/src/assets/images/login.webp new file mode 100644 index 0000000..ae2cc8f Binary files /dev/null and b/{{cookiecutter.project_slug}}/ManagerUI/src/assets/images/login.webp differ diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/components/Layout/LayoutIndex.vue b/{{cookiecutter.project_slug}}/ManagerUI/src/components/Layout/LayoutIndex.vue new file mode 100644 index 0000000..79e6c2f --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/components/Layout/LayoutIndex.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/components/Layout/LayoutMenu.vue b/{{cookiecutter.project_slug}}/ManagerUI/src/components/Layout/LayoutMenu.vue new file mode 100644 index 0000000..ec79983 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/components/Layout/LayoutMenu.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/directive/hasAuth.js b/{{cookiecutter.project_slug}}/ManagerUI/src/directive/hasAuth.js new file mode 100644 index 0000000..8b7856f --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/directive/hasAuth.js @@ -0,0 +1,20 @@ +import { useSystemStore } from '@/stores/system.js' + +export default function (el, binding) { + const systemStore = useSystemStore() + const { value } = binding + + if (systemStore.isSa) return true + + if (value && value instanceof Array && value.length > 0) { + let has = systemStore.auths.some((item) => { + return value.includes(item) + }) + + if (!has) { + el.parentNode && el.parentNode.removeChild(el) + } + } else { + throw new Error('需要指定权限') + } +} diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/directive/index.js b/{{cookiecutter.project_slug}}/ManagerUI/src/directive/index.js new file mode 100644 index 0000000..4537674 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/directive/index.js @@ -0,0 +1,9 @@ +import hasAuth from '@/directive/hasAuth.js' +import isa from '@/directive/isa.js' + +export default { + install(app) { + app.directive('hasAuth', hasAuth) + app.directive('isa', isa) + }, +} diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/directive/isa.js b/{{cookiecutter.project_slug}}/ManagerUI/src/directive/isa.js new file mode 100644 index 0000000..4df0a09 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/directive/isa.js @@ -0,0 +1,11 @@ +import { useSystemStore } from '@/stores/system.js' + +export default function (el) { + const systemStore = useSystemStore() + + if (systemStore.isSa) { + return true + } else { + el.parentNode && el.parentNode.removeChild(el) + } +} diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/http/api.js b/{{cookiecutter.project_slug}}/ManagerUI/src/http/api.js new file mode 100644 index 0000000..5a3992f --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/http/api.js @@ -0,0 +1,20 @@ +import axios from 'axios' +import router from '@/router/index.js' + +const http = axios.create({ baseURL: import.meta.env.VITE_BASE_URL }) +http.interceptors.request.use( + (config) => config, + (error) => Promise.reject(error), +) +http.interceptors.response.use( + (response) => { + return [null, response.data] + }, + (error) => { + let response = error.response + if (response.status === 401) return router.push('/login') + return [response.data, null] + }, +) + +export default http diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/main.js b/{{cookiecutter.project_slug}}/ManagerUI/src/main.js new file mode 100644 index 0000000..7477f98 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/main.js @@ -0,0 +1,26 @@ +import './assets/css/main.css' + +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' +import App from './App.vue' +import router from './router' +import dayjs from 'dayjs' +import localizedFormat from 'dayjs/plugin/localizedFormat' +import relativeTime from 'dayjs/plugin/relativeTime' +import 'dayjs/locale/zh-cn' +import directive from '@/directive/index.js' + +const app = createApp(App) + +dayjs.extend(localizedFormat) +dayjs.locale('zh-cn') +dayjs.extend(relativeTime) + +const pinia = createPinia() +pinia.use(piniaPluginPersistedstate) +app.use(pinia) +app.use(router) +app.use(directive) + +app.mount('#app') diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/router/index.js b/{{cookiecutter.project_slug}}/ManagerUI/src/router/index.js new file mode 100644 index 0000000..1793cd0 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/router/index.js @@ -0,0 +1,93 @@ +import { createRouter, createWebHashHistory } from 'vue-router' +import LayoutIndex from '@/components/Layout/LayoutIndex.vue' +import { useSystemStore } from '@/stores/system.js' + +const router = createRouter({ + history: createWebHashHistory(import.meta.env.BASE_URL), + routes: [ + { + path: '/login', + component: () => import('@/views/LoginView.vue'), + }, + { + path: '/', + component: LayoutIndex, + redirect: '/home', + children: [ + { + path: '/home', + meta: { needLogin: true }, + component: () => import('@/views/HomeView.vue'), + }, + { + path: '/system', + children: [ + { + path: '/system/account', + meta: { needLogin: true, needMenu: true }, + component: () => import('@/views/AccountView.vue'), + }, + { + path: '/system/role', + meta: { needLogin: true, needMenu: true }, + component: () => import('@/views/RoleView.vue'), + }, + { + path: '/system/authority', + meta: { needLogin: true, needMenu: true }, + component: () => import('@/views/AuthorityView.vue'), + }, + { + path: '/system/tenant', + meta: { needLogin: true, needMenu: true }, + component: () => import('@/views/TenantView.vue'), + }, + { + path: '/system/logger', + meta: { needLogin: true, needMenu: true }, + component: () => import('@/views/LoggerView.vue'), + }, + ], + }, + { + path: '/message', + name: 'Message', + component: () => import('@/views/MessageView.vue'), + }, + { + path: '/webview', + name: 'WebView', + props: (route) => ({ link: route.query.link || '#' }), + component: () => import('@/views/WebView.vue'), + }, + { + path: '403', + component: () => import('@/views/exception/403View.vue'), + }, + { + path: '500', + component: () => import('@/views/exception/500View.vue'), + }, + { + path: '/:pathMatch(.*)*', + component: () => import('@/views/exception/404View.vue'), + }, + ], + }, + ], +}) + +router.beforeEach((to, from, next) => { + const systemStore = useSystemStore() + // 判断是否需要登录 + if (to.meta.needLogin && !systemStore.isLogin) { + return next('/login') + } + // 判断是否拥有菜单权限 + if (to.meta.needMenu && !systemStore.menus.some((item) => to.path.startsWith(item.path))) { + return next('/403') + } + return next() +}) + +export default router diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/stores/system.js b/{{cookiecutter.project_slug}}/ManagerUI/src/stores/system.js new file mode 100644 index 0000000..d4ddb01 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/stores/system.js @@ -0,0 +1,73 @@ +import { defineStore } from 'pinia' +import { jwtDecode } from 'jwt-decode' +import Cookies from 'js-cookie' +import axios from '@/http/api.js' +import { fromArray, map } from 'tree-lodash' +import { toRaw } from 'vue' + +const TokenKey = 'Sa-Token' + +export const useSystemStore = defineStore('system', { + state: () => { + return { + token: '', // 令牌 + name: '', // 用户名 + realName: '', // 显示姓名 + tenant: '', // 租户 + auths: [], // 角色和权限 + menus: [], // 菜单 + } + }, + getters: { + isSa(state) { + return !state.tenant + }, + isLogin(state) { + return !!state.token + }, + }, + actions: { + // 解析令牌 + parseToken() { + let token = Cookies.get(TokenKey) + if (token) { + this.token = token + var payload = jwtDecode(token) + this.name = payload['name'] + this.realName = payload['real_name'] + this.tenant = payload['tenant'] + } else { + useSystemStore().$reset() + } + }, + // 获得角色和权限 + ownAuths() { + let token = Cookies.get(TokenKey) + if (token) { + axios.get('/manager/own/auths').then(([_, res]) => (this.auths = res.data || [])) + } + }, + // 获得菜单 + ownMenus() { + let token = Cookies.get(TokenKey) + if (token) { + axios.get('/manager/own/menus').then(([_, res]) => (this.menus = res.data || [])) + } + }, + // 格式化菜单 + fmtMenus() { + return map(fromArray(toRaw(this.menus), { parentKey: 'parentId' }), (item) => ({ + key: item.path || item.value, + label: item.name, + link: item.link || '', + })) + }, + // 初始化 + init() { + this.parseToken() + this.ownAuths() + this.ownMenus() + }, + }, + persist: true, +}) diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/utils/crypto.js b/{{cookiecutter.project_slug}}/ManagerUI/src/utils/crypto.js new file mode 100644 index 0000000..eae9595 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/utils/crypto.js @@ -0,0 +1,11 @@ +import JSEncrypt from 'jsencrypt' + +const publicKey = ` + MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDORIceR8iDNIdH366iHZ9LCrkq VF84SRgO0JsZO79vc/1hcsECcs7kQCtFD9kj5Bz4P4iMJQ+hZeaPBKmrfHl91DDr hjuACgA3Pk0Pr5TBdN3eemA0Ri50NyjhoGpJvE8dZe1sbn4lfQwtOsx+kmP+Ixb3 oa6wdPQb3gfnQJqxDQIDAQAB +` + +export function encrypt(text) { + const encryptor = new JSEncrypt() + encryptor.setPublicKey(publicKey) // 设置公钥 + return encryptor.encrypt(text) // 对数据进行加密 +} diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/views/AccountView.vue b/{{cookiecutter.project_slug}}/ManagerUI/src/views/AccountView.vue new file mode 100644 index 0000000..2dedf36 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/views/AccountView.vue @@ -0,0 +1,578 @@ + + + + + diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/views/AuthorityView.vue b/{{cookiecutter.project_slug}}/ManagerUI/src/views/AuthorityView.vue new file mode 100644 index 0000000..082e27b --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/views/AuthorityView.vue @@ -0,0 +1,548 @@ + + + + + diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/views/HomeView.vue b/{{cookiecutter.project_slug}}/ManagerUI/src/views/HomeView.vue new file mode 100644 index 0000000..be9d2fa --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/views/HomeView.vue @@ -0,0 +1,9 @@ + + + + + diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/views/LoggerView.vue b/{{cookiecutter.project_slug}}/ManagerUI/src/views/LoggerView.vue new file mode 100644 index 0000000..c98dd36 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/views/LoggerView.vue @@ -0,0 +1,268 @@ + + + + + diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/views/LoginView.vue b/{{cookiecutter.project_slug}}/ManagerUI/src/views/LoginView.vue new file mode 100644 index 0000000..0a7e949 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/views/LoginView.vue @@ -0,0 +1,232 @@ + + + + + diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/views/MessageView.vue b/{{cookiecutter.project_slug}}/ManagerUI/src/views/MessageView.vue new file mode 100644 index 0000000..7cfbd40 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/views/MessageView.vue @@ -0,0 +1,371 @@ + + + + + diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/views/RoleView.vue b/{{cookiecutter.project_slug}}/ManagerUI/src/views/RoleView.vue new file mode 100644 index 0000000..428217b --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/views/RoleView.vue @@ -0,0 +1,527 @@ + + + + + diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/views/TenantView.vue b/{{cookiecutter.project_slug}}/ManagerUI/src/views/TenantView.vue new file mode 100644 index 0000000..caf4254 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/views/TenantView.vue @@ -0,0 +1,432 @@ + + + + + diff --git a/{{cookiecutter.project_slug}}/ManagerUI/src/views/WebView.vue b/{{cookiecutter.project_slug}}/ManagerUI/src/views/WebView.vue new file mode 100644 index 0000000..c991fe8 --- /dev/null +++ b/{{cookiecutter.project_slug}}/ManagerUI/src/views/WebView.vue @@ -0,0 +1,31 @@ + + +