uni-app + vant 实现可搜索的popup

使用场景:

        产品要求需要下拉选择,并且可以搜索对应的值,针对移动端没有类似的案例,因此vant+uni-app相结合,实现了可搜索的popup,具体代码如下:

dom解构

<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>

JavaScript部分:

<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内容:

<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>

附加CommonSearch组件内容:

<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: '越南' },
])

大家可自行复制代码体验,如有不足,可留言更改;如有对大家帮助,欢迎大家点赞收藏。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/583353.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024年的Java版本选择?java 17 安装

文章目录 2024年的Java版本选择&#xff1f;java 1.8 和 java17 什么区别&#xff1f;java 17 安装windows 11安装java 17C:\Program Files\Common Files\Oracle\Java\javapath是什么 2024年的Java版本选择&#xff1f; 3年前&#xff0c;java 1.8是市场主流&#xff08;还有一…

STM32用HAL库函数实现硬件IIC

/*出处&#xff1a;【STM32入门教程-2024】第12集 IIC通信与温湿度传感器AHT20(DHT20)_哔哩哔哩_bilibili */ AHT20驱动 这篇笔记我主要介绍代码实现&#xff0c;想要了解原理的请自己看视频&#xff0c;我不过多赘述了。 AHT20通信数据帧格式&#xff1a; ①对照手册上的通…

面对网络安全,做好风险评估对企业会带来哪些帮助

随着信息技术的飞速发展&#xff0c;网络安全问题日益凸显&#xff0c;成为企业不容忽视的重要议题。企业作为社会经济活动的主要参与者&#xff0c;其网络安全不仅关系到自身的生存与发展&#xff0c;更与国家的经济安全、社会稳定息息相关。因此&#xff0c;企业必须高度重视…

K8s: Prometheus 服务结构以及基础抓取数据服务部署

Prometheus 发布应用之后&#xff0c;就有持续运维的事情&#xff0c;就是平台监控Prometheus 是一个云原生的日志监控平台&#xff0c;是一个实时标准的一个技术它是著名的 cncf 里的一个重要的开源项目 上面整个图片是在云原生应用及K8s应用架构下的一个日志监控的一个标准的…

ezplot--Matlab学习

目录 一、代码 二、效果 ​编辑 三、ezplot讲解 四、如何自定义一个函数 一、代码 clc; clear; t0:32; x4(t) cos(2*pi*t/4).*sin(2*pi*t/4); x8(t) cos(2*pi*t/8).*sin(2*pi*t/8); x16(t) cos(2*pi*t/16).*sin(2*pi*t/16); subplot(3,1,1) ezplot(x4,[0,32]); subplot…

《软件设计师教程:数据库系统基础知识大总结》

​ 个人主页&#xff1a;李仙桎 &#x1f525; 个人专栏: 《软件设计师》 ⛺️生活的理想&#xff0c;就是为了理想的生活! ​ ⛺️前言&#xff1a;各位铁汁们好啊&#xff01;&#xff01;&#xff01;今天继续正式学习中级软件设计师考试相关的内容&#xff0c;后续不断更新…

python学习笔记B-11:序列结构之列表--二维列表的遍历和生成式

二维列表的遍历方式&#xff0c;使用双层for循环&#xff0c;遍历索引号。 二维列表的生成式&#xff0c;也是使用类似双层循环的形式生成。 print("##初始化二维列表&#xff0c;每个元素就是1个列表") lst [["东方延续","太空军自然选择号舰长&qu…

【AI心理咨询应用】继Woebot之后,国内诞生的“LLM+CBT”应用:白小喵

导言 AI认知行为疗法&#xff08;Cognitive Behavioral Therapy&#xff0c;CBT&#xff09;早在2017年便有了首例&#xff0c;即美国知名CBT治疗机器人Woebot。 然而&#xff0c;Woebot在CBT的完整落地上仍有缺陷问题&#xff0c;LLM的出现促进了对该问题的解决&#xff0c;…

typeScript 安装

1、安装typescript 安装npm i -g typescript 查看是否安装成功 tsc -v 2、使用ts // 浏览器不支持ts 需要编译成 es5 let str: string ts 在终端输入 tsc index.ts 会将其生成一个对应的index.js文件 在电脑上首次使用TS的时候&#xff0c;执行TS 的命令的时候报错 打开PowerS…

