index.vue 5.3 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. {{ generateTitle(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. import { generateTitle } from '@/utils/i18n'
  55. const menuLoading = ref(false)
  56. const menuList = ref([])
  57. const menuFilterText = ref('')
  58. const mainRef = ref(null) // 右侧的大容器
  59. const saveRef = ref(null) // 右侧的表单
  60. const menuRef = ref(null) // 左侧菜单树
  61. const menuProps = {
  62. label: data => {
  63. return data.title
  64. }
  65. }
  66. let newMenuIndex = 1
  67. // methods
  68. const getMenu = async () => {
  69. //加载树数据
  70. menuLoading.value = true
  71. try {
  72. let data = await resource.resourceListTreeApi()
  73. menuLoading.value = false
  74. menuList.value = data
  75. } catch (e) {
  76. console.log(e)
  77. menuLoading.value = false
  78. }
  79. }
  80. const menuClick = (data, node) => {
  81. //树点击
  82. let pid = +node.level === 1 ? undefined : node.parent.data.id
  83. saveRef.value.setData(data, pid)
  84. mainRef.value.$el.scrollTop = 0
  85. }
  86. const menuFilterNode = (value, data) => {
  87. //树过滤
  88. if (!value) return true
  89. let targetText = data.title
  90. return targetText.indexOf(value) !== -1
  91. }
  92. const add = async (node, data) => {
  93. let newMenuName = '未命名' + newMenuIndex++
  94. let newMenuData = {
  95. pid: '0',
  96. parentName: '',
  97. path: '',
  98. component: '',
  99. title: newMenuName,
  100. type: '0'
  101. }
  102. if (data) {
  103. newMenuData.pid = data.id
  104. newMenuData.parentName = data.title
  105. }
  106. menuLoading.value = true
  107. try {
  108. let res = await resource.resourceAddOrEditSaveApi(newMenuData)
  109. menuLoading.value = false
  110. newMenuData.id = res
  111. menuRef.value.append(newMenuData, node)
  112. menuRef.value.setCurrentKey(newMenuData.id)
  113. let pid = node ? node.data.id : ''
  114. saveRef.value.setData(newMenuData, pid)
  115. } catch (e) {
  116. menuLoading.value = false
  117. console.log(e, '新增菜单失败')
  118. }
  119. }
  120. const delMenu = () => {
  121. //删除菜单
  122. let checkedNodes = menuRef.value.getCheckedNodes()
  123. if (!checkedNodes.length) {
  124. return ElMessage.warning('请选择需要删除的项')
  125. }
  126. ElMessageBox.confirm(`确认删除已选择的菜单吗?`, '提示', {
  127. type: 'warning',
  128. confirmButtonText: '删除',
  129. confirmButtonClass: 'el-button--danger'
  130. })
  131. .then(async () => {
  132. menuLoading.value = true
  133. let reqData = checkedNodes.map(item => item.id)
  134. try {
  135. await resource.resourceDeleteApi(reqData)
  136. checkedNodes.forEach(item => {
  137. let node = menuRef.value.getNode(item)
  138. if (node.isCurrent) {
  139. saveRef.value.setData({})
  140. }
  141. menuRef.value.remove(item)
  142. })
  143. menuLoading.value = false
  144. } catch (e) {
  145. menuLoading.value = false
  146. console.log('删除左侧菜单失败', e)
  147. }
  148. })
  149. .catch(() => {})
  150. }
  151. onMounted(() => {
  152. getMenu()
  153. })
  154. </script>
  155. <style scoped lang="scss">
  156. .pageWrap {
  157. flex: 1;
  158. display: flex;
  159. height: 100%;
  160. //background: #fff;
  161. }
  162. // 角色的树结构样式
  163. :deep(.menu-tree) {
  164. .el-tree-node__content {
  165. height: 36px;
  166. }
  167. .el-tree-node__content .el-tree-node__label .icon {
  168. margin-right: 5px;
  169. }
  170. }
  171. .nopadding {
  172. padding: 0px;
  173. }
  174. .content-warp {
  175. flex: 1;
  176. //width: calc(100% - 250px);
  177. width: calc(100% - 210px);
  178. }
  179. .container-bg {
  180. background: var(--el-bg-color-overlay);
  181. }
  182. .custom-tree-node {
  183. display: flex;
  184. flex: 1;
  185. align-items: center;
  186. justify-content: space-between;
  187. font-size: 14px;
  188. padding-right: 24px;
  189. height: 100%;
  190. }
  191. .custom-tree-node .label {
  192. display: flex;
  193. align-items: center;
  194. height: 100%;
  195. }
  196. .custom-tree-node .label .el-tag {
  197. margin-left: 5px;
  198. }
  199. .custom-tree-node .do {
  200. display: none;
  201. }
  202. .custom-tree-node .do i {
  203. margin-left: 5px;
  204. color: #999;
  205. padding: 5px;
  206. font-size: 24px;
  207. }
  208. .custom-tree-node .do i:hover {
  209. color: #333;
  210. }
  211. .custom-tree-node:hover .do {
  212. display: inline-block;
  213. }
  214. </style>