赞
踩
使用场景:
产品要求需要下拉选择,并且可以搜索对应的值,针对移动端没有类似的案例,因此vant+uni-app相结合,实现了可搜索的popup,具体代码如下:
- <template>
- <!-- uni-app结合vant组件库,实现可搜索的弹层,只能单选 -->
- <view class="popup-vant-select" @click.prevent="handleOpen">
- <!-- :class="{ open: popupOpenFlag, clear: (props.clear && selectLabel) }" -->
- <text
- class="icon"
- :class="{ open: popupOpenFlag, clear: props.clear && selectLabel }"
- @click.stop="handleClear"
- ></text>
- <!-- 下拉框中显示默认的值 -->
- <view v-if="!selectLabel" class="placeholder">{{ props.placeholder }}</view>
- <!-- 下拉框中显示选择的值 -->
- <view v-else>{{ selectLabel }}</view>
- <uni-popup ref="popupRef" type="bottom" background-color="#fff" :is-mask-click="false">
- <view class="select-box">
- <view v-if="props.title" class="title">这里可以设置标题</view>
- <view class="btn-box">
- <text class="cancel" @click="handleCancel">取消</text>
- <text class="confirm" @click="handleConfirm">确定</text>
- </view>
- <CommonSearch
- v-if="props.filterable"
- @input="hanndleInput"
- placeholder="请输入"
- background="#fff"
- />
- <!-- option-height:选项高度;visible-option-num:可见的选项个数 -->
- <Picker
- :show-toolbar="false"
- v-model="selectValue"
- :columns="list"
- option-height="40rpx"
- visible-option-num="4"
- :columns-field-names="customFieldName"
- />
- </view>
- </uni-popup>
- </view>
- </template>
- <script setup lang="ts">
- import { ref, watch, type PropType } from 'vue'
- import { Picker } from 'vant'
- import 'vant/lib/picker/style'
- // import type { PickerCancelEventParams, PickerChangeEventParams, PickerConfirmEventParams } from 'vant'
-
- export interface OptionItem {
- value: number | string
- label: string
- }
-
- const props = defineProps({
- title: {
- type: String,
- default: '',
- },
- modelValue: {
- type: String || (Number as PropType<string | number>),
- default: '',
- },
- options: {
- type: Array as PropType<OptionItem[]>,
- default: () => [],
- },
- filterable: {
- type: Boolean,
- default: true,
- },
- clear: {
- type: Boolean,
- default: true,
- },
- placeholder: {
- type: String,
- default: '请选择',
- },
- // 只有单选,没有多选功能
- multiple: {
- type: Boolean,
- default: false,
- },
- })
- const customFieldName = {
- text: 'label',
- value: 'value',
- }
- const list = ref<OptionItem[]>([])
-
- // 选中的value
- const selectValue = ref<string[]>([])
- // 选中的label
- const selectLabel = ref<string>()
- // 是否打开弹层标志【用于设置下拉框右侧图标】
- const popupOpenFlag = ref(false)
-
- // 弹出层组件的ref
- const popupRef = ref<{
- open: (type?: UniHelper.UniPopupType) => void
- close: () => void
- }>()
-
- // 默认显示所有内容
- watch(
- () => props.options,
- (val) => {
- list.value = val
- },
- { immediate: true, deep: true },
- )
-
- const emits = defineEmits(['update:modelValue', 'change'])
-
- // 手动点击打开弹层
- const handleOpen = () => {
- popupRef.value?.open()
- popupOpenFlag.value = true
- }
-
- // 确认选择时触发
- const handleConfirm = () => {
- // if (!props.multiple) {
- // // 单选逻辑: 单选时,只返回选中值的key即可
- // emits('update:modelValue', selectValue.value[0])
- // } else {
- // // 多选逻辑: 直接返回选中元素的key值数组
- // emits('update:modelValue', selectValue.value)
- // }
- emits('update:modelValue', selectValue.value[0])
- // 如果需要在选中元素发生变化时,做一些其他操作,可以直接使用change方法
- emits('change', selectValue.value)
- selectLabel.value = handleLabel(selectValue.value[0], list.value)
-
- // 关闭popup弹层
- popupRef.value?.close()
- popupOpenFlag.value = false
- }
-
- // 取消时触发
- const handleCancel = () => {
- popupRef.value?.close()
- popupOpenFlag.value = false
- }
-
- // 根据value查找对应的label
- const handleLabel = (value: string | number, options: OptionItem[]) => {
- const item = options.find((e) => e.value === value)
- return item?.label
- }
-
- // 搜索
- const hanndleInput = (val: string) => {
- if (!val) {
- // 当输入值为空时,不过滤
- list.value = JSON.parse(JSON.stringify(props.options))
- } else {
- // 根据输入的值,过滤下拉选项
- let res: OptionItem[] = []
- let arr: OptionItem[] = []
- // 将输入的关键词,切割成数组,检查下拉选项中,是否包含各个字符,利用filter去重
- const strArr: string[] = val
- .split('')
- .filter((item, index, self) => self.indexOf(item) === index)
- strArr.forEach((str) => {
- // 只要包含有输入的字符,都筛选出来
- arr = props.options.filter((e) => e.label.indexOf(str) > -1)
- // 将模糊搜索到的下拉选项赋值给res
- res = res.concat(arr)
- })
- // 下拉选项赋值
- list.value = res
- }
- }
-
- // 清空选项内容
- const handleClear = () => {
- selectValue.value = []
- selectLabel.value = ''
- }
- </script>
- <style lang="scss" scoped>
- .popup-vant-select {
- /** 此样式是下拉框的样式 */
- position: relative;
- background-color: #fff;
- width: 100%;
- height: 80rpx;
- // line-height: 80rpx;
- border-radius: 9rpx;
- border: 1rpx solid #e9ebf0;
- font-family: PingFangSC, PingFangSC-Semibold;
- font-size: 32rpx;
- font-weight: 400;
- /** 此处设置padding-top而不使用line-height的原因: 是因为该组件内部使用LyenuSearch,如果设置了line-height,则会影响LyenuSearch中的图标位置 */
- padding: 18rpx 10rpx 0 20rpx;
-
- .placeholder {
- color: #98a0b3;
- font-size: 28rpx;
- font-weight: 400;
- }
-
- .select-box {
- // height: 30vh;
- background-color: #fff;
- padding: 30rpx 0 100rpx;
-
- .title {
- text-align: center;
- color: #262e40;
- font-weight: 600;
- }
-
- .btn-box {
- display: flex;
- justify-content: space-between;
- border-bottom: 1px solid #e9ebf0;
- padding: 30rpx;
-
- .cancel {
- color: #888;
- }
-
- .confirm {
- color: $theme-color-primary;
- }
- }
- }
-
- :deep(.van-picker-column__item--selected) {
- font-weight: 600;
- }
-
- .icon::after {
- // 字体图标右箭头
- content: '\e602';
- font-family: 'iconfont';
- position: absolute;
- right: 10rpx;
- }
-
- .open::after {
- // 字体图标下箭头
- content: '\e605';
- font-family: 'iconfont';
- }
-
- .clear::after {
- // 关闭按钮
- content: '\e603';
- font-family: 'iconfont';
- font-size: 20rpx;
- }
- }
- </style>
- <template>
- <view class="search-box" :style="setBackGround">
- <input
- class="input"
- type="text"
- :placeholder="props.placeholder"
- v-model="content"
- :confirm-type="props.confimrType"
- @confirm="handleConfirm"
- @input="handleInput"
- />
- <view class="search-icon" @click="hanndleSearch">
- <text class="iconfont icon-sousuo"></text>
- </view>
- </view>
- </template>
- <script setup lang="ts">
- import { ref, computed } from 'vue'
-
- const props = defineProps({
- placeholder: {
- type: String,
- default: '请输入',
- },
- // 设置键盘右下角按钮的文字
- confimrType: {
- type: String,
- // 可输入的值有:seand(发送)、search(搜索)、next(下一个)、go(前往)、done(完成)
- default: 'done',
- },
- background: {
- type: String,
- default: '#f3f7fa',
- },
- })
-
- const content = ref()
-
- const emit = defineEmits(['change', 'confirm', 'input'])
-
- // 点击小图片,确认搜索
- const hanndleSearch = () => {
- emit('change', content.value)
- }
- // 点击输入键盘的右下角的按钮
- const handleConfirm = () => {
- emit('confirm', content.value)
- }
- // 实时输入事件
- const handleInput = () => {
- emit('input', content.value)
- }
-
- // 设置背景
- const setBackGround = computed(() => `background-color: ${props.background};`)
- </script>
- <style lang="scss" scoped>
- .search-box {
- position: fixed;
- // background-color: #f3f7fa;
- width: 100%;
- z-index: 5;
-
- .input {
- width: 690rpx;
- height: 76rpx;
- margin: 30rpx;
- padding: 0 60rpx 0 20rpx;
- border-radius: 45rpx;
- border: 1rpx solid #dcdfe6;
- font-size: 28rpx;
- }
-
- .input-placeholder {
- color: #dcdfe6;
- font-size: 28rpx;
- }
-
- .search-icon {
- width: 34rpx;
- height: 36rpx;
- z-index: 8;
- position: absolute;
- right: 50rpx;
- top: 50rpx;
- /* 防止图标遮挡输入框点击事件 */
- // pointer-events: none;
- font-size: 28rpx;
- }
- }
- </style>
- <VantSelect v-model="selectValue" :options="countryOptions" />
-
- const selectValue = ref('')
-
- const countryOptions = ref([
- { value: 'china', label: '中国' },
- { value: 'USA', label: '美国' },
- { value: 'Brazil', label: '巴西' },
- { value: 'Japan', label: '日本' },
- { value: 'SouthKorea', label: '韩国' },
- { value: 'NorthKorea', label: '朝鲜' },
- { value: 'Vietnam', label: '越南' },
- ])
大家可自行复制代码体验,如有不足,可留言更改;如有对大家帮助,欢迎大家点赞收藏。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。