index.vue 5.1 KB

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