Newer
Older
xc-business-system / src / components / DeptSelect / index.vue
dutingting on 9 May 2024 7 KB bug修复,3. 需求2024042803
<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 { getAllDeptList, getDept, 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',
  }, // 输入框大小
  pid: {
    type: String,
    default: '',
  },
  needPid: { // 是否需要pid去请求某个部门及下面的部门
    type: Boolean,
    default: false,
  },
  needSelf: { // 是否需要包括自己 与是否需要pid配合使用
    type: Boolean,
    default: false,
  },
  deptNotControlledByPermissions: { // 查询部门不熟当前用户的权限控制
    type: Boolean,
    default: false,
  },
})
const emit = defineEmits(['update:modelValue', 'change'])
// ------------------------------定义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()
async function fetchPcode() {
  // 如果未指定需要展示的部门类型,获取全部部门
  if (!props.deptType) {
    // if (dictStore.allDeptList.length === 0) { // 如果store中未存储,从服务器上获取
    if (props.needPid) {
      const list: any = []
      if (props.needSelf == true) {
        // 包括自己
        await getDept({ deptId: props.pid }).then((res) => {
          if (res.code === 200 && res.data.total > 0) {
            list.push({
              id: res.data.list[0].id,
              name: res.data.list[0].fullName,
              pid: res.data.list[0].pid,
            })
          }
        })
      }

      // 查询子部门
      getAllDeptList({ pid: props.pid }).then((res) => {
        res.data.forEach((item: any) => {
          list.push(item)
        })
        dictStore.setAllDeptList(list)
        refreshList(list)
      })
    }
    else {
      if (props.deptNotControlledByPermissions) { // 无权限控制
        getDept().then((res) => {
          const list = res.data
          dictStore.setAllDeptList(list)
          refreshList(list)
        })
      }
      else {
        getDeptTreeList().then((res) => {
          const list = res.data
          dictStore.setAllDeptList(list)
          refreshList(list)
        })
      }
    }
  }
  else { // 如果指定了需要展示的部门类型
    // if (dictStore.companyList.length === 0) { // 如果store中未存储,从服务器上获取
    if (props.needPid) {
      const list: any = []

      if (props.needSelf == true) {
        // 包括自己
        await getDept({ deptId: props.pid }).then((res) => {
          if (res.code === 200 && res.data.total > 0) {
            list.push({
              id: res.data.list[0].id,
              name: res.data.list[0].fullName,
              pid: res.data.list[0].pid,
            })
          }
        })
      }
      getAllDeptList({ pid: props.pid }).then((res) => {
        res.data.forEach((item: any) => {
          list.push(item)
        })
        dictStore.setAllDeptList(list)
        refreshList(list)
      })
    }
    else {
      if (props.deptNotControlledByPermissions) { // 无权限控制
        getDept().then((res) => {
          const list = res.data
          dictStore.setAllDeptList(list)
          refreshList(list)
        })
      }
      else {
        getDeptTreeList({ deptType: props.deptType }).then((res) => {
          const list = res.data
          dictStore.setAllCompanyList(list)
          refreshList(list)
        })
      }
    }
  }
}
function refreshList(list: DeptTreeNode[]) {
  originList.value = [...list]
  // 如果该下拉框非必须显示,并且列表长度小于等于1
  if (!props.deptShow && list.length <= 1) {
    showDeptSelect.value = false // 不显示下拉框
    deptTreeList.value = list
  }
  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()
// })
onMounted(() => {
  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 []
  }
}

const changeDept = (val: any) => {
  emit('change', val)
}

watch(() => props.pid, (newVal) => {
  if (newVal) {
    fetchPcode()
  }
})
// --------暴露方法
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"
      :disabled="props.disabled"
      @check-change="changeDept"
    />
    <el-select v-else v-model="value" :placeholder="props.placeholder" :size="props.size" :disabled="props.disabled" :clearable="props.clearable" class="full-width-input" @change="changeDept">
      <el-option v-for="(item, index) in deptTreeList" :key="index" :label="item.name" :value="item.id" />
    </el-select>
  </div>
</template>

<style lang="scss" scoped>
.full-width-input {
  width: 100%;
}
</style>