Vue2选择器(Select)

简介: 这是一个基于Vue3的选择器组件(VueAmazingSelector),已在npm上发布,方便快捷地通过`yarn add vue-amazing-selector`进行安装。此组件允许全局或局部注册,并提供了丰富的自定义属性,如选项数据、选择器文本字段名、值字段名、默认文字、禁用状态、清除功能等。同时,它还支持调整选择框的尺寸和下拉项数量,并附有详细的使用示例和代码片段,便于开发者快速集成和定制。

该组件已发布到npm,使用时直接安装即可:yarn add vue-amazing-selector

// global use
import Vue from 'vue'
import VueAmazingSelector from 'vue-amazing-selector'
Vue.use(VueAmazingSelector)

// local use
import { VueAmazingSelector } from 'vue-amazing-selector'
export default {
  components: {
    VueAmazingSelector
  }
}

可自定义设置以下属性:

  • 选项数据(options),类型:Array,默认值[]

  • 选择器字典项的文本字段名(label),类型:string,默认 'label'

  • 选择器字典项的值字段名(value),类型:string,默认 'value'

  • 选择框默认文字(placeholder),类型:string,默认 '请选择'

  • 是否禁用下拉(disabled),类型:boolean,默认 false

  • 是否支持清除(allowClear),类型:boolean,默认false

  • 选择框宽度(width),类型:number,默认200px

  • 选择框高度(height),类型:number,默认36px

  • 下拉面板最多能展示的下拉项数,超过后滚动显示(num),类型:number,默认 6

  • 当前选中的option条目(selectedValue)v-model,类型:string | number,默认 null

效果图如下:

展开图如下:

①创建选择器组件VueAmazingSelector.vue:

<template>
  <div class="m-amazing-select" :style="`height: ${height}px;`">
    <div
      :class="['m-select-wrap', {'hover': !disabled, 'focus': showOptions, 'disabled': disabled}]"
      :style="`width: ${width - 2}px; height: ${height - 2}px;`"
      tabindex="0"
      @mouseenter="onInputEnter"
      @mouseleave="onInputLeave"
      @blur="activeBlur && !disabled ? onBlur() : e => e.preventDefault()"
      @click="disabled ? e => e.preventDefault() : openSelect()">
      <div
        :class="['u-select-input', {'placeholder': !selectedName}]"
        :style="`line-height: ${height - 2}px;width: ${width - 37}px; height: ${height - 2}px;`"
        :title="selectedName"
      >{
  { selectedName || placeholder }}</div>
      <svg :class="['triangle', {'rotate': showOptions, 'show': !showClose}]" viewBox="64 64 896 896" data-icon="down" aria-hidden="true" focusable="false"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg>
      <svg @click.stop="onClear" :class="['close', {'show': showClose}]" focusable="false" data-icon="close-circle" aria-hidden="true" viewBox="64 64 896 896"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></svg>
    </div>
    <transition name="fade">
      <div
        v-show="showOptions"
        class="m-options-panel"
        @mouseenter="onEnter"
        @mouseleave="onLeave"
        :style="`top: ${height + 6}px; line-height: ${height - 12}px; max-height: ${ num * (height - 2) }px; width: ${width}px;`">
        <p
          v-for="(option, index) in options" :key="index"
          :class="['u-option', {'option-selected': option[label]===selectedName, 'option-hover': !option.disabled&&option[value]===hoverValue, 'option-disabled': option.disabled }]"
          :title="option[label]"
          @mouseenter="onHover(option[value])"
          @click="option.disabled ? e => e.preventDefault() : onChange(option[value], option[label], index)">
          {
  { option[label] }}
        </p>
      </div>
    </transition>
  </div>
