基于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...

以上。

相关推荐
我血条子呢2 小时前
【CSS】类似渐变色弯曲border
前端·css
DanyHope2 小时前
LeetCode 两数之和:从 O (n²) 到 O (n),空间换时间的经典实践
前端·javascript·算法·leetcode·职场和发展
hgz07102 小时前
企业级多项目部署与Tomcat运维实战
前端·firefox
zhoumeina992 小时前
懒加载图片
前端·javascript·vue.js
用户1887871069842 小时前
SVG描边 - CSS3实现动画绘制矢量图
前端
码上行走2 小时前
【实战】Flex布局-上下自适应
前端
DarkLONGLOVE2 小时前
Vue的“小外挂”:玩转自定义指令
前端·javascript·vue.js
ccino .2 小时前
pdf-xss文件制作过程
前端·pdf·xss
崇山峻岭之间2 小时前
Matlab学习记录06
前端·学习·matlab