✨ [新增]下拉框支持多选;初版本提交
This commit is contained in:
parent
7ff38feec5
commit
06b7fc3c3b
@ -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 |
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
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
|
<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>
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user