本文主要实现一下两个功能
前端: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-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
# 环境 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关闭
database: 0
host: localhost
port: 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: 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:
#数据库相关配置
db-config:
#主键类型 AUTO:"数据库ID自增", INPUT:"用户输入ID", ID_WORKER:"全局唯一ID (数字类型唯一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
效果图
后端项目结构
数据表创建
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
*/
@Override
public 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字段添加当前node
parent.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
}
]
}
]
}
/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"
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>