| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- <script lang="ts" setup>
- import { defaultProps, handleTree } from '@/utils/tree'
- import { ElTree } from 'element-plus'
- import * as DeptApi from '@/api/system/dept'
- import { Search } from '@element-plus/icons-vue'
- const props = defineProps({
- deptId: {
- type: Number,
- required: true
- },
- modelValue: {
- type: Number,
- default: undefined
- },
- topId: {
- type: Number,
- required: true
- },
- title: {
- type: String,
- default: '部门'
- },
- initSelect: {
- type: Boolean,
- default: true
- },
- showTitle: {
- type: Boolean,
- default: true
- }
- })
- const emits = defineEmits(['update:modelValue', 'node-click'])
- const deptName = ref('')
- const deptList = ref<Tree[]>([])
- const treeRef = ref<InstanceType<typeof ElTree>>()
- const expandedKeys = ref<number[]>([])
- const sortTreeBySort = (treeNodes: Tree[]) => {
- if (!treeNodes || !Array.isArray(treeNodes)) return treeNodes
- const sortedNodes = [...treeNodes].sort((a, b) => {
- const sortA = a.sort != null ? a.sort : 999999
- const sortB = b.sort != null ? b.sort : 999999
- return sortA - sortB
- })
- sortedNodes.forEach((node) => {
- if (node.children && Array.isArray(node.children)) {
- node.children = sortTreeBySort(node.children)
- }
- })
- return sortedNodes
- }
- const loadTree = async () => {
- try {
- let id = props.deptId
- if (id !== props.topId) {
- const depts = await DeptApi.specifiedSimpleDepts(props.topId)
- const self = depts.find((item) => item.id === props.deptId)
- if (depts.length && !self) {
- id = props.topId
- }
- }
- if (props.initSelect) {
- emits('update:modelValue', id)
- }
- const res = await DeptApi.specifiedSimpleDepts(id)
- deptList.value = sortTreeBySort(handleTree(res))
- // 加载完成后,如果有选中值,尝试高亮并展开
- nextTick(() => {
- if (props.modelValue && treeRef.value) {
- treeRef.value.setCurrentKey(props.modelValue)
- expandedKeys.value = [props.modelValue] // 默认展开选中的节点
- } else if (deptList.value.length > 0) {
- // 默认展开第一级
- expandedKeys.value = deptList.value.map((item) => item.id)
- }
- })
- } catch (error) {
- console.error('加载部门树失败:', error)
- }
- }
- const handleNodeClick = (data: Tree) => {
- // 1. 更新 v-model
- emits('update:modelValue', data.id)
- // 2. 抛出点击事件供父组件其他用途
- emits('node-click', data)
- }
- /** 筛选节点逻辑 */
- const filterNode = (value: string, data: Tree) => {
- if (!value) return true
- return data.name.includes(value)
- }
- /** 监听输入框进行过滤 */
- watch(deptName, (val) => {
- treeRef.value?.filter(val)
- })
- watch(
- () => props.deptId,
- (newVal, oldVal) => {
- if (newVal !== oldVal) {
- loadTree()
- }
- }
- )
- watch(
- () => props.modelValue,
- (newVal) => {
- if (newVal && treeRef.value) {
- // 设置高亮
- treeRef.value.setCurrentKey(newVal)
- // 自动展开该节点 (将新ID加入展开数组)
- if (!expandedKeys.value.includes(newVal)) {
- expandedKeys.value.push(newVal)
- }
- }
- },
- { immediate: true }
- )
- /** 初始化 */
- onMounted(() => {
- console.log('props :>> ', props)
- loadTree()
- })
- </script>
- <template>
- <div class="gap-4 flex flex-col h-full">
- <h1 v-if="showTitle" class="text-lg font-medium">{{ props.title }}</h1>
- <el-input
- v-model="deptName"
- size="default"
- placeholder="请输入部门名称"
- clearable
- :prefix-icon="Search"
- />
- <div class="flex-1 relative">
- <el-auto-resizer class="absolute">
- <template #default="{ height }">
- <el-scrollbar :style="{ height: `${height}px` }">
- <el-tree
- ref="treeRef"
- :data="deptList"
- :props="defaultProps"
- :expand-on-click-node="false"
- :filter-node-method="filterNode"
- node-key="id"
- highlight-current
- :default-expanded-keys="expandedKeys"
- @node-click="handleNodeClick"
- />
- </el-scrollbar>
- </template>
- </el-auto-resizer>
- </div>
- </div>
- </template>
|