uniapp和vue3+ts创建自定义下拉选择框组件

使用uniapp开发小程序的时候,使用了uview的ui组件,但是里面没有下拉选择组件,只有Picker 选择器,但是我们想要使用下拉选择的组件,所以需要自定义个一个下拉选择的自定义组件,我就只能自己动手创建这个自定义组件了,支持:多选,单选,下拉框在框内还是框外

实现的效果:

自定义组件源代码:Select.vue

<template>
  <!-- <view class="uni-select-dc" :style="{ 'z-index': zindex }"> -->
  <view class="uni-select-dc" :style="{ 'z-index': zindex }">
    <view class="uni-select-dc-select" :class="{ active: active }" @click.stop="handleSelect">
      <!-- 禁用mask -->
      <view class="uni-disabled" v-if="disabled"></view>
      <!-- 清空 -->
      <view class="close-icon close-postion" v-if="realValue.length && !active && !disabled && showClearIcon">
        <text @click.stop="handleRemove(null)"></text>
      </view>
      <!-- 显示框 -->
      <view class="uni-select-multiple" v-show="realValue.length">
        <view class="uni-select-multiple-item" v-if="multiple" v-for="(item, index) in changevalue" :key="index">
          {{ item.text }}
          <view class="close-icon" v-if="showValueClear">
            <text @click.stop="handleRemove(index)">
            </text>
          </view>
        </view>
        <!-- 单选时展示内容 -->
        <view v-else class="single-text">
          {{ changevalue.length ? changevalue[0].text : "" }}
        </view>
      </view>
      <!-- 为空时的显示文案 -->
      <view v-if="realValue.length == 0 && showplaceholder">{{
        placeholder
      }}</view>
      <!-- 右边的下拉箭头 -->
      <view :class="{ disabled: disabled, 'uni-select-dc-icon': !downInner, 'uni-select-dc-inner': downInner }">
        <text></text>
      </view>
    </view>
    <!-- 下拉选项 -->
    <scroll-view class="uni-select-dc-options" :scroll-y="true" v-show="active">
      <template>
        <view class="uni-select-dc-item" :class="{ active: realValue.includes((item as any)[svalue]) }"
          v-for="(item, index) in options" :key="index" @click.stop="handleChange(index, item)">
          {{ (item as any)[slabel] }}
        </view>
      </template>
    </scroll-view>
  </view>
</template>
 
<script lang="ts" setup>
import { onMounted, reactive, ref } from "vue";

const props = defineProps({
  // 是否显示全部清空按钮
  showClearIcon: {
    type: Boolean,
    default: false,
  },
  // 是否多选
  multiple: {
    type: Boolean,
    default: false,
  },
  // 下拉箭头是否在框内
  downInner: {
    type: Boolean,
    default: true,
  },
  // 是否显示单个删除
  showValueClear: {
    type: Boolean,
    default: true,
  },
  zindex: {
    type: Number,
    default: 999,
  },
  // 禁用选择
  disabled: {
    type: Boolean,
    default: false,
  },
  options: {
    type: Array,
    default() {
      return [];
    },
  },
  value: {
    type: Array,
    default() {
      return [];
    },
  },
  placeholder: {
    type: String,
    default: "请选择",
  },
  showplaceholder: {
    type: Boolean,
    default: true,
  },
  // 默认取text
  slabel: {
    type: String,
    default: "text",
  },
  // 默认取value
  svalue: {
    type: String,
    default: "value",
  },
});
const emit = defineEmits(["change"]);
const active = ref<boolean>(false); // 组件是否激活,
let changevalue = reactive<Record<any, any>>([]);
let realValue = reactive<Record<string, any>>([]);
onMounted(() => {
  init();
});

