123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- package tree_menu
- import (
- "github.com/druidcaesa/gotool"
- "sort"
- "ulink-admin/modules/system/models/model"
- )
- // Tree 统一定义菜单树的数据结构,也可以自定义添加其他字段
- type Tree struct {
- Name string `json:"name,omitempty"` //节点名字
- Path string `json:"path,omitempty"`
- Hidden bool `json:"hidden"`
- Redirect string `json:"redirect,omitempty"`
- Component string `json:"component,omitempty"`
- AlwaysShow bool `json:"alwaysShow"`
- Data interface{} `json:"-"` //自定义对象
- Meta interface{} `json:"meta"`
- Leaf bool `json:"-"` //叶子节点
- Selected bool `json:"-"` //选中
- PartialSelected bool `json:"-"` //部分选中
- Children []Tree `json:"children"` //子节点
- Id int64 `json:"id"`
- Label string `json:"label"`
- }
- // ConvertToINodeArray 其他的结构体想要生成菜单树,直接实现这个接口
- type INode interface {
- // GetName 获取显示名字
- GetName() string
- // GetMenuId 获取id
- GetMenuId() int64
- // GetParentId 获取父id
- GetParentId() int64
- // GetData 获取附加数据
- GetData() interface{}
- // IsRoot 判断当前节点是否是顶层根节点
- IsRoot() bool
- // GetPath 路径
- GetPath() string
- GetId() int64
- GetLabel() string
- }
- type INodes []INode
- func (nodes INodes) Len() int {
- return len(nodes)
- }
- func (nodes INodes) Swap(i, j int) {
- nodes[i], nodes[j] = nodes[j], nodes[i]
- }
- func (nodes INodes) Less(i, j int) bool {
- return nodes[i].GetMenuId() < nodes[j].GetMenuId()
- }
- // GenerateTree 自定义的结构体实现 INode 接口后调用此方法生成树结构
- // nodes 需要生成树的节点
- // selectedNode 生成树后选中的节点
- // menuTrees 生成成功后的树结构对象
- func GenerateTree(nodes, selectedNodes []INode) (trees []Tree) {
- trees = []Tree{}
- // 定义顶层根和子节点
- var roots, childs []INode
- for _, v := range nodes {
- if v.IsRoot() {
- // 判断顶层根节点
- roots = append(roots, v)
- }
- childs = append(childs, v)
- }
- for _, v := range roots {
- var flag = false
- var component = "Layout"
- m := make(map[string]interface{})
- if v.GetData() != nil {
- menu := v.GetData().(model.SysMenu)
- m["title"] = menu.MenuName
- m["icon"] = menu.Icon
- m["noCache"] = menu.IsCache == 1
- if menu.IsFrame == 0 {
- m["link"] = menu.Path
- } else {
- m["link"] = nil
- }
- flag = menu.Visible
- if !gotool.StrUtils.HasEmpty(menu.Component) {
- component = menu.Component
- }
- }
- childTree := &Tree{
- Name: v.GetName(),
- Data: v.GetData(),
- Path: "/" + v.GetPath(),
- Hidden: flag,
- AlwaysShow: true,
- Redirect: "noRedirect",
- Meta: m,
- Component: component,
- Id: v.GetId(),
- Label: v.GetLabel(),
- }
- // 递归之前,根据父节点确认 childTree 的选中状态
- childTree.Selected = nodeSelected(v, selectedNodes, childTree.Children)
- // 递归
- recursiveTree(childTree, childs, selectedNodes)
- // 递归之后,根据子节点确认 childTree 的选中状态
- if !childTree.Selected {
- childTree.Selected = nodeSelected(v, selectedNodes, childTree.Children)
- }
- // 递归之后,根据子节点确认 childTree 的半选中状态
- childTree.PartialSelected = nodePartialSelected(childTree.Children)
- // 递归之后,根据子节确认是否是叶子节点
- childTree.Leaf = len(childTree.Children) == 0
- trees = append(trees, *childTree)
- }
- return
- }
- // recursiveTree 递归生成树结构
- // tree 递归的树对象
- // nodes 递归的节点
- // selectedNodes 选中的节点
- func recursiveTree(tree *Tree, nodes, selectedNodes []INode) {
- data := tree.Data.(INode)
- for _, v := range nodes {
- if v.IsRoot() {
- // 如果当前节点是顶层根节点就跳过
- continue
- }
- var flag = false
- var component = "Layout"
- m := make(map[string]interface{})
- if v.GetData() != nil {
- menu := v.GetData().(model.SysMenu)
- flag = menu.Visible
- m["title"] = menu.MenuName
- m["icon"] = menu.Icon
- m["noCache"] = menu.IsCache == 1
- if menu.IsFrame == 0 {
- m["link"] = menu.Path
- } else {
- m["link"] = nil
- }
- if !gotool.StrUtils.HasEmpty(menu.Component) {
- component = menu.Component
- }
- }
- if data.GetMenuId() == v.GetParentId() {
- childTree := &Tree{
- Name: v.GetName(),
- Data: v.GetData(),
- Path: v.GetPath(),
- Hidden: flag,
- Meta: m,
- Component: component,
- Id: v.GetId(),
- Label: v.GetLabel(),
- }
- // 递归之前,根据子节点和父节点确认 childTree 的选中状态
- childTree.Selected = nodeSelected(v, selectedNodes, childTree.Children) || tree.Selected
- recursiveTree(childTree, nodes, selectedNodes)
- if !childTree.Selected {
- // 递归之后,根据子节点确认 childTree 的选中状态
- childTree.Selected = nodeSelected(v, selectedNodes, childTree.Children)
- }
- // 递归之后,根据子节点确认 childTree 的半选中状态
- childTree.PartialSelected = nodePartialSelected(childTree.Children)
- // 递归之后,根据子节确认是否是叶子节点
- childTree.Leaf = len(childTree.Children) == 0
- tree.Children = append(tree.Children, *childTree)
- }
- }
- }
- // FindRelationNode 在 allTree 中查询 nodes 中节点的所有父节点
- // nodes 要查询父节点的子节点数组
- // allTree 所有节点数组
- func FindRelationNode(nodes, allNodes []INode) (respNodes []INode) {
- nodeMap := make(map[int64]INode)
- for _, v := range nodes {
- recursiveFindRelationNode(nodeMap, allNodes, v, 0)
- }
- for _, v := range nodeMap {
- respNodes = append(respNodes, v)
- }
- sort.Sort(INodes(respNodes))
- return
- }
- // recursiveFindRelationNode 递归查询关联父子节点
- // nodeMap 查询结果搜集到map中
- // allNodes 所有节点
- // node 递归节点
- // t 递归查找类型:0 查找父、子节点;1 只查找父节点;2 只查找子节点
- func recursiveFindRelationNode(nodeMap map[int64]INode, allNodes []INode, node INode, t int) {
- nodeMap[node.GetMenuId()] = node
- for _, v := range allNodes {
- if _, ok := nodeMap[v.GetMenuId()]; ok {
- continue
- }
- // 查找父节点
- if t == 0 || t == 1 {
- if node.GetParentId() == v.GetMenuId() {
- nodeMap[v.GetMenuId()] = v
- if v.IsRoot() {
- // 是顶层根节点时,不再进行递归
- continue
- }
- recursiveFindRelationNode(nodeMap, allNodes, v, 1)
- }
- }
- // 查找子节点
- if t == 0 || t == 2 {
- if node.GetMenuId() == v.GetParentId() {
- nodeMap[v.GetMenuId()] = v
- recursiveFindRelationNode(nodeMap, allNodes, v, 2)
- }
- }
- }
- }
- // nodeSelected 判断节点的选中状态
- // node 进行判断节点
- func nodeSelected(node INode, selectedNodes []INode, children []Tree) bool {
- for _, v := range selectedNodes {
- if node.GetMenuId() == v.GetMenuId() {
- // 1. 如果选择节点数组中存在当前节点
- return true
- }
- }
- if len(children) == 0 {
- // 2. 不满足前置条件1,且没有子节点
- return false
- }
- selectedNum := 0
- for _, v := range children {
- if v.Selected {
- selectedNum++
- }
- }
- if selectedNum == len(children) {
- // 不满足前置条件1,2 ,且子节点全部是选中状态
- return true
- }
- return false
- }
- // nodePartialSelected 判断节点的半选中状态
- func nodePartialSelected(trees []Tree) bool {
- selectedNum := 0
- for _, v := range trees {
- if v.Selected {
- selectedNum++
- }
- }
- if selectedNum == len(trees) || selectedNum == 0 {
- // 子节点全选中,或一个也没有选中
- return false
- }
- return true
- }
- type SystemMenus []model.SysMenu
- // ConvertToINodeArray 将当前数组转换成父类 INode 接口 数组
- func (s SystemMenus) ConvertToINodeArray() (nodes []INode) {
- for _, v := range s {
- nodes = append(nodes, v)
- }
- return
- }
- // GetTree 获取树结构数据
- func (s SystemMenus) GetTree() []Tree {
- array := s.ConvertToINodeArray()
- return GenerateTree(array, nil)
- }
|