Newer
Older
smart-metering-front / src / components / DeptSelect / index.vue
Stephanie on 1 Dec 2022 4 KB first commit
<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
      }
    }
  }
}
// 输入过滤
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%;">
    <ElTreeSelect
      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"
    />
    <ElSelect v-else v-model="value" :placeholder="props.placeholder" :size="props.size" :disabled="props.disabled" :clearable="props.clearable" class="full-width-input">
      <ElOption />
    </ElSelect>
  </div>
</template>

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