index.vue 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. <template>
  2. <div class="pageWrap">
  3. <el-aside v-loading="menuLoading" width="300px" style="background: #fff; margin-right: 10px">
  4. <el-container style="height: 100%">
  5. <header style="padding: 13px 15px; border-bottom: 1px solid var(--el-border-color-light)">
  6. <el-input v-model="menuFilterText" placeholder="输入关键字进行过滤" clearable></el-input>
  7. </header>
  8. <el-main class="nopadding">
  9. <el-tree
  10. ref="menuRef"
  11. class="menu-tree"
  12. node-key="id"
  13. :data="menuList"
  14. :props="menuProps"
  15. highlight-current
  16. :expand-on-click-node="false"
  17. check-strictly
  18. show-checkbox
  19. :filter-node-method="menuFilterNode"
  20. @node-click="menuClick"
  21. >
  22. <template #default="{ node, data }">
  23. <span class="custom-tree-node el-tree-node__label">
  24. <span class="label">
  25. {{ node.label }}
  26. </span>
  27. <span class="do">
  28. <el-icon @click.stop="add(node, data)"><Plus /></el-icon>
  29. </span>
  30. </span>
  31. </template>
  32. </el-tree>
  33. </el-main>
  34. <el-footer style="height: 51px">
  35. <el-button :icon="Refresh" @click="getMenu()"></el-button>
  36. <el-button type="primary" :icon="Plus" @click="add()"></el-button>
  37. <el-button type="danger" plain :icon="Delete" @click="delMenu"></el-button>
  38. </el-footer>
  39. </el-container>
  40. </el-aside>
  41. <el-container style="background: #fff" class="container-bg">
  42. <el-main ref="mainRef" class="nopadding" style="padding: 20px">
  43. <save ref="saveRef" :menu="menuList"></save>
  44. </el-main>
  45. </el-container>
  46. </div>
  47. </template>
  48. <script setup name="menu">
  49. import Save from './save'
  50. import resource from '@/api/system/resource'
  51. import { Plus, Refresh, Delete } from '@element-plus/icons-vue'
  52. import { onMounted, ref } from 'vue'
  53. import { ElMessage, ElMessageBox } from 'element-plus'
  54. const menuLoading = ref(false)
  55. const menuList = ref([])
  56. const menuFilterText = ref('')
  57. const mainRef = ref(null) // 右侧的大容器
  58. const saveRef = ref(null) // 右侧的表单
  59. const menuRef = ref(null) // 左侧菜单树
  60. const menuProps = {
  61. label: data => {
  62. return data.title
  63. }
  64. }
  65. let newMenuIndex = 1
  66. // methods
  67. const getMenu = async () => {
  68. //加载树数据
  69. menuLoading.value = true
  70. try {
  71. let data = await resource.resourceListTreeApi()
  72. menuLoading.value = false
  73. menuList.value = data
  74. } catch (e) {
  75. console.log(e)
  76. menuLoading.value = false
  77. }
  78. }
  79. const menuClick = (data, node) => {
  80. //树点击
  81. let pid = +node.level === 1 ? undefined : node.parent.data.id
  82. saveRef.value.setData(data, pid)
  83. mainRef.value.$el.scrollTop = 0
  84. }
  85. const menuFilterNode = (value, data) => {
  86. //树过滤
  87. if (!value) return true
  88. let targetText = data.title
  89. return targetText.indexOf(value) !== -1
  90. }
  91. const add = async (node, data) => {
  92. let newMenuName = '未命名' + newMenuIndex++
  93. let newMenuData = {
  94. pid: '0',
  95. parentName: '',
  96. path: '',
  97. component: '',
  98. title: newMenuName,
  99. type: '0'
  100. }
  101. if (data) {
  102. newMenuData.pid = data.id
  103. newMenuData.parentName = data.title
  104. }
  105. menuLoading.value = true
  106. try {
  107. let res = await resource.resourceAddOrEditSaveApi(newMenuData)
  108. menuLoading.value = false
  109. newMenuData.id = res
  110. menuRef.value.append(newMenuData, node)
  111. menuRef.value.setCurrentKey(newMenuData.id)
  112. let pid = node ? node.data.id : ''
  113. saveRef.value.setData(newMenuData, pid)
  114. } catch (e) {
  115. menuLoading.value = false
  116. console.log(e, '新增菜单失败')
  117. }
  118. }
  119. const delMenu = () => {
  120. //删除菜单
  121. let checkedNodes = menuRef.value.getCheckedNodes()
  122. if (!checkedNodes.length) {
  123. return ElMessage.warning('请选择需要删除的项')
  124. }
  125. ElMessageBox.confirm(`确认删除已选择的菜单吗?`, '提示', {
  126. type: 'warning',
  127. confirmButtonText: '删除',
  128. confirmButtonClass: 'el-button--danger'
  129. })
  130. .then(async () => {
  131. menuLoading.value = true
  132. let reqData = checkedNodes.map(item => item.id)
  133. try {
  134. await resource.resourceDeleteApi(reqData)
  135. checkedNodes.forEach(item => {
  136. let node = menuRef.value.getNode(item)
  137. if (node.isCurrent) {
  138. saveRef.value.setData({})
  139. }
  140. menuRef.value.remove(item)
  141. })
  142. menuLoading.value = false
  143. } catch (e) {
  144. menuLoading.value = false
  145. console.log('删除左侧菜单失败', e)
  146. }
  147. })
  148. .catch(() => {})
  149. }
  150. onMounted(() => {
  151. getMenu()
  152. })
  153. </script>
  154. <style scoped lang="scss">
  155. .pageWrap {
  156. flex: 1;
  157. display: flex;
  158. height: 100%;
  159. //background: #fff;
  160. }
  161. // 角色的树结构样式
  162. :deep(.menu-tree) {
  163. .el-tree-node__content {
  164. height: 36px;
  165. }
  166. .el-tree-node__content .el-tree-node__label .icon {
  167. margin-right: 5px;
  168. }
  169. }
  170. .nopadding {
  171. padding: 0px;
  172. }
  173. .content-warp {
  174. flex: 1;
  175. //width: calc(100% - 250px);
  176. width: calc(100% - 210px);
  177. }
  178. .container-bg {
  179. background: var(--el-bg-color-overlay);
  180. }
  181. .custom-tree-node {
  182. display: flex;
  183. flex: 1;
  184. align-items: center;
  185. justify-content: space-between;
  186. font-size: 14px;
  187. padding-right: 24px;
  188. height: 100%;
  189. }
  190. .custom-tree-node .label {
  191. display: flex;
  192. align-items: center;
  193. height: 100%;
  194. }
  195. .custom-tree-node .label .el-tag {
  196. margin-left: 5px;
  197. }
  198. .custom-tree-node .do {
  199. display: none;
  200. }
  201. .custom-tree-node .do i {
  202. margin-left: 5px;
  203. color: #999;
  204. padding: 5px;
  205. font-size: 24px;
  206. }
  207. .custom-tree-node .do i:hover {
  208. color: #333;
  209. }
  210. .custom-tree-node:hover .do {
  211. display: inline-block;
  212. }
  213. </style>