前言:最近寫項目,發(fā)現(xiàn)了一些很有意思的功能,想寫文章,錄視頻把這些內(nèi)容記錄下。但這些功能太零碎,如果為每個功能都單獨搭建一個項目,這明顯不合適。于是我想,就搭建一個項目,把那些我想將的小功能全部整合到一起。實現(xiàn)搭一次環(huán)境,處處使用。
本文主要實現(xiàn)一下兩個功能
- 前后端項目搭建
- 表格展示樹形數(shù)據(jù)
已錄制視頻
b站視頻鏈接
倉庫地址https://github.com/xuhuafeifei/fgbg-font-and-back.git
1. 前后端項目環(huán)境搭建
前端: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
后端
-
構(gòu)建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-8 max-threads: 1000 min-spare-threads: 30 port: 9006 # connection-timeout: 5000ms servlet: context-path: /api_demo spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: root initial-size: 10 max-active: 100 min-idle: 10 max-wait: 60000 pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 #Oracle需要打開注釋 #validation-query: SELECT 1 FROM DUAL test-while-idle: true test-on-borrow: false test-on-return: false stat-view-servlet: enabled: true url-pattern: /druid/* #login-username: admin #login-password: admin filter: stat: log-slow-sql: true slow-sql-millis: 1000 merge-sql: false wall: config: multi-statement-allow: true # 環(huán)境 dev|test|prod profiles: active: dev # jackson時間格式化 jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss servlet: multipart: max-file-size: 100MB max-request-size: 100MB enabled: true redis: open: false # 是否開啟redis緩存 true開啟 false關(guān)閉 database: 0 host: localhost port: 6379 # password: 123456 # 密碼(默認為空) timeout: 6000ms # 連接超時時長(毫秒) jedis: pool: max-active: 1000 # 連接池最大連接數(shù)(使用負值表示沒有限制) max-wait: -1ms # 連接池最大阻塞等待時間(使用負值表示沒有限制) max-idle: 10 # 連接池中的最大空閑連接 min-idle: 5 # 連接池中的最小空閑連接 mvc: throw-exception-if-no-handler-found: true pathmatch: matching-strategy: ANT_PATH_MATCHER # resources: # add-mappings: false #mybatis mybatis-plus: logging: level: # org.springframework: warn org.apache.ibatis.logging: debug # com.tmxk.municipal.**.dao: debug mapper-locations: classpath*:/mapper/**/*.xml #實體掃描,多個package用逗號或者分號分隔 typeAliasesPackage: io.renren.modules.*.entity global-config: #數(shù)據(jù)庫相關(guān)配置 db-config: #主鍵類型 AUTO:"數(shù)據(jù)庫ID自增", INPUT:"用戶輸入ID", ID_WORKER:"全局唯一ID (數(shù)字類型唯一ID)", UUID:"全局唯一ID UUID"; # id-type: ASSIGN_ID id-type: AUTO logic-delete-value: 0 logic-not-delete-value: 1 banner: false #原生配置 configuration: map-underscore-to-camel-case: true cache-enabled: false call-setters-on-nulls: true jdbc-type-for-null: 'null' log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl renren: redis: open: false shiro: redis: false # APP模塊,是通過jwt認證的,如果要使用APP模塊,則需要修改【加密秘鑰】 jwt: # 加密秘鑰 secret: f4e2e52034348f86b67cde581c0f9eb5[www.renren.io] # token有效時長,7天,單位秒 expire: 604800 header: token
2. table-tree
效果圖
后端項目結(jié)構(gòu)
2.1 后端準(zhǔn)備
-
數(shù)據(jù)表創(chuàng)建
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 = '用戶所屬單位設(shè)置表' ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
-
common模塊
-
R【通用返回類】
/** * 返回數(shù)據(jù) * * @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, "未知異常,請聯(lián)系管理員"); } 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, "主管未設(shè)置或不存在"), MANAGER_NOT_EQUAL(412002, "主管id不一致, 維修設(shè)施信息和維修工單的負責(zé)主管不一致"), INSTALLREPAIR_STATE_NOT_CORRECT(412003, "維修信息狀態(tài)(status)錯誤,用戶新上傳的維修信息狀態(tài)應(yīng)為0(等待維修)"), INSTALLREPAIR_ISREPAIR_NOT_CORRECT(412004, "維修信息是否需要維修字段設(shè)置錯誤,允許的狀態(tài)有0(不需要維修),1(需要維修),2(審核中)"), STATUS_NOT_FOUND(412005, "維修信息狀態(tài)設(shè)置錯誤,允許的狀態(tài)有0(等待維修),1(正在維修),2(維修完成)"), SMS_SEND_FAIL(412006, "短信發(fā)送錯誤, 請聯(lián)系管理員"), SYSTEM_ERROR(412007, "服務(wù)器異常"), USER_NOT_FOUND(412008, "用戶不存在"), DATA_ERROR(412009, "數(shù)據(jù)異常,服務(wù)器未接收到數(shù)據(jù)或傳輸數(shù)據(jù)為空"), NOT_CONTAIN_SPECIAL_CHAR(412010, "填寫字符串信息不應(yīng)該包含特殊字符"), 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, "您不是主管,請通過普通用戶方式登錄或者聯(lián)系管理員升級為主管"), SESSIONID_INVALID(412019, "sessionId有誤,服務(wù)器中不存在"), NOT_NULL_MARK(412020, "mark為空"), CODE_ERROR(412021, "code錯誤"), APPEALS_LACK_PROPERTY(412022, "appeals缺少property作為區(qū)分三表的標(biāo)志"), APPEALS_WRONG_PROPERTY(412023, "appeals的property錯誤"), LOGIN_TIMEOUT(412024, "登錄超時"), NO_WORKER_REPAIR(412025, "本條記錄沒有工人負責(zé),請先設(shè)置負責(zé)工人"), WORKER_HAD_FOUND(412026, "本條記錄已有負責(zé)的工人,請勿重復(fù)設(shè)置"), WORKER_NOT_EXIST(412027, "工人不存在"), VISIT_TOO_FREQUENCY(412028, "您的訪問過于頻繁,被系統(tǒng)認定為機器人,請稍后訪問"), THINGS_DATA_LENGTH_INVALID(412029, "微信訂閱消息的thing.DATA類型數(shù)據(jù)長度不能大于20"), REAPEAT_ORDER(412030, "重復(fù)下單"), REAPEAT_RECE_ORDER(412031, "重復(fù)接單"), REAPEAT_UPLOAD(412032, "重復(fù)提交"); /** * 錯誤碼 */ 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; /** * 用戶所屬單位設(shè)置表 * @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數(shù)據(jù), 并返回樹形結(jié)構(gòu) * * @return */ @Override public List<TbUnit> listAll() { // 查詢所有數(shù)據(jù) 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的父親, 并把當(dāng)前節(jié)點(node)添加到父親節(jié)點的children里面 Integer indexParent = map.get(pid); // 獲取父親節(jié)點 TbUnit parent = list.get(indexParent); if (parent.getChildren() == null) { parent.setChildren(new ArrayList<>()); } // 向父親節(jié)點的children字段添加當(dāng)前node parent.getChildren().add(node); } } // 過濾出一級節(jié)點 List<TbUnit> ans = list.stream().filter(e -> e.getPid() == null).collect(Collectors.toList()); return ans; }
-
返回的json數(shù)據(jù)
{ "msg": "success", "code": 0, "data": [ { "id": 9, "unit": "浙江省", "pid": null, "children": [ { "id": 10, "unit": "杭州市", "pid": 9, "children": [ { "id": 11, "unit": "濱江區(qū)", "pid": 10, "children": null }, { "id": 12, "unit": "余杭區(qū)", "pid": 10, "children": null } ] }, { "id": 13, "unit": "寧波市", "pid": 9, "children": null }, { "id": 14, "unit": "溫州市", "pid": 9, "children": null } ] } ] }
2.2 前端準(zhǔn)備
-
/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數(shù)據(jù) */ export const getData = () => { return http.request<R<Array<UnitEntity>>>("get", baseUrlApi("unit/list")); };
-
/src/api/utils.ts文章來源:http://www.zghlxwxcb.cn/news/detail-789924.html
export const baseUrlApi = (url: string) => `/api_demo/${url}`; /** 后端返回通用數(shù)據(jù)類型 */ export type R<T> = { code: Number; msg: String; data: T; }; /** 同步休眠函數(shù), 參數(shù)為毫秒 */ export const sleep = (ms: number): Promise<void> => { return new Promise(resolve => setTimeout(resolve, ms)); }; /** 分頁數(shù)據(jù)類型 */ export type PageUtils<T> = { /** 總記錄數(shù) */ totalCount: number; /** 每頁記錄數(shù) */ pageSize: number; /** 總頁數(shù) */ totalPage: number; /** 當(dāng)前頁數(shù) */ currPage: number; /** 列表數(shù)據(jù) */ 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文章來源地址http://www.zghlxwxcb.cn/news/detail-789924.html
<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"
border
default-expand-all
>
<el-table-column prop="id" label="序號" sortable />
<el-table-column prop="unit" label="單位" sortable />
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作"
>
<template v-slot="scope">
<el-button
type="text"
size="small"
@click="addOrUpdateHandle(scope.row.id)"
>修改</el-button
>
</template>
</el-table-column>
</el-table>
</template>
到了這里,關(guān)于【前后端的那些事】開源!前后端環(huán)境搭建+樹形結(jié)構(gòu)表格實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!