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

news2024/11/19 13:20:53

使用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选中的值

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

公网环境固定域名异地远程访问内网BUG管理系统

文章目录 前言1. 本地安装配置BUG管理系统2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射本地服务3. 测试公网远程访问4. 配置固定二级子域名4.1 保留一个二级子域名5.1 配置二级子域名6. 使用固定二级子域名远程 前言 BUG管理软件,作为软件测试工程师的必备工具之一。在…

【OpenGauss源码学习 —— 列存储(ColumnTableSample)】

执行算子&#xff08;ColumnTableSample&#xff09; 概述ColumnTableSample 类ColumnTableSample::ColumnTableSample 构造函数ColumnTableSample::~ColumnTableSample 析构函数ExecCStoreScan 函数ColumnTableSample::scanVecSample 函数ColumnTableSample::getMaxOffset 函数…

前端实现移动端Tab栏(附带源码)

文章目录 先上图,附带源码index.html 主要视图层Main.css 主要样式demo.css主要的JS,在index.html 引入即可先上图,附带源码 提示:一款JS和CSS3炫酷Tabbar导航栏动画特效。该Tabbar导航栏在点击切换时,会有类似波浪运动的动画效果,非常炫酷。 index.html 主要视图层 &l…

Hadoop-- hdfs

1、HDFS中的三个进程&#xff1a;NameNode&#xff08;NN&#xff09;、DataNode(DN)、SecondNameNode(SNN) 2、NameNode&#xff08;NN&#xff09; 1、作用&#xff1a; 1、接收客户端的一个读、写的服务&#xff0c;在namenode上存储了数据文件和datanode的映射的关系。 …

Python实现WOA智能鲸鱼优化算法优化随机森林回归模型(RandomForestRegressor算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 鲸鱼优化算法 (whale optimization algorithm,WOA)是 2016 年由澳大利亚格里菲斯大学的Mirjalili 等提…

[C语言 数据结构] 栈

1.什么是栈&#xff1f; 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。 压…

Java高级编程-----网络编程

网络通信协议 通过计算机网络可以实现多台计算机连接&#xff0c;但是不同计算机的操作系统和硬件体系结构不同&#xff0c;为了提供通信支持&#xff0c;位于同一个网络中的计算机在进行连接和通信时必须要遵守一定的规则&#xff0c;这就好比在道路中行驶的汽车一定要遵守交…

C++ LibCurl实现Web隐藏目录扫描

LibCurl是一个开源的免费的多协议数据传输开源库&#xff0c;该框架具备跨平台性&#xff0c;开源免费&#xff0c;并提供了包括HTTP、FTP、SMTP、POP3等协议的功能&#xff0c;使用libcurl可以方便地进行网络数据传输操作&#xff0c;如发送HTTP请求、下载文件、发送电子邮件等…

Android AIDL中使用Surface问题

1.构建ITest.aidl文件 package com.xxx.xxxx;import android.view.Surface;interface IMonitorService {boolean addSurface(in Surface surface);boolean removeSurface(in Surface surface); } 2.构建时报错 3.Surface源码分析 android.view.Surface中包含两个Surface类&am…

泛型进阶:通配符

基本概念 对泛型不了解的可以看这篇博客&#xff1a;数据结构前瞻-CSDN博客 一般来说&#xff0c;&#xff1f;在泛型里的使用就是通配符 看看下面的代码 class Message<T> {private T message ;public T getMessage() {return message;}public void setMessage(T m…

新生儿斜视:原因、科普和注意事项

引言&#xff1a; 新生儿斜视是一种儿童眼部常见的问题&#xff0c;指的是眼球的定位不正常&#xff0c;造成双眼无法同时注视同一个物体。了解新生儿斜视的原因、科普相关知识&#xff0c;并提供适当的注意事项&#xff0c;对于早期发现和处理这一问题至关重要。本文将深入探…

通过css设置元素隐藏和显示

背景&#xff1a;鼠标悬浮时显示删除&#xff0c;放开后显示组件名 解决&#xff1a;通过display:none和display:block显示和隐藏元素&#xff1b; 使用 div p选择器选择当前div的下一个紧跟的p元素 <div v-if"!preview" class"name">{{propertyDa…

企业办公室信息安全保密办法——推荐用天锐绿盾数据安全防泄密系统 | 防止核心文件数据、资料泄露

天锐绿盾数据安全防泄密系统是一种有效的办公室信息安全保密办法。该系统采用驱动层透明加密技术&#xff0c;对电子文件进行自动加密&#xff0c;保护数据的安全性和隐私性。 PC端访问地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c…

配置Java环境变量不生效的解决办法

问题&#xff1a; 直接更换Java_HOME的JDK安装路径后&#xff0c;竟然环境变量不生效&#xff0c;在cmd窗口输入java -version或者javac -version后报错&#xff1f;&#xff1f;&#xff1f;这是为什么呢&#xff1f; 问题剖析&#xff1a; 在使用安装版本的JDK程序时&#…

论文阅读:“基于快速特征点提取和描述算法与色调、饱和度和明度的图像特征点匹配算法”

文章目录 摘要引言方法实验结果图像预处理结果对比图像配准结果对比 参考文献 摘要 提出了一种基于快速点特征提取和描述&#xff08;ORB&#xff09;算法与色调、饱和度和明度&#xff08;HSV&#xff09;的图像特征点匹配算法。首先利用双边滤波和均值滤波结合对图像进行预处…

深度剖析倍增算法求解最近公共祖先(LCA)的细枝末节

1. LCA&#xff08;最近公共祖先&#xff09; 倍增算法的基本思想在前面的博文中有较详细的介绍&#xff0c;本文不再复述。此文仅讲解如何使用倍增算法求解多叉树中节点之间的最近公共祖先问题。 什么是最近公共祖先问题&#xff1f; 字面而言&#xff0c;指在树上查询两个…

C++入门第八篇---STL模板---list的模拟实现

前言&#xff1a; 有了前面的string和vector两个模板的基础&#xff0c;我们接下来就来模拟实现一下list链表模板&#xff0c;我还是要强调的一点是&#xff0c;我们模拟实现模板的目的是熟练的去使用以及去学习一些对于我们本身学习C有用的知识和用法&#xff0c;而不是单纯的…

35+大龄程序员从焦虑到收入飙升:我的搞钱副业套路分享

37岁大龄程序员&#xff0c;一度觉得自己的职场生涯到头了。既没有晋升和加薪的机会&#xff0c;外面的公司要么接不住我的薪资&#xff0c;要么就是卷得不行&#xff0c;无法兼顾工作和家庭&#xff0c;感觉陷入了死局…… 好在我又重新振作起来&#xff0c;决定用副业和兼职…

Markdown使用emoji图标【美化你的文章】

Markdown使用emoji图标【美化你的文章】 &#x1f308;笔者的文章美化&#xff0c;图标设计 在撰写文章时&#xff0c;使用 Emoji 图标可以为你的文章增添一些趣味和个性化&#xff0c;让它更加吸引眼球&#xff01;✨✨ 首先&#xff0c;Emoji 图标是一种简单而有趣的方式来…

PostgreSQL中所的锁

为了确保复杂的事务可以安全地同时运行&#xff0c;PostgreSQL提供了各种级别的锁来控制对各种数据对象的并发访问&#xff0c;使得对数据库关键部分的更改序列化。事务并发运行&#xff0c;直到它们尝试获取互相冲突的锁为止(比如两个事务更新同一行时)。当多个事务同时在数据…