</template>
<script>
export default {
  name: 'VueAmazingSelector',
  model: {
    prop: 'selectedValue',
    event: 'model'
  },
  props: {
    options: { // 选项数据
      type: Array,
      default: () => []
    },
    label: { // 选择器字典项的文本字段名
      type: String,
      default: 'label'
    },
    value: { // 选择器字典项的值字段名
      type: String,
      default: 'value'
    },
    placeholder: { // 选择框默认文字
      type: String,
      default: '请选择'
    },
    disabled: { // 是否禁用下拉
      type: Boolean,
      default: false
    },
    allowClear: { // 是否支持清除
      type: Boolean,
      default: false
    },
    width: { // 选择框宽度
      type: Number,
      default: 200
    },
    height: { // 选择框高度
      type: Number,
      default: 36
    },
    num: { // 下拉面板最多能展示的下拉项数,超过后滚动显示
      type: Number,
      default: 6
    },
    selectedValue: { // 当前选中的option条目(v-model)
      type: [Number, String],
      default: null
    }
  },
  data () {
    return {
      selectedName: null,
      hoverValue: null, // 鼠标悬浮项的value值
      activeBlur: true, // 是否激活blur事件
      showClose: false, // 清除按钮显隐
      showOptions: false // options面板
    }
  },
  watch: {
    options () {
      this.initSelector()
      console.log('options')
    },
    selectedValue () {
      this.initSelector()
      console.log('selectedValue')
    }
  },
  mounted () {
    this.initSelector()
  },
  methods: {
    initSelector () {
      if (this.selectedValue) {
        const target = this.options.find(option => option[this.value] === this.selectedValue)
        if (target) {
          this.selectedName = target[this.label]
          this.hoverValue = target[this.value]
        } else {
          this.selectedName = this.selectedValue
          this.hoverValue = null
        }
      } else {
        this.selectedName = null
        this.hoverValue = null
      }
    },
    onBlur () {
      if (this.showOptions) {
        this.showOptions = false
      }
    },
    onInputEnter () {
  // console.log('input enter')
      if (this.allowClear && this.selectedName) {
        this.showClose = true
      }
    },
    onInputLeave () {
      // console.log('input leave')
      if (this.allowClear && this.showClose) {
        this.showClose = false
      }
    },
    onHover (value) {
      this.hoverValue = value
    },
    onEnter () {
      this.activeBlur = false
    },
    onLeave () {
      this.hoverValue = null
      this.activeBlur = true
    },
    openSelect () {
      this.showOptions = !this.showOptions
      if (!this.hoverValue && this.selectedName) {
        const target = this.options.find(option => option[this.label] === this.selectedName)
        this.hoverValue = target ? target[this.value] : null
      }
    },
    onClear () {
      this.showClose = false
      this.selectedName = null
      this.hoverValue = null
    },
    onChange (value, label, index) { // 选中下拉项后的回调
      if (this.selectedValue !== value) {
        this.selectedName = label
        this.hoverValue = value
        this.$emit('model', value)
        this.$emit('change', value, label, index)
      }
      this.showOptions = false
    }
  }
}
</script>
<style lang="less" scoped>
@themeColor: #1890ff; // 自定义主题色
P {
  margin: 0;
}
.m-amazing-select {
  position: relative;
  display: inline-block;
  font-size: 14px;
  font-weight: 400;
  color: rgba(0,0,0,.65);
}
.fade-enter-active, .fade-leave-active {
  transform: scaleY(1);
  transform-origin: 0% 0%;
  opacity: 1;
  transition: all .3s;
}
.fade-enter {
  transform: scaleY(0.8);
  transform-origin: 0% 0%;
  opacity: 0;
}
.fade-leave-to {
  transform: scaleY(1);
  opacity: 0;
}
.m-select-wrap {
  position: relative;
  display: inline-block;
  border: 1px solid #d9d9d9;
  border-radius: 4px;
  cursor: pointer;
  transition: all .3s cubic-bezier(.645,.045,.355,1);
  .u-select-input {
    display: block;
    text-align: left;
    margin-left: 11px;
    margin-right: 24px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .placeholder {
    color: #bfbfbf;
  }
  .triangle {
    position: absolute;
    top: 0;
    bottom: 0;
    margin: auto 0;
    right: 12px;
    width: 12px;
    height: 12px;
    fill: rgba(0,0,0,.25);
    opacity: 0;
    pointer-events: none;
    transition: all 0.3s ease-in-out;
  }
  .rotate {
    transform: rotate(180deg);
    -webkit-transform: rotate(180deg);
  }
  .close {
    opacity: 0;
    pointer-events: none;
    transition: all 0.3s ease-in-out;
    position: absolute;
    top: 0;
    bottom: 0;
    margin: auto 0;
    right: 12px;
    width: 12px;
    height: 12px;
    fill: rgba(140, 140, 140, 0.6);
    &:hover {
      fill: rgba(100, 100, 100,.8);
    }
  }
  .show {
    opacity: 1;
    pointer-events: auto;
  }
}
.hover { // 悬浮时样式
  &:hover {
    border-color: @themeColor;
  }
}
.focus { // 激活时样式
  border-color: @themeColor;
  box-shadow: 0 0 0 2px fade(@themeColor, 20%);
}
.disabled { // 下拉禁用样式
  color: rgba(0,0,0,.25);
  background: #f5f5f5;
  user-select: none;
  cursor: not-allowed;
}
.m-options-panel {
  position: absolute;
  z-index: 999;
  overflow: auto;
  background: #FFF;
  padding: 4px 0;
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0,0,0,15%);
  .u-option { // 下拉项默认样式
    text-align: left;
    position: relative;
    display: block;
    padding: 5px 12px;
    font-weight: 400;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    cursor: pointer;
    transition: background .3s ease;
  }
  .option-selected { // 被选中的下拉项样式
    font-weight: 600;
    background: #fafafa;
  }
  .option-hover { // 悬浮时的下拉项样式
    // background: #e6f7ff;
    background: saturate(fade(@themeColor, 12%), 30%);
  }
  .option-disabled { // 禁用某个下拉选项时的样式
    color: rgba(0,0,0,.25);
    user-select: none;
    cursor: not-allowed;
  }
}
</style>