// 初始化函数
const init = () => {
  if (props.value.length > 0) {
    props.options.forEach((item) => {
      props.value.forEach((i) => {
        if ((item as any)[props.svalue] === i) {
          changevalue.push(item);
        }
      })
    })
    realValue = props.value;
    console.log("props---", changevalue);

  } else {
    changevalue = [];
    realValue = [];
  }
};
// 点击展示选项
const handleSelect = () => {
  if (props.disabled) return;
  active.value = !active.value;
};
// 移除数据
const handleRemove = (index: any) => {
  if (index === null) {
    realValue = [];
    changevalue = [];
  } else {
    realValue.splice(index, 1);
    changevalue.splice(index, 1);
  }
  emit("change", changevalue, realValue);
};
// 点击组件某一项
const handleChange = (index, item) => {
  console.log("选中了某一项", index, item);
  // 如果是单选框,选中一项后直接关闭
  if (!props.multiple) {
    console.log("关闭下拉框");
    changevalue.length = 0
    realValue.length = 0
    changevalue.push(item);
    realValue.push(item[props.svalue])
    active.value = !active.value;
  } else {
    // 多选操作
    const arrIndex = realValue.indexOf(item[props.svalue]);
    if (arrIndex > -1) {
      // 如果该选项已经选中,当点击后就不选中
      changevalue.splice(arrIndex, 1);
      realValue.splice(arrIndex, 1);
    } else {
      // 否则选中该选项
      changevalue.push(item);
      realValue.push(item[props.svalue]);
    }
  }
  // 触发回调函数
  emit("change", changevalue, realValue);
};
</script>
 
