|
@@ -0,0 +1,323 @@
|
|
|
+<script lang="ts" setup>
|
|
|
+// import { ref, reactive, onMounted, inject, watchEffect, watch, computed } from 'vue';
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ modelValue: { type: null, required: true },
|
|
|
+ multiple: { type: Boolean, required: false, default: false },
|
|
|
+ size: { type: String as () => '' | 'large' | 'default' | 'small', required: true, default: () => '' },
|
|
|
+ placeholder: { type: String, required: false, default: () => '请选择' },
|
|
|
+ disabled: { type: Boolean, required: false, default: false },
|
|
|
+ options: { type: Array as () => optionsType, required: false, default: () => [] },
|
|
|
+ clearable: { type: Boolean, required: false, default: true },
|
|
|
+ width: { type: String, required: false, default: () => '100%' },
|
|
|
+ url: { type: String, required: false, default: () => '' },
|
|
|
+});
|
|
|
+
|
|
|
+type optionsType = {
|
|
|
+ label: string,
|
|
|
+ value: string | number,
|
|
|
+ jobNumber?: string,
|
|
|
+}[]
|
|
|
+
|
|
|
+interface Emits {
|
|
|
+ (event: "change", value: any): void;
|
|
|
+ /**
|
|
|
+ * 双向绑定事件
|
|
|
+ * @param value 对应数据
|
|
|
+ */
|
|
|
+ (event: "update:modelValue", value: any): void;
|
|
|
+}
|
|
|
+
|
|
|
+const emit = defineEmits<Emits>();
|
|
|
+const personnelArray = ref<optionsType>([]);
|
|
|
+const { userInfo = {}, personnelList = [] } = JSON.parse(sessionStorage.getItem('storeInfo') || '{}')
|
|
|
+const textSize = ref({
|
|
|
+ 'small': { height: '20px', fontSize: '12px' },
|
|
|
+ 'default': { height: '28px', fontSize: '14px' },
|
|
|
+ '': { height: '28px', fontSize: '14px' },
|
|
|
+ 'large': { height: '36px', fontSize: '14px' },
|
|
|
+})
|
|
|
+
|
|
|
+const selectLoading = ref(false);
|
|
|
+const controlTranslation = reactive({
|
|
|
+ visibleFlag: false // 下拉框出现隐藏
|
|
|
+})
|
|
|
+
|
|
|
+const selectedValue = ref(props.modelValue); // 响应式绑定 v-model 的值
|
|
|
+
|
|
|
+watch(() => props.modelValue, (newValue, _oldValue) => {
|
|
|
+ if(newValue != selectedValue.value) {
|
|
|
+ selectedValue.value = newValue
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+const getSelectedLabel = computed(() => {
|
|
|
+ if (!props.multiple) {
|
|
|
+ const item = getPersonnelListItems(selectedValue.value || props.modelValue);
|
|
|
+ return item ? item.label : props.placeholder
|
|
|
+ }
|
|
|
+
|
|
|
+ if (props.multiple) {
|
|
|
+ if (Array.isArray(selectedValue.value)) {
|
|
|
+ if (selectedValue.value.length <= 0) {
|
|
|
+ return props.placeholder
|
|
|
+ }
|
|
|
+ const item = getPersonnelListItems(selectedValue.value || props.modelValue);
|
|
|
+ return item ? item.label : props.placeholder
|
|
|
+ } else {
|
|
|
+ return props.placeholder
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return props.placeholder
|
|
|
+})
|
|
|
+
|
|
|
+function tagClose(_evt: MouseEvent) {
|
|
|
+ if (Array.isArray(selectedValue.value)) {
|
|
|
+ selectedValue.value.shift()
|
|
|
+ updateValue(selectedValue.value)
|
|
|
+ }
|
|
|
+}
|
|
|
+function getPersonnelListItems(val: any) {
|
|
|
+ let value = val;
|
|
|
+ if (Array.isArray(val) && val.length > 0) {
|
|
|
+ value = val[0]
|
|
|
+ }
|
|
|
+ return personnelList.find((item: any) => item.value == value)
|
|
|
+}
|
|
|
+
|
|
|
+function getUserList(keyword: string = '') {
|
|
|
+ newPost(props.url, keyword).then(res => {
|
|
|
+ personnelArray.value = (res || [])
|
|
|
+ }).finally(() => {
|
|
|
+ selectLoading.value = false
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function newPost(url: string, keyword: string): Promise<any> {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const token: any = sessionStorage.getItem('token');
|
|
|
+
|
|
|
+ if (!token) {
|
|
|
+ reject(new Error("Token is missing"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const body = new URLSearchParams();
|
|
|
+ body.append('keyword', keyword);
|
|
|
+
|
|
|
+ fetch(url, {
|
|
|
+ method: "POST", // 确保请求方法正确,如果是其他方法需要修改
|
|
|
+ headers: {
|
|
|
+ "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
|
+ "Token": token
|
|
|
+ },
|
|
|
+ body: body.toString()
|
|
|
+ })
|
|
|
+ .then(resp => {
|
|
|
+ if (!resp.ok) {
|
|
|
+ reject(new Error(`Error: ${resp.statusText}`));
|
|
|
+ }
|
|
|
+ return resp.json();
|
|
|
+ })
|
|
|
+ .then((json) => {
|
|
|
+ const res = json.data;
|
|
|
+ if (Array.isArray(res)) {
|
|
|
+ const personnelArray = res.map(data => ({
|
|
|
+ label: data['label'],
|
|
|
+ value: data['value'],
|
|
|
+ }));
|
|
|
+ resolve(personnelArray);
|
|
|
+ } else {
|
|
|
+ reject(new Error("Invalid data format"));
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch((error) => {
|
|
|
+ reject(error);
|
|
|
+ });
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function visibleChange(visible: boolean) { // 下拉框出现/隐藏时触发
|
|
|
+ if(visible) {
|
|
|
+ personnelArray.value = personnelList
|
|
|
+ }
|
|
|
+ controlTranslation.visibleFlag = visible
|
|
|
+}
|
|
|
+
|
|
|
+const filterMethod = debounce(filterMethods, 500)
|
|
|
+function filterMethods(val: string) {
|
|
|
+ if (val == '') {
|
|
|
+ personnelArray.value = personnelList
|
|
|
+ selectLoading.value = false
|
|
|
+ return personnelArray.value
|
|
|
+ }
|
|
|
+ selectLoading.value = true
|
|
|
+ getUserList(val)
|
|
|
+}
|
|
|
+
|
|
|
+function updateValue(val: any) { // 值改变的时候触发
|
|
|
+ emit('update:modelValue', selectedValue.value)
|
|
|
+ emit('change', val)
|
|
|
+}
|
|
|
+
|
|
|
+function debounce(func: Function, delay: number) {
|
|
|
+ let timer: any;
|
|
|
+
|
|
|
+ return (...args: any[]) => {
|
|
|
+ if (timer) {
|
|
|
+ clearTimeout(timer);
|
|
|
+ }
|
|
|
+
|
|
|
+ timer = setTimeout(() => {
|
|
|
+ func(...args);
|
|
|
+ }, delay);
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ if (personnelList.length == 0) {
|
|
|
+ getUserList()
|
|
|
+ } else {
|
|
|
+ personnelArray.value = personnelList
|
|
|
+ }
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <!-- <el-select-v2 v-model="selectedValue" :multiple="multiple" :size="size"
|
|
|
+ :loading="selectLoading" :placeholder="placeholder" :disabled="disabled" :clearable="clearable" filterable collapse-tags :options="(personnelArray || [])"
|
|
|
+ :style="`width: ${width}`" :class="`custom-select-v2-reset ${!controlTranslation.visibleFlag ? 'setUpInputReset' : ''}`"
|
|
|
+ @change="updateValue"
|
|
|
+ @visible-change="visibleChange" :filter-method="filterMethod">
|
|
|
+ <template #prefix v-if="!multiple">
|
|
|
+ <div class="selectV2SingleChoiceReset">
|
|
|
+ <div v-if="!controlTranslation.visibleFlag" class="selectSingleChoice" :style="`line-height: ${textSize[size].height};font-size:${textSize[size].fontSize}`">
|
|
|
+ <template v-if="getSelectedLabel == placeholder">
|
|
|
+ <span style="color: #A8ABB2">{{ placeholder }}</span>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <span style="color: #606266;">
|
|
|
+ {{ getSelectedLabel }}
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template #prefix v-if="multiple">
|
|
|
+ <div class="selectV2SingleChoiceTwoReset">
|
|
|
+ <div class="selectSingleChoice" :style="`line-height: ${textSize[size].height};font-size:${textSize[size].fontSize}`">
|
|
|
+ <template v-if="getSelectedLabel == placeholder">
|
|
|
+ <span style="color: #A8ABB2">{{ placeholder }}</span>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <el-tag type="info" :size="size" closable @close="tagClose" style="margin-right: 5px;">
|
|
|
+ {{ getSelectedLabel }}
|
|
|
+ </el-tag>
|
|
|
+ <el-tag type="info" :size="size" v-if="selectedValue.length > 1">+{{ selectedValue.length }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template #default="{ item }">
|
|
|
+ <div class="flex items-center">
|
|
|
+ {{ item.label }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-select-v2> -->
|
|
|
+
|
|
|
+ <el-select v-model="selectedValue" :multiple="multiple" :size="size"
|
|
|
+ :loading="selectLoading" :placeholder="placeholder" :disabled="disabled" :clearable="clearable" filterable collapse-tags
|
|
|
+ :style="`width: ${width}`" :class="`custom-select-v2-reset ${!controlTranslation.visibleFlag ? 'setUpInputReset' : ''}`"
|
|
|
+ @change="updateValue"
|
|
|
+ @visible-change="visibleChange" :filter-method="filterMethod">
|
|
|
+ <template #prefix v-if="!multiple">
|
|
|
+ <div class="selectV2SingleChoiceReset">
|
|
|
+ <div v-if="!controlTranslation.visibleFlag" class="selectSingleChoice" :style="`line-height: ${textSize[size].height};font-size:${textSize[size].fontSize}`">
|
|
|
+ <template v-if="getSelectedLabel == placeholder">
|
|
|
+ <span style="color: #A8ABB2">{{ placeholder }}</span>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <span style="color: #606266;">
|
|
|
+ {{ getSelectedLabel }} a
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template #prefix v-if="multiple">
|
|
|
+ <div class="selectV2SingleChoiceTwoReset">
|
|
|
+ <div class="selectSingleChoice" :style="`line-height: ${textSize[size].height};font-size:${textSize[size].fontSize}`">
|
|
|
+ <template v-if="getSelectedLabel == placeholder">
|
|
|
+ <span style="color: #A8ABB2">{{ placeholder }}</span>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <el-tag type="info" :size="size" closable @close="tagClose" style="margin-right: 5px;">
|
|
|
+ {{ getSelectedLabel }} a
|
|
|
+ </el-tag>
|
|
|
+ <el-tag type="info" :size="size" v-if="selectedValue.length > 1">+{{ selectedValue.length }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <el-option
|
|
|
+ v-for="item in (personnelArray || [])"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ >
|
|
|
+ <span style="float: left">{{ item.label }}</span>
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
+</template>
|
|
|
+reset
|
|
|
+<style lang="scss">
|
|
|
+.custom-select-v2-reset {
|
|
|
+ position: relative;
|
|
|
+ .selectV2SingleChoiceReset, .selectV2SingleChoiceTwoReset {
|
|
|
+ position: absolute;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ transform: translate(0,-50%);
|
|
|
+ .selectSingleChoice {
|
|
|
+ background: #fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .el-select-v2__placeholder {
|
|
|
+ display: none !important;
|
|
|
+ }
|
|
|
+ .el-select-v2__selected-item {
|
|
|
+ .el-tag {
|
|
|
+ display: none !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .selectV2SingleChoiceReset {
|
|
|
+ top: 50%;
|
|
|
+ left: 14px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .selectV2SingleChoiceTwoReset {
|
|
|
+ position: relative;
|
|
|
+ transform: translate(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-input__inner::placeholder {
|
|
|
+ color: transparent !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-select__tags {
|
|
|
+ .el-tag {
|
|
|
+ display: none !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-select-tags-wrapper {
|
|
|
+ width: 110px !important;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.setUpInputReset .el-input__inner {
|
|
|
+ color: #fff !important;
|
|
|
+}
|
|
|
+</style>
|