departmentSelection.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <script lang="ts" setup>
  2. import { ref, reactive, onMounted, inject, watchEffect, computed } from 'vue';
  3. import type { CascaderProps, CascaderNode } from 'element-plus'
  4. import { cloneDeep, debounce } from 'lodash';
  5. import { post } from "@/utils/request";
  6. import { useStore } from '@/store/index'
  7. import { Emits } from '../type';
  8. import { storeToRefs } from 'pinia';
  9. import { updateDepTreeData, generateUniqueId } from '@/utils/tools'
  10. const emit = defineEmits<Emits>();
  11. const props = defineProps({
  12. modelValue: { type: [String, Number, Array, Object, Boolean], required: true },
  13. size: { type: String as () => assemblySize, required: true, default: () => 'small' },
  14. placeholder: { type: String, required: false, default: () => '请选择' },
  15. multiple: { type: Boolean, required: false, default: false },
  16. disabled: { type: Boolean, required: false, default: false },
  17. clearable: { type: Boolean, required: false, default: true },
  18. options: { type: Array as () => any, required: false, default: () => [] },
  19. width: { type: String, required: false, default: () => '100%' },
  20. isAddChineseCharacters: { type: Boolean, required: false, default: false },
  21. props: {
  22. type: Object as () => CascaderProps, required: false, default: () => {
  23. return {
  24. value: 'id',
  25. expandTrigger: 'hover' as const,
  26. checkStrictly: true
  27. }
  28. }
  29. }
  30. })
  31. const { departmentList, userInfo } = storeToRefs(useStore());
  32. const { setValue } = useStore()
  33. const departmentVal = ref(props.modelValue); // 响应式绑定 v-model 的值
  34. const departmentValTwo = ref(props.modelValue);
  35. const departmentProps = ref<CascaderProps>({})
  36. const departmantArray = ref<any>([]);
  37. const visibleFlag = ref<boolean>(false);
  38. const deptRef = ref<any>(null)
  39. const deptLabel = computed(() => {
  40. if (Array.isArray(departmentVal.value)) {
  41. const deptId = departmentVal.value[departmentVal.value.length - 1]
  42. return findLabelById(departmantArray.value, deptId)
  43. }
  44. return ''
  45. })
  46. async function filterMethod(node: CascaderNode, keyword: string) {
  47. const { userNameNeedTranslate } = userInfo.value
  48. if(userNameNeedTranslate == 0) {
  49. return [keyword].includes(node.label)
  50. }
  51. const { data = [] } = await post(`/department/listAllMemb`, { keyword, cursor: '2' })
  52. const keywordList = data.flatMap(extractLabels)
  53. return [...keywordList].includes(node.label)
  54. }
  55. function getDeptList(keyword: string = '') {
  56. post(`/department/listAllMemb`, { keyword }).then(res => {
  57. const deptList = updateDepTreeData(res.data, props.isAddChineseCharacters)
  58. departmantArray.value = deptList
  59. if (!keyword) {
  60. setValue((deptList || []), 'departmentList')
  61. }
  62. })
  63. }
  64. function visibleChange(visible: boolean) { // 下拉框出现/隐藏时触发
  65. if (!props.multiple) {
  66. setTimeout(() => {
  67. departmentVal.value = visible ? [] : cloneDeep(departmentValTwo.value)
  68. }, 10)
  69. }
  70. visibleFlag.value = visible
  71. }
  72. function updateValue(val: any) { // 值改变的时候触发
  73. if (!props.multiple) {
  74. deptRef.value.$refs.input.value = '';
  75. deptRef.value.togglePopperVisible()
  76. }
  77. departmentValTwo.value = cloneDeep(val)
  78. emit('update:modelValue', departmentVal.value)
  79. emit('change', val)
  80. }
  81. function findLabelById(tree: any, id: any) {
  82. for (let node of tree) {
  83. if (node.id == id) {
  84. return node.label;
  85. }
  86. if (node.children && node.children.length > 0) {
  87. const result: any = findLabelById(node.children, id);
  88. if (result) {
  89. return result;
  90. }
  91. }
  92. }
  93. return null;
  94. }
  95. function extractLabels(node: any) {
  96. let labels: any[] = [];
  97. if (node.label) {
  98. labels.push(node.label);
  99. }
  100. if (node.children && Array.isArray(node.children)) {
  101. node.children.forEach((child: any) => {
  102. labels = labels.concat(extractLabels(child));
  103. });
  104. }
  105. return labels;
  106. }
  107. onMounted(() => {
  108. departmentProps.value = props.props
  109. if (departmentList.value.length == 0) {
  110. if (props.options.length > 0) {
  111. departmantArray.value = props.options
  112. } else {
  113. getDeptList()
  114. }
  115. } else {
  116. departmantArray.value = departmentList.value
  117. }
  118. })
  119. </script>
  120. <template>
  121. <div class="departmentSelectionDiv" :style="`width: ${width}`">
  122. <el-cascader v-model="departmentVal" :ref="'deptRef'" :options="departmantArray" :props="departmentProps"
  123. :size="size" :clearable="clearable" :placeholder="placeholder" filterable :show-all-levels="false"
  124. :class="`departmentSelection ${!multiple && !visibleFlag ? 'clearColor' : ''}`" :style="`width: ${width}`"
  125. :filter-method="filterMethod" @visible-change="visibleChange" @change="updateValue">
  126. <template #default="{ node, data }">
  127. <span>
  128. <TextTranslation translationTypes="departmentName" :translationValue="data.label"></TextTranslation>
  129. </span>
  130. </template>
  131. </el-cascader>
  132. <!-- 覆盖 -->
  133. <div class="coverDept" v-if="!visibleFlag">
  134. <TextTranslation translationTypes="departmentName" :translationValue="deptLabel"></TextTranslation>
  135. </div>
  136. </div>
  137. </template>
  138. <style>
  139. .clearColor .el-input .el-input__inner {
  140. color: transparent !important;
  141. }
  142. </style>
  143. <style lang="scss" scoped>
  144. .departmentSelectionDiv {
  145. position: relative;
  146. display: inline-block;
  147. .coverDept {
  148. position: absolute;
  149. top: 50%;
  150. transform: translateY(-50%);
  151. left: 6px;
  152. background: #fff;
  153. color: #303133;
  154. font-size: 12px;
  155. height: calc(100% - 4px);
  156. }
  157. }
  158. </style>