Vue development menu management page

Our general background management projects are configured in the background to configure routing and permissions, and if we need to add menus in the front end, we need to write a menu management page for configuration.

1. New menu management page

Create a new menu package under views, and create a new SysMenu.vue page under this package

2. Menu management page code

<!-- Menu management page -->
<template>
 <div>
   <!-- 1. Add button area -->
   <div style="display: flex;justify-content: flex-start">
     <el-button type="primary" icon="el-icon-plus" size="small" @click="addFolder">Add</el-button>
   </div>
   <!-- 2. Divider area -->
   <el-divider style="margin-top: 20px"></el-divider>
   <!-- 3. Menu table area -->
   <el-table
     :data="menuTreeList"
     style="width: 100%; margin-bottom: 20px;"
     row-key="id"
     border
     :default-expand-all="false"
     height="72vh"
     :tree-props="{children: 'children'}">
     <el-table-column
       prop="name"
       label="menu name"
>
     </el-table-column>
     <el-table-column

       prop="type"
       label="type">
       <!--Conditional judgment-->
       <template slot-scope="scope">
         <!--Get the type attribute and make a conditional judgment -->
         <el-tag v-if="scope.row.type===0">Directory</el-tag>
         <el-tag type="success" v-else-if="scope.row.type===1">menu</el-tag>
         <el-tag type="info" v-else>button</el-tag>
       </template>
     </el-table-column>
     <el-table-column

       prop="icon"
       label="icon">
       <template slot-scope="scope">
         <i :class="scope.row.icon"></i>
       </template>
     </el-table-column>
     <el-table-column prop="perms" label="permission identification"/>
     <el-table-column prop="path" label="routing address"/>
     <el-table-column prop="component" label="component path"/>
     <el-table-column prop="sortValue" label="sort"/>
     <el-table-column label="status" width="80">
       <template slot-scope="scope">
         <el-switch
           :value="scope.row.status === 1" :disabled="true">
         </el-switch>
       </template>
     </el-table-column>
     <el-table-column prop="createTime" label="create time" width="160"/>
     <el-table-column label="Operation" width="180" fixed="right">
       <template slot-scope="scope">
         <el-button type="success" v-if="scope.row.type !== 2" icon="el-icon-plus" size="mini" @click=\ "addLastNode(scope.row)" title="Add"/>
         <el-button type="primary" icon="el-icon-edit" size="mini" @click="alertNode(scope.row)" title="Edit"/ >
         <el-button type="danger" icon="el-icon-delete" size="mini" @click="deleteMenuData(scope.row.id)" title="Delete" " :disabled="scope.row.children.length > 0"/>
       </template>
     </el-table-column>
   </el-table>
   <!-- 4. Add and update the pop-up window area -->
   <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="40%" @open="onOpen" @close="onClose">
     <el-form ref="menuForm" :model="menu" :rules="menuRules" size="small" label-width="150px">
       <el-form-item v-if="menu.parentName !== undefined" label="Superior Directory" prop="parentName">
         <el-input v-model="menu.parentName" :disabled='true' clearable :style="{width: '100%'}"></el-input>
       </el-form-item>
       <el-form-item label="menu type" prop="type">
         <el-radio-group v-model="menu.type" size="mini" :disabled='typeDisabled'>
           <el-radio :label="0" :disabled="typeDisabled0">Directory</el-radio>
           <el-radio :label="1" :disabled="typeDisabled1">Menu</el-radio>
           <el-radio :label="2" :disabled="typeDisabled2">button</el-radio>
         </el-radio-group>
       </el-form-item>
       <el-form-item label="menu name" prop="name">
         <el-input v-model="menu.name" clearable :style="{width: '100%'}"></el-input>
       </el-form-item>
       <el-form-item v-if="menu.type !== 2" label="icon" prop="icon">
         <el-select v-model="menu.icon" placeholder="Please select" filterable clearable :style="{width: '100%'}">
           <el-option v-for="(item, index) in iconOptions" :key="index" :value="item.class"
                      :disabled="item.disabled">
              <span style="float: left;">
                <i :class="item. class"></i>
              </span>
             <span style="padding-left: 6px;">{<!-- -->{<!-- --> item.class }}</span>
           </el-option>
         </el-select>
       </el-form-item>
       <el-form-item label="sort" prop="sortValue">
         <el-input-number v-model="menu.sortValue" placeholder="sort" :step='1' step-strictly :min='1'>
         </el-input-number>
       </el-form-item>
       <el-form-item v-if="menu.type === 2" label="permission character" prop="perms">
         <span slot="label">
           <el-tooltip content="Authority characters defined in the Controller layer, such as: @PreAuthorize(hasAuthority('bnt.sysMenu.add'))" placement="top">
              <i></i>
           </el-tooltip>
           permission character
         </span>
         <el-input v-model="menu.perms" placeholder="Please enter permission characters" clearable :style="{width: '100%'}"></el-input>
       </el-form-item>
       <el-form-item v-if="menu.type === 1" label="component address" prop="component">
           <span slot="label">
             <el-tooltip content="component address, such as: views/main/menu/SysMenu" placement="top">
                 <i></i>
             </el-tooltip>
                 component address
           </span>
         <el-input v-model="menu.component" placeholder="Please enter the component address" clearable :style="{width: '100%'}"></el-input>
       </el-form-item>
       <el-form-item v-if="menu.type === 1" label="routing address" prop="path">
         <span slot="label">
            <el-tooltip content="routing address, such as: sysMenu" placement="top">
               <i></i>
            </el-tooltip>
            routing address
         </span>
         <el-input v-model="menu.path" placeholder="Please enter the routing address" clearable :style="{width: '100%'}"></el-input>
       </el-form-item>
       <el-form-item label="status" prop="status" required>
         <el-switch v-model="menu.status" :active-value='1' :inactive-value='0'></el-switch>
       </el-form-item>
     </el-form>
     <div slot="footer">
       <el-button @click="close">Cancel</el-button>
       <el-button type="primary" @click="handelConfirm" :disabled="saveBtnDisabled">OK</el-button>
     </div>
   </el-dialog>
 </div>
