Vant的checkbox配合popup和input定制选择器

前言

配合PC端用户交互逻辑,移动端需要有类似el-select交互的选择器,即主要功能考虑为:多选,单选,过滤,远程搜索。

效果

Vant的checkbox配合popup和input定制选择器_第1张图片

代码

为了延续el-select的代码逻辑,可以将我们的选择器分成两部分:选项item和外面处理层。

以下为组件代码逻辑

<template>
  <van-cell v-show="visible" clickable :title="currentLabel" @click="toggle()">
    <template #right-icon>
      <van-checkbox ref="checkboxes" :name="currentValue" @click="clickItem">
        <template #icon="props">
          <van-icon v-if="props.checked" name="success" />
          <span v-else></span>
        </template>
      </van-checkbox>
    </template>
  </van-cell>
</template>
<script>
export default {
  name: 'VantSelect',
  props: {
    value: {
      required: true
    },
    label: {
      type: [String, Number],
      default: ''
    },
    created: Boolean,
    disabled: {
      type: Boolean,
      default: false
    }
  },
  inject: ['select'],
  data() {
    return {
      visible: true
    };
  },
  computed: {
    isObject() {
      return Object.prototype.toString.call(this.value).toLowerCase() === '[object object]';
    },

    currentLabel() {
      return this.label || (this.isObject ? '' : this.value);
    },

    currentValue() {
      return this.value || this.label || '';
    }
  },
  created() {
    this.select.checkBox.push(this);
  },
  methods: {
    escapeRegexpString(value = '') {
      return String(value).replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
    },
    queryChange(query) {
      this.visible = new RegExp(this.escapeRegexpString(query), 'i').test(this.currentLabel);
    },
    clickItem(item) {
      this.$emit('click', item);
    },
    toggle() {
      if (!this.select.multiple) this.select.clear();
      this.$refs.checkboxes.toggle();
    }
  }
};
</script>
<style></style>

<!--
* @Author:Min Huang
* @Date: 2023/6/1
* @Desc: 选择器
-->
<template>
  <div class="vant-select">
    <van-popup
      v-model="showPicker"
      position="bottom"
      round
      :style="{ height: '45%' }"
      :close-on-click-overlay="false"
      @click-overlay="close"
    >
      <div>
        <van-search v-if="remote || filterable" v-model="searchText" placeholder="搜索" @input="onSearch"> </van-search>
        <div class="btn-nav">
        	<span @click="close">取消</span>
        	<span @click="onConfirm">确认</span>
        </div>
        <div :style="{ height: filterable ? 'calc(45vh - 2.2rem)' : 'calc(45vh - 1rem)', overflowY: 'scroll' }">
          <van-checkbox-group
            ref="checkBoxGroup"
            v-bind="Object.assign($attrs, bindsData)"
            v-on="$listeners"
            @change="changeItem"
          >
            <van-cell-group>
              <slot :data="{ multiple }"></slot>
            </van-cell-group>
          </van-checkbox-group>
        </div>
      </div>
    </van-popup>
  </div>
</template>
<script>
export default {
  name: 'VantSelect',
  provide() {
    return {
      select: this
    };
  },
  props: {
    showPicker: {
      type: Boolean,
      default: false
    },
    filterable: {
      type: Boolean,
      default: false
    },
    //是否为远程搜索
    remote: {
      type: Boolean,
      default: false
    },
    multiple: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      searchText: '',
      checkBox: []
    };
  },
  computed: {
    bindsData() {
      if (!this.multiple) {
        return {
          max: 1
        };
      }
      return null;
    }
  },
  watch: {},
  methods: {
    changeItem(item) {
      this.$emit('change', item);
    },
    /**
     * 搜索时
     */
    onSearch() {
      if (this.remote) {
        this.$emit('search', this.searchText);
      } else if (this.filterable) {
        this.checkBox.forEach(item => item.queryChange(this.searchText));
      }
    },
    /**
     * 确认回调
     * @param value
     */
    onConfirm() {
      this.searchText = '';
      this.clear();
      this.$emit('update:showPicker', false);
      this.$emit('confirm', this.$refs.checkBoxGroup.value);
    },
    close() {
      this.searchText = '';
      this.clear();
      this.$emit('update:showPicker', false);
      this.$emit('close');
    },
    clear() {
      this.$refs.checkBoxGroup.$emit('input', []);
    }
  }
};
</script>

<style lang="scss" scoped>
.vant-select {
  .van-popup {
    overflow-y: hidden;
  }
}
.search-dev {
  .van-cell {
    font-size: 0.28rem;
    line-height: 0.36rem;
  }
  background: #ffffff;
  position: relative;
  .search-text {
  }
}
.btn-nav {
  margin: 0.28rem;
  display: flex;
  justify-content: space-between;
  font-size: 0.24rem;
  color: #aaa6a6;
}
</style>

使用

<template>
<div>
	<van-button color="#46B0E5" round plain size="small" @click="showPicker = true">
            点击
    </van-button>
	<van-select v-model="value" :show-picker.sync="showPicker" filterable multiple @confirm="confirm">
    	<template>
        	<van-select-item v-for="item in selectList" :key="item.key" :label="item.label" 			:value="item.value">
        	</van-select-item>
    	</template>
	</van-select>
</div>
<script>
export default {
	data() {
		return {
			showPicker: false,
			value: [],
			selectList: [
				{
					key: '1',
					label: '黄油',
					value: '0001'
				},
				{
					key: '2',
					label: '面包',
					value: '0002'
				},
				{
					key: '3',
					label: '拿铁',
					value: '0003'
				},
				{
					key: '4',
					label: '橙汁',
					value: '0004'
				},
				{
					key: '5',
					label: '泡芙',
					value: '0005'
				}
			]
		},
		methods: {
			confirm(res) {
				console.log(res);
			}
		}
	}
}
</script>

扩展

1. icon自定义,可以加个插槽
2. 目前不论单选还是多选,绑定的值都必须是数组,所以可以针对绑定值进行处理,另外添加一个定制值,checkboxGroup的值就放在组件内部进行监听自处理

你可能感兴趣的:(前端,vue.js,javascript)