tree.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <script setup lang="ts">
  2. import { handleTree } from "@/utils/tree";
  3. import type { ElTree } from "element-plus";
  4. import { getDeptList } from "@/api/system";
  5. import { useRenderIcon } from "@/components/ReIcon/src/hooks";
  6. import { ref, watch, onMounted, getCurrentInstance } from "vue";
  7. interface Tree {
  8. id: number;
  9. name: string;
  10. highlight?: boolean;
  11. children?: Tree[];
  12. }
  13. const defaultProps = {
  14. children: "children",
  15. label: "name"
  16. };
  17. const treeData = ref([]);
  18. const searchValue = ref("");
  19. const { proxy } = getCurrentInstance();
  20. const treeRef = ref<InstanceType<typeof ElTree>>();
  21. const highlightMap = ref({});
  22. const filterNode = (value: string, data: Tree) => {
  23. if (!value) return true;
  24. return data.name.includes(value);
  25. };
  26. function nodeClick(value) {
  27. const nodeId = value.$treeNodeId;
  28. highlightMap.value[nodeId] = highlightMap.value[nodeId]?.highlight
  29. ? Object.assign({ id: nodeId }, highlightMap.value[nodeId], {
  30. highlight: false
  31. })
  32. : Object.assign({ id: nodeId }, highlightMap.value[nodeId], {
  33. highlight: true
  34. });
  35. Object.values(highlightMap.value).forEach((v: Tree) => {
  36. if (v.id !== nodeId) {
  37. v.highlight = false;
  38. }
  39. });
  40. }
  41. function toggleRowExpansionAll(status) {
  42. const nodes = (proxy.$refs["treeRef"] as any).store._getAllNodes();
  43. for (let i = 0; i < nodes.length; i++) {
  44. nodes[i].expanded = status;
  45. }
  46. }
  47. // 重置状态(选中状态、搜索框值、树初始化)
  48. function onReset() {
  49. highlightMap.value = {};
  50. searchValue.value = "";
  51. toggleRowExpansionAll(true);
  52. }
  53. watch(searchValue, val => {
  54. treeRef.value!.filter(val);
  55. });
  56. onMounted(async () => {
  57. const { data } = await getDeptList();
  58. treeData.value = handleTree(data as any);
  59. });
  60. </script>
  61. <template>
  62. <div class="max-w-[260px] h-full min-h-[780px] bg-bg_color">
  63. <div class="flex items-center h-[34px]">
  64. <p class="flex-1 ml-2 font-bold text-base truncate" title="部门列表">
  65. 部门列表
  66. </p>
  67. <el-input
  68. style="flex: 2"
  69. size="small"
  70. v-model="searchValue"
  71. placeholder="请输入部门名称"
  72. clearable
  73. >
  74. <template #suffix>
  75. <el-icon class="el-input__icon">
  76. <IconifyIconOffline
  77. v-show="searchValue.length === 0"
  78. icon="search"
  79. />
  80. </el-icon>
  81. </template>
  82. </el-input>
  83. <el-dropdown>
  84. <IconifyIconOffline
  85. class="w-[28px] cursor-pointer"
  86. width="18px"
  87. icon="more-vertical"
  88. />
  89. <template #dropdown>
  90. <el-dropdown-menu>
  91. <el-dropdown-item>
  92. <el-button
  93. class="reset-margin !h-[20px] !text-gray-500 dark:!text-white dark:hover:!text-primary"
  94. link
  95. type="primary"
  96. :icon="useRenderIcon('expand')"
  97. @click="toggleRowExpansionAll(true)"
  98. >
  99. 展开全部
  100. </el-button>
  101. </el-dropdown-item>
  102. <el-dropdown-item>
  103. <el-button
  104. class="reset-margin !h-[20px] !text-gray-500 dark:!text-white dark:hover:!text-primary"
  105. link
  106. type="primary"
  107. :icon="useRenderIcon('unExpand')"
  108. @click="toggleRowExpansionAll(false)"
  109. >
  110. 折叠全部
  111. </el-button>
  112. </el-dropdown-item>
  113. <el-dropdown-item>
  114. <el-button
  115. class="reset-margin !h-[20px] !text-gray-500 dark:!text-white dark:hover:!text-primary"
  116. link
  117. type="primary"
  118. :icon="useRenderIcon('reset')"
  119. @click="onReset"
  120. >
  121. 重置状态
  122. </el-button>
  123. </el-dropdown-item>
  124. </el-dropdown-menu>
  125. </template>
  126. </el-dropdown>
  127. </div>
  128. <el-divider />
  129. <el-tree
  130. ref="treeRef"
  131. :data="treeData"
  132. node-key="id"
  133. size="small"
  134. :props="defaultProps"
  135. default-expand-all
  136. :expand-on-click-node="false"
  137. :filter-node-method="filterNode"
  138. @node-click="nodeClick"
  139. >
  140. <template #default="{ node, data }">
  141. <span
  142. :class="[
  143. 'pl-1',
  144. 'pr-1',
  145. 'rounded',
  146. 'flex',
  147. 'items-center',
  148. 'select-none',
  149. searchValue.trim().length > 0 &&
  150. node.label.includes(searchValue) &&
  151. 'text-red-500',
  152. highlightMap[node.id]?.highlight ? 'dark:text-primary' : ''
  153. ]"
  154. :style="{
  155. background: highlightMap[node.id]?.highlight
  156. ? 'var(--el-color-primary-light-7)'
  157. : 'transparent'
  158. }"
  159. >
  160. <IconifyIconOffline
  161. :icon="
  162. data.type === 1
  163. ? 'office-building'
  164. : data.type === 2
  165. ? 'location-company'
  166. : 'dept'
  167. "
  168. />
  169. {{ node.label }}
  170. </span>
  171. </template>
  172. </el-tree>
  173. </div>
  174. </template>
  175. <style lang="scss" scoped>
  176. :deep(.el-divider) {
  177. margin: 0;
  178. }
  179. </style>