</template>

<script>
 /* Introduce menu management api interface */
 import menu from '../../../api/menu/menu'

 // menu form
 const menuFrom = {<!-- -->
   parentName: undefined,
   type: 0,
   name: undefined,
   icon: undefined,
   sortValue: undefined,
   perms: undefined,
   component: undefined,
   path: undefined,
   status: 1,
 };

 export default {<!-- -->
   name: "SysMenu",
   data() {<!-- -->
     return {<!-- -->
       menuTreeList: [],
       /* popup area field */
       dialogVisible: false,
       dialogTitle: '',
       // Controls whether the menu type radio button is available
       typeDisabled: false,
       typeDisabled0: false,
       typeDisabled1: false,
       typeDisabled2: false,
       // Control the popup window to determine whether the button is available
       saveBtnDisabled: false,
       menu: menuFrom,
       menuRules: {<!-- -->
         parentName: [{<!-- -->
           required: true,
           message: '',
           trigger: 'blur'
         }],
         type: [{<!-- -->
           required: true,
           message: 'The menu type cannot be empty',
           trigger: 'change'
         }],
         name: [{<!-- -->
           required: true,
           message: '',
           trigger: 'blur'
         }],
         icon: [{<!-- -->
           required: true,
           message: 'Please choose',
           trigger: 'change'
         }],
         sortValue: [{<!-- -->
           required: true,
           message: 'Sorting',
           trigger: 'blur'
         }],
         perms: [{<!-- -->
           required: true,
           message: 'Please enter the permission character',
           trigger: 'blur'
         }],
         component: [{<!-- -->
           required: true,
           message: 'Please enter the component address',
           trigger: 'blur'
         }],
         path: [{<!-- -->
           required: true,
           message: 'Please enter the routing address',
           trigger: 'blur'
         }],
       },
       iconOptions: [
         {<!-- -->
           class: "el-icon-s-tools",
         },
         {<!-- -->
           class: "el-icon-s-custom",
         },
         {<!-- -->
           class: "el-icon-setting",
         },
         {<!-- -->
           class: "el-icon-user-solid",
         },
         {<!-- -->
           class: "el-icon-s-help",
         },
         {<!-- -->
           class: "el-icon-phone",
         },
         {<!-- -->
           class: "el-icon-s-unfold",
         },
         {<!-- -->
           class: "el-icon-s-operation",
         },
         {<!-- -->
           class: "el-icon-more-outline",
         },
         {<!-- -->
           class: "el-icon-s-check",
         },
         {<!-- -->
           class: "el-icon-tickets",
         },
         {<!-- -->
           class: "el-icon-s-goods",
         },
         {<!-- -->
           class: "el-icon-document-remove",
         },
         {<!-- -->
           class: "el-icon-warning",
         },
         {<!-- -->
           class: "el-icon-warning-outline",
         },
         {<!-- -->
           class: "el-icon-question",
         },
         {<!-- -->
           class: "el-icon-info",
         }]
     }
   },
   methods: {<!-- -->
     /* get menu tree */
     async getMenuTree() {<!-- -->
       const {<!-- --> data } = await menu.getMenuTree();
       this.menuTreeList = data.data.result
     },
     /* New menu */
     async addMenuData(menuVo) {<!-- -->
       const {<!-- --> data } = await menu.addMenuData(menuVo);
       this.$message({<!-- -->
         message: data.message || 'operation succeeded',
         type: data.code === 200?'success':'error'
       });
       // refresh page
       this. getMenuTree()
     },
     /* update menu */
     async updateMenuData(menuVo) {<!-- -->
       const {<!-- --> data } = await menu.updateMenuData(menuVo);
       this.$message({<!-- -->
         message: data.message || 'operation succeeded',
         type: data.code === 200?'success':'error'
       });
       // refresh page
       this. getMenuTree()
     },
     /* delete menu */
     deleteMenuData(id) {<!-- -->
       this.$confirm('This operation will permanently delete the file, do you want to continue?', 'Prompt', {<!-- -->
         confirmButtonText: 'OK',
         cancelButtonText: 'Cancel',
         type: 'warning'
       }).then(() => {<!-- -->
         return menu.deleteMenuData(id)
       }).then((res) => {<!-- -->
         // refresh page
         this. getMenuTree()
         this.$message({<!-- -->
           message: res.data.message || 'Operation succeeded',
           type: res.data.code === 200?'success':'error'
         });
       }).catch(() => {<!-- -->
         this.$message({<!-- -->
           type: 'info',
           message: 'Delete canceled'
         });
       });
     },
     /* Popup area method */
     // add directory/menu
     addFolder() {<!-- -->
       // Clear the menu form content
       this.menu = Object.assign({<!-- -->},menuFrom);
       this.dialogVisible = true;
       this.dialogTitle = 'Add directory/menu node'
       // disable button radio button
       this.typeDisabled = false;
       this.typeDisabled2 = true;
       // The default menu type radio button selects the directory
       this.menu.type = Number(0)

     },
     // Add a lower-level node method
     addLastNode(row) {<!-- -->
       // Clear the menu form content
       this.menu = Object.assign({<!-- -->},menuFrom);
       this.dialogVisible = true;
       switch (row.type) {<!-- -->
         case 0:
           // On behalf of the directory, you can add menu nodes or directory nodes under the directory
           this.dialogTitle = 'Add directory/menu node';
           // Assign values to the parent directory
           this.menu.parentName = row.name;
           this.menu.parentId = row.id;
           // disable button radio button
           this. typeDisabled = false;
           this.typeDisabled2 = true;
           // default menu type single button check menu
           this.menu.type = Number(1);
           break;
         case 1:
           // Represents the menu, only button nodes can be added under the menu
           this.dialogTitle = 'Add button node';
           // Assign values to the parent directory
           this.menu.parentId = row.id;
           this.menu.parentName = row.name;
           // add button by default
           this.menu.type = Number(2);
           // disable menu type radio button
           this.typeDisabled = true;
           break;
       }
     },
     // modify node method
     alertNode(row) {<!-- -->
       // Assign the current row data to the menu form
       this.menu = Object.assign({<!-- -->},row);
       this.dialogVisible = true;
       switch (row.type) {<!-- -->
         case 0:
           // Represents a directory, there may be directories and menus under the directory
           this.dialogTitle = 'Edit directory node';
           // disable button radio button
           this.typeDisabled = true;
           break;
         case 1:
           // On behalf of the menu, there are only buttons under the menu
           this.dialogTitle = 'Edit menu node';
           // disable menu type radio button
           this.typeDisabled = true;
           break;
         case 2:
           // On behalf of the menu, there are only buttons under the menu
           this.dialogTitle = 'Edit button node';
           // disable menu type radio button
           this.typeDisabled = true;
           break;
       }
     },
     onOpen() {<!-- -->},
     onClose() {<!-- -->
       this.$refs['menuForm'].resetFields()
     },
     close() {<!-- -->
       // Restore form submit button usage
       this. saveBtnDisabled = false
       this.dialogVisible = false
     },
     // Confirm button in the popup page
     handelConfirm() {<!-- -->
       this.$refs['menuForm'].validate(valid => {<!-- -->
         if (!valid) return;
         // Call backend new menu and modify menu methods
         // Prevent form from being submitted repeatedly
         this. saveBtnDisabled = true
         if (!this.menu.id) {<!-- -->
           // Indicates the new menu
           this.addMenuData(this.menu)
         } else {<!-- -->
           // Represents the update menu
           this. updateMenuData(this. menu)
         }
         this. close()
       })
     }
   },
   // Get data when the page loads
   created () {<!-- -->
     // Get menu tree list
     this. getMenuTree()
   }
 }
