[新增]下拉框支持多选;初版本提交

This commit is contained in:
xumi 2021-12-20 23:02:13 +08:00
parent 7ff38feec5
commit 06b7fc3c3b
5 changed files with 255 additions and 65 deletions

View File

@ -4,7 +4,7 @@
::: demo ::: demo
<template> <template>
<lay-select> <lay-select v-model="value">
<lay-select-option value="1" label="学习"></lay-select-option> <lay-select-option value="1" label="学习"></lay-select-option>
<lay-select-option value="2" label="编码"></lay-select-option> <lay-select-option value="2" label="编码"></lay-select-option>
<lay-select-option value="3" label="运动"></lay-select-option> <lay-select-option value="3" label="运动"></lay-select-option>
@ -16,8 +16,9 @@ import { ref } from 'vue'
export default { export default {
setup() { setup() {
const value = ref(null);
return { return {
value
} }
} }
} }
@ -33,7 +34,7 @@ export default {
<template> <template>
<lay-select v-model="selected"> <lay-select v-model="selected">
<lay-select-option value="1" label="学习"></lay-select-option> <lay-select-option value="1" label="学习"></lay-select-option>
<lay-select-option value="2" label="编码" disabled="true"></lay-select-option> <lay-select-option value="2" label="编码" disabled></lay-select-option>
<lay-select-option value="3" label="运动"></lay-select-option> <lay-select-option value="3" label="运动"></lay-select-option>
</lay-select> </lay-select>
</template> </template>
@ -55,15 +56,56 @@ export default {
::: :::
::: title 多选使用
:::
::: demo
<template>
<lay-select v-model="mvalue" @change="change" multiple>
<lay-select-option value="1" label="学习"></lay-select-option>
<lay-select-option value="2" label="编码" disabled></lay-select-option>
<lay-select-option value="3" label="运动"></lay-select-option>
<lay-select-option value="4" label="唱歌"></lay-select-option>
<lay-select-option value="5" label="跳舞"></lay-select-option>
<lay-select-option value="6" label="打篮球"></lay-select-option>
<lay-select-option value="7" label="rap"></lay-select-option>
</lay-select>
</template>
<script>
import { ref,watch } from 'vue'
export default {
setup() {
const mvalue = ref(['1','2']);
const change = function(val){
console.log(val, mvalue.value)
}
return {
mvalue,
change
}
}
}
</script>
:::
::: title select 属性 ::: title select 属性
::: :::
::: table ::: table
| Name | Description | Accepted Values | | 属性 | 描述 | 类型 | 可选值 | 默认值 |
| ------- | -------------- | --------------- | | ------------ | --------------------- | ------------------------- | -------------- | -------- |
| name | 原生 name 属性 | -- | | v-model | 选中值 | `string`/`number`/`Array` | - | - |
| v-model | 选中值 | -- | | name | 原生 name 属性 | `string` | - | - |
| placeholder | 默认空提示语 | `string` | - | `请选择` |
| disabled | 是否禁用 | `boolean` | `true` `false` | `false` |
| showEmpty | 是否增加空提示选项 | `boolean` | `true` `false` | `true` |
| multiple | 是否为多选 | `boolean` | `true` `false` | `false` |
::: :::
@ -72,10 +114,14 @@ export default {
::: table ::: table
| Name | Description | Accepted Values |
| ----- | ----------- | --------------- | ::: table
| label | 标签 | -- |
| value | 值 | -- | | 属性 | 描述 | 类型 | 可选值 | 默认值 |
| ------------ | --------------------- | ------------------------- | -------------- | -------- |
| label | 标签值(`必填`) | `string` | - | - |
| value | 值 | `string` / `number` | - | - |
| disabled | 是否禁用 | `boolean` | `true` `false` | `false` |
::: :::
@ -84,9 +130,9 @@ export default {
::: table ::: table
| Name | Description | Accepted Values | | 属性 | 描述 | 接收值 |
| ------- | ----------- | --------------- | | ------- | ----------------- | --------------- |
| default | 默认 | -- | | default | 默认(`label`) | - |
::: :::
@ -95,8 +141,8 @@ export default {
::: table ::: table
| Name | Description | Accepted Values | | 属性 | 描述 | 接收值 |
| ------ | ----------- | --------------- | | ------ | ---------- | --------------- |
| change | 切换事件 | value | | change | 切换事件 | value |
::: :::

View 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;
}
}
}
}

View File

@ -2,27 +2,54 @@
<div <div
ref="selectRef" ref="selectRef"
class="layui-unselect layui-form-select" class="layui-unselect layui-form-select"
:class="[openState ? 'layui-form-selected' : '']" :class="{'layui-form-selected' : openState}"
> >
<div class="layui-select-title" @click="open"> <div class="layui-select-title" @click="open">
<input <input
type="text" type="text"
placeholder="请选择" :placeholder="selectItem.value !== null && Array.isArray(selectItem.value) && selectItem.value.length > 0 ? '' : (emptyMessage ?? placeholder)"
:value="selectItem.label" :disabled="disabled"
readonly="" readonly
:value="!selectItem.multiple && selectItem.value !== null ? selectItem.label : null"
:name="name" :name="name"
class="layui-input layui-unselect" :class="['layui-input', 'layui-unselect', {'layui-disabled': disabled}]"
/><i class="layui-edge" /> />
<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> </div>
<dl class="layui-anim layui-anim-upbit"> <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 /> <slot />
</dl> </dl>
</div> </div>
</template> </template>
<script setup name="LaySelect" lang="ts"> <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 { useClickOutside } from '@layui/hooks-vue'
import { SelectItem} from '../type'
const selectRef = ref<null | HTMLElement>(null) const selectRef = ref<null | HTMLElement>(null)
const isClickOutside = useClickOutside(selectRef) const isClickOutside = useClickOutside(selectRef)
@ -33,35 +60,92 @@ watch(isClickOutside, () => {
} }
}) })
const props = defineProps<{ const props = withDefaults(defineProps<{
modelValue?: string modelValue?: string | number | [] | null
name?: string 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 openState = ref(false)
const open = function () { const open = function () {
//
if (props.disabled) {
openState.value = false;
return;
}
openState.value = !openState.value 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 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) watch(()=>selectItem.value.value, (val) =>{
emit('update:modelValue', item.value) emit('update:modelValue', val)
emit('change', val)
}, {
deep : true
}) })
watch(()=>props.modelValue, function (value) { watch(()=>props.modelValue, (value) => {
if (!value) { selectItem.value.value = value;
selectItem.label = null; if (!value && value !== 0) {
selectItem.value = ''; props.multiple && (selectItem.value.value = []);
emit('update:modelValue', null); 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> </script>

View File

@ -2,47 +2,59 @@
<dd <dd
:value="value" :value="value"
:class="[ :class="[
selectItem.value === value ? 'layui-this' : '', {'layui-this' : selected},
disabled ? 'layui-disabled' : '', {'layui-disabled' : disabled}
]" ]"
@click="selectHandle" @click="selectHandle"
> >
{{ label }} <template v-if="selectItem.multiple">
<lay-checkbox skin="primary" v-model="selected" @change="selectHandle" label=""></lay-checkbox>
</template>
<slot>{{ label }}</slot>
</dd> </dd>
</template> </template>
<script setup name="LaySelectOption" lang="ts"> <script lang="ts">
import { SelectItem } from '../type' export default {
import { defineProps, inject, onMounted, Ref } from 'vue' name : 'LaySelectOption'
}
</script>
<script setup lang="ts">
import LayCheckbox from '../checkbox';
import { SelectItem, SelectItemHandle } from '../type'
import { computed, defineProps, inject, onMounted, Ref} from 'vue'
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
value: string value: string | null | undefined
label: string label?: string
disabled?: boolean | string disabled?: boolean
}>(), }>(),
{ {
disabled: false, disabled: false,
} }
) )
const selectItem = inject('selectItem') as SelectItem const selectItemHandle = inject('selectItemHandle') as SelectItemHandle
const openState = inject('openState') as Ref<boolean> const selectItem = inject('selectItem') as Ref<SelectItem>
const selectHandle = function () { const selectHandle = function () {
if (props.disabled) { !props.disabled && callSelectItemHandle(!selected.value);
return
}
openState.value = false
selectItem.value = props.value
selectItem.label = props.label
} }
const callSelectItemHandle = function(isChecked ?: boolean){
// init selected selectItemHandle({
onMounted(() => { value : props.value,
if (selectItem.value === props.value) { label : props.label,
selectItem.value = props.value disabled : props.disabled
selectItem.label = props.label }, isChecked);
}
const selected = computed(()=>{
const selectValues = selectItem.value.value;
if (Array.isArray(selectValues)) {
return (selectValues as any[]).indexOf(props.value) > -1;
} }
return selectItem.value.value === props.value
}) })
onMounted(() => selected.value && callSelectItemHandle())
</script> </script>

View File

@ -1,4 +1,11 @@
export type SelectValueType = string | string[] | number | number[] | null;
export interface SelectItem { export interface SelectItem {
label?: string value?: SelectValueType
value?: string label?: null | string | string[]
disabled ?: boolean
multiple ?: boolean
}
export interface SelectItemHandle {
(selectItem: SelectItem, isChecked ?: boolean) : void
} }