<style lang="scss" scoped>
.uni-select-dc {
  position: relative;
  z-index: 999;

  .uni-select-mask {
    width: 100%;
    height: 100%;
  }

  /* 删除按钮样式*/
  .close-icon {
    height: 100%;
    width: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    // z-index: 3;
    cursor: pointer;

    text {
      position: relative;
      background: #c0c4cc;
      width: 13px;
      height: 13px;
      border-radius: 50%;
      border: 1px solid #bbb;

      &::before,
      &::after {
        content: "";
        position: absolute;
        left: 20%;
        top: 50%;
        height: 1px;
        width: 60%;
        transform: rotate(45deg);
        background-color: #909399;
      }

      &::after {
        transform: rotate(-45deg);
      }
    }
  }

  //所有情空的定位
  .close-postion {
    position: absolute;
    right: 35px;
    top: 0;
    height: 100%;
    width: 15px;
  }

  /* 多选盒子 */
  .uni-select-multiple {
    display: flex;
    flex-wrap: nowrap;
    overflow: scroll;

    .single-text {
      color: #333;
    }

    .uni-select-multiple-item {
      background: #f4f4f5;
      margin-right: 5px;
      padding: 2px 4px;
      border-radius: 4px;
      color: #909399;
      display: flex;
      flex-shrink: 0;
    }
  }

  // select部分
  .uni-select-dc-select {
    user-select: none;
    position: relative;
    z-index: 3;
    height: 30px;
    padding: 0 30px 0 10px;
    box-sizing: border-box;
    border-radius: 4px;
    border: 1px solid rgb(229, 229, 229);
    display: flex;
    align-items: center;
    font-size: 12px;
    color: #999;
    min-width: 210px;

    .uni-disabled {
      position: absolute;
      left: 0;
      width: 100%;
      height: 100%;
      z-index: 19;
      cursor: no-drop;
      background: rgba(255, 255, 255, 0.5);
    }

    .uni-select-dc-input {
      font-size: 14px;
      color: #999;
      display: block;
      width: 96%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      line-height: 30px;
      box-sizing: border-box;

      &.active {
        color: #333;
      }
    }

    .uni-select-dc-icon {
      cursor: pointer;
      position: absolute;
      right: 0;
      top: 0;
      height: 100%;
      width: 30px;
      display: flex;
      align-items: center;
      justify-content: center;
      border-left: 1px solid rgb(229, 229, 229);

      text {
        display: block;
        width: 0;
        height: 0;
        border-width: 12rpx 12rpx 0;
        border-style: solid;
        border-color: #bbb transparent transparent;
        transition: 0.3s;
      }

      &.disabled {
        cursor: no-drop;

        text {
          width: 20rpx;
          height: 20rpx;
          border: 2px solid #ff0000;
          border-radius: 50%;
          transition: 0.3s;
          position: relative;
          z-index: 999;

          &::after {
            content: "";
            position: absolute;
            top: 50%;
            left: 0;
            width: 100%;
            height: 2px;
            margin-top: -1px;
            background-color: #ff0000;
            transform: rotate(45deg);
          }
        }
      }
    }

    .uni-select-dc-inner {
      cursor: pointer;
      position: absolute;
      right: 0;
      top: 0;
      height: 100%;
      width: 30px;
      display: flex;
      align-items: center;
      justify-content: center;

      text {
        display: block;
        width: 10px;
        height: 10px;
        position: absolute;
        right: 10px;
        top: 6px;
        border: 1px solid #bbb;
        transform: rotate(-45deg);
        border-color: transparent transparent#bbb #bbb;
        transition: 0.3s;
      }

      &.disabled {
        cursor: no-drop;

        text {
          width: 20rpx;
          height: 20rpx;
          border: 2px solid #ff0000;
          border-radius: 50%;
          transition: 0.3s;
          position: relative;
          z-index: 999;

          &::after {
            content: "";
            position: absolute;
            top: 50%;
            left: 0;
            width: 100%;
            height: 2px;
            margin-top: -1px;
            background-color: #ff0000;
            transform: rotate(45deg);
          }
        }
      }
    }

    // 激活之后,图标旋转180度
    &.active .uni-select-dc-icon {
      text {
        transform: rotate(180deg);
      }
    }

    &.active .uni-select-dc-inner {
      text {
        position: absolute;
        right: 10px;
        top: 12px;
        transform: rotate(-225deg);
      }
    }
  }

  // options部分
  .uni-select-dc-options {
    user-select: none;
    position: absolute;
    top: calc(100% + 5px);
    left: 0;
    width: 100%;
    height: 400rpx;
    border-radius: 4px;
    border: 1px solid rgb(229, 229, 229);
    background: #fff;
    padding: 5px 0;
    box-sizing: border-box;
    z-index: 9;

    .uni-select-dc-item {
      padding: 0 10px;
      box-sizing: border-box;
      cursor: pointer;
      line-height: 2.5;
      transition: 0.3s;
      font-size: 14px;

      &.active {
        color: #409eff;

        background-color: #f5f7fa &hover {
          color: #409eff;
          background-color: #f5f7fa;
        }
      }

      &:hover {
        background-color: #f5f5f5;
      }
    }
  }
}
</style>

父组件调用:

<template>
  <view class="arena-main">
    自定义下拉选择组件:
    <Select :value="monIndex" downInner :options="options" @change="changeValue">        
    </Select>
  </view>
</template>

<script setup lang="ts">
import Select from "@/components/select/index.vue"
import { reactive } from "vue";

let monIndex = reactive([1]);
const changeValue = (item: any, value: any) => {
  console.log("父组件接收到选中的值", item, value);
  monIndex = value;
};

const options = [
  { value: 0, text: "测试1" },
  { value: 1, text: "测试2" },
  { value: 2, text: "测试3" },
  { value: 3, text: "测试4" },
  { value: 4, text: "测试5" },
];


</script>

<style lang="scss" scoped>
.arena-main {
  padding: 10px;
}
</style>

使用参数说明:

monIndex:选中的值列表

options:可选项列表

multiple:是否为多选

downInner:下拉箭头是否在选择框内

showValueClear:是否显示单个删除

disabled:是否禁用

placeholder:占位符

slabel:label标签展示某项

svalue:value选中的值

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

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

相关文章

5月22日比特币披萨日,今天你吃披萨了吗?