</script>

<style scoped>
 /* divider style */
 .el-divider --horizontal {<!-- -->
   display: block;
   height: 1px;
   width: 100%;
   margin: 10px 0;
 }
</style>

3. Configure the routing of your menu management page

Add menu management page routing information in the index.js main page framework routing under router

{<!-- -->
/* Menu management route */
path: '/sysMenu',
name: 'SysMenu',
component: () => import('../views/menu/SysMenu')
}

4. Icon content

iconOptions: [
 {<!-- -->
   class: "el-icon-s-tools",
 },
 {<!-- -->
   class: "el-icon-s-custom",
 },
 {<!-- -->
   class: "el-icon-setting",
 },
 {<!-- -->
   class: "el-icon-user-solid",
 },
 {<!-- -->
   class: "el-icon-s-help",
 },
 {<!-- -->
   class: "el-icon-phone",
 },
 {<!-- -->
   class: "el-icon-s-unfold",
 },
 {<!-- -->
   class: "el-icon-s-operation",
 },
 {<!-- -->
   class: "el-icon-more-outline",
 },
 {<!-- -->
   class: "el-icon-s-check",
 },
 {<!-- -->
   class: "el-icon-tickets",
 },
 {<!-- -->
   class: "el-icon-s-goods",
 },
 {<!-- -->
   class: "el-icon-document-remove",
 },
 {<!-- -->
   class: "el-icon-warning",
 },
 {<!-- -->
   class: "el-icon-warning-outline",
 },
 {<!-- -->
   class: "el-icon-question",
 },
 {<!-- -->
   class: "el-icon-info",
 }]

