一、前端扁平数据转换为树结构
先上代码,这个我刚刚上网上找了,很多人已经发过了,我还以为是公司里面人独创的呢 |
---|
后面有详细的解释 |
这个主要是面向实际开发环境的,你可以省略掉除了source的所有其它参数,因为它们是为了通用性,因为总有些人十分具备没必要的个人特色 |
比如所有人都用0来作为码表根结点parentId,而它非要用12580 |
/**
* 构造树型结构数据
* @param {*} source 数据源
* @param {*} id id字段 默认 'id'
* @param {*} parentId 父节点字段 默认 'parentId'
* @param {*} children 孩子节点字段 默认 'children'
* @param {*} rootId 根Id 默认 0
*/
export function treeData (source, id, parentId, children, rootId) {
id = id || 'id'
parentId = parentId || 'parentId'
children = children || 'children'
rootId = rootId || 0
const cloneData = JSON.parse(JSON.stringify(source))// 对源数据深度克隆
return cloneData.filter(father => {
const branchArr = cloneData.filter(child => father[id] == child[parentId])// 返回每一项的子级数组
branchArr.length > 0 ? father[children] = branchArr : delete father[children]// 如果存在子级,则给父级添加一个children属性,并赋值
return father[parentId] == rootId // 返回第一层
})
}
最近在公司整合项目,发现后端这块后台统一返回的都是简单的扁平数据 |
---|
处理树形结构的代码主要写在前端,我们来一步一步分析 |
---|
/**
* 构造树型结构数据
* @param {*} source 数据源
* @param {*} id id字段 默认 'id'
* @param {*} parentId 父节点字段 默认 'parentId'
* @param {*} children 孩子节点字段 默认 'children'
* @param {*} rootId 根Id 默认 0
*/
export function treeData (source, id, parentId, children, rootId) {
id = id || 'id'
parentId = parentId || 'parentId'
children = children || 'children'
rootId = rootId || 0
const cloneData = JSON.parse(JSON.stringify(source))// 对源数据深度克隆
return cloneData.filter(father => {
/*
father[id] === father.id ,取的都是father对象中的id属性值
cloneData.filter(child => father[id] === child[parentId])//就是将所有parentId = 当前对象id的数组过滤出来
*/
const branchArr = cloneData.filter(child => father[id] == child[parentId])// 返回每一项的子级数组
/*
branchArr.length > 0 如果大于0表示有子级
father[children] = branchArr 给father添加一个children属性,并将刚才的子级数组赋值给children属性
delete father[children] 删除father对象的children属性
delete:delete操作符,用于删除指定对象的某个属性
*/
branchArr.length > 0 ? father[children] = branchArr : delete father[children]// 如果存在子级,则给父级添加一个children属性,并赋值
/*
father[parentId] == rootId,判断father.parentId是否等于rootId,如果等于就是true,返回这条数据不被过滤掉
*/
return father[parentId] == rootId // 返回第一层
})
}
为了通用性的考虑声明
解决改变原数组的诟病
过滤代码
二、后端递归删除树形结构
1、controller
/**
* 删除${tableComment}
* @Param id 节点id
*/
@PostMapping("remove/{id}")
public R remove(@PathVariable String id)
{
return toAjax(oeCourseSubjectService.deleteOeCourseSubjectById(id));
}
2、mapper
用到的有selectOeCourseSubjectList
,selectOeCourseSubjectById
,selectOeCourseSubjectByParentId
,deleteOeCourseSubjectById
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.edu.imau.zy.online_education.mapper.OeCourseSubjectMapper">
<resultMap type="OeCourseSubject" id="OeCourseSubjectResult">
<result property="id" column="id" />
<result property="title" column="title" />
<result property="parentId" column="parent_id" />
<result property="sort" column="sort" />
<result property="gmtCreate" column="gmt_create" />
<result property="gmtModified" column="gmt_modified" />
</resultMap>
<sql id="selectOeCourseSubjectVo">
select id, title, parent_id, sort,gmt_create, gmt_modified from oe_course_subject
</sql>
<select id="selectOeCourseSubjectList" parameterType="OeCourseSubject" resultMap="OeCourseSubjectResult">
<include refid="selectOeCourseSubjectVo"/>
<where>
<trim>
<if test="title != null and title != ''">and title = #{title}</if>
</trim>
</where>
ORDER BY
sort asc
</select>
<select id="selectOeCourseSubjectById" parameterType="String" resultMap="OeCourseSubjectResult">
<include refid="selectOeCourseSubjectVo"/>
where id = #{id}
</select>
<select id="selectOeCourseSubjectByParentId" parameterType="String" resultMap="OeCourseSubjectResult">
<include refid="selectOeCourseSubjectVo"/>
where parent_id = #{id}
</select>
<insert id="insertOeCourseSubject" parameterType="OeCourseSubject">
insert into oe_course_subject
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null and id != ''">id,</if>
<if test="title != null and title != ''">title,</if>
<if test="parentId != null and parentId != ''">parent_id,</if>
<if test="sort != null and sort != ''">sort,</if>
<if test="gmtCreate != null ">gmt_create,</if>
<if test="gmtModified != null ">gmt_modified,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null and id != ''">#{id},</if>
<if test="title != null and title != ''">#{title},</if>
<if test="parentId != null and parentId != ''">#{parentId},</if>
<if test="sort != null and sort != ''">#{sort},</if>
<if test="gmtCreate != null ">#{gmtCreate},</if>
<if test="gmtModified != null ">#{gmtModified},</if>
</trim>
</insert>
<update id="updateOeCourseSubject" parameterType="OeCourseSubject">
update oe_course_subject
<trim prefix="SET" suffixOverrides=",">
<if test="title != null and title != ''">title = #{title},</if>
<if test="parentId != null and parentId != ''">parent_id = #{parentId},</if>
<if test="sort != null and sort != ''">sort = #{sort},</if>
<if test="gmtCreate != null ">gmt_create = #{gmtCreate},</if>
<if test="gmtModified != null ">gmt_modified = #{gmtModified},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteOeCourseSubjectById" parameterType="String">
delete from oe_course_subject where id = #{id}
</delete>
<delete id="deleteOeCourseSubjectByIds" parameterType="String">
delete from oe_course_subject where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>
3、service
/**
* 删除在线教育课程分类信息
*
* @param id 在线教育课程分类ID
* @return 结果
*/
@Override
public int deleteOeCourseSubjectById(String id)
{
int index = 0;//定义一个int变量,用来返回删除了多少条数据
List<OeCourseSubject> list = build(id);//获取递归后得到的所有需要删除的节点
for (OeCourseSubject oeCourseSubject:list) {//遍历所有需要删除的节点
//删除每一个节点
oeCourseSubjectMapper.deleteOeCourseSubjectById(oeCourseSubject.getId());
index++;
}
return index;
}
/**
* 递归删除
* 1、先获取所有子级(包括它自己),保存到一个容器中
* 2、递归找出所有子级包括它本身的节点,保存到一个集合中
* 3、遍历第二步得到的容器,删除每一条数据
* @param id 根结点id
* @return 封装了所有需要删除的菜单的容器
*/
private List<OeCourseSubject> build(String id){
//获取当前根节点对象
OeCourseSubject treeNode = oeCourseSubjectMapper.selectOeCourseSubjectById(id);
//用来保存所有需要删除菜单的容器
List<OeCourseSubject> list = new ArrayList<>();
//获取所有菜单
List<OeCourseSubject> allList = oeCourseSubjectMapper.selectOeCourseSubjectList(null);
list.add(treeNode);//将根结点添加到需要删除的容器
findChildren(treeNode,allList,list);//进入递归
return list;
}
/**
* 递归获取所有需要删除节点
* @param node 上级节点
* @param allList 所有节点
* @param list 需要删除的节点,将查询到的子节点全部添加进去即可
*/
private void findChildren(OeCourseSubject node,List<OeCourseSubject> allList,List<OeCourseSubject> list){
//判断当前节点是否有子节点
if(oeCourseSubjectMapper.selectOeCourseSubjectByParentId(node.getId())!=null){
//遍历节点
for(OeCourseSubject ocs :allList){
//判断parent_id是否等于当前节点id,找到所有直接子节点
if (node.getId().equals(ocs.getParentId())){
list.add(ocs);//将此对象放在需要删除的集合中
findChildren(ocs,allList,list);//递归
}
}
}
}