123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- <script setup lang="ts">
- import { useRenderIcon } from "@/components/ReIcon/src/hooks";
- import { ref, computed, watch, getCurrentInstance } from "vue";
- import Dept from "@iconify-icons/ri/git-branch-line";
- // import Reset from "@iconify-icons/ri/restart-line";
- import More2Fill from "@iconify-icons/ri/more-2-fill";
- import OfficeBuilding from "@iconify-icons/ep/office-building";
- import LocationCompany from "@iconify-icons/ep/add-location";
- import ExpandIcon from "./svg/expand.svg?component";
- import UnExpandIcon from "./svg/unexpand.svg?component";
- interface Tree {
- id: number;
- name: string;
- highlight?: boolean;
- children?: Tree[];
- }
- const props = defineProps({
- treeLoading: Boolean,
- treeData: Array
- });
- const emit = defineEmits(["tree-select"]);
- const treeRef = ref();
- const isExpand = ref(true);
- const searchValue = ref("");
- const highlightMap = ref({});
- const { proxy } = getCurrentInstance();
- const defaultProps = {
- children: "children",
- label: "name"
- };
- const buttonClass = computed(() => {
- return [
- "!h-[20px]",
- "reset-margin",
- "!text-gray-500",
- "dark:!text-white",
- "dark:hover:!text-primary"
- ];
- });
- const filterNode = (value: string, data: Tree) => {
- if (!value) return true;
- return data.name.includes(value);
- };
- function nodeClick(value) {
- const nodeId = value.$treeNodeId;
- highlightMap.value[nodeId] = highlightMap.value[nodeId]?.highlight
- ? Object.assign({ id: nodeId }, highlightMap.value[nodeId], {
- highlight: false
- })
- : Object.assign({ id: nodeId }, highlightMap.value[nodeId], {
- highlight: true
- });
- Object.values(highlightMap.value).forEach((v: Tree) => {
- if (v.id !== nodeId) {
- v.highlight = false;
- }
- });
- emit(
- "tree-select",
- highlightMap.value[nodeId]?.highlight
- ? Object.assign({ ...value, selected: true })
- : Object.assign({ ...value, selected: false })
- );
- }
- function toggleRowExpansionAll(status) {
- isExpand.value = status;
- const nodes = (proxy.$refs["treeRef"] as any).store._getAllNodes();
- for (let i = 0; i < nodes.length; i++) {
- nodes[i].expanded = status;
- }
- }
- /** 重置部门树状态(选中状态、搜索框值、树初始化) */
- function onTreeReset() {
- highlightMap.value = {};
- searchValue.value = "";
- toggleRowExpansionAll(true);
- }
- watch(searchValue, val => {
- treeRef.value!.filter(val);
- });
- defineExpose({ onTreeReset });
- </script>
- <template>
- <div
- v-loading="props.treeLoading"
- class="h-full bg-bg_color overflow-auto"
- :style="{ minHeight: `calc(100vh - 145px)` }"
- >
- <div class="flex items-center h-[34px]">
- <el-input
- v-model="searchValue"
- class="ml-2"
- size="small"
- placeholder="请输入部门名称"
- clearable
- >
- <template #suffix>
- <el-icon class="el-input__icon">
- <IconifyIconOffline
- v-show="searchValue.length === 0"
- icon="ri:search-line"
- />
- </el-icon>
- </template>
- </el-input>
- <el-dropdown :hide-on-click="false">
- <IconifyIconOffline
- class="w-[28px] cursor-pointer"
- width="18px"
- :icon="More2Fill"
- />
- <template #dropdown>
- <el-dropdown-menu>
- <el-dropdown-item>
- <el-button
- :class="buttonClass"
- link
- type="primary"
- :icon="useRenderIcon(isExpand ? ExpandIcon : UnExpandIcon)"
- @click="toggleRowExpansionAll(isExpand ? false : true)"
- >
- {{ isExpand ? "折叠全部" : "展开全部" }}
- </el-button>
- </el-dropdown-item>
- <!-- <el-dropdown-item>
- <el-button
- :class="buttonClass"
- link
- type="primary"
- :icon="useRenderIcon(Reset)"
- @click="onTreeReset"
- >
- 重置状态
- </el-button>
- </el-dropdown-item> -->
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- </div>
- <el-divider />
- <el-tree
- ref="treeRef"
- :data="props.treeData"
- node-key="id"
- size="small"
- :props="defaultProps"
- default-expand-all
- :expand-on-click-node="false"
- :filter-node-method="filterNode"
- @node-click="nodeClick"
- >
- <template #default="{ node, data }">
- <span
- :class="[
- 'pl-1',
- 'pr-1',
- 'rounded',
- 'flex',
- 'items-center',
- 'select-none',
- 'hover:text-primary',
- searchValue.trim().length > 0 &&
- node.label.includes(searchValue) &&
- 'text-red-500',
- highlightMap[node.id]?.highlight ? 'dark:text-primary' : ''
- ]"
- :style="{
- color: highlightMap[node.id]?.highlight
- ? 'var(--el-color-primary)'
- : '',
- background: highlightMap[node.id]?.highlight
- ? 'var(--el-color-primary-light-7)'
- : 'transparent'
- }"
- >
- <IconifyIconOffline
- :icon="
- data.type === 1
- ? OfficeBuilding
- : data.type === 2
- ? LocationCompany
- : Dept
- "
- />
- {{ node.label }}
- </span>
- </template>
- </el-tree>
- </div>
- </template>
- <style lang="scss" scoped>
- :deep(.el-divider) {
- margin: 0;
- }
- :deep(.el-tree) {
- --el-tree-node-hover-bg-color: transparent;
- }
- </style>
|