153 lines
3.9 KiB
Vue
153 lines
3.9 KiB
Vue
|
|
<template>
|
||
|
|
<el-select
|
||
|
|
v-model="cvalue"
|
||
|
|
class="base-selector"
|
||
|
|
popper-class="base-selector-popper"
|
||
|
|
ref="elselect"
|
||
|
|
clearable
|
||
|
|
:filterable="config.filterable"
|
||
|
|
:filter-method="config.filterMethod ? config.filterMethod : filterMethod"
|
||
|
|
:multiple="config.multiple"
|
||
|
|
:remote-method="config.remote ? remoteFilter : null"
|
||
|
|
:remote="config.remote"
|
||
|
|
:placeholder="computePlaceholder"
|
||
|
|
:loading="loading"
|
||
|
|
v-bind="$attrs"
|
||
|
|
@change="changeSelect"
|
||
|
|
>
|
||
|
|
<template #prefix v-if="config.filterable">
|
||
|
|
<font-awesome-icon :icon="['fas', 'search']" />
|
||
|
|
</template>
|
||
|
|
<el-checkbox v-if="config.multiple && config.useSelectAll" v-model="checked" @change="selectAll">全选</el-checkbox>
|
||
|
|
<el-option
|
||
|
|
v-for="item in optionData"
|
||
|
|
:disabled="doDisabledOptionsBy(item)"
|
||
|
|
v-show="!doDisabledOptionsBy(item)"
|
||
|
|
:key="item[optionProps.key]"
|
||
|
|
:label="item[optionProps.label]"
|
||
|
|
:value="item[optionProps.value]"
|
||
|
|
/>
|
||
|
|
<slot name="loadMore"></slot>
|
||
|
|
</el-select>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script setup>
|
||
|
|
import { ref, computed, watch, onMounted } from 'vue'
|
||
|
|
|
||
|
|
const props = defineProps({
|
||
|
|
config: {
|
||
|
|
type: Object,
|
||
|
|
default: () => ({
|
||
|
|
remote: false,
|
||
|
|
filterable: false,
|
||
|
|
multiple: false,
|
||
|
|
useSelectAll: true,
|
||
|
|
filterMethod: null,
|
||
|
|
}),
|
||
|
|
},
|
||
|
|
optionProps: {
|
||
|
|
type: Object,
|
||
|
|
default: () => ({ key: 'value', label: 'display', value: 'value' }),
|
||
|
|
},
|
||
|
|
modelValue: {
|
||
|
|
type: [Array, String, Number],
|
||
|
|
default: () => (config.multiple ? [] : ''),
|
||
|
|
},
|
||
|
|
data: {
|
||
|
|
type: Array,
|
||
|
|
default: () => [],
|
||
|
|
},
|
||
|
|
disabledOptions: {
|
||
|
|
type: Array,
|
||
|
|
default: () => [],
|
||
|
|
},
|
||
|
|
disabledOptionsBy: Function,
|
||
|
|
placeholder: String,
|
||
|
|
})
|
||
|
|
|
||
|
|
const emit = defineEmits(['update:modelValue', 'remote-filter', 'selected-clear', 'select-load-option', 'change'])
|
||
|
|
|
||
|
|
const loading = ref(false)
|
||
|
|
const checked = ref(false)
|
||
|
|
const elselect = ref(null)
|
||
|
|
// 计算属性
|
||
|
|
const cvalue = computed({
|
||
|
|
get: () => props.modelValue,
|
||
|
|
set: (val) => emit('update:modelValue', val),
|
||
|
|
})
|
||
|
|
|
||
|
|
const computePlaceholder = computed(() => props.placeholder || (props.config.filterable ? '请输入查找内容' : '请选择'))
|
||
|
|
|
||
|
|
const optionData = ref(props.data)
|
||
|
|
|
||
|
|
// 方法
|
||
|
|
const doDisabledOptionsBy = (item) =>
|
||
|
|
props.disabledOptionsBy ? props.disabledOptionsBy(item) : props.disabledOptions.includes(item[props.optionProps.value])
|
||
|
|
|
||
|
|
const remoteFilter = (query) => {
|
||
|
|
loading.value = true
|
||
|
|
setTimeout(() => {
|
||
|
|
loading.value = false
|
||
|
|
emit('remote-filter', query)
|
||
|
|
}, 200)
|
||
|
|
}
|
||
|
|
|
||
|
|
const filterMethod = (query) => {
|
||
|
|
if (!query) optionData.value = props.data
|
||
|
|
|
||
|
|
optionData.value = props.data.filter((option) => {
|
||
|
|
const label = option[props.optionProps.label]
|
||
|
|
const value = option[props.optionProps.value]
|
||
|
|
|
||
|
|
return label.toLowerCase().includes(query.toLowerCase()) || value.toString().toLowerCase().includes(query.toLowerCase())
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
const selectAll = () => {
|
||
|
|
cvalue.value = checked.value ? optionData.value.map((obj) => obj[props.optionProps.value]) : []
|
||
|
|
}
|
||
|
|
|
||
|
|
const changeSelect = (val) => {
|
||
|
|
checked.value = Array.isArray(val) ? val.length === optionData.value.length : false
|
||
|
|
emit('change', val)
|
||
|
|
}
|
||
|
|
|
||
|
|
watch(
|
||
|
|
() => props.data,
|
||
|
|
(val) => {
|
||
|
|
optionData.value = val
|
||
|
|
}
|
||
|
|
)
|
||
|
|
// 生命周期
|
||
|
|
onMounted(() => {
|
||
|
|
emit('select-load-option')
|
||
|
|
})
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style lang="scss">
|
||
|
|
.base-selector {
|
||
|
|
.filter-prefix {
|
||
|
|
text-align: center;
|
||
|
|
width: 25px;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.base-selector-popper {
|
||
|
|
.left-info-bar {
|
||
|
|
font-size: 12px;
|
||
|
|
padding: 0 20px;
|
||
|
|
position: relative;
|
||
|
|
white-space: nowrap;
|
||
|
|
overflow: hidden;
|
||
|
|
text-overflow: ellipsis;
|
||
|
|
height: 34px;
|
||
|
|
line-height: 34px;
|
||
|
|
-webkit-box-sizing: border-box;
|
||
|
|
box-sizing: border-box;
|
||
|
|
cursor: pointer;
|
||
|
|
color: var(--el-color-primary);
|
||
|
|
font-weight: 700;
|
||
|
|
font-weight: 700;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</style>
|