zoukankan      html  css  js  c++  java
  • vue自定义可输入的选择框组件

    vue自定义可输入的选择框组件

    props:

    属性 说明 类型 默认值
    selectDataList 下拉框中的内容 Array 空数组([])
    value 输入框中的内容 String 空字符串("")
    transfer 是否将下拉框加入到body的直接子节点中,在 Tabs、带有 fixed 的 Table 列内使用时,建议添加此属性,它将不受父级样式影响,从而达到更好的效果 Boolean false
    placeholder 输入框中的提示信息 String 请选择

    事件 events:

    时间名 说明 返回值
    inputChange 在输入框中改变值时才会触发,js改变输入框绑定的值不会触发 输入框中当前值
    selectChange 选择下拉框内容发生改变时触发 当前选择的值

    InputAndSelect.vue 文件

    <style lang="scss" scoped>
      .icon-unfold-transform {
        -webkit-transform: rotate(180deg);
        transform: rotate(180deg);
      }
    
      .select-dropdown {
        position: absolute;
        z-index: 1060;
        will-change: top, left;
        visibility: visible;
        top: 28px;
        left: 0px;
         inherit;
        max-height: 210px;
        padding: 5px 0;
        margin: 5px 0;
        background-color: #fff;
        box-sizing: border-box;
        border-radius: 2px;
        box-shadow: 0 1px 6px rgba(0, 0, 0, .2);
        overflow: auto;
    
        .select-not-found {
          text-align: center;
          color: #999;
        }
    
        .select-dropdown-list {
          .select-item {
            height: 30px;
            line-height: 16px;
            display: flex;
            align-items: baseline;
            margin: 0;
            padding: 7px 12px;
            clear: both;
            color: #333;
            font-size: 12px;
            white-space: nowrap;
            list-style: none;
            cursor: pointer;
    
            transition: all .2s ease-in-out;
          }
        }
      }
    
      .select-input {
         inherit;
        height: 28px;
        position: relative;
        text-align: left;
        box-sizing: border-box;
        outline: 0;
        background-color: #fff;
        border-radius: 2px;
        border: 1px solid #d7dde4;
        -webkit-transition: all .2s ease-in-out;
        transition: all .2s ease-in-out;
    
        > input {
           100%;
          vertical-align: top;
          display: inline-block;
          height: 28px;
          line-height: 28px;
          padding: 0 24px 0 8px;
          font-size: 12px;
          outline: 0;
          border: none;
          -webkit-box-sizing: border-box;
          box-sizing: border-box;
          color: #333;
          background-color: transparent;
          position: relative;
        }
    
        .select-arrow {
          position: absolute;
          right: 0;
          top: -2px;
          height: 28px;
          line-height: 28px;
          padding: 0 10px;
          cursor: pointer;
    
          > i {
            font-size: 12px;
            color: #9ea7b4;
            transition: all .2s ease-in-out;
            display: block;
          }
        }
      }
    
      .select-input-color {
        border-color: #3597f5;
        box-shadow: 0 0 0 2px rgba(41, 141, 255, .2);
      }
    
      .select-input:hover {
        border-color: #3597f5;
      }
    
    </style>
    <template>
      <div style="inherit;">
        <div class="parent" style="position: relative;inherit;">
          <div class="select-input" :class="{'select-input-color':iconUnfoldClass['icon-unfold-transform']}">
            <input class="inputDom" :placeholder="placeholder" v-model="value" type="text" autocomplete="off"
                   @input="inputChange"
            >
            <div @click="changeIconDirection($event)" class="select-arrow">
              <i class="iconfont icon-unfold"
                 :class="iconUnfoldClass"></i>
            </div>
          </div>
          <div class="select-dropdown" :style="{display: iconUnfoldClass['icon-unfold-transform']?'block':'none'}">
    
            <ul class="select-not-found" :style="{display: !isExistedSelectData?'block':'none'}">
              <li>无匹配数据</li>
            </ul>
            <ul class="select-dropdown-list" :style="{display: isExistedSelectData?'block':'none'}">
              <li @click="selectChange(item)" class="select-item" v-for="(item,index) in selectDataList"
                  :key="index">
                {{item.label}}
              </li>
            </ul>
          </div>
        </div>
      </div>
    </template>
    
    <script>
      export default {
        name: "InputAndSelect",
        props: {
          selectDataList: {
            type: Array,
            default: [],
            // 验证必须有label和value
            validator: function (value) {
              return value.every(item => {
                return item.hasOwnProperty("label") && item.hasOwnProperty("value")
              })
            }
          },
          // value 与 this.$emit('input', this.value) 配合实现input 输入框 v-model双向绑定功能
          value: {
            type: String,
            default: "",
          },
          // 是否将下拉框加入到body的直接子节点中
          // 在 Tabs、带有 fixed 的 Table 列内使用时,建议添加此属性,它将不受父级样式影响,从而达到更好的效果
          transfer: {
            type: Boolean,
            default: false,
          },
          placeholder: {
            type: String,
            default: "请选择",
          }
    
        },
        data() {
          return {
            iconUnfoldClass: {
              'icon-unfold-transform': false
            },
            //记录点击切换按钮时对应的事件
            ev: null
          }
        },
        methods: {
          //在输入框中改变值时才会触发,js改变输入框绑定的值不会触发
          inputChange() {
            console.log("inputValue改变了====", this.value)
            this.$emit('input', this.value)
            this.$emit('inputChange', this.value)
          },
          //选择内容发生改变时触发
          selectChange(item) {
            this.value = item.value
            this.iconUnfoldClass["icon-unfold-transform"] = false
            this.$emit('input', this.value)
            this.$emit('selectChange', item)
          },
          changeIconDirection(e) {
            //点击切换图标按钮时,先保存当前点击的事件,在后面监听的click事件中,判断如果不是当前点击的事件,则收起下拉框
            this.ev = e
            this.iconUnfoldClass["icon-unfold-transform"] = !this.iconUnfoldClass["icon-unfold-transform"]
            if (this.iconUnfoldClass["icon-unfold-transform"]) {
              this.checkTransfer()
              let inputDom = this.$el.querySelector(".inputDom")
              inputDom.focus()
            }
          },
          checkTransfer() {
            if (this.transfer) {
              // 组件监听页面resize只能用addEventListener,否则不会生效
              window.addEventListener('resize', this.checkTransfer, false)
              // 监听scroll事件的事件传递必须使用捕获阶段,让外部元素事件先触发
              // document.addEventListener('scroll', this.checkTransfer, true)
    
              //window.innerHeight  document.documentElement.clientHeight 都是获取可是去高度
              //window.innerHeight获取的高度包含横向滚动条,而document.documentElement.clientHeight不包含横向滚动条
              let bodyHeight = document.documentElement.clientHeight // body 可视区域高度
              let matchHeight = this.matchDom.clientHeight // 匹配DOM的高度
              let rect = this.matchParent.getBoundingClientRect() // 取出匹配父级DOM的矩形对象
              // getBoundingClientRect.bottom为元素下边与页面上边的距离,所以元素下边与页面下边距离 = 页面高度 - getBoundingClientRect.bottom
              let bottom = bodyHeight - rect.bottom
              this.matchDom.style.left = rect.left + 'px' // 匹配DOM的left与父级一致
              let dropdownWidth = rect.width + 'px' //下拉框宽度与父级dom一致
              this.matchDom.style.width = dropdownWidth
              if (bottom >= matchHeight) { // 父级距离页面下边的高度大于等于匹配DOM的高度,则往下展示
                this.matchDom.style.bottom = 'auto'
                this.matchDom.style.top = (rect.top + rect.height) + 'px' // 匹配DOM的top = 父级矩形对象top + 父级的高度
              } else { // 父级距离页面下边的高度小于匹配DOM的高度,则往上展示
                this.matchDom.style.top = 'auto'
                this.matchDom.style.bottom = (bottom + rect.height) + 'px' // 匹配DOM的bottom = 父级矩形对象bottom + 父级的高度
              }
            }
          },
          // 点击切换图标按钮以外的其他地方触发,关闭下拉框
          clickOther(e) {
            // 判断如果不是当前点击的事件,则收起下拉框
            if (this.ev == e) return
            if (this.iconUnfoldClass["icon-unfold-transform"]) {
              this.iconUnfoldClass["icon-unfold-transform"] = false
            }
          }
        },
        computed: {
          isExistedSelectData() {
            return this.selectDataList.length > 0 ? true : false
          },
    
          matchDom() { // 匹配框,需要相对于body
            return this.$el.getElementsByClassName('select-dropdown')[0]
          },
          matchParent() { // 匹配框父级
            return this.$el.getElementsByClassName('parent')[0]
          }
    
        },
        created() {
          if (this.transfer) {
            // 将匹配的下拉框从原有位置移到body节点中
            this.$nextTick(() => {
              const body = document.querySelector('body')
              // 将匹配DOM添加到body中
              if (body.append) { // 在IE11中 document.appendChild会报错: javascript runtime error:HierarchyRequestError
                body.append(this.matchDom)
              } else {
                body.appendChild(this.matchDom)
              }
            })
          }
        },
        mounted() {
          window.addEventListener("click", this.clickOther);
        },
      }
    </script>
    
  • 相关阅读:
    若依启动非常慢
    【转】上火了一吃就好的食物
    实型输入控制
    【转】中国地理的几个概念
    如何在阅读邮件时时保留原有html格式正文的样式
    好的代码不是设计出来的,而是重构出来的
    .net用OLEDB方式操作SqlServer和Sybase
    悬浮框实现
    C#操作xml SelectNodes,SelectSingleNode总是返回NULL 与 xPath 介绍
    ODBC驱动重新注册
  • 原文地址:https://www.cnblogs.com/yloved/p/13503789.html
Copyright © 2011-2022 走看看