Icon drop-down box

<el-select v-model="menu.icon" placeholder="Please select" filterable clearable :style="{width: '100%'}">
   <el-option v-for="(item, index) in iconOptions" :key="index" :label="item.class" :value="item.class"
                      :disabled="item.disabled">
       <span style="float: left;">
           <i :class="item. class"></i>
       </span>
       <span style="padding-left: 6px;">{<!-- -->{<!-- --> item.class }}</span>
   </el-option>
</el-select>

5. Menu management api

/* import axios instance */
import request from '../../utils/request'

/* API request function related to menu management */
const api_path = '/admin/system/menu'

export default {<!-- -->
 /*
  Get the background menu tree list
  */
 getMenuTree() {<!-- -->
   return request({<!-- -->
     method: 'get',
     url: `${<!-- -->api_path}/getMenuTree`
   })
 },

 /**
  * Add menu data
  * @param menuVo menu form content
  */
 addMenuData(menuVo) {<!-- -->
   return request({<!-- -->
     method: 'post',
     url: `${<!-- -->api_path}/addMenuData`,
     data: menuVo
   })
 },

 /**
  * Update menu data
  * @param menuVo menu form content
  */
 updateMenuData(menuVo) {<!-- -->
   return request({<!-- -->
     method: 'put',
     url: `${<!-- -->api_path}/updateMenuData`,
     data: menuVo
   })
 },

 /**
  * Delete menu data
  * @param id menu id
  */
 deleteMenuData(id) {<!-- -->
   return request({<!-- -->
     method: 'delete',
     url: `${<!-- -->api_path}/remove/${<!-- -->id}`,
   })
 }
}

final effect