比特币披萨日 1. Laszlo Hanyecz2. 最贵披萨诞生记3. 梭哈买披萨4. 未完待续 2010年5月22日&#xff0c;美国佛罗里达州的程序员Laszlo Hanyecz&#xff08;拉兹洛哈涅克斯&#xff09;用10000个比特币购买了棒约翰&#xff08;Papa Johns&#xff09;比萨店一个价值25美元的奶…

【Linux】指令详解(一)

目录 1. 前言2. 与指令相关的知识2.1 文件2.2 路径 3. 常见指令3.1 pwd3.2 ls3.2.1 ls -l3.2.2 ls -la 3.3 mkdir3.4 cd3.5 clear3.6 touch 1. 前言 来学习一些Linux的指令和一些相关的知识。 第一步那肯定是打开自己的xshell。 这里可以修改字体和大小。 可以使用ctrl回车全…

【汇编】“转移”综述、操作符offset、jmp指令

文章目录 前言一、转移综述1.1 :背景&#xff1a;1.2 转移指令1.3 转移指令的分类按转移行为根据指令对IP修改的范围不同 二、操作符offset2.1 offset操作符是干什么的&#xff1f;标号是什么&#xff1f; 2.2 nop是什么&#xff1f; 三、jmp指令3.1 jmp指令的功能3.2 jmp指令&…

Python - Wave2lip 环境配置与 Wave2lip x GFP-GAN 实战 [超详细!]

一.引言 前面介绍了 GFP-GAN 的原理与应用&#xff0c;其用于优化图像画质。本文关注另外一个相关的项目 Wave2lip&#xff0c;其可以通过人物视频与自定义音频进行适配&#xff0c;改变视频中人物的嘴型与音频对应。 二.Wave2Lip 简介 Wave2lip 研究 lip-syncing 以达到视频…

工具及方法 - 多邻国: Duolingo

网站&#xff1a;Duolingo 有iOS和Android应用&#xff0c;在App Store和Google Play上都能下载。也可以使用网页版。我就在iOS上安装了付费版&#xff0c;为了小朋友学习英语&#xff0c;一年的费用&#xffe5;588。 目前学习中的课程是英语、日语和粤语。英语是小学课程&a…

单/三相dq解耦控制与特定次谐波抑制

1. 单相整流器dq坐标系下建模 单相整流器的拓扑如图所示&#xff0c;可知 u a b u s − L d i s d t − R i s {u_{ab}} {u_{s}} - L\frac{{d{i_s}}}{{dt}} - R{i_s} uab​us​−Ldtdis​​−Ris​。   将电压和电流写成dq的形式。 { u s U s m sin ⁡ ( ω t ) i s I …

c语言从入门到实战——回调函数与qsort的讲解和模拟实现

回调函数与qsort的讲解和模拟实现 前言1. 回调函数是什么&#xff1f;2. qsort2.1 使用qsort函数排序整型数据2.2 使用qsort排序结构数据 3. qsort函数的模拟实现 前言 回调函数是一个函数&#xff0c;它作为参数传递给另一个函数&#xff0c;并且能够在该函数内部被调用。在C…

代码随想录算法训练营第六十天丨 单调栈03

84.柱状图中最大的矩形 思路 单调栈 本地单调栈的解法和接雨水的题目是遥相呼应的。 为什么这么说呢&#xff0c;42. 接雨水 (opens new window)是找每个柱子左右两边第一个大于该柱子高度的柱子&#xff0c;而本题是找每个柱子左右两边第一个小于该柱子的柱子。 这里就涉…

腾讯云轻量数据库1核1G性能测评、租用费用和详细介绍

腾讯云轻量数据库服务采用腾讯云自研的新一代云原生数据库 TDSQL-C&#xff0c;融合了传统数据库、云计算与新硬件技术的优势&#xff0c;100%兼容 MySQL&#xff0c;实现超百万级 QPS 的高吞吐&#xff0c;128TB 海量分布式智能存储&#xff0c;保障数据安全可靠。腾讯云百科t…

#gStore-weekly | gBuilder功能详解之数据入库、定时任务、抽取日志、数据库管理等

