✨ [新增]下拉框支持多选;初版本提交
This commit is contained in:
41
src/module/select/index.less
Normal file
41
src/module/select/index.less
Normal file
@@ -0,0 +1,41 @@
|
||||
dl.layui-anim-upbit>dd input[type=checkbox] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
dl.layui-anim-upbit>dd .layui-form-checkbox
|
||||
,.layui-form-item dl.layui-anim-upbit>dd .layui-form-checkbox[lay-skin] {
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
// 多选样式
|
||||
.layui-multiple-select-row {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 30px;
|
||||
padding: 5px 0 5px 10px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
.layui-multiple-select-badge{
|
||||
width: ms-max-content;
|
||||
width: max-content;
|
||||
.layui-badge {
|
||||
margin-left: 5px;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
&:first-of-type{
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
.layui-icon {
|
||||
font-size: 12px;
|
||||
padding-left: 3px;
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
color: #FF5722;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,27 +2,54 @@
|
||||
<div
|
||||
ref="selectRef"
|
||||
class="layui-unselect layui-form-select"
|
||||
:class="[openState ? 'layui-form-selected' : '']"
|
||||
:class="{'layui-form-selected' : openState}"
|
||||
>
|
||||
<div class="layui-select-title" @click="open">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="请选择"
|
||||
:value="selectItem.label"
|
||||
readonly=""
|
||||
:placeholder="selectItem.value !== null && Array.isArray(selectItem.value) && selectItem.value.length > 0 ? '' : (emptyMessage ?? placeholder)"
|
||||
:disabled="disabled"
|
||||
readonly
|
||||
:value="!selectItem.multiple && selectItem.value !== null ? selectItem.label : null"
|
||||
:name="name"
|
||||
class="layui-input layui-unselect"
|
||||
/><i class="layui-edge" />
|
||||
:class="['layui-input', 'layui-unselect', {'layui-disabled': disabled}]"
|
||||
/>
|
||||
<i :class="['layui-edge', {'layui-disabled': disabled}]" />
|
||||
<!-- 多选 -->
|
||||
<div v-if="selectItem.multiple && Array.isArray(selectItem.label)" class="layui-multiple-select-row">
|
||||
<div class="layui-multiple-select-badge">
|
||||
<template v-for="(item, index) in selectItem.label" :key="index">
|
||||
<lay-badge theme="green">
|
||||
<span>{{item}}</span>
|
||||
<i :class="['layui-icon', {'layui-icon-close': true}]"
|
||||
v-if="!disabled
|
||||
&& !(Array.isArray(selectItem.value) && selectItem.value.length > 0 && disabledItemMap[selectItem.value[index]])"
|
||||
@click="removeItemHandle($event, {
|
||||
label: item,
|
||||
value: Array.isArray(selectItem.value)? selectItem.value[index] : null
|
||||
})">
|
||||
</i>
|
||||
</lay-badge>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<dl class="layui-anim layui-anim-upbit">
|
||||
<!-- 多选不支持空提示 -->
|
||||
<template v-if="!multiple && showEmpty">
|
||||
<lay-select-option :value="null" :label="emptyMessage??placeholder"></lay-select-option>
|
||||
</template>
|
||||
<slot />
|
||||
</dl>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="LaySelect" lang="ts">
|
||||
import { defineProps, provide, reactive, ref, watch } from 'vue'
|
||||
import './index.less';
|
||||
import LaySelectOption from '../selectOption/index.vue'
|
||||
import { defineProps, provide, isProxy, ref, watch, computed, reactive, toRefs, Ref } from 'vue'
|
||||
import { useClickOutside } from '@layui/hooks-vue'
|
||||
import { SelectItem} from '../type'
|
||||
|
||||
const selectRef = ref<null | HTMLElement>(null)
|
||||
const isClickOutside = useClickOutside(selectRef)
|
||||
@@ -33,35 +60,92 @@ watch(isClickOutside, () => {
|
||||
}
|
||||
})
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue?: string
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue?: string | number | [] | null
|
||||
name?: string
|
||||
}>()
|
||||
placeholder?: string
|
||||
disabled?: boolean
|
||||
showEmpty?: boolean
|
||||
emptyMessage?: string
|
||||
multiple?: boolean
|
||||
}>(), {
|
||||
modelValue : null,
|
||||
placeholder : '请选择',
|
||||
disabled : false,
|
||||
showEmpty : true,
|
||||
multiple : false
|
||||
})
|
||||
|
||||
const openState = ref(false)
|
||||
|
||||
const open = function () {
|
||||
// 禁用
|
||||
if (props.disabled) {
|
||||
openState.value = false;
|
||||
return;
|
||||
}
|
||||
openState.value = !openState.value
|
||||
}
|
||||
|
||||
const selectItem = reactive({ label: null, value: props.modelValue })
|
||||
|
||||
provide('selectItem', selectItem)
|
||||
provide('openState', openState)
|
||||
|
||||
// select update 时, 通知 change 事件
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
const selectItem = ref<SelectItem>({
|
||||
value : !props.multiple ? props.modelValue : (props.modelValue ? ([] as any[]).concat(props.modelValue) : []),
|
||||
label : props.multiple ? [] : null,
|
||||
multiple : props.multiple
|
||||
} as SelectItem);
|
||||
|
||||
watch(selectItem, function (item) {
|
||||
emit('change', item.value)
|
||||
emit('update:modelValue', item.value)
|
||||
|
||||
watch(()=>selectItem.value.value, (val) =>{
|
||||
emit('update:modelValue', val)
|
||||
emit('change', val)
|
||||
}, {
|
||||
deep : true
|
||||
})
|
||||
|
||||
watch(()=>props.modelValue, function (value) {
|
||||
if (!value) {
|
||||
selectItem.label = null;
|
||||
selectItem.value = '';
|
||||
emit('update:modelValue', null);
|
||||
watch(()=>props.modelValue, (value) => {
|
||||
selectItem.value.value = value;
|
||||
if (!value && value !== 0) {
|
||||
props.multiple && (selectItem.value.value = []);
|
||||
selectItem.value.label = props.multiple ? [] : null;
|
||||
}
|
||||
})
|
||||
|
||||
// 禁止操作子项
|
||||
const disabledItemMap : {[key: string|number]:boolean} = {};
|
||||
const selectItemHandle = function(_selectItem : SelectItem, isChecked ?: boolean) {
|
||||
if (!props.multiple) {
|
||||
openState.value = false;
|
||||
}
|
||||
disabledItemMap[(_selectItem.value as string | number)] = _selectItem.disabled as boolean;
|
||||
if (typeof isChecked !== 'boolean') {
|
||||
props.multiple ? (selectItem.value.label as any[]).push(_selectItem.label) : selectItem.value.label = _selectItem.label;
|
||||
return ;
|
||||
}
|
||||
let values = selectItem.value.value;
|
||||
if (props.multiple && Array.isArray(values)) {
|
||||
const _values = values as any[];
|
||||
const _labels = selectItem.value.label as any[];
|
||||
if (isChecked) {
|
||||
_values.push(_selectItem.value);
|
||||
_labels.push(_selectItem.label);
|
||||
} else {
|
||||
_values.splice(_values.indexOf(_selectItem.value), 1);
|
||||
_labels.splice(_labels.indexOf(_selectItem.label), 1);
|
||||
}
|
||||
selectItem.value.value = _values;
|
||||
selectItem.value.label = _labels;
|
||||
} else {
|
||||
selectItem.value.value = _selectItem.value;
|
||||
selectItem.value.label = _selectItem.label;
|
||||
}
|
||||
}
|
||||
|
||||
const removeItemHandle = function(e : MouseEvent, _selectItem : SelectItem) {
|
||||
e.stopPropagation();
|
||||
selectItemHandle(_selectItem, false);
|
||||
}
|
||||
|
||||
provide('selectItemHandle', selectItemHandle)
|
||||
provide('selectItem', selectItem)
|
||||
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user