SysDeptTree.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <template>
  2. <div>
  3. <div v-if="showSearch" class="search-container">
  4. <a-input-search
  5. v-model:value="searchValue"
  6. :placeholder="$t('system.views.dept.search.deptName')"
  7. />
  8. </div>
  9. <Spin :spinning="loading">
  10. <a-tree
  11. v-bind="getAttrs"
  12. :expanded-keys="expandedKeys"
  13. :auto-expand-parent="autoExpandParent"
  14. @expand="onExpand"
  15. :field-names="fieldNames"
  16. :tree-data="computedTreeData"
  17. >
  18. <template #title="{ deptName }">
  19. <span v-if="!showSearch">
  20. {{ deptName }}
  21. </span>
  22. <span v-else-if="deptName.indexOf(searchValue) > -1">
  23. {{ deptName.substr(0, deptName.indexOf(searchValue)) }}
  24. <span style="color: #f50">{{ searchValue }}</span>
  25. {{ deptName.substr(deptName.indexOf(searchValue) + searchValue.length) }}
  26. </span>
  27. <span v-else>{{ deptName }}</span>
  28. </template>
  29. </a-tree>
  30. </Spin>
  31. </div>
  32. </template>
  33. <script lang="ts">
  34. import { computed, defineComponent, onMounted, reactive, ref, toRefs, unref, watch } from 'vue';
  35. import { Spin } from 'ant-design-vue';
  36. import { errorMessage } from '@/utils/message/SystemNotice';
  37. import TreeUtils from '@/utils/TreeUtils';
  38. import { propTypes } from '@/utils/propTypes';
  39. import { ApiServiceEnum, defHttp } from '@/utils/http/axios';
  40. const getParentKey = (key: number, treeData: Array<any>): number => {
  41. let parentKey;
  42. for (let i = 0; i < treeData.length; i++) {
  43. const node = treeData[i];
  44. if (node.children) {
  45. if (node.children.some((item: any) => item.deptId === key)) {
  46. parentKey = node.deptId;
  47. } else {
  48. const secondParentKey = getParentKey(key, node.children);
  49. if (secondParentKey) {
  50. parentKey = secondParentKey;
  51. }
  52. }
  53. }
  54. }
  55. return parentKey;
  56. };
  57. export default defineComponent({
  58. name: 'SysDeptTree',
  59. components: { Spin },
  60. props: {
  61. // 是否支持搜索
  62. showSearch: propTypes.bool.def(true),
  63. // 是否异步加载
  64. async: propTypes.bool,
  65. },
  66. setup(props, { attrs }) {
  67. const { async: asyncRef } = toRefs(props);
  68. const searchValue = ref<string>('');
  69. const dataList = ref<Array<any>>([]);
  70. const autoExpandParent = ref(false);
  71. const expandedKeys = ref<Array<number>>([]);
  72. const loading = ref(false);
  73. const getAttrs = computed(() => {
  74. const result: any = {
  75. ...attrs,
  76. };
  77. if (unref(asyncRef)) {
  78. result.loadData = handleAsyncLoadData;
  79. }
  80. return result;
  81. });
  82. /**
  83. * 树形数据计算属性
  84. */
  85. const computedTreeData = computed(() => {
  86. const async = unref(asyncRef);
  87. if (async) {
  88. return unref(dataList);
  89. }
  90. return (
  91. TreeUtils.convertList2Tree(
  92. dataList.value,
  93. (item) => item.deptId,
  94. (item) => item.parentId,
  95. 0,
  96. ) || []
  97. );
  98. });
  99. const onExpand = (keys: Array<number>) => {
  100. expandedKeys.value = keys;
  101. autoExpandParent.value = false;
  102. };
  103. /**
  104. * 所有数据
  105. */
  106. const getAllDataList = computed(() => {
  107. const result: any[] = [];
  108. if (unref(asyncRef)) {
  109. recursionAddChildren(unref(dataList), result);
  110. } else {
  111. result.push(...unref(dataList));
  112. }
  113. return result;
  114. });
  115. const recursionAddChildren = (list: any[], allData: any[]) => {
  116. list.forEach((item) => {
  117. allData.push(item);
  118. if (item.children && item.children.length > 0) {
  119. recursionAddChildren(item.children, allData);
  120. }
  121. });
  122. };
  123. watch(searchValue, (value) => {
  124. const allData = unref(getAllDataList);
  125. expandedKeys.value = allData
  126. .map(({ deptName, deptId }: any) => {
  127. if (deptName.indexOf(value) > -1) {
  128. return getParentKey(deptId, computedTreeData.value);
  129. }
  130. return null;
  131. })
  132. .filter((item, i, self) => item && self.indexOf(item) === i) as Array<number>;
  133. autoExpandParent.value = true;
  134. });
  135. const handleAsyncLoadData = async (treeNode) => {
  136. const dataRef = treeNode.dataRef;
  137. dataRef.children = await loadData(dataRef.deptId);
  138. dataList.value = [...unref(dataList)];
  139. };
  140. const reload = () => loadData();
  141. /**
  142. * 加载数据函数
  143. */
  144. const loadData = async (parentId?: number | null) => {
  145. const parameter: Recordable = {
  146. sortName: 'seq',
  147. sortOrder: 'asc',
  148. };
  149. if (parentId !== undefined && parentId !== null) {
  150. parameter.parameter = {
  151. 'parentId@=': parentId,
  152. };
  153. }
  154. try {
  155. loading.value = true;
  156. const result = (await defHttp.post({
  157. service: ApiServiceEnum.SMART_SYSTEM,
  158. url: 'sys/dept/list',
  159. data: parameter,
  160. })) as any[];
  161. result.forEach((item) => {
  162. if (item.hasChild !== true) {
  163. item.isLeaf = true;
  164. }
  165. });
  166. if (unref(asyncRef)) {
  167. if (parentId === 0) {
  168. dataList.value = result;
  169. } else {
  170. return result;
  171. }
  172. } else {
  173. dataList.value = result;
  174. }
  175. } catch (e) {
  176. errorMessage(e);
  177. } finally {
  178. loading.value = false;
  179. }
  180. };
  181. /**
  182. * 加载数据
  183. */
  184. onMounted(() => {
  185. let parentId: number | undefined = undefined;
  186. if (unref(asyncRef)) {
  187. parentId = 0;
  188. }
  189. loadData(parentId);
  190. });
  191. return {
  192. computedTreeData,
  193. autoExpandParent,
  194. onExpand,
  195. loadData,
  196. loading,
  197. expandedKeys,
  198. fieldNames: reactive({
  199. children: 'children',
  200. title: 'deptName',
  201. key: 'deptId',
  202. }),
  203. getAttrs,
  204. handleAsyncLoadData,
  205. searchValue,
  206. reload,
  207. };
  208. },
  209. });
  210. </script>
  211. <style scoped lang="less">
  212. .search-container {
  213. margin-bottom: 10px;
  214. }
  215. </style>