gBuilder提供了一系列强大的功能模块&#xff0c;涵盖了数据入库、定时任务、抽取日志以及数据库管理与查询等关键领域。用户可以轻松地进行数据库的创建、定时任务的设定和执行、抽取日志的管理以及数据库的导入、导出、备份和还原操作。此外&#xff0c;高效的数据库查询功能…

微服务学习|Nacos配置管理:统一配置管理、配置热更新、配置共享、搭建Nacos集群

统一配置管理 在微服务当中&#xff0c;提供一个配置中心来将一些配置提取出来&#xff0c;进行统一的使用&#xff0c;Nacos既可以充当注册中心&#xff0c;也提供配置中心的功能。 1.在Nacos中添加配置文件 在Nacos控制台&#xff0c;我们可以在配置管理中&#xff0c;添加…

常见树种(贵州省):009楠木、樟木、桂木种类

摘要&#xff1a;本专栏树种介绍图片来源于PPBC中国植物图像库&#xff08;下附网址&#xff09;&#xff0c;本文整理仅做交流学习使用&#xff0c;同时便于查找&#xff0c;如有侵权请联系删除。 图片网址&#xff1a;PPBC中国植物图像库——最大的植物分类图片库 一、楠木 …

【腾讯云 HAI域探秘】高性能服务器引领AI革新浪潮:从AI绘画、知识问答到PyTorch图像分类、视频检测的全方位探索

目录 1 HAI&#xff08;高性能应用服务&#xff09;简介2 HAI的应用场景2.1 HAI在AI作画中的灵活性与效率2.2 深入探索LLM语言模型的应用与性能2.3 HAI支持的AI模型开发环境与工具 3 基于stable difussio的AI 绘画应用实践3.1 使用AI模型中的stable diffusion模型服务3.2 设置和…

算法 LeetCode 题解 | 两个数组的交集

大家好&#xff0c;我是木川 一、题目描述 给定两个数组 nums1 和 nums2 &#xff0c;返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,2,2,1], nums2 [2,2] 输出&#xff1a;[2] 示例…

SpringCloud 微服务全栈体系(十五)

第十一章 分布式搜索引擎 elasticsearch 五、RestClient 操作文档 为了与索引库操作分离&#xff0c;再次参加一个测试类&#xff0c;做两件事情&#xff1a; 初始化 RestHighLevelClient酒店数据在数据库&#xff0c;需要利用 IHotelService 去查询&#xff0c;所以注入这个接…

48. 旋转图像

给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&…

python实现炫酷的屏幕保护程序

shigen日更文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 上次的文章如何实现一个下班倒计时程序的阅读量很高&#xff0c;觉得也很实用酷炫&#xff0c;下边是昨天的体验…

24 - 内存持续上升,我该如何排查问题?

我想你肯定遇到过内存溢出&#xff0c;或是内存使用率过高的问题。碰到内存持续上升的情况&#xff0c;其实我们很难从业务日志中查看到具体的问题&#xff0c;那么面对多个进程以及大量业务线程&#xff0c;我们该如何精准地找到背后的原因呢&#xff1f; 1、常用的监控和诊断…

机器人制作开源方案 | 智能照科植物花架

作者&#xff1a;付菲菲、于海鑫、王子敏单位&#xff1a;黑河学院指导老师&#xff1a;索向峰、李岩 1. 概述 1.1设计背景​ 随着时代的发展&#xff0c;城市化脚步加快、城市人口密度越来越大、城市生活节奏快压力大作息难成规律。城市建筑建筑面积迅速增加、而绿…

Linux shell编程学习笔记28:脚本调试 set命令

0 引入 在Linux Shell 脚本编程的过程中&#xff0c;编写简单功能的脚本&#xff0c;代码不多&#xff0c;一般阅读起来没什么难度&#xff0c;有问题也比较有查出原因和修正。但是当脚本要实现的功能较多&#xff0c;代码变得较为复杂时&#xff0c;阅读起来就不那么容易看明…