vue3.x vant 实现select效果 多选

由于vant移动端组件中,没有提供select组件。下面我封装一个支持可模糊搜素,远程搜素select选择器
效果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
1.封装的组件(vanMulSelectPicker)

<template>
  <div class="vant-mul-select-picker">
    <!-- 模拟单元格样式 -->
    <div class="vant-cell" @click="cellClick">
      <div class="vant-label">{{ $attrs.label }}</div>
      <div class="vant-tag-box">
        <div v-if="state.resultLabel.length == 0">{{ $attrs.placeholder || "请选择" }}</div>
        <van-tag @close="closeTag(index)" v-else size="large" type="primary" closeable v-for="(item, index) in state.resultLabel" :key="index">{{
          item
        }}</van-tag>
      </div>
      <van-icon name="arrow" />
    </div>
    <!-- 弹出层 -->
    <van-popup teleport="body" v-model:show="state.showPicker" position="bottom">
      <div class="van-picker__toolbar">
        <button type="button" class="van-picker__cancel" @click="state.showPicker = false">取消</button>
        <div class="van-ellipsis van-picker__title">{{ $attrs.title }}</div>
        <button type="button" class="van-picker__confirm" @click="confirmClick">确认</button>
      </div>
      <div class="mul-sel__content">
        <van-field v-if="props.isSearch" v-model="state.searchVal" input-align="left" placeholder="搜索" @update:model-value="inputChange" />
        <van-loading  size="30px" vertical v-if="loading">加载中...</van-loading>
        <van-cell title="全选" v-if="checkAllShow">
          <template #right-icon>
            <van-checkbox v-model="state.checkedAll" name="all" @click="toggleAll" />
          </template>
        </van-cell>
        <van-empty image="search" v-if="state.columns.length == 0 && !loading" description="" />
        <van-checkbox-group v-else ref="checkboxGroup" v-model="state.checkboxValue">
          <van-cell-group>
            <van-cell
              v-for="(item, index) in state.columns"
              :key="item[options.value]"
              :title="item[options.label]"
              clickable
              @click.stop="toggle(index)"
            >
              <template #right-icon>
                <van-checkbox shape="square" ref="checkboxes" @click.stop.native="() => {}" :name="item[options.value]" />
              </template>
            </van-cell>
          </van-cell-group>
        </van-checkbox-group>
      </div>
    </van-popup>
  </div>
</template>

<script setup>
import { reactive, ref, watch } from "vue";
import { debounce } from "lodash-es";
const emit = defineEmits(["confirm", "remoteMethed", "update:modelValue", "change"]);
//props
const props = defineProps({
  checkAllShow: Boolean, //全选是否展示
  modelValue: {
    type: Array,
    default: () => {
      return [];
    },
  },
  columns: Array,
  remote: Boolean,
  loading: Boolean,
  options: {
    type: Object,
    default: () => {
      return { label: "label", value: "value" };
    },
  },
  //是否支持搜索
  isSearch: {
    type: Boolean,
    default: false,
  },
});
//data响应式数据
const state = reactive({
  showPicker: false,
  searchVal: "",
  resultLabel: [],
  checkboxValue: props.modelValue,
  columns: [],
  checkedAll: false,
});
//单元格点击 展示弹出层
const cellClick = () => {
  showPopu();
};
//删除选项
const closeTag = (index) => {
  state.checkboxValue.splice(index, 1);
};
//展示弹出层
const showPopu = () => {
  state.showPicker = true;
  //清空搜素栏
  state.searchVal = "";
  state.columns = props.columns;
};
const setResultLabel = () => {
  state.resultLabel = [];
  state.checkboxValue?.forEach((item) => {
    let label = state.columns.find((col) => col[props.options.value] == item)?.[props.options.label];
    let labelTitle = label ? label : item;
    state.resultLabel.push(labelTitle);
  });
};
//确定点击
const confirmClick = () => {
  state.showPicker = false;
  emit("confirm", state.checkboxValue);
};
//单个选择
let checkboxes = ref();
const toggle = (index) => {
  checkboxes.value[index].toggle();
};
//全选
const checkboxGroup = ref();
const toggleAll = () => {
  checkboxGroup.value?.toggleAll(state.checkedAll);
};
//防抖处理 模糊搜索value值改变触发
const inputChange = debounce(() => {
  //获取远程数据
  if (props.remote) {
    emit("remoteMethed", state.searchVal);
    return;
  }
  //获取所有的选项
  if (!state.searchVal) {
    state.columns = props.columns;
  } else {
    state.columns = props.columns.filter((item) => item[props.options.label].indexOf(state.searchVal) > -1);
  }
}, 500);
watch(
  () => state.checkboxValue,
  (newVal) => {
    setResultLabel();
    emit("update:modelValue", newVal);
    emit("change", newVal);
  },
  {
    immediate: true,
    deep: true,
  }
);
watch(
  () => props.columns,
  (newVal) => {
    state.columns = newVal;
  },
  { immediate: true, deep: true }
);
</script>

<style lang="scss" scoped>
.vant-mul-select-picker {
  padding: 0 15px;
}
.vant-cell {
  position: relative;
  display: flex;
  font-size: 14px;
  padding: 10px 0;
  box-sizing: border-box;
  border-bottom: 1px solid #f2f3f5;
  .vant-label {
    width: 100px;
  }
  .vant-tag-box {
    margin-right: 20px;
    color: #c8c9cc;
    width: calc(100% - 120px);
    .van-tag {
      margin-right: 5px;
      margin-bottom: 5px;
    }
  }

  .van-icon-arrow {
    position: absolute;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
    color: #969799;
    font-size: 16px;
  }
}
.mul-sel__content {
  position: relative;
  height: 250px;
  overflow-y: auto;
  padding-bottom: 20px;
  .van-loading {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
  }
}
</style>

注意:需要你安装一下lodash-es (因为远程搜索我加了一个防抖处理)
npm install lodash-es
2.父组件使用

<template>
  <div class="sel-picker">
    <van-mul-select-picker
      label="城市"
      placeholder="请选择城市"
      v-model="state.mulVal"
      :columns="state.columns"
      :options="{ label: 'name', value: 'id' }"
      isSearch
      checkAllShow
      @confirm="confirm"
      @change="change"
    ></van-mul-select-picker>
  </div>
</template>

<script setup lang="ts">
import { reactive } from "vue";
import vanMulSelectPicker from "@/components/vanMulSelectPicker.vue";
let state = reactive({
  mulVal: [],
  columns: [
    { name: "北京", id: "beijing" },
    { name: "上海", id: "shanghai" },
    { name: "深圳", id: "shenzhen" },
    { name: "广州", id: "guangzhou" },
    { name: "杭州", id: "hangzhou" },
    { name: "重庆", id: "chongqing" },
  ],
});
const change = (selVal) => {
  console.log(state.mulVal);
  console.log("selVal", selVal);
};
 const confirm = (item) => {
  console.log(item);
  state.selectItem = item;
};
</script>
<style scoped lang="scss"></style>

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值