文章目录
- 1. 前后端项目环境搭建
- 2. table-tree
- 2.1 后端准备
- 2.2 前端准备
前言:最近写项目,发现了一些很有意思的功能,想写文章,录视频把这些内容记录下。但这些功能太零碎,如果为每个功能都单独搭建一个项目,这明显不合适。于是我想,就搭建一个项目,把那些我想将的小功能全部整合到一起。实现搭一次环境,处处使用。
本文主要实现一下两个功能
- 前后端项目搭建
- 表格展示树形数据
已录制视频
b站视频链接
1. 前后端项目环境搭建
前端:pure-admin-thin
+ renren-fast-vue
后端:springboot 2.7.5
+ renren-fast
前端
-
克隆pure-admin-thin
git clone https://github.com/pure-admin/pure-admin-thin.git
-
安装前端依赖
pnpm i
-
运行前端项目
pnpm run dev
后端
-
构建springboot项目
-
pom.xml
<properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><mybatisplus.version>3.3.1</mybatisplus.version><mysql.version>8.0.28</mysql.version><mssql.version>4.0</mssql.version><oracle.version>11.2.0.3</oracle.version><druid.version>1.1.13</druid.version><quartz.version>2.3.0</quartz.version><commons.lang.version>2.6</commons.lang.version><commons.fileupload.version>1.2.2</commons.fileupload.version><commons.io.version>2.5</commons.io.version><commons.codec.version>1.10</commons.codec.version><commons.configuration.version>1.10</commons.configuration.version><shiro.version>1.9.0</shiro.version><jwt.version>0.7.0</jwt.version><kaptcha.version>0.0.9</kaptcha.version><qiniu.version>7.2.23</qiniu.version><aliyun.oss.version>2.8.3</aliyun.oss.version><qcloud.cos.version>4.4</qcloud.cos.version><swagger.version>2.7.0</swagger.version><joda.time.version>2.9.9</joda.time.version><gson.version>2.8.5</gson.version><hutool.version>4.1.1</hutool.version><lombok.version>1.18.4</lombok.version></properties><dependencies><dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId></dependency><!--日志--><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-access</artifactId></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId></dependency><dependency><groupId>commons-dbcp</groupId><artifactId>commons-dbcp</artifactId><version>1.4</version></dependency><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.2.0</version></dependency><dependency><groupId>com.aliyun</groupId><artifactId>dysmsapi20170525</artifactId><version>2.0.23</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version></dependency><dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatisplus.version}</version><exclusions><exclusion><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>${jwt.version}</version></dependency><dependency><groupId>com.github.axet</groupId><artifactId>kaptcha</artifactId><version>${kaptcha.version}</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>${swagger.version}</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>${swagger.version}</version></dependency><dependency><groupId>com.qiniu</groupId><artifactId>qiniu-java-sdk</artifactId><version>${qiniu.version}</version></dependency><dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>${aliyun.oss.version}</version></dependency><dependency><groupId>com.qcloud</groupId><artifactId>cos_api</artifactId><version>${qcloud.cos.version}</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions></dependency><dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId><version>${joda.time.version}</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.79</version></dependency><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>${gson.version}</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>${hutool.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>${commons.lang.version}</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId></dependency><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.7.5</version></dependency><!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis --><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>2.7.5</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.13</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.2</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build>
-
yml
# Tomcat server:tomcat:uri-encoding: UTF-8max-threads: 1000min-spare-threads: 30port: 9006 # connection-timeout: 5000msservlet:context-path: /api_demospring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedruid:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghaiusername: rootpassword: rootinitial-size: 10max-active: 100min-idle: 10max-wait: 60000pool-prepared-statements: truemax-pool-prepared-statement-per-connection-size: 20time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000#Oracle需要打开注释#validation-query: SELECT 1 FROM DUALtest-while-idle: truetest-on-borrow: falsetest-on-return: falsestat-view-servlet:enabled: trueurl-pattern: /druid/*#login-username: admin#login-password: adminfilter:stat:log-slow-sql: trueslow-sql-millis: 1000merge-sql: falsewall:config:multi-statement-allow: true# 环境 dev|test|prodprofiles:active: dev# jackson时间格式化jackson:time-zone: GMT+8date-format: yyyy-MM-dd HH:mm:ssservlet:multipart:max-file-size: 100MBmax-request-size: 100MBenabled: trueredis:open: false # 是否开启redis缓存 true开启 false关闭database: 0host: localhostport: 6379# password: 123456 # 密码(默认为空)timeout: 6000ms # 连接超时时长(毫秒)jedis:pool:max-active: 1000 # 连接池最大连接数(使用负值表示没有限制)max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)max-idle: 10 # 连接池中的最大空闲连接min-idle: 5 # 连接池中的最小空闲连接mvc:throw-exception-if-no-handler-found: truepathmatch:matching-strategy: ANT_PATH_MATCHER # resources: # add-mappings: false#mybatis mybatis-plus:logging:level:# org.springframework: warnorg.apache.ibatis.logging: debug# com.tmxk.municipal.**.dao: debugmapper-locations: classpath*:/mapper/**/*.xml#实体扫描,多个package用逗号或者分号分隔typeAliasesPackage: io.renren.modules.*.entityglobal-config:#数据库相关配置db-config:#主键类型 AUTO:"数据库ID自增", INPUT:"用户输入ID", ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";# id-type: ASSIGN_IDid-type: AUTOlogic-delete-value: 0logic-not-delete-value: 1banner: false#原生配置configuration:map-underscore-to-camel-case: truecache-enabled: falsecall-setters-on-nulls: truejdbc-type-for-null: 'null'log-impl: org.apache.ibatis.logging.slf4j.Slf4jImplrenren:redis:open: falseshiro:redis: false# APP模块,是通过jwt认证的,如果要使用APP模块,则需要修改【加密秘钥】jwt:# 加密秘钥secret: f4e2e52034348f86b67cde581c0f9eb5[www.renren.io]# token有效时长,7天,单位秒expire: 604800header: token
2. table-tree
效果图
后端项目结构
2.1 后端准备
-
数据表创建
DROP TABLE IF EXISTS `tb_unit`; CREATE TABLE `tb_unit` (`id` int NOT NULL AUTO_INCREMENT COMMENT '主键',`unit` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_croatian_ci NULL DEFAULT NULL COMMENT '单位名称',`pid` int NULL DEFAULT NULL COMMENT '父id',PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_croatian_ci COMMENT = '用户所属单位设置表' ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;
-
common模块
-
R【通用返回类】
/*** 返回数据** @author Mark sunlightcs@gmail.com*/ public class R extends HashMap<String, Object> {private static final long serialVersionUID = 1L;public static String data = "data";public R() {put("code", 0);put("msg", "success");}public static R error() {return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员");}public static R error(ErrorCode code) {return error(code.getErrorCode(), code.getMsg());}public static R error(String msg) {return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);}public static R error(int code, String msg) {R r = new R();r.put("code", code);r.put("msg", msg);return r;}public static R ok(String msg) {R r = new R();r.put("msg", msg);return r;}public static R ok(Map<String, Object> map) {R r = new R();r.putAll(map);return r;}public static R ok() {return new R();}public R put(String key, Object value) {super.put(key, value);return this;} }
-
ErrorCode
public enum ErrorCode {MANAGER_NOT_FOUND(412001, "主管未设置或不存在"),MANAGER_NOT_EQUAL(412002, "主管id不一致, 维修设施信息和维修工单的负责主管不一致"),INSTALLREPAIR_STATE_NOT_CORRECT(412003, "维修信息状态(status)错误,用户新上传的维修信息状态应为0(等待维修)"),INSTALLREPAIR_ISREPAIR_NOT_CORRECT(412004, "维修信息是否需要维修字段设置错误,允许的状态有0(不需要维修),1(需要维修),2(审核中)"),STATUS_NOT_FOUND(412005, "维修信息状态设置错误,允许的状态有0(等待维修),1(正在维修),2(维修完成)"),SMS_SEND_FAIL(412006, "短信发送错误, 请联系管理员"),SYSTEM_ERROR(412007, "服务器异常"),USER_NOT_FOUND(412008, "用户不存在"),DATA_ERROR(412009, "数据异常,服务器未接收到数据或传输数据为空"),NOT_CONTAIN_SPECIAL_CHAR(412010, "填写字符串信息不应该包含特殊字符"),PASSWORD_TO_SHORT(412011, "密码过短,不该小于6位"),REGISTER_ERROR(412012, "注册失败"),PASSWORD_NOT_EQUAL(412013, "两次密码不一致"),USERNAME_DUPLICATE(412014, "用户名已存在"),NOT_LOGIN(412015, "未登录"),INVALID_SESSION_KEY(412016, "sessionKey异常,请重试"),DECRYPTION_ERROR(412017, "用户信息解密异常,请重试"),NOT_MANAGER(412018, "您不是主管,请通过普通用户方式登录或者联系管理员升级为主管"),SESSIONID_INVALID(412019, "sessionId有误,服务器中不存在"),NOT_NULL_MARK(412020, "mark为空"),CODE_ERROR(412021, "code错误"),APPEALS_LACK_PROPERTY(412022, "appeals缺少property作为区分三表的标志"),APPEALS_WRONG_PROPERTY(412023, "appeals的property错误"),LOGIN_TIMEOUT(412024, "登录超时"),NO_WORKER_REPAIR(412025, "本条记录没有工人负责,请先设置负责工人"),WORKER_HAD_FOUND(412026, "本条记录已有负责的工人,请勿重复设置"),WORKER_NOT_EXIST(412027, "工人不存在"),VISIT_TOO_FREQUENCY(412028, "您的访问过于频繁,被系统认定为机器人,请稍后访问"),THINGS_DATA_LENGTH_INVALID(412029, "微信订阅消息的thing.DATA类型数据长度不能大于20"),REAPEAT_ORDER(412030, "重复下单"),REAPEAT_RECE_ORDER(412031, "重复接单"),REAPEAT_UPLOAD(412032, "重复提交");/*** 错误码*/private int errorCode;/*** 错误信息*/private String msg;ErrorCode() {}ErrorCode(int errorCode, String msg) {this.errorCode = errorCode;this.msg = msg;}public int getErrorCode() {return errorCode;}public String getMsg() {return msg;} }
-
-
entity
package com.fgbg.demo.entity;import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import java.io.Serializable; import java.util.List;import lombok.Data;/*** 用户所属单位设置表* @TableName tb_unit*/ @TableName(value ="tb_unit") @Data public class TbUnit implements Serializable {/*** 主键*/@TableId(type = IdType.AUTO)private Integer id;/*** 单位名称*/private String unit;/*** 父id*/private Integer pid;@TableField(exist = false)private List<TbUnit> children;@TableField(exist = false)private static final long serialVersionUID = 1L; }
-
controller
@RequestMapping("/list")public R list() {List<TbUnit> list = unitService.listAll();/*----{code: 0,msg: 'xxx',data: [A, B, C, D, E, F, G, H]}---*/return R.ok().put("data", list);}
-
service【核心逻辑】
/*** 查询所有的unit数据, 并返回树形结构** @return*/@Overridepublic List<TbUnit> listAll() {// 查询所有数据List<TbUnit> list = this.list();// 建立map映射(id->index)HashMap<Integer, Integer> map = new HashMap<>();for (int index = 0; index < list.size(); index++) {Integer id = list.get(index).getId();map.put(id, index);}// ...for (int i = 0; i < list.size(); i++) {TbUnit node = list.get(i);Integer pid = node.getPid();// 有父亲if (pid != null) {// 找到pid的父亲, 并把当前节点(node)添加到父亲节点的children里面Integer indexParent = map.get(pid);// 获取父亲节点TbUnit parent = list.get(indexParent);if (parent.getChildren() == null) {parent.setChildren(new ArrayList<>());}// 向父亲节点的children字段添加当前nodeparent.getChildren().add(node);}}// 过滤出一级节点List<TbUnit> ans = list.stream().filter(e -> e.getPid() == null).collect(Collectors.toList());return ans;}
-
返回的json数据
{"msg": "success","code": 0,"data": [{"id": 9,"unit": "浙江省","pid": null,"children": [{"id": 10,"unit": "杭州市","pid": 9,"children": [{"id": 11,"unit": "滨江区","pid": 10,"children": null},{"id": 12,"unit": "余杭区","pid": 10,"children": null}]},{"id": 13,"unit": "宁波市","pid": 9,"children": null},{"id": 14,"unit": "温州市","pid": 9,"children": null}]}] }
2.2 前端准备
-
/src/api/tree.ts
import { http } from "@/utils/http"; import { R, baseUrlApi } from "./utils";export class UnitEntity {id: Number;unit: String;pid: Number;children: Array<UnitEntity>; }/** 获取全部的unit数据 */ export const getData = () => {return http.request<R<Array<UnitEntity>>>("get", baseUrlApi("unit/list")); };
-
/src/api/utils.ts
export const baseUrlApi = (url: string) => `/api_demo/${url}`;/** 后端返回通用数据类型 */ export type R<T> = {code: Number;msg: String;data: T; };/** 同步休眠函数, 参数为毫秒 */ export const sleep = (ms: number): Promise<void> => {return new Promise(resolve => setTimeout(resolve, ms)); };/** 分页数据类型 */ export type PageUtils<T> = {/** 总记录数 */totalCount: number;/** 每页记录数 */pageSize: number;/** 总页数 */totalPage: number;/** 当前页数 */currPage: number;/** 列表数据 */list: Array<T>; };export const getStoreUser = () => {const res = sessionStorage.getItem("user-info");// const res = sessionStorage.getItem("user-info");console.log(res);return JSON.parse(res); };
-
/src/views/welecome/index.vue
<script setup lang="ts">import { ref, onMounted } from "vue";import { UnitEntity, getData } from "@/api/tree.ts";defineOptions({name: "Welcome"});const tableData = ref<Array<UnitEntity>>();onMounted(() => {getData().then(res => {console.log(res);if (res.code === 0) {tableData.value = res.data;}});});</script><template><el-table:data="tableData"style="width: 100%; margin-bottom: 20px"row-key="id"borderdefault-expand-all><el-table-column prop="id" label="序号" sortable /><el-table-column prop="unit" label="单位" sortable /><el-table-columnfixed="right"header-align="center"align="center"width="150"label="操作"><template v-slot="scope"><el-buttontype="text"size="small"@click="addOrUpdateHandle(scope.row.id)">修改</el-button></template></el-table-column></el-table></template>