基于vant3的搜索选择组件

效果如下:

使用:

ini 复制代码
<search-list
  :list="List"
  :isShow="true"
  :isAddShow="true"
  :keyName="key"
  :typeName="name"
  @item-check="itemCheck"
  @show-status="showStatus"
  @call-back="callBack"
/>

参数值:

vbnet 复制代码
List: any[] = [{},{}];
isAddShow: 新增的模块是否显示  不填默认为false
keyName: 搜素及显示的键名
typeName: 分类名
itemCheck: 返回选中数据
showStatus: 返回弹窗是否show  param: boolean
callBack: 点击新增按钮返回数据  param: 搜素框中的输入值

例:

ini 复制代码
List= [
  {
    driverId: '111011101',
    driverName: '司机姓名',
    driverTel: '13112341234',
  }
]
keyName='driverName' 搜素列表显示driverName的数据,搜索页搜索driverName
typeName='司机' 搜索弹窗:【以下不是我的司机】 【点击建立新的司机】
itemCheck: 返回数据为: params: {driverId:'',driverName:'',driverTel:''}
callBack: 返回数据为'司机'

vue:

ini 复制代码
<template>
  <van-popup v-model:show="show" round position="bottom" :closeable="true" :close-icon="closeImg">
    <div class="list-box">
      <div class="top-box">
        <p class="title">请选择{{ typeName }}</p>
        <div class="search-box">
          <van-search
            v-model="searchValue"
            show-action
            autocomplete="off"
            autofocus
            placeholder="请输入搜索关键词"
            @search="onSearch"
            @clear="onClear"
          >
            <template #action>
              <div @click="onSearch" class="search-btn">搜索</div>
            </template>
          </van-search>
        </div>
      </div>

      <div class="list-box">
        <van-list finished-text="没有更多了">
          <van-cell class="add-box" v-if="addShow ? addShow : false">
            <div class="cell-title">以下不是我的{{ typeName }}</div>
            <div class="cell-info" @click="target">
              点击建立
              <span class="blue">新的{{ typeName }}</span>
            </div>
          </van-cell>
          <template v-for="item in listArr" :key="item">
            <van-cell @click="checkItem(item)">
              <span
                v-for="res in item[keyName]"
                :key="res"
                :class="
                  searchValue.length > 0 &&
                  searchValue.indexOf(res) > -1 &&
                  item[keyName].includes(searchValue)
                    ? 'active'
                    : ''
                "
              >
                {{ res }}
              </span>
            </van-cell>
          </template>
        </van-list>
      </div>
    </div>
  </van-popup>
</template>

ts:

ini 复制代码
<script lang="ts">
  import { defineComponent, ref, watch, watchEffect } from 'vue';
  import { Popup, Cell, Search, List, Toast } from 'vant';

  import closeImg from '/@/assets/images/close-1.png';

  export default defineComponent({
    name: 'SearchDriver',
    components: {
      [Popup.name]: Popup,
      [Cell.name]: Cell,
      [Search.name]: Search,
      [List.name]: List,
    },
    props: {
      list: {
        type: Array,
        required: true,
        default: () => {
          return [''];
        },
      },
      isShow: {
        type: Boolean,
        default: false,
      },
      keyName: {
        type: String,
        default: '',
      },
      typeName: {
        type: String,
        default: '司机',
      },
      isAddShow: {
        type: Boolean,
        default: false,
      },
    },
    emits: ['itemCheck', 'showStatus', 'callBack'],
    setup(props, { emit }) {
      const searchValue = ref<string>('');
      // 数组
      let propList = ref<string[]>(props.list as string[]);
      let newList = ref<string[]>(props.list as string[]);
      // 弹窗是否显示
      let show = ref<boolean>(props.isShow);
      // 新增模块是否显示
      let addShow = ref<boolean>(props.isAddShow);
      // 列表显示数据
      let listArr = ref<any>();
      // 类型
      let type = ref<string>(props.typeName);

      const dataMap = (arr: any[]) => {
        if (!arr || arr.length < 1) {
          return [];
        }
        return arr;
      };

      listArr.value = dataMap(propList.value);

      const onSearch = () => {
        console.log(searchValue.value);
      };

      // 点击新增按钮后
      const target = () => {
        emit('callBack', searchValue.value);
      };

      // 过滤掉不符合条件的数据
      const filterData = (list: any[]) => {
        newList.value = list.filter(
          (item) => item[props.keyName] && item[props.keyName].indexOf(searchValue.value) > -1
        );
        listArr.value = dataMap(newList.value);
      };

      // 选中数据
      const checkItem = (item: any) => {
        if (!item || item.length < 1) {
          Toast('请重新选择');
          return;
        }
        searchValue.value = item[props.keyName];
        show.value = false;
        emit('itemCheck', item);
        emit('showStatus', false);
      };

      const onClear = () => {
        searchValue.value = '';
        show.value = false;
        emit('itemCheck', '');
        emit('showStatus', false);
      };

      watchEffect(() => {
        show.value = props.isShow;
        propList.value = props.list as string[];
        newList.value = props.list as string[];
        type.value = props.typeName;
        filterData(newList.value);
      });

      watch(show, () => {
        emit('showStatus', show.value);
      });

      watch(type, (old, newValue) => {
        if (old !== newValue) {
          searchValue.value = '';
        }
      });

      watch(searchValue, (val, oldVal) => {
        const len = val.length;
        const oldLen = oldVal.length;
        if (len > oldLen) {
          // 写入
          filterData(newList.value);
        } else {
          // 删除
          filterData(propList.value);
        }
      });

      return {
        searchValue,
        show,
        listArr,
        onSearch,
        onClear,
        checkItem,
        target,
        addShow,
        closeImg,
      };
    },
  });
