Java + Element-UI 實現簡單的樹形菜單


一、簡單入門級樹形菜單實現(純后台邏輯)

1、簡介

(1)開發環境
  IDEA + JDK1.8 + mysql 1.8
  SpringBoot 2.2.6 + mybatis-plus
  此處僅后台開發(返回 json 數據),前台頁面展示后續會講解。

(2)數據表
  如下,僅供參考,可以添加 修改時間、創建時間、邏輯刪除等字段。

DROP DATABASE IF EXISTS test;

CREATE DATABASE test;

USE test;

/* 用於測試 樹形的菜單(以商品為例) */
CREATE TABLE tree_menu(
    menu_id             bigint NOT NULL AUTO_INCREMENT COMMENT "當前菜單ID",
    name                char(50) COMMENT "菜單名",
    parent_menu_id      bigint COMMENT "當前菜單的父菜單 ID",
    meun_level          int COMMENT "當前菜單的層級",
    sort                int COMMENT "排序",
    PRIMARY KEY (menu_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT="樹形菜單";

 

 

 

(3)插入測試數據
  為了數據直觀顯示,如下插入數據,每段數據代表一個菜單欄。
注:
  parent_menu_id 從 0 開始,0 表示第一級菜單(沒有父菜單)。
  meun_level 從 1 開始,1 表示第一級菜單,2 表示第二級菜單。

INSERT INTO tree_menu(menu_id, name, parent_menu_id, meun_level, sort)
VALUES
    (1, '廚具', 0, 1, 0),
        (2, '刀具', 1, 2, 0),(3, '烹飪工具', 1, 2, 0),(4, '餐具', 1, 2, 0),
          (5, '菜刀', 2, 3, 0),(6, '剪刀', 2, 3, 0),(7, '水果刀', 2, 3, 0),
          (8, '炒菜鍋', 3, 3, 0),(9, '壓力鍋', 3, 3, 0),(10, '平底鍋', 3, 3, 0),
          (11, '筷子', 4, 3, 0),(12, '碗', 4, 3, 0),(13, '果盤', 4, 3, 0),
        
    (14, '家用電器', 0, 1, 0),
        (15, '大家電', 14, 2, 0),(16, '生活家電', 14, 2, 0),
          (17, '電視', 15, 3, 0),(18, '電腦', 15, 3, 0),(19, '洗衣機', 15, 3, 0),(20, '冰箱', 15, 3, 0),
          (21, '電風扇', 16, 3, 0),(22, '吸塵器', 16, 3, 0),(23, '飲水機', 16, 3, 0),(24, '加濕器', 16, 3, 0),
    
    (25, '數碼', 0, 1, 0),
        (26, '攝像攝影', 25, 2, 0),(27, '影音娛樂', 25, 2, 0),(28, '教育學習', 25, 2, 0),
          (29, '數碼相機', 26, 3, 0),(30, '單反相機', 26, 3, 0),(31, '攝像機', 26, 3, 0),(32, '拍立得', 26, 3, 0),
          (33, 'MP3/MP4/MP5/PSP', 27, 3, 0),(34, '音箱', 27, 3, 0),(35, '麥克風', 27, 3, 0),
          (36, '學生平板', 28, 3, 0),(37, '復讀機', 28, 3, 0),(38, '電子辭典', 28, 3, 0),(39, '點讀機', 28, 3, 0)

 

2、構建基本開發環境

(1)使用 Easycode 插件根據數據表逆向生成相關代碼。
參考地址:
  https://www.cnblogs.com/l-y-h/p/12781586.html#_label0_2

 

 

 

(2)測試代碼是否能成功調用。
  Step1:給 TreeMenuDao 加上 @Mapper 注解。

 

 

 

  Step2:啟動服務並訪問 TreeMenuController。

 

 

 

  Step3:訪問 http://localhost:9000/treeMenu/selectOne?id=1,查詢成功即逆向生成的代碼沒問題。

 

 

 

3、實現樹形菜單(返回 json 數據)

(1)實現思路:
  每條記錄里都有 當前菜單 ID,以及 當前菜單的父菜單 ID。
  想要查詢出菜單的樹形結構,可以根據這兩個 ID 來實現。
思路:
  首先一次性從數據庫中查詢出所有的菜單數據。
  然后定位到 第一級 菜單,遞歸遍歷出其所有的子菜單。

(2)一次性查詢出所有的數據。
  Step1:在 service 中添加一個查詢所有數據的方法。

/**
 * 查詢數據庫所有數據
 * @return
 */
List<TreeMenu> queryAll();

 

 

  Step2:在 service 的實現類中重寫該方法。

/**
 * 查詢數據庫所有數據
 * @return 數據庫所有數據
 */
@Override
public List<TreeMenu> queryAll() {
    return treeMenuDao.queryAll(null);
}

 

 

  Step3:修改 controller,調用該方法。

/**
 * 獲取數據庫所有數據
 * @return 所有數據
 */
@GetMapping("selectAll")
public Result selectAll() {
    return Result.ok().data("items", treeMenuService.queryAll());
}

 

 

  Step4:啟動服務,訪問。打開控制台,可以看到返回的 json 數據。

 

 

(3)對查詢的數據進行處理,返回樹形的 json 數據。
  Step1:對於菜單實體類,增加一個實體類屬性,用於保存其子菜單數據。

/**
 * 用於保存一個菜單的子菜單
 */
@TableField(exist = false)
private List<TreeMenu> treeMenu;

 

 

  Step2:在 service 中添加一個查詢所有數據並返回樹形 json 的方法。

/**
 * 查詢數據庫數據,並處理后返回 樹形數據
 * @return 樹形數據
 */
List<TreeMenu> listWithTree();

 

 

  Step3:在 service 的實現類中重寫該方法。

/**
 * 查詢數據庫數據,並處理后返回 樹形數據
 * @return 樹形數據
 */
@Override
public List<TreeMenu> listWithTree() {
    // 查找所有菜單數據
    List<TreeMenu> lists = treeMenuDao.queryAll(null);
    // 把數據組合成樹形結構
    List<TreeMenu> result = lists.stream()
            // 查找第一級菜單
            .filter(meun -> meun.getMeunLevel() == 1)
            // 查找子菜單並放到第一級菜單中
            .map(menu -> {
                menu.setTreeMenu(getChildren(menu, lists));
                return menu;
            })
            // 根據排序字段排序
            .sorted((menu1, menu2) -> {
                return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
            })
            // 把處理結果收集成一個 List 集合
            .collect(Collectors.toList());
    return result;
}

/**
 * 遞歸獲取子菜單
 * @param root 當前菜單
 * @param all 總的數據
 * @return 子菜單
 */
public List<TreeMenu> getChildren(TreeMenu root, List<TreeMenu> all) {
    List<TreeMenu> children = all.stream()
            // 根據 父菜單 ID 查找當前菜單 ID,以便於找到 當前菜單的子菜單
            .filter(menu -> menu.getParentMenuId() == root.getMenuId())
            // 遞歸查找子菜單的子菜單
            .map((menu) -> {
                menu.setTreeMenu(getChildren(menu, all));
                return menu;
            })
            // 根據排序字段排序
            .sorted((menu1, menu2) -> {
                return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
            })
            // 把處理結果收集成一個 List 集合
            .collect(Collectors.toList());
    return children;
}

 

 

  Step4:在 controller 中調用該方法。

/**
 * 獲取數據庫數據,並處理成樹形結構
 * @return 樹形結構數據
 */
@GetMapping("selectAllWithTree")
public Result selectAllWithTree() {
    return Result.ok().data("items", treeMenuService.listWithTree());
}

 

 

  Step5:啟動服務,訪問。打開控制台,可以看到返回的樹形 json 數據。

 

 

 

 

二、Vue + ElementUI 展示樹形數據

1、簡介

  前面使用后台處理返回了 樹形結構 的 json 數據,現在需要將 json 數據按照一定的方式顯示出來即可。
  使用 vue-cli 3.0 創建 vue 項目。
  使用 element-ui 作為頁面顯示。
  使用 Axios 向后台發送請求並返回數據。

2、 構建基本開發環境

(1)使用 vue-cli (圖形化界面)創建一個 vue 項目。
參考地址:
  https://www.cnblogs.com/l-y-h/p/11241503.html

 

 

 

 

(2)添加 element-ui 依賴

【官網:】
    https://element.eleme.cn/#/zh-CN
    
【文檔:】
    https://element.eleme.cn/#/zh-CN/component/installation
    
【安裝方式一:(npm 安裝)】
    npm install element-ui
    
【安裝方式二:(CDN 方式引入)】
    <!-- 引入樣式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- 引入組件庫 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script> 

此處使用 npm 方式安裝。

 

 

(3)在 vue 項目中 引入 element-ui。
  在 main.js 中引入完整的 element-ui。

【main.js】

import Vue from 'vue'
import App from './App.vue'
// 引入 element-ui
import ElementUI from 'element-ui'
// 引入 element-ui 的 css 文件
import 'element-ui/lib/theme-chalk/index.css';
// 聲明使用 element-ui
Vue.use(ElementUI);

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

 

 

(4)引入樹形控件。
  直接從官網選擇一個模板,加以修改即可。
  如下,復制基本模板到 HelloWorld.vue 組件中,並加以修改。

【Tree 樹形控件:】
    https://element.eleme.cn/#/zh-CN/component/tree

【HelloWorld.vue】
<template>
    <el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
</template>
<script>
  export default {
    data() {
      return {
        data: [{
          label: '一級 1',
          children: [{
            label: '二級 1-1',
            children: [{
              label: '三級 1-1-1'
            }]
          }]
        }, {
          label: '一級 2',
          children: [{
            label: '二級 2-1',
          }]
        }],
        defaultProps: {
          children: 'children',
          label: 'label'
        }
      };
    },
    methods: {
      handleNodeClick(data) {
        console.log(data);
      }
    }
  };
</script> 

 

 

運行項目、查看效果如下。

 

 

3、使用 Axios 向后台發請求,獲取 json 數據

(1)vue 項目添加 Axios。

【參考地址:】
    https://www.cnblogs.com/l-y-h/p/11656129.html#_label1
    
【npm 安裝:】
    npm install axios

 

(2)使用 Axios 發送請求。

【引入 Axios】
    import axios from 'axios';
    
【HelloWorld.vue】
<template>
    <el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
</template>
<script>
    import axios from 'axios';
    export default {
        data() {
            return {
                data: [],
                defaultProps: {
                    children: 'treeMenu',
                    label: 'name'
                }
            };
        },
        methods: {
            handleNodeClick(data) {
                console.log(data);
            }
        },
        created() {
            axios.get(`http://localhost:9000/treeMenu/selectAllWithTree`)
            .then(response => {
                console.log(response);
                this.data = response.data.data.items;
            })
            .catch(error => {
                console.log(error);
            });
        }
    };
</script>

 

 

(3)解決跨域問題。
后端解決:
  可以使用 @CrossOrigin 注解,在 方法上添加該注解。

@CrossOrigin
@GetMapping("selectAllWithTree")
public Result selectAllWithTree() {
    return Result.ok().data("items", treeMenuService.listWithTree());
}

 

前端解決:

【參考地址:】
    https://www.cnblogs.com/l-y-h/p/11815452.html

此處,我使用后端解決,添加了 @CrossOrigin 注解。

 

 

 

 

4、element-ui 常用屬性分析

(1)樹形控件常用屬性分析

【屬性:】
    data            Array 類型,用於保存樹的 數據。
    props           Object 類型,用於定義配置選項。
注:
    props 可以用來指定 data 里面的屬性標簽名(其用來指定屬性名,而非屬性值)。
常用屬性:
    label          用於指定節點對象中標簽屬性的屬性名。
    children       用於指定節點對象中子對象屬性的屬性名。 
    isLeaf         用於指定節點對象中葉子節點的屬性名,僅在 lazy = true 時生效。
    disabled      用於指定節點對象中是否禁用節點的屬性名。

 

 

 

 

【屬性:】
    node-key             String 類型,用於表示唯一的樹節點。
    load                 Function 類型,僅 lazy = true 時生效,用於加載子樹據。
    lazy                 boolean 類型,默認為 false,是否懶加載子節點。
    show-checkbox        boolean 類型,默認為 false,是否展開復選框(節點是否能被選擇)。

【舉例:】
<template>
    <el-tree :props="props" :load="loadNode" lazy show-checkbox></el-tree>
</template>
<script>
    export default {
        data() {
            return {
                props: {
                    // 指定 data 中屬性名
                    label: 'name',
                    isLeaf: 'leaf'
                },
            };
        },
        methods: {
            loadNode(node, resolve) {
                // 初始加載節點數據
                if (node.level === 0) {
                    return resolve([{
                        name: 'region'
                    },{
                        name: "region2"
                    }]);
                }
                // 點擊第一級節點后,觸發延時操作,返回子節點
                if (node.level === 1) {
                    setTimeout(() => {
                        const data = [{
                            name: 'leaf'
                        }, {
                            name: 'zone',
                            disabled: true,
                            leaf: true
                        }];
                    
                        resolve(data);
                    }, 500);
                }
                // 點擊第二級節點后,沒有節點返回,返回 null。
                if (node.level > 1) {
                    return resolve([]);
                }
            }
        }
    };
</script>

 

 

當然,還有其他屬性,比如可以自定義樹節點數據,可以實現拖拽功能等。
此處不過多敘述,詳情可以參考官方文檔。

此處僅演示了 獲取全部數據 的代碼,可以根據項目情況,自行完善增加節點、刪除節點、批量刪除節點、拖拽節點等操作。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM