<script lang="ts" setup name="DeptSelect"> import { ElTreeSelect } from 'element-plus' import type { Ref } from 'vue' import type { DeptTreeNode } from '@/views/system/dept/dept-interface' import useDictStore from '@/store/modules/dict' import { getDeptTreeList } from '@/api/system/dept' import { judgeTree, toTreeList } from '@/utils/structure' const props = defineProps({ // 数据绑定 modelValue: { type: [Number, String], default: '', }, // 输入 placeholder: { type: String, default: '请选择父级', }, // 占位符 needTop: { type: Boolean, default: true, }, // 是否需要显示顶级 deptType: { type: String, default: '', }, // 显示的下拉组织类型:公司/部门 disabled: { type: Boolean, default: false, }, // 是否禁用 clearable: { type: Boolean, default: true, }, // 是否禁用 deptShow: { type: Boolean, default: false, }, // 是否强制显示部门下拉列表 size: { type: String, default: 'default', }, // 输入框大小 }) const emit = defineEmits(['update:modelValue']) // ------------定义props参数---------- const value = computed({ get() { return props.modelValue }, set(value) { emit('update:modelValue', value) }, }) // 接口获取到的原始dept数据 const originList: Ref<DeptTreeNode[]> = ref([]) // 转化后的dept列表 const deptTreeList: Ref<DeptTreeNode[]> = ref([]) // 是否是多级数据,多级采用tree-select组件 const multiData = ref(false) const defaultProps = { parent: 'pid', value: 'id', label: 'name', children: 'children', } const showDeptSelect = ref(true) const dictStore = useDictStore() function fetchPcode() { // 如果未指定需要展示的部门类型,获取全部部门 if (!props.deptType) { if (dictStore.allDeptList.length === 0) { // 如果store中未存储,从服务器上获取 getDeptTreeList().then((res) => { const list = res.data dictStore.setAllDeptList(list) refreshList(list) }) } else { refreshList(dictStore.allDeptList) } } else { // 如果指定了需要展示的部门类型 if (dictStore.companyList.length === 0) { // 如果store中未存储,从服务器上获取 getDeptTreeList({ deptType: props.deptType }).then((res) => { const list = res.data dictStore.setAllCompanyList(list) refreshList(list) }) } else { refreshList(dictStore.companyList) } } } function refreshList(list: DeptTreeNode[]) { originList.value = [...list] // 如果该下拉框非必须显示,并且列表长度小于等于1 if (!props.deptShow && list.length <= 1) { showDeptSelect.value = false // 不显示下拉框 } else { if (list) { if (judgeTree(list)) { multiData.value = true deptTreeList.value = toTreeList<DeptTreeNode>(list, '0', props.needTop) // 如果不需要第一级,将第一级去掉 if (!props.needTop && deptTreeList.value.length === 1 && deptTreeList.value[0].children) { deptTreeList.value = deptTreeList.value[0].children } } else { // 否则不需要转树,直接使用el-select控件 deptTreeList.value = list multiData.value = false } } } showDeptSelect.value = true } // 输入过滤 const filterTree = (value: string, data: { name: string }) => data.name?.includes(value) // --------onMounted------ onBeforeMount(() => { fetchPcode() }) function refreshTree() { fetchPcode() } // 获取原始部门列表-用于暴露给外部 function fetchDeptTree() { return originList.value } /** * 获取某个pid的第一子集-用于暴露给外部 * @param pid 父id */ function findChildren(pid?: string): string[] { if (pid) { const deptids = [] // 储存了该pid和其子集 const deptlist = originList.value if (Array.isArray(deptlist)) { for (const dept of deptlist) { if (dept.pid === pid) { deptids.push(dept.id) } } } return deptids } else { return [] } } // --------暴露方法 defineExpose({ refreshTree, fetchDeptTree, findChildren }) </script> <template> <div style="width: 100%;"> <el-tree-select v-if="multiData" v-model="value" :data="deptTreeList" node-key="id" :filter-node-method="filterTree" :props="defaultProps" :expand-on-click-node="false" :render-after-expand="false" :size="props.size" :placeholder="props.placeholder" :clearable="props.clearable" check-strictly check-on-click-node filterable class="full-width-input" /> <el-select v-else v-model="value" :placeholder="props.placeholder" :size="props.size" :disabled="props.disabled" :clearable="props.clearable" class="full-width-input"> <el-option /> </el-select> </div> </template> <style lang="scss" scoped> .full-width-input { width: 100%; } </style>