<!--
下拉框组件
modelValue:双向绑定
data: 下拉可选项
disabled: 是否禁用
-->
<template>
<el-select
v-bind="props.props"
:modelValue="modelValue"
fit-input-width
:suffix-icon="elSelectArrowIcon"
:disabled="disabled"
:filterable="filterable"
:placeholder="placeholder"
:popper-class="props.props.popperClass"
:remote-method="remoteMethod"
@change="handleInput"
@keyup.enter="keyupEnter()"
@blur="blur"
@clear="clear"
@focus="focus"
v-limitLength="props.maxlength"
>
<div
:infiniteScrollImmediate="false"
:infinite-scroll-delay="500"
:infiniteScrollDisabled="!props?.props?.infiniteScroll"
v-infinite-scroll="scrollLoad"
ref="scrollRef"
>
<el-option
v-for="(item, index) in selectData"
:key="item + index"
:label="item.label"
:value="item.value"
>
<BaseToolTip placement="top" :rows="1" :content="item.label">
{{ item.label }}
</BaseToolTip>
</el-option>
</div>
</el-select>
</template>
<script>
import { defineComponent } from 'vue';
export default defineComponent({
type: 'BaseSelect',
inheritAttrs: false,
});
</script>
<script setup>
import SvgIcon from '@/components/autoImport/SvgIcon/index.vue';
import { computed, h, ref } from 'vue';
const props = defineProps({
modelValue: {
type: [String, Number],
default: '',
},
options: {
type: Array,
default: () => [],
validator(value) {
const arr = ['label', 'value'];
const result = value.every(item => arr.every(key => Object.keys(item).includes(key)));
return result;
},
},
disabled: {
type: Boolean,
default: false,
},
placeholder: {
type: String,
default: '请选择',
},
filterable: {
type: Boolean,
default: false,
},
props: {
type: Object,
},
maxlength: {
type: Number,
},
});
const elSelectArrowIcon = (props, context) =>
h(SvgIcon, {
iconClass: 'el-select-arrow-icon',
className: 'el-select-arrow-icon',
});
const pageNo = ref(1);
const keyword = ref('');
const scrollRef = ref(null);
const selectData = computed(() => [...props.options]);
const emits = defineEmits(['change', 'focus', 'blur']);
const handleInput = value => {
emits('update:modelValue', `${value}`.trim());
emits('change', `${value}`.trim());
};
// 回车触发查询
const keyupEnter = (val = props.modelValue, isResetPageNo = true) => {
const str = `${val || ''}`.trim();
keyword.value = str;
isResetPageNo && (pageNo.value = 1);
if (props?.props?.remote && props?.props?.remoteMethod) {
props.props.remoteMethod(str, { pageNo: pageNo.value });
}
};
// 触发远程搜索
const remoteMethod = (val, isResetPageNo = true) => {
if (isResetPageNo) {
scrollRef.value.parentNode.parentNode.scrollTop = 0;
!(keyword.value === val && !val && selectData.value.length) && keyupEnter(val);
} else {
keyupEnter(val);
}
};
// 触发滚动加载回调
const scrollLoad = () => {
if (props?.props?.infiniteScroll) {
pageNo.value = pageNo.value + 1; // 翻页+1
keyupEnter(keyword.value, false);
}
};
const clear = () => {
keyupEnter('');
};
const blur = () => {
emits('blur');
};
const focus = e => {
!keyword.value && !selectData.value.length && keyupEnter();
emits('focus', e);
};
const vLimitLength = {
created: (el, { value }) => {
const input = el.getElementsByTagName('input')[0];
if (input) {
input.setAttribute('maxlength', value);
}
},
};
</script>
<style lang="scss">
.el-select {
width: 100%;
.el-input {
&.is-focus {
.el-input__inner {
border-color: #2367f1;
}
}
.el-input__suffix {
right: 8px;
}
}
.el-input__inner:focus {
border-color: #2367f1;
}
&:hover {
.el-input__inner {
border-color: #2367f1;
}
}
.el-select-arrow-icon {
width: 14px;
height: 6px;
}
}
.el-popper.el-select__popper {
&[role='tooltip'] {
background: #ffffff;
box-shadow: 0px 2px 6px 0px rgba(10, 80, 200, 0.18);
border: 0;
}
.el-select-dropdown {
border-radius: 2px;
.el-select-dropdown__list {
padding: 10px 0;
.el-select-dropdown__item {
height: 36px;
line-height: 36px;
color: #333;
font-size: 14px;
padding-left: 8px;
padding-right: 10px;
&:hover {
background-color: #e7eff9;
}
&.selected {
color: #2367f1;
font-weight: normal;
}
&.hover {
background-color: #fff;
&:hover {
background-color: #e7eff9;
}
}
}
}
}
.el-popper__arrow {
display: none;
}
}
</style>
console