|
@@ -0,0 +1,227 @@
|
|
|
+<template>
|
|
|
+ <div class="pageWrap">
|
|
|
+ <el-aside v-loading="menuLoading" width="300px" style="background: #fff; margin-right: 10px">
|
|
|
+ <el-container style="height: 100%">
|
|
|
+ <el-header>
|
|
|
+ <el-input v-model="menuFilterText" placeholder="输入关键字进行过滤" clearable></el-input>
|
|
|
+ </el-header>
|
|
|
+ <el-main class="nopadding">
|
|
|
+ <el-tree
|
|
|
+ ref="menuRef"
|
|
|
+ class="menu-tree"
|
|
|
+ node-key="id"
|
|
|
+ :data="menuList"
|
|
|
+ :props="menuProps"
|
|
|
+ highlight-current
|
|
|
+ :expand-on-click-node="false"
|
|
|
+ check-strictly
|
|
|
+ show-checkbox
|
|
|
+ :filter-node-method="menuFilterNode"
|
|
|
+ @node-click="menuClick"
|
|
|
+ >
|
|
|
+ <template #default="{ node, data }">
|
|
|
+ <span class="custom-tree-node el-tree-node__label">
|
|
|
+ <span class="label">
|
|
|
+ {{ node.label }}
|
|
|
+ </span>
|
|
|
+ <span class="do">
|
|
|
+ <el-icon @click.stop="add(node, data)"><Plus /></el-icon>
|
|
|
+ </span>
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </el-tree>
|
|
|
+ </el-main>
|
|
|
+ <el-footer style="height: 51px">
|
|
|
+ <el-button :icon="Refresh" @click="getMenu()"></el-button>
|
|
|
+ <el-button type="primary" :icon="Plus" @click="add()"></el-button>
|
|
|
+ <el-button type="danger" plain :icon="Delete" @click="delMenu"></el-button>
|
|
|
+ </el-footer>
|
|
|
+ </el-container>
|
|
|
+ </el-aside>
|
|
|
+ <el-container style="background: #fff" class="container-bg">
|
|
|
+ <el-main ref="mainRef" class="nopadding" style="padding: 20px">
|
|
|
+ <save ref="saveRef" :menu="menuList"></save>
|
|
|
+ </el-main>
|
|
|
+ </el-container>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import Save from './save'
|
|
|
+import resource from '@/api/system/resource'
|
|
|
+import { Plus, Refresh, Delete } from '@element-plus/icons-vue'
|
|
|
+import { onMounted, ref } from 'vue'
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
+
|
|
|
+const menuLoading = ref(false)
|
|
|
+const menuList = ref([])
|
|
|
+const menuFilterText = ref('')
|
|
|
+const mainRef = ref(null) // 右侧的大容器
|
|
|
+const saveRef = ref(null) // 右侧的表单
|
|
|
+const menuRef = ref(null) // 左侧菜单树
|
|
|
+const menuProps = {
|
|
|
+ label: data => {
|
|
|
+ return data.title
|
|
|
+ }
|
|
|
+}
|
|
|
+let newMenuIndex = 1
|
|
|
+
|
|
|
+// methods
|
|
|
+const getMenu = async () => {
|
|
|
+ //加载树数据
|
|
|
+ menuLoading.value = true
|
|
|
+ try {
|
|
|
+ let data = await resource.resourceListTreeApi()
|
|
|
+ menuLoading.value = false
|
|
|
+ menuList.value = data
|
|
|
+ } catch (e) {
|
|
|
+ console.log(e)
|
|
|
+ menuLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const menuClick = (data, node) => {
|
|
|
+ //树点击
|
|
|
+ let pid = +node.level === 1 ? undefined : node.parent.data.id
|
|
|
+ saveRef.value.setData(data, pid)
|
|
|
+ mainRef.value.$el.scrollTop = 0
|
|
|
+}
|
|
|
+
|
|
|
+const menuFilterNode = (value, data) => {
|
|
|
+ //树过滤
|
|
|
+ if (!value) return true
|
|
|
+ let targetText = data.title
|
|
|
+ return targetText.indexOf(value) !== -1
|
|
|
+}
|
|
|
+
|
|
|
+const add = async (node, data) => {
|
|
|
+ let newMenuName = '未命名' + newMenuIndex++
|
|
|
+ let newMenuData = {
|
|
|
+ pid: data ? data.id : '0',
|
|
|
+ name: newMenuName,
|
|
|
+ path: '',
|
|
|
+ component: '',
|
|
|
+ title: newMenuName,
|
|
|
+ type: '0'
|
|
|
+ }
|
|
|
+ menuLoading.value = true
|
|
|
+ try {
|
|
|
+ let res = await resource.resourceAddOrEditSaveApi(newMenuData)
|
|
|
+ menuLoading.value = false
|
|
|
+ newMenuData.id = res.data
|
|
|
+ menuRef.value.append(newMenuData, node)
|
|
|
+ menuRef.value.setCurrentKey(newMenuData.id)
|
|
|
+ let pid = node ? node.data.id : ''
|
|
|
+ saveRef.value.setData(newMenuData, pid)
|
|
|
+ } catch (e) {
|
|
|
+ menuLoading.value = false
|
|
|
+ console.log(e, '新增菜单失败')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const delMenu = () => {
|
|
|
+ //删除菜单
|
|
|
+ let checkedNodes = menuRef.value.getCheckedNodes()
|
|
|
+ if (!checkedNodes.length) {
|
|
|
+ return ElMessage.warning('请选择需要删除的项')
|
|
|
+ }
|
|
|
+ ElMessageBox.confirm(`确认删除已选择的菜单吗?`, '提示', {
|
|
|
+ type: 'warning',
|
|
|
+ confirmButtonText: '删除',
|
|
|
+ confirmButtonClass: 'el-button--danger'
|
|
|
+ })
|
|
|
+ .then(async () => {
|
|
|
+ menuLoading.value = true
|
|
|
+ let reqData = checkedNodes.map(item => item.id)
|
|
|
+ try {
|
|
|
+ await resource.resourceDeleteApi(reqData)
|
|
|
+ checkedNodes.forEach(item => {
|
|
|
+ let node = menuRef.value.getNode(item)
|
|
|
+ if (node.isCurrent) {
|
|
|
+ saveRef.value.setData({})
|
|
|
+ }
|
|
|
+ menuRef.value.remove(item)
|
|
|
+ })
|
|
|
+ menuLoading.value = false
|
|
|
+ } catch (e) {
|
|
|
+ menuLoading.value = false
|
|
|
+ console.log('删除左侧菜单失败', e)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(() => {})
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ getMenu()
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.pageWrap {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ height: 100%;
|
|
|
+ //background: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+// 角色的树结构样式
|
|
|
+:deep(.menu-tree) {
|
|
|
+ .el-tree-node__content {
|
|
|
+ height: 36px;
|
|
|
+ }
|
|
|
+ .el-tree-node__content .el-tree-node__label .icon {
|
|
|
+ margin-right: 5px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.nopadding {
|
|
|
+ padding: 0px;
|
|
|
+}
|
|
|
+
|
|
|
+.content-warp {
|
|
|
+ flex: 1;
|
|
|
+ //width: calc(100% - 250px);
|
|
|
+ width: calc(100% - 210px);
|
|
|
+}
|
|
|
+.container-bg {
|
|
|
+ background: var(--el-bg-color-overlay);
|
|
|
+}
|
|
|
+.custom-tree-node {
|
|
|
+ display: flex;
|
|
|
+ flex: 1;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ font-size: 14px;
|
|
|
+ padding-right: 24px;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.custom-tree-node .label {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.custom-tree-node .label .el-tag {
|
|
|
+ margin-left: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.custom-tree-node .do {
|
|
|
+ display: none;
|
|
|
+}
|
|
|
+
|
|
|
+.custom-tree-node .do i {
|
|
|
+ margin-left: 5px;
|
|
|
+ color: #999;
|
|
|
+ padding: 5px;
|
|
|
+ font-size: 24px;
|
|
|
+}
|
|
|
+
|
|
|
+.custom-tree-node .do i:hover {
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.custom-tree-node:hover .do {
|
|
|
+ display: inline-block;
|
|
|
+}
|
|
|
+</style>
|