RustGUI学习(iced)之小部件(三):如何使用下拉列表pick_list?

前言 本专栏是学习Rust的GUI库iced的合集&#xff0c;将介绍iced涉及的各个小部件分别介绍&#xff0c;最后会汇总为一个总的程序。 iced是RustGUI中比较强大的一个&#xff0c;目前处于发展中&#xff08;即版本可能会改变&#xff09;&#xff0c;本专栏基于版本0.12.1. 概述…

2024年,新手做抖音小店想要赚钱,必须明白三件事!

大家好&#xff0c;我是电商糖果 有不少小店的商家都说过&#xff0c;现在的抖音小店比三四年前复杂了。 三四年前抖音小店刚刚出现&#xff0c;平台规则还没有那么多&#xff0c;很多机制也不太成熟。 那个时期的抖店说是捡钱的&#xff0c;一点儿都不假。 但是如果说你现…

Linux详解:进程等待

文章目录 进程等待等待的必要性进程等待的方法waitwaitpid获取子进程status阻塞等待 与 非阻塞等待 进程等待 等待的必要性 子进程退出&#xff0c;父进程不进行回收的话&#xff0c;就可能造成僵尸进程&#xff0c;进而造成内存泄露 如果进程进入了僵尸状态&#xff0c;kill…

机器学习:驱动现代交通运输革命的AI智慧引擎

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

玄子Share-引导过程与服务控制

玄子Share-引导过程与服务控制 Linux操作系统引导过程 系统初始化进程 init 进程 由 Linux 内核加载运行 /sbin/init 程序init 进程是系统中第一个进程init 进程的 PID&#xff08;进程标记&#xff09;号永远为 1 Systemd Systemd是Linux操作系统的一种init软件CentOS7中采用…

【Linux开发 第十二篇】搭建JavaEE环境

搭建开发环境 搭建javaEE环境 搭建javaEE环境 在Linux下开发JavaEE需要安装软件包&#xff1a; 安装jdk 安装步骤&#xff1a; 在opt目录下创建jdk目录通过xftp上床到jdk目录中进入到jdk目录中&#xff0c;解压jdk压缩包在/usr/local下创建java目录将解压完成的jdk文件移动…

SpringBoot框架学习笔记(一):依赖管理和自动配置

本文为个人笔记&#xff0c;仅供学习参考之用&#xff0c;如有不当之处请指出。 本文基于springboot2.5.3版本&#xff0c;开发环境需要是 jdk 8 或以上&#xff0c;maven 在 3.5 1 SpringBoot 基本介绍 1.1 官方文档 &#xff08;1&#xff09; 官网 : https://spring.io/pr…

张朝阳对话华为Fellow陈海波:万物智联时代,鸿蒙如何实现“换道超车”?

随着智能终端设备的普及和万物智联时代的加速到来&#xff0c;鸿蒙生态的高速发展正引发全行业的关注。 搜狐创始人、董事局主席兼CEO、物理学博士张朝阳与华为Fellow、基础软件首席科学家陈海波带来了一场关于鸿蒙生态的公开课。鸿蒙技术架构有哪些领先性?HarmonyOS发布5年来…

compose调用系统分享功能分享图片文件

compose调用系统分享功能图片文件 简介UI界面提供给外部程序的文件访问权限创建FileProvider设置共享文件夹 通用分享工具虚拟机验证结果参考 本系列用于新人安卓基础入门学习笔记&#xff0c;有任何不同的见解欢迎留言 运行环境 jdk17 andriod 34 compose material3 简介 本案…

Hadoop3:集群搭建及常用命令与shell脚本整理(入门篇,从零开始搭建)

一、集群环境说明 1、用VMware安装3台Centos7.9虚拟机 2、虚拟机配置&#xff1a;2C&#xff0c;2G内存&#xff0c;50G存储 3、集群架构 从表格中&#xff0c;可以看出&#xff0c;Hadoop集群&#xff0c;主要有2部分&#xff0c;一个是HDFS服务&#xff0c;一个是YARN服务 …

[系统安全] 六十.威胁狩猎 (1)APT攻击检测及防御与常见APT组织的攻击案例分析

您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列。因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全、逆向分析和恶意代码检测,“系统安全”系列文章会更加聚焦,更加系…
最新文章