| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- <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
- // 1. 校验 ID 范围逻辑 (保持原有逻辑:确保 deptId 在 topId 范围内)
- 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
- }
- }
- // 2. 获取最终 ID 对应的部门列表
- const res = await DeptApi.specifiedSimpleDepts(id)
- // 3. 处理 modelValue 的赋值逻辑 (关键修改点)
- if (props.initSelect) {
- // 检查传入的 modelValue 是否存在于当前加载的树数据中
- const isModelValueValid = props.modelValue && res.some((item) => item.id === props.modelValue)
- if (!isModelValueValid) {
- emits('update:modelValue', id)
- }
- }
- // 4. 生成树结构
- deptList.value = sortTreeBySort(handleTree(res))
- // 5. 界面交互:高亮并展开
- nextTick(() => {
- // 优先使用 props.modelValue (如果刚才触发了 update,父组件可能还没传回来,所以这里取 props.modelValue 或者 id)
- // 但为了稳妥,我们再次检查逻辑
- const targetKey = props.modelValue ? props.modelValue : props.initSelect ? id : null
- if (targetKey && treeRef.value) {
- treeRef.value.setCurrentKey(targetKey)
- // 确保该节点被展开
- if (!expandedKeys.value.includes(targetKey)) {
- expandedKeys.value.push(targetKey)
- }
- } else if (deptList.value.length > 0) {
- // 如果没有选中项,默认展开第一级
- expandedKeys.value = deptList.value.map((item) => item.id)
- }
- })
- } catch (error) {
- console.error('加载部门树失败:', error)
- }
- }
- const handleNodeClick = (data: Tree) => {
- emits('update:modelValue', data.id)
- 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)
- if (!expandedKeys.value.includes(newVal)) {
- expandedKeys.value.push(newVal)
- }
- }
- },
- { immediate: true }
- )
- onMounted(() => {
- 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>
|