orgTree.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. <template>
  2. <el-card class="box-card" shadow="hover" style="height: 100%" body-style="height:100%; overflow:auto">
  3. <template #header>
  4. <div class="card-header">
  5. <div class="tree-h-flex" v-if="!props.tenantId">
  6. <el-select v-if="userStore.userInfos.accountType == 999" v-model="state.tenantId" @change="initTreeData()" placeholder="请选择租户" class="w100 mb10">
  7. <el-option :value="item.value" :label="`${item.label} (${item.host})`" v-for="(item, index) in state.tenantList" :key="index" />
  8. </el-select>
  9. </div>
  10. <div class="tree-h-flex">
  11. <div class="tree-h-left">
  12. <el-input :prefix-icon="Search" v-model="filterText" placeholder="机构名称" />
  13. </div>
  14. <div class="tree-h-right">
  15. <el-dropdown @command="handleCommand">
  16. <el-button style="margin-left: 8px; width: 34px">
  17. <el-icon class="el-icon--center">
  18. <more-filled />
  19. </el-icon>
  20. </el-button>
  21. <template #dropdown>
  22. <el-dropdown-menu>
  23. <el-dropdown-item command="expandAll">全部展开</el-dropdown-item>
  24. <el-dropdown-item command="collapseAll">全部折叠</el-dropdown-item>
  25. <el-dropdown-item command="rootNode">根节点</el-dropdown-item>
  26. <el-dropdown-item command="refresh">刷新</el-dropdown-item>
  27. </el-dropdown-menu>
  28. </template>
  29. </el-dropdown>
  30. </div>
  31. </div>
  32. </div>
  33. </template>
  34. <div style="margin-bottom: 45px" v-loading="state.loading">
  35. <el-tree
  36. ref="treeRef"
  37. class="filter-tree"
  38. :data="state.orgData"
  39. node-key="id"
  40. :props="{ children: 'children', label: 'name' }"
  41. :filter-node-method="filterNode"
  42. @node-click="nodeClick"
  43. :show-checkbox="state.isShowCheckbox"
  44. :default-checked-keys="state.ownOrgData"
  45. highlight-current
  46. check-strictly
  47. >
  48. <template #default="{ node }">
  49. <el-icon v-if="node.level == 1" size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-School /></el-icon>
  50. <el-icon v-else-if="node.level == 2" size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-PriceTag /></el-icon>
  51. <el-icon v-else size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-CollectionTag /></el-icon>
  52. {{ node.label }}
  53. </template>
  54. </el-tree>
  55. </div>
  56. </el-card>
  57. </template>
  58. <script lang="ts" setup>
  59. import { onMounted, reactive, ref, watch } from 'vue';
  60. import type { ElTree } from 'element-plus';
  61. import { Search, MoreFilled } from '@element-plus/icons-vue';
  62. import { getAPI } from '/@/utils/axios-utils';
  63. import {SysOrgApi, SysTenantApi} from '/@/api-services/api';
  64. import { OrgTreeOutput, SysOrg } from '/@/api-services/models';
  65. import { useUserInfo } from "/@/stores/userInfo";
  66. const props = defineProps({
  67. tenantId: Number,
  68. });
  69. const userStore = useUserInfo();
  70. const filterText = ref('');
  71. const treeRef = ref<InstanceType<typeof ElTree>>();
  72. const state = reactive({
  73. loading: false,
  74. tenantList: [] as Array<any>,
  75. tenantId: props.tenantId as number,
  76. orgData: [] as Array<OrgTreeOutput>,
  77. isShowCheckbox: false,
  78. ownOrgData: [] as Array<OrgTreeOutput>,
  79. });
  80. onMounted( async () => {
  81. if (userStore.userInfos.accountType == 999) {
  82. state.tenantList = await getAPI(SysTenantApi).apiSysTenantListGet().then(res => res.data.result ?? []);
  83. }
  84. initTreeData();
  85. });
  86. watch(filterText, (val) => {
  87. treeRef.value!.filter(val);
  88. });
  89. const initTreeData = async () => {
  90. state.loading = true;
  91. const res = await getAPI(SysOrgApi).apiSysOrgTreeGet(0, undefined, undefined, undefined, state.tenantId);
  92. state.orgData = res.data.result ?? [];
  93. state.loading = false;
  94. };
  95. // 设置默认选择
  96. const setCheckedKeys = (data: any) => {
  97. const isArray = Array.isArray(data);
  98. treeRef.value!.setCheckedKeys([]);
  99. if (!isArray) {
  100. treeRef.value!.setCurrentNode(data);
  101. nodeClick(data);
  102. }
  103. state.ownOrgData = isArray ? data : [data];
  104. state.isShowCheckbox = isArray;
  105. };
  106. // 获取已经选择
  107. const getCheckedKeys = () => {
  108. return treeRef.value!.getCheckedKeys();
  109. };
  110. const filterNode = (value: string, data: any) => {
  111. if (!value) return true;
  112. return data.name.includes(value);
  113. };
  114. const handleCommand = async (command: string | number | object) => {
  115. if ('expandAll' == command) {
  116. for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
  117. treeRef.value!.store._getAllNodes()[i].expanded = true;
  118. }
  119. } else if ('collapseAll' == command) {
  120. for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
  121. treeRef.value!.store._getAllNodes()[i].expanded = false;
  122. }
  123. } else if ('refresh' == command) {
  124. initTreeData();
  125. } else if ('rootNode' == command) {
  126. emits('node-click', { id: 0, name: '' });
  127. }
  128. };
  129. // 与父组件的交互逻辑
  130. const emits = defineEmits(['node-click']);
  131. const nodeClick = (node: any) => {
  132. emits('node-click', node);
  133. };
  134. // 导出对象
  135. defineExpose({ initTreeData, setCheckedKeys, getCheckedKeys });
  136. </script>
  137. <style lang="scss" scoped>
  138. .tree-h-flex {
  139. display: flex;
  140. }
  141. .tree-h-left {
  142. flex: 1;
  143. width: 100%;
  144. }
  145. .tree-h-right {
  146. width: 42px;
  147. min-width: 42px;
  148. }
  149. </style>