</script>

scss:

css 复制代码
@charset "UTF-8";

$topHeight: 160px;
$vnoTop: 36px;
$checkColor: #1777f2;

.list-box {
  position: relative;
  min-height: 50vh;
  padding-top: $vnoTop;

  .top-box {
    position: absolute;
    top: $vnoTop;
    left: 0;
    z-index: 1;
    width: 100%;
    height: $topHeight;
    background-color: #fff;
    border-bottom: 1px solid #e5e5e5;

    .title {
      margin-left: 40px;
      font-size: 36px;
      line-height: 50px;
      color: rgb(0 0 0 / 85%);
    }

    .search-box {
      width: 696px;
      margin-left: 15px;

      .search-btn {
        font-size: 28px;
        font-weight: 400;
        color: $checkColor;
      }
    }
  }

  .list-box {
    height: 76vh;
    margin-top: $topHeight;
    overflow: auto;

    .add-box {
      padding-left: 84px;
      background-image: url('../../assets/images/search.png');
      background-position: 40px 30px;
      background-repeat: no-repeat;
      background-size: 30px 30px;

      .cell-title {
        font-size: 32px;
        font-weight: 400;
        color: #1c1c1c;
      }

      .cell-info {
        font-size: 28px;
        font-weight: 400;
        color: rgb(0 0 0 / 25%);

        .blue {
          color: #1777f2;
        }
      }
    }
  }

  .van-cell {
    position: relative;

    &::before {
      position: absolute;
      right: var(--van-padding-md);
      bottom: 0;
      left: var(--van-padding-md);
      pointer-events: none;
      border-bottom: 1px solid var(--van-cell-border-color);
      content: ' ';
      transform: scaleY(0.5);
      box-sizing: border-box;
    }

    span.active {
      color: $checkColor;
    }
  }
}

博客园:www.cnblogs.com/wttt123/p/1...

以上。

相关推荐
一袋米扛几楼987 分钟前
【Git】规范化协作:详解 GitHub 工作流中的 Issue、Branch 与 Pull Request 最佳实践
前端·git·github·issue
网络点点滴20 分钟前
前端与后端的区别与联系
前端
EnCi Zheng1 小时前
M5-markconv自定义CSS样式指南 [特殊字符]
前端·css·python
kyriewen1 小时前
你的网页慢,用户不说直接走——前端性能监控教你“读心术”
前端·性能优化·监控
广州华水科技1 小时前
北斗GNSS变形监测在大坝安全监测中的应用与优势分析
前端
前端老石人1 小时前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html
CAE虚拟与现实1 小时前
五一假期闲来无事,来个前段、后端的说明吧
前端·后端·vtk·three.js·前后端
Sarvartha1 小时前
三目运算符
linux·服务器·前端
晓晨的博客1 小时前
ROS1录制的bag包转换为ROS2格式
前端·chrome
Wect1 小时前
LeetCode 72. 编辑距离:动态规划经典题解
前端·算法·typescript