介绍
组合了列表选择、弹出框组件,实现了便捷快速的选择功能。
代码演示
基础使用
使用 v-model 双向绑定当前值,使用 v-model:visible 控制弹出框显隐。
vue
<template>
<s-select-popout v-model="value" v-model:visible="visible" title="请选择" @change="onChange">
<s-select-option
v-for="item in regionData"
:key="item.code"
:label="item.name"
:value="item.code"
/>
</s-select-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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 visible = ref(false)
const value = ref<number>()
const onChange = (value: any) => {
console.log('change', value)
}
</script>vue
<template>
<s-select-popout v-model="value" v-model:visible="visible" title="请选择" @change="onChange">
<s-select-option
v-for="item in regionData"
:key="item.code"
:label="item.name"
:value="item.code"
/>
</s-select-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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 visible = ref(false)
const value = ref()
const onChange = (value) => {
console.log('change', value)
}
</script>选项分组
你可以使用 SelectOptionGroup 组件对选项进行分组,label 属性为分组名。
vue
<template>
<s-select-popout v-model="value" v-model:visible="visible" title="请选择" @change="onChange">
<s-select-option-group
v-for="group in regionData"
:key="group.code"
:label="group.name"
:value="group.code"
>
<s-select-option
v-for="item in group.children"
:key="item.code"
:label="item.name"
:value="item.code"
/>
</s-select-option-group>
</s-select-popout>
<s-list card>
<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 { ref } from 'vue'
import { getRegionData } from 'region-data'
const regionData = getRegionData().slice(16, 20)
const visible = ref(false)
const value = ref<number | undefined>(450100)
const onChange = (value: any) => {
console.log('change', value)
}
</script>vue
<template>
<s-select-popout v-model="value" v-model:visible="visible" title="请选择" @change="onChange">
<s-select-option-group
v-for="group in regionData"
:key="group.code"
:label="group.name"
:value="group.code"
>
<s-select-option
v-for="item in group.children"
:key="item.code"
:label="item.name"
:value="item.code"
/>
</s-select-option-group>
</s-select-popout>
<s-list card>
<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 { ref } from 'vue'
import { getRegionData } from 'region-data'
const regionData = getRegionData().slice(16, 20)
const visible = ref(false)
const value = ref(450100)
const onChange = (value) => {
console.log('change', value)
}
</script>多选
设置 multiple 属性即可开启多选。
vue
<template>
<s-select-popout
v-model="value"
v-model:visible="visible"
title="请选择"
multiple
@change="onChange"
>
<s-select-option
v-for="item in regionData"
:key="item.code"
:label="item.name"
:value="item.code"
/>
</s-select-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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 visible = ref(false)
const value = ref<number[]>()
const onChange = (value: any) => {
console.log('change', value)
}
</script>vue
<template>
<s-select-popout
v-model="value"
v-model:visible="visible"
title="请选择"
multiple
@change="onChange"
>
<s-select-option
v-for="item in regionData"
:key="item.code"
:label="item.name"
:value="item.code"
/>
</s-select-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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 visible = ref(false)
const value = ref()
const onChange = (value) => {
console.log('change', value)
}
</script>显示工具栏
多选时,可是设置 show-toolbar 显示工具栏,工具栏提供“选择所有”和“清空选择”功能按钮,并显示当前所有的个数。
vue
<template>
<s-select-popout
v-model="value"
v-model:visible="visible"
title="请选择"
multiple
show-toolbar
@change="onChange"
>
<s-select-option
v-for="item in regionData"
:key="item.code"
:label="item.name"
:value="item.code"
/>
</s-select-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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 visible = ref(false)
const value = ref<number[]>()
const onChange = (value: any) => {
console.log('change', value)
}
</script>vue
<template>
<s-select-popout
v-model="value"
v-model:visible="visible"
title="请选择"
multiple
show-toolbar
@change="onChange"
>
<s-select-option
v-for="item in regionData"
:key="item.code"
:label="item.name"
:value="item.code"
/>
</s-select-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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 visible = ref(false)
const value = ref()
const onChange = (value) => {
console.log('change', value)
}
</script>最多选择个数
多选时,可以设置 multiple-limit 属性限制最多选择个数。
vue
<template>
<s-select-popout
v-model="value"
v-model:visible="visible"
title="请选择"
multiple
show-toolbar
:multiple-limit="3"
@change="onChange"
>
<s-select-option
v-for="item in regionData"
:key="item.code"
:label="item.name"
:value="item.code"
/>
</s-select-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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 visible = ref(false)
const value = ref<number[]>()
const onChange = (value: any) => {
console.log('change', value)
}
</script>vue
<template>
<s-select-popout
v-model="value"
v-model:visible="visible"
title="请选择"
multiple
show-toolbar
:multiple-limit="3"
@change="onChange"
>
<s-select-option
v-for="item in regionData"
:key="item.code"
:label="item.name"
:value="item.code"
/>
</s-select-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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 visible = ref(false)
const value = ref()
const onChange = (value) => {
console.log('change', value)
}
</script>禁用选项
设置选项的 disabled 属性,可以禁止选择此选项。
vue
<template>
<s-select-popout v-model="value" v-model:visible="visible" title="请选择" @change="onChange">
<s-select-option
v-for="(item, i) in regionData"
:key="item.code"
:label="item.name"
:value="item.code"
:disabled="i % 4 === 0"
/>
</s-select-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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 visible = ref(false)
const value = ref<number>()
const onChange = (value: any) => {
console.log('change', value)
}
</script>vue
<template>
<s-select-popout v-model="value" v-model:visible="visible" title="请选择" @change="onChange">
<s-select-option
v-for="(item, i) in regionData"
:key="item.code"
:label="item.name"
:value="item.code"
:disabled="i % 4 === 0"
/>
</s-select-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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 visible = ref(false)
const value = ref()
const onChange = (value) => {
console.log('change', value)
}
</script>自定义选项标签
默认选项标签只能设置文字,如果要自定义结构,可以使用选项的 label 插槽。
vue
<template>
<s-select-popout
v-model="value"
v-model:visible="visible"
:options="colors"
title="请选择"
@change="onChange"
>
<template #option-label="{ label, value }">
<div class="flex gap-2">
<div class="size-6 rounded-sm" :style="{ background: value }"></div>
<div>{{ label }}</div>
</div>
</template>
</s-select-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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',
},
{
value: '#6222C9',
label: 'purple',
},
]
const visible = ref(false)
const value = ref<string>()
const onChange = (value: any) => {
console.log('change', value)
}
</script>vue
<template>
<s-select-popout
v-model="value"
v-model:visible="visible"
:options="colors"
title="请选择"
@change="onChange"
>
<template #option-label="{ label, value }">
<div class="flex gap-2">
<div class="size-6 rounded-sm" :style="{ background: value }"></div>
<div>{{ label }}</div>
</div>
</template>
</s-select-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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',
},
{
value: '#6222C9',
label: 'purple',
},
]
const visible = ref(false)
const value = ref()
const onChange = (value) => {
console.log('change', value)
}
</script>自定义整个选项
如果要自定义整个选项结构,可以使用默认插槽,并设置选项的 plain 属性来清除默认样式。
vue
<template>
<s-select-popout v-model="value" v-model:visible="visible" title="请选择" @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-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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 visible = ref(false)
const value = ref<string>()
const onChange = (value: any) => {
console.log('change', value)
}
</script>vue
<template>
<s-select-popout v-model="value" v-model:visible="visible" title="请选择" @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-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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 visible = ref(false)
const value = ref()
const onChange = (value) => {
console.log('change', value)
}
</script>筛选选项
如果选项数量太多,可以设置 filterable 属性显示筛选输入框;并设置 filter-method 属性,这个属性是一个回调函数,会在输入框值改变时触发,并接收输入框值作为参数。筛选逻辑需自行实现。
vue
<template>
<s-select-popout
v-model="value"
v-model:visible="visible"
title="请选择"
filterable
filter-placeholder="请输入过滤关键词"
:filter-method="filterMethod"
@change="onChange"
>
<s-select-option
v-for="item in filteredData"
:key="item.code"
:label="item.name"
:value="item.code"
/>
</s-select-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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 filteredData = ref(regionData)
const visible = ref(false)
const value = ref<number>()
const filterMethod = (query: string) => {
filteredData.value = query ? regionData.filter((item) => item.name.includes(query)) : regionData
}
const onChange = (value: any) => {
console.log('change', value)
}
</script>vue
<template>
<s-select-popout
v-model="value"
v-model:visible="visible"
title="请选择"
filterable
filter-placeholder="请输入过滤关键词"
:filter-method="filterMethod"
@change="onChange"
>
<s-select-option
v-for="item in filteredData"
:key="item.code"
:label="item.name"
:value="item.code"
/>
</s-select-popout>
<s-list card>
<s-list-item arrow hover @click="visible = true">
<template #title>
{{ value ? JSON.stringify(value) : '请选择' }}
</template>
</s-list-item>
<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 filteredData = ref(regionData)
const visible = ref(false)
const value = ref()
const filterMethod = (query) => {
filteredData.value = query ? regionData.filter((item) => item.name.includes(query)) : regionData
}
const onChange = (value) => {
console.log('change', value)
}
</script>远程
使用 v-model:filter-value 绑定筛选值,从远程获取数据,可以在 bottom 插槽或默认插槽(自渲染 option)中放置 LoadMore 组件来加载分页数据。
vue
<template>
<div>
<s-select-popout
ref="selectPopout"
v-model="value"
v-model:visible="visible"
title="请选择"
filterable
filter-placeholder="请输入过滤关键词"
v-model:filter-value="filterValue"
:filter-loading="status === LoadMoreStatus.LOADING"
@change="onChange"
>
<s-select-option
v-for="item in listData"
:key="item.code"
:label="item.name"
:value="item.code"
/>
<s-load-more
:style="{ visibility: listData.length === 0 ? 'hidden' : 'visible' }"
:status="status"
@load="onLoad"
/>
</s-select-popout>
<s-list card>
<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>
</div>
</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<string>()
const onChange = (value: any) => {
console.log('change', value)
}
const listData = ref<
{
code: string
name: string
}[]
>([])
const selectPopoutRef = useTemplateRef('selectPopout')
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 }) => {
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>
<div>
<s-select-popout
ref="selectPopout"
v-model="value"
v-model:visible="visible"
title="请选择"
filterable
filter-placeholder="请输入过滤关键词"
v-model:filter-value="filterValue"
:filter-loading="status === LoadMoreStatus.LOADING"
@change="onChange"
>
<s-select-option
v-for="item in listData"
:key="item.code"
:label="item.name"
:value="item.code"
/>
<s-load-more
:style="{ visibility: listData.length === 0 ? 'hidden' : 'visible' }"
:status="status"
@load="onLoad"
/>
</s-select-popout>
<s-list card>
<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>
</div>
</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 onChange = (value) => {
console.log('change', value)
}
const listData = ref([])
const selectPopoutRef = useTemplateRef('selectPopout')
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 }) => {
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
SelectPopoutProps
继承 SelectProps 和 FormPopoutProps。
SelectPopoutSlots
继承 SelectSlots
SelectPopoutEmits
继承 MotionEmits 和 SelectEmits。
| 事件 | 描述 | 类型 |
|---|---|---|
| update:visible | 弹出框显隐时触发 | (visible: boolean) => void |
| confirm | 点击确定按钮时触发 | (value: any) => void |