介绍
组合了列表选择弹出框、输入框组件,实现了便捷快速的选择功能。
代码演示
基础使用
使用 v-model 绑定当前值,通过 title 和 placeholder 属性设置弹出框标题和输入框占位文本。
在点击输入框后会显示列表选择弹出框。
相比较于 Select 和 SelectPopout 组件,SelectInput 组件需要传递 options 属性,因为需要通过此属性获取当前值对应的标签,以便显示在输入框内。
vue
<template>
<s-list card>
<s-list-item>
<s-select-input
v-model="value"
title="请选择"
placeholder="请选择"
clearable
:options="regionData"
:option-keys="{ label: 'name', value: 'code', children: '' }"
@change="onChange"
/>
</s-list-item>
<s-list-item title="当前值:" :value="JSON.stringify(value) ?? 'undefined'" />
<s-list-item title="设置为:440000 (广东省)" arrow hover @click="value = 440000" />
<s-list-item title="清空" arrow hover @click="value = undefined" />
</s-list>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { getRegionData } from 'region-data'
const regionData = getRegionData()
const value = ref<number>()
const onChange = (value: any) => {
console.log('change', value)
}
</script>vue
<template>
<s-list card>
<s-list-item>
<s-select-input
v-model="value"
title="请选择"
placeholder="请选择"
clearable
:options="regionData"
:option-keys="{ label: 'name', value: 'code', children: '' }"
@change="onChange"
/>
</s-list-item>
<s-list-item title="当前值:" :value="JSON.stringify(value) ?? 'undefined'" />
<s-list-item title="设置为:440000 (广东省)" arrow hover @click="value = 440000" />
<s-list-item title="清空" arrow hover @click="value = undefined" />
</s-list>
</template>
<script setup lang="js">
import { ref } from 'vue'
import { getRegionData } from 'region-data'
const regionData = getRegionData()
const value = ref()
const onChange = (value) => {
console.log('change', value)
}
</script>自定义整个选项
SelectInput 组件依然可以像 Select 和 SelectPopout 组件一样使用插槽自定义内容。
但 options 属性还是必传的。
vue
<template>
<s-list card>
<s-list-item>
<s-select-input
v-model="value"
title="请选择"
placeholder="请选择"
clearable
:options="colors"
@change="onChange"
>
<div class="grid grid-cols-2 gap-2 px-4 pb-2">
<s-select-option
v-for="item in colors"
:key="item.value"
:value="item.value"
plain
#default="{ selected }"
>
<s-button block variant="outlined" :color="selected ? 'primary' : 'secondary'">
<div class="size-6 rounded-sm" :style="{ background: item.value }"></div>
<div class="ms-2 w-16 text-start">{{ item.label }}</div>
</s-button>
</s-select-option>
</div>
</s-select-input>
</s-list-item>
<s-list-item title="当前值:" :value="JSON.stringify(value) ?? 'undefined'" />
<s-list-item title="设置为:#FFDE0A (yellow)" arrow hover @click="value = '#FFDE0A'" />
<s-list-item title="清空" arrow hover @click="value = undefined" />
</s-list>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const colors = [
{
value: '#E63415',
label: 'red',
},
{
value: '#FF6600',
label: 'orange',
},
{
value: '#FFDE0A',
label: 'yellow',
},
{
value: '#1EC79D',
label: 'green',
},
{
value: '#14CCCC',
label: 'cyan',
},
{
value: '#4167F0',
label: 'blue',
},
]
const value = ref<string>()
const onChange = (value: any) => {
console.log('change', value)
}
</script>vue
<template>
<s-list card>
<s-list-item>
<s-select-input
v-model="value"
title="请选择"
placeholder="请选择"
clearable
:options="colors"
@change="onChange"
>
<div class="grid grid-cols-2 gap-2 px-4 pb-2">
<s-select-option
v-for="item in colors"
:key="item.value"
:value="item.value"
plain
#default="{ selected }"
>
<s-button block variant="outlined" :color="selected ? 'primary' : 'secondary'">
<div class="size-6 rounded-sm" :style="{ background: item.value }"></div>
<div class="ms-2 w-16 text-start">{{ item.label }}</div>
</s-button>
</s-select-option>
</div>
</s-select-input>
</s-list-item>
<s-list-item title="当前值:" :value="JSON.stringify(value) ?? 'undefined'" />
<s-list-item title="设置为:#FFDE0A (yellow)" arrow hover @click="value = '#FFDE0A'" />
<s-list-item title="清空" arrow hover @click="value = undefined" />
</s-list>
</template>
<script setup lang="js">
import { ref } from 'vue'
const colors = [
{
value: '#E63415',
label: 'red',
},
{
value: '#FF6600',
label: 'orange',
},
{
value: '#FFDE0A',
label: 'yellow',
},
{
value: '#1EC79D',
label: 'green',
},
{
value: '#14CCCC',
label: 'cyan',
},
{
value: '#4167F0',
label: 'blue',
},
]
const value = ref()
const onChange = (value) => {
console.log('change', value)
}
</script>多选
默认选择的选项的标签最多 10 会显示在输入框,其余通过 +<rest> 的形式展示。可以通过 max-labels 属性设置最多展示的标签数。
vue
<template>
<s-list card>
<s-list-item>
<s-select-input
v-model="value"
title="请选择"
placeholder="请选择"
clearable
multiple
show-toolbar
:max-labels="3"
:options="regionData"
:option-keys="{ label: 'name', value: 'code', children: '' }"
@change="onChange"
/>
</s-list-item>
<s-list-item title="当前值:" :value="JSON.stringify(value) ?? 'undefined'" />
<s-list-item
title="设置为:[440000, 450000] (广东省,广西)"
arrow
hover
@click="value = [440000, 450000]"
/>
<s-list-item title="清空" arrow hover @click="value = undefined" />
</s-list>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { getRegionData } from 'region-data'
const regionData = getRegionData()
const value = ref<number[]>()
const onChange = (value: any) => {
console.log('change', value)
}
</script>vue
<template>
<s-list card>
<s-list-item>
<s-select-input
v-model="value"
title="请选择"
placeholder="请选择"
clearable
multiple
show-toolbar
:max-labels="3"
:options="regionData"
:option-keys="{ label: 'name', value: 'code', children: '' }"
@change="onChange"
/>
</s-list-item>
<s-list-item title="当前值:" :value="JSON.stringify(value) ?? 'undefined'" />
<s-list-item
title="设置为:[440000, 450000] (广东省,广西)"
arrow
hover
@click="value = [440000, 450000]"
/>
<s-list-item title="清空" arrow hover @click="value = undefined" />
</s-list>
</template>
<script setup lang="js">
import { ref } from 'vue'
import { getRegionData } from 'region-data'
const regionData = getRegionData()
const value = ref()
const onChange = (value) => {
console.log('change', value)
}
</script>远程
使用 v-model:filter-value 绑定筛选值,从远程获取数据,可以在 bottom 插槽或默认插槽(自渲染 option)中放置 LoadMore 组件来加载分页数据。
vue
<template>
<s-list card>
<s-list-item>
<s-select-input
ref="selectInput"
v-model="value"
v-model:visible="visible"
title="请选择"
placeholder="请选择"
filterable
filter-placeholder="请输入过滤关键词"
v-model:filter-value="filterValue"
:filter-loading="status === LoadMoreStatus.LOADING"
:options="listData"
:option-keys="{ label: 'name', value: 'code' }"
:map-label="mapLabel"
@change="onChange"
>
<template #bottom>
<s-load-more
:style="{ visibility: listData.length === 0 ? 'hidden' : 'visible' }"
:status="status"
@load="onLoad"
/>
</template>
</s-select-input>
</s-list-item>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<s-list-item title="设置为:440100 (广州市)" arrow hover @click="value = 440100" />
<s-list-item title="清空" arrow hover @click="value = undefined" />
</s-list>
</template>
<script setup lang="ts">
import { nextTick, ref, useTemplateRef, watch } from 'vue'
import { getCities } from '@/api'
import { debounce, LoadMoreStatus } from 'sard'
const visible = ref(false)
const value = ref<number>()
const mapLabel = {
440100: '广州市',
}
const onChange = (value: any) => {
console.log('change', value)
}
const listData = ref<
{
code: string
name: string
}[]
>([])
const selectPopoutRef = useTemplateRef('selectInput')
const filterValue = ref('州')
const status = ref<LoadMoreStatus>(LoadMoreStatus.INCOMPLETE)
let page = 1
const getData = async (query: string) => {
status.value = LoadMoreStatus.LOADING
console.log(page, query)
return getCities({ page, name: query })
.then(({ list, total }) => {
console.log(list, total)
listData.value = page === 1 ? list : [...listData.value, ...list]
if (listData.value.length >= total || list.length === 0) {
status.value = LoadMoreStatus.COMPLETE
} else {
status.value = LoadMoreStatus.INCOMPLETE
page++
}
nextTick(() => {
if (page === 1) {
selectPopoutRef.value?.scrollTop()
}
})
})
.catch(() => {
status.value = LoadMoreStatus.ERROR
})
}
const debouncedGetData = debounce(() => {
page = 1
getData(filterValue.value)
}, 500)
watch(filterValue, () => {
debouncedGetData()
})
const onLoad = () => {
getData(filterValue.value)
}
</script>vue
<template>
<s-list card>
<s-list-item>
<s-select-input
ref="selectInput"
v-model="value"
v-model:visible="visible"
title="请选择"
placeholder="请选择"
filterable
filter-placeholder="请输入过滤关键词"
v-model:filter-value="filterValue"
:filter-loading="status === LoadMoreStatus.LOADING"
:options="listData"
:option-keys="{ label: 'name', value: 'code' }"
:map-label="mapLabel"
@change="onChange"
>
<template #bottom>
<s-load-more
:style="{ visibility: listData.length === 0 ? 'hidden' : 'visible' }"
:status="status"
@load="onLoad"
/>
</template>
</s-select-input>
</s-list-item>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<s-list-item title="设置为:440100 (广州市)" arrow hover @click="value = 440100" />
<s-list-item title="清空" arrow hover @click="value = undefined" />
</s-list>
</template>
<script setup lang="js">
import { nextTick, ref, useTemplateRef, watch } from 'vue'
import { getCities } from '@/api'
import { debounce, LoadMoreStatus } from 'sard'
const visible = ref(false)
const value = ref()
const mapLabel = { 440100: '广州市' }
const onChange = (value) => {
console.log('change', value)
}
const listData = ref([])
const selectPopoutRef = useTemplateRef('selectInput')
const filterValue = ref('州')
const status = ref(LoadMoreStatus.INCOMPLETE)
let page = 1
const getData = async (query) => {
status.value = LoadMoreStatus.LOADING
console.log(page, query)
return getCities({
page,
name: query,
})
.then(({ list, total }) => {
console.log(list, total)
listData.value = page === 1 ? list : [...listData.value, ...list]
if (listData.value.length >= total || list.length === 0) {
status.value = LoadMoreStatus.COMPLETE
} else {
status.value = LoadMoreStatus.INCOMPLETE
page++
}
nextTick(() => {
if (page === 1) {
selectPopoutRef.value?.scrollTop()
}
})
})
.catch(() => {
status.value = LoadMoreStatus.ERROR
})
}
const debouncedGetData = debounce(() => {
page = 1
getData(filterValue.value)
}, 500)
watch(filterValue, () => {
debouncedGetData()
})
const onLoad = () => {
getData(filterValue.value)
}
</script>API
SelectInputProps
继承 SelectPopoutProps 和 PopoutInputProps 。
| 属性 | 描述 | 类型 | 默认值 |
|---|---|---|---|
| max-labels | 多选时,输入框最大展示标签个数,设为 -1 表示不限制 | number | 10 |
| map-label | 选项标签映射表,远程加载时如果选项标签不在列表中,可以通过此属性配置一个映射表来显示标签 | Record<any, any> | - |
SelectInputSlots
继承 SelectPopoutSlots和PopoutInputSlots