index.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <template>
  2. <div class="head-container">
  3. <div class="head-container-header">
  4. <div class="head-container-header-input">
  5. <el-input v-model="searchName" suffix-icon="search" :placeholder="placeholder" clearable @change="getdeptTree" />
  6. </div>
  7. <div class="head-container-header-dropdown" v-if="showExpand">
  8. <el-dropdown :hide-on-click="false">
  9. <el-icon style="transform: rotate(90deg)">
  10. <MoreFilled />
  11. </el-icon>
  12. <template #dropdown>
  13. <el-dropdown-menu>
  14. <el-dropdown-item>
  15. <el-button
  16. :class="buttonClass"
  17. link
  18. type="primary"
  19. :icon="isExpand ? 'expand' : 'fold'"
  20. @click="toggleRowExpansionAll(isExpand ? false : true)"
  21. >
  22. {{ isExpand ? '折叠' : '展开' }}
  23. </el-button>
  24. </el-dropdown-item>
  25. </el-dropdown-menu>
  26. </template>
  27. </el-dropdown>
  28. </div>
  29. </div>
  30. <el-tree
  31. class="mt20"
  32. :data="state.List"
  33. :props="props.props"
  34. :expand-on-click-node="false"
  35. ref="deptTreeRef"
  36. :loading="state.localLoading"
  37. node-key="id"
  38. highlight-current
  39. default-expand-all
  40. @node-click="handleNodeClick"
  41. >
  42. <template #default="{ node, data }" v-if="$slots.default">
  43. <slot :node="node" :data="data"></slot>
  44. </template>
  45. </el-tree>
  46. </div>
  47. </template>
  48. <script setup lang="ts" name="query-tree">
  49. import { useMessage } from '/@/hooks/message';
  50. const emit = defineEmits(['search', 'nodeClick']);
  51. const props = defineProps({
  52. /**
  53. * 树结构属性配置。
  54. *
  55. * @default { label: 'name', children: 'children', value: 'id' }
  56. */
  57. props: {
  58. type: Object,
  59. default: () => {
  60. return {
  61. label: 'name',
  62. children: 'children',
  63. value: 'id',
  64. };
  65. },
  66. },
  67. /**
  68. * 输入框占位符。
  69. *
  70. * @default ''
  71. */
  72. placeholder: {
  73. type: String,
  74. default: '',
  75. },
  76. /**
  77. * 是否显示加载中状态。
  78. *
  79. * @default false
  80. */
  81. loading: {
  82. type: Boolean,
  83. default: false,
  84. },
  85. /**
  86. * 查询函数,必须返回 Promise 类型数据。
  87. */
  88. query: {
  89. type: Function,
  90. required: true,
  91. },
  92. /**
  93. * 是否显示折叠控制
  94. */
  95. showExpand: {
  96. type: Boolean,
  97. default: false,
  98. },
  99. });
  100. const state = reactive({
  101. List: [], // 树形结构列表数据
  102. localLoading: props.loading, // 是否加载中
  103. });
  104. const deptTreeRef = ref(); // 部门树形结构组件实例引用
  105. const searchName = ref(); // 查询关键字
  106. const isExpand = ref(true); // 是否展开所有节点
  107. const buttonClass = computed(() => {
  108. return ['!h-[20px]', 'reset-margin', '!text-gray-500', 'dark:!text-white', 'dark:hover:!text-primary'];
  109. });
  110. /**
  111. * 点击树形结构节点触发的事件。
  112. *
  113. * @param item 被点击的节点数据。
  114. */
  115. const handleNodeClick = (item: any) => {
  116. emit('nodeClick', item);
  117. };
  118. /**
  119. * 获取部门树形结构数据。
  120. */
  121. const getdeptTree = () => {
  122. if (props.query instanceof Function) {
  123. state.localLoading = true;
  124. // 调用传入的查询函数,并将查询关键字作为参数传入
  125. const result = props.query(unref(searchName));
  126. // 如果查询结果为 Promise 类型,则进行后续处理
  127. if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') {
  128. result
  129. .then((r: any) => {
  130. state.List = r.data;
  131. })
  132. .catch((err) => {
  133. useMessage().error(err.msg);
  134. });
  135. }
  136. }
  137. };
  138. /**
  139. * 切换所有节点的展开/收起状态。
  140. *
  141. * @param status 目标状态,true 为展开,false 为收起。
  142. */
  143. const toggleRowExpansionAll = (status) => {
  144. isExpand.value = status;
  145. const nodes = deptTreeRef.value.store._getAllNodes();
  146. for (let i = 0; i < nodes.length; i++) {
  147. nodes[i].expanded = status;
  148. }
  149. };
  150. onMounted(() => {
  151. getdeptTree();
  152. });
  153. // 方便父组件调用刷新树方法
  154. defineExpose({
  155. getdeptTree,
  156. });
  157. </script>
  158. <style lang="scss" scoped>
  159. .head-container {
  160. &-header {
  161. display: flex;
  162. align-items: center;
  163. &-input {
  164. width: 90%;
  165. }
  166. &-dropdown {
  167. flex: 1;
  168. margin-left: 5%;
  169. }
  170. }
  171. }
  172. </style>