②在要使用的页面引入:

<VueAmazingSelector
    :options="options"
    label="label"
    value="value"
    placeholder="请选择城市"
    :disabled="false"
    :width="160"
    :height="36"
    :num="6"
    allowClear
    v-model="selectedValue"
    @change="onChange" />
data () {
    return {
        options: [
        {
          label: '北京市',
          value: 1
        },
        {
          label: '上海市上海市上海市上海市',
          value: 2,
          disabled: true
        },
        {
          label: '郑州市',
          value: 3
        },
        {
          label: '纽约市纽约市纽约市纽约市',
          value: 4
        },
        {
          label: '旧金山',
          value: 5
        },
        {
          label: '悉尼市',
          value: 6
        },
        {
          label: '伦敦市',
          value: 7
        },
        {
          label: '巴黎市',
          value: 8
        }],
        selectedValue: 1
     }
},
watch: {
    selectedValue (to) {
      console.log('selectedValue:', to)
    }
},
mounted () {
    setTimeout(() => { // 模拟接口调用
      this.selectedValue = 3
    }, 1000)
},
methods: {
    onChange (value, label, index) {
      console.log('item:', value, label, index)
    }
}
相关文章
|
11月前
|
JavaScript
Vue2日期选择器插件(vue-datepicker-local)
这是一个基于 Vue 的日期选择器组件库,支持年份、月份、日期和时间的选择,并且均可进行范围选择。用户可以自定义日期格式与组件样式。该示例展示了如何配置组件以限制可选日期范围,并提供了相应的代码实现。
1501 0
Vue2日期选择器插件(vue-datepicker-local)
|
缓存 资源调度
解决安装依赖时报错:npm ERR! code ERESOLVE
解决安装依赖时报错:npm ERR! code ERESOLVE
4877 0
解决安装依赖时报错:npm ERR! code ERESOLVE
|
存储 关系型数据库 MySQL
MySQL中varchar的最大长度是多少
MySQL中varchar的最大长度是多少
1076 0
|
11月前
Vue2分页(Pagination)
本文介绍了如何在 Vue3 中创建一个自定义分页组件(Pagination)。该组件支持传入多个参数,如当前页数、每页条数、是否隐藏单页分页等,并提供了丰富的功能,包括快速跳转、显示数据总量及分页器位置调整。通过示例代码展示了组件的具体实现方式,包括模板结构、计算属性、监听方法及样式设置。此外,还介绍了如何在项目中引入并使用该分页组件。
317 0
Vue2分页(Pagination)
|
11月前
|
移动开发 资源调度 JavaScript
Vue2使用触摸滑动插件(Swiper)
这篇文章介绍了在Vue 3框架中如何使用Swiper插件来创建不同类型的触摸滑动组件,包括轮播图、淡入淡出效果和走马灯效果,并提供了详细的配置选项和使用示例。
766 1
Vue2使用触摸滑动插件(Swiper)
|
缓存 Linux 开发工具
CentOS 7- 配置阿里镜像源
阿里镜像官方地址http://mirrors.aliyun.com/ 1、点击官方提供的相应系统的帮助 :2、查看不同版本的系统操作: 下载源1、安装wget yum install -y wget2、下载CentOS 7的repo文件wget -O /etc/yum.
244307 0
|
3月前
|
运维 NoSQL Cloud Native
国内独家|阿里云首发MongoDB 8.0,性能提升“快”人一步
阿里云作为MongoDB的最佳战略合作伙伴,在国内独家发布了8.0版本,支撑广大用户进一步提升业务效率。
|
11月前
|
资源调度 JavaScript 前端开发
Vue2项目使用v-viewer插件实现图片预览、切换、缩放、旋转...
这篇文章介绍了在Vue 2项目中如何通过`v-viewer`插件实现图片的预览、切换、缩放、旋转等功能,并提供了插件的安装、配置和使用方法。
2843 0
Vue2项目使用v-viewer插件实现图片预览、切换、缩放、旋转...
|
11月前
Vue2时间轴(Timeline)
本文介绍了一个基于 Vue3 的时间轴组件 `Timeline`,允许用户自定义时间轴宽度和描述文本。通过在 `Timeline.vue` 中使用模板、脚本及样式代码,文章展示了如何创建并使用该组件,并提供了示例代码以供参考。
826 2
Vue2时间轴(Timeline)