|
@@ -0,0 +1,234 @@
|
|
|
|
+<script lang="ts" setup>
|
|
|
|
+import { ref, reactive, onMounted, inject, watchEffect, computed } from 'vue';
|
|
|
|
+import type { CascaderProps, CascaderNode } from 'element-plus'
|
|
|
|
+import { cloneDeep, debounce } from 'lodash';
|
|
|
|
+import { post } from "@/utils/request";
|
|
|
|
+import { useStore } from '@/store/index'
|
|
|
|
+import { Emits } from '../type';
|
|
|
|
+import { storeToRefs } from 'pinia';
|
|
|
|
+import { updateDepTreeData, generateUniqueId } from '@/utils/tools'
|
|
|
|
+const emit = defineEmits<Emits>();
|
|
|
|
+
|
|
|
|
+const props = defineProps({
|
|
|
|
+ modelValue: { type: [String, Number, Array, Object, Boolean], required: true },
|
|
|
|
+ size: { type: String as () => assemblySize, required: true, default: () => 'small' },
|
|
|
|
+ placeholder: { type: String, required: false, default: () => '请选择' },
|
|
|
|
+ multiple: { type: Boolean, required: false, default: false }, // 多选
|
|
|
|
+ anyLevel: { type: Boolean, required: false, default: true }, // 选择任意一级
|
|
|
|
+ checkStrictly: { type: Boolean, required: false, default: false }, // 是否父子级相互不关联
|
|
|
|
+ disabled: { type: Boolean, required: false, default: false },
|
|
|
|
+ clearable: { type: Boolean, required: false, default: true },
|
|
|
|
+ options: { type: Array as () => any, required: false, default: () => [] },
|
|
|
|
+ width: { type: String, required: false, default: () => '100%' },
|
|
|
|
+ isAddChineseCharacters: { type: Boolean, required: false, default: false },
|
|
|
|
+ props: {
|
|
|
|
+ type: Object as () => CascaderProps, required: false, default: () => {
|
|
|
|
+ return {
|
|
|
|
+ value: 'id',
|
|
|
|
+ expandTrigger: 'hover' as const,
|
|
|
|
+ checkStrictly: true
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+const { departmentList, userInfo } = storeToRefs(useStore());
|
|
|
|
+const { setValue } = useStore()
|
|
|
|
+const treeSelectVal = ref(props.modelValue); // 响应式绑定 v-model 的值
|
|
|
|
+const treeSelectArray = ref<any>([]);
|
|
|
|
+const visibleFlag = ref(false);
|
|
|
|
+const selectLoading = ref(false);
|
|
|
|
+const treeSelectRef = ref();
|
|
|
|
+const searchCriteria = ref<string[]>([])
|
|
|
|
+
|
|
|
|
+const getSelectedLabel = computed(() => {
|
|
|
|
+ console.log(treeSelectVal.value)
|
|
|
|
+ if (!props.multiple) {
|
|
|
|
+ return treeSelectVal.value ? findLabelById(treeSelectArray.value, treeSelectVal.value) : props.placeholder
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (props.multiple) {
|
|
|
|
+ if (Array.isArray(treeSelectVal.value)) {
|
|
|
|
+ if (treeSelectVal.value.length <= 0) {
|
|
|
|
+ return props.placeholder
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return treeSelectVal.value ? findLabelById(treeSelectArray.value, treeSelectVal.value[0]) : props.placeholder
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return props.placeholder
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+const filterMethod = debounce(filterMethods, 500)
|
|
|
|
+async function filterMethods(val: string) {
|
|
|
|
+ const { userNameNeedTranslate } = userInfo.value
|
|
|
|
+ if (val == '') {
|
|
|
|
+ treeSelectArray.value = departmentList.value
|
|
|
|
+ selectLoading.value = false
|
|
|
|
+ searchCriteria.value = []
|
|
|
|
+ treeSelectRef.value.filter()
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ if (userNameNeedTranslate == 0) {
|
|
|
|
+ searchCriteria.value = [val]
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ treeSelectRef.value.filter()
|
|
|
|
+ }, 10)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ if (userNameNeedTranslate == 1) {
|
|
|
|
+ selectLoading.value = true
|
|
|
|
+ const { data = [] } = await post(`/department/listAllMemb`, { keyword: val })
|
|
|
|
+ const keywordList = data.flatMap(extractLabels)
|
|
|
|
+ searchCriteria.value = keywordList
|
|
|
|
+ console.log(searchCriteria.value)
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ selectLoading.value = false
|
|
|
|
+ treeSelectRef.value.filter()
|
|
|
|
+ }, 100)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function filterNode(_value: string, data: Tree) {
|
|
|
|
+ const { userNameNeedTranslate } = userInfo.value
|
|
|
|
+ if (searchCriteria.value.length == 0) {
|
|
|
|
+ return true
|
|
|
|
+ }
|
|
|
|
+ if (userNameNeedTranslate == 0) {
|
|
|
|
+ return data.label.indexOf(searchCriteria.value[0]) > -1
|
|
|
|
+ }
|
|
|
|
+ if (userNameNeedTranslate == 0) {
|
|
|
|
+ return [...searchCriteria.value].includes(data.label)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function tagClose(_evt: MouseEvent) {
|
|
|
|
+ if (Array.isArray(treeSelectVal.value)) {
|
|
|
|
+ treeSelectVal.value.shift()
|
|
|
|
+ updateValue(treeSelectVal.value)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function getDeptList(keyword: string = '') {
|
|
|
|
+ post(`/department/listAllMemb`, { keyword }).then(res => {
|
|
|
|
+ const deptList = updateDepTreeData(res.data, props.isAddChineseCharacters)
|
|
|
|
+ treeSelectArray.value = deptList
|
|
|
|
+ if (!keyword) {
|
|
|
|
+ setValue((deptList || []), 'departmentList')
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function visibleChange(visible: boolean) { // 下拉框出现/隐藏时触发
|
|
|
|
+ visibleFlag.value = visible
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function findLabelById(tree: any, id: any) {
|
|
|
|
+ for (let node of tree) {
|
|
|
|
+ if (node.id == id) {
|
|
|
|
+ return node.label;
|
|
|
|
+ }
|
|
|
|
+ if (node.children && node.children.length > 0) {
|
|
|
|
+ const result: any = findLabelById(node.children, id);
|
|
|
|
+ if (result) {
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return null;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function extractLabels(node: any) {
|
|
|
|
+ let labels: any[] = [];
|
|
|
|
+ if (node.label) {
|
|
|
|
+ labels.push(node.label);
|
|
|
|
+ }
|
|
|
|
+ if (node.children && Array.isArray(node.children)) {
|
|
|
|
+ node.children.forEach((child: any) => {
|
|
|
|
+ labels = labels.concat(extractLabels(child));
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ return labels;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function updateValue(val: any) { // 值改变的时候触发
|
|
|
|
+ emit('update:modelValue', treeSelectVal.value)
|
|
|
|
+ emit('change', val)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+onMounted(() => {
|
|
|
|
+ if (departmentList.value.length == 0) {
|
|
|
|
+ if (props.options.length > 0) {
|
|
|
|
+ treeSelectArray.value = props.options
|
|
|
|
+ } else {
|
|
|
|
+ getDeptList()
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ treeSelectArray.value = departmentList.value
|
|
|
|
+ }
|
|
|
|
+})
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<template>
|
|
|
|
+ <el-tree-select v-model="treeSelectVal" ref="treeSelectRef" :data="treeSelectArray" :size="size"
|
|
|
|
+ :loading="selectLoading" :clearable="clearable" :placeholder="placeholder" :show-checkbox="anyLevel"
|
|
|
|
+ :multiple="multiple" node-key="id" :render-after-expand="true" :check-strictly="checkStrictly" filterable
|
|
|
|
+ collapse-tags :style="`width: ${width}`" :class="`custom-select ${!visibleFlag ? 'setUpInput' : ''}`"
|
|
|
|
+ @change="updateValue" @visible-change="visibleChange" :filter-method="filterMethod"
|
|
|
|
+ :filter-node-method="filterNode">
|
|
|
|
+ <!-- 单选 -->
|
|
|
|
+ <template #prefix v-if="!multiple">
|
|
|
|
+ <div style="height: 100%;display: flex;align-items: center;">
|
|
|
|
+ <div v-if="!visibleFlag" class="selectSingleChoice">
|
|
|
|
+ <template v-if="getSelectedLabel == placeholder">
|
|
|
|
+ {{ placeholder }}
|
|
|
|
+ </template>
|
|
|
|
+ <template v-else>
|
|
|
|
+ <span style="color: #303133;">
|
|
|
|
+ <TextTranslation translationTypes="departmentName" :translationValue="getSelectedLabel"></TextTranslation>
|
|
|
|
+ </span>
|
|
|
|
+ </template>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </template>
|
|
|
|
+ <!-- 多选 -->
|
|
|
|
+ <template #tag v-if="multiple">
|
|
|
|
+ <template v-if="Array.isArray(treeSelectVal) && treeSelectVal.length > 0">
|
|
|
|
+ <el-tag type="info" :size="size" closable @close="tagClose">
|
|
|
|
+ <TextTranslation translationTypes="departmentName" :translationValue="getSelectedLabel"></TextTranslation>
|
|
|
|
+ </el-tag>
|
|
|
|
+ <el-tag type="info" :size="size" v-if="treeSelectVal.length > 1">+{{ treeSelectVal.length }}</el-tag>
|
|
|
|
+ </template>
|
|
|
|
+ <template v-else>
|
|
|
|
+ <span style="color: #A8ABB2">{{ placeholder }}</span>
|
|
|
|
+ </template>
|
|
|
|
+ </template>
|
|
|
|
+ <!-- 主要内容 -->
|
|
|
|
+ <template #default="{ node, data }">
|
|
|
|
+ <TextTranslation translationTypes="departmentName" :translationValue="node.label"></TextTranslation>
|
|
|
|
+ </template>
|
|
|
|
+ </el-tree-select>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
+.custom-select {
|
|
|
|
+ :deep(.el-select__placeholder.is-transparent) {
|
|
|
|
+ color: transparent !important;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ :deep(.el-select__placeholder) {
|
|
|
|
+ span {
|
|
|
|
+ display: none;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ :deep(.el-select__input) {
|
|
|
|
+ margin-left: 0;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.setUpInput :deep(.el-input__inner) {
|
|
|
|
+ color: #fff !important;
|
|
|
|
+}
|
|
|
|
+</style>
|