精华内容
下载资源
问答
  • 点击项目工程,其他的标题也都跟着展示出来了 代码是这么写的 ``` (item, index) in navData" :key="index"> :title="`${item.title}(${item.size})`" :border-intent="false" is-link :arrow-direction=...
  • vue实现下拉效果

    千次阅读 2019-05-14 11:22:22
    手把手教你实现vue下拉菜单组件 96 阿乾哥 0.4 2019.02.26 00:20* 字数 2050 阅读 930评论 0喜欢 3 这篇文章我们一起来实现一个vue下拉菜单组件。 像这种基本UI组件,网上已经有很多了,为什么要自己实现呢?其实...

    手把手教你实现vue下拉菜单组件
    96 阿乾哥
    0.4 2019.02.26 00:20* 字数 2050 阅读 930评论 0喜欢 3

    这篇文章我们一起来实现一个vue的下拉菜单组件。
    像这种基本UI组件,网上已经有很多了,为什么要自己实现呢?其实并不是有意重复造轮子,而是想通过这个过程回顾一下vue组件开发的一些细节和注意事项。

    为什么选择下拉菜单组件?
    因为:麻雀虽小五脏俱全,这个小小的组件涉及到了不少vue组件开发的知识点。
    好了,那就开始吧!

    首先创建一个vue-cli的项目,笔者用的是vue-cli3,创建过程略,然后创建一个vue组件:DropDownList.vue

    在编写模板之前,我们来分析一下这个组件的视图结构和功能。
    下拉菜单组件应该由两部分组成:

    选中项的文本
    待选菜单(默认隐藏)
    

    它的主要功能包括:

    鼠标经过下拉菜单组件,显示待选菜单
    鼠标滑出下拉菜单组件,隐藏待选菜单
    鼠标点击待选菜单中的条目,选中项文本更新,组件派发change事件
    我们编写如下这样的模板:
    

    选中项文本右侧的i标签,用来实现下拉菜单的三角形图标,在下文的css中我们用背景图来实现。
    我们给根元素div已经添加了鼠标经过和滑出的回调函数,具体实现见下文。

    接下来我们为这个下拉菜单编写样式,在模板下方添加style标签,为了防止和其他组件的样式发生冲突,笔者建议大家在开发组件时,都给style加上scoped属性。另外,笔者在这里用到了scss,具体代码如下:

    关于样式,这里就不详细展开了,只说其中几个需要注意的点:

    那个i元素的样式,我用到了一个网络图片,大家可以自行更换
    待选菜单ul在css里并没有让它隐藏,因为我们要通过js来控制,具体原因见下文
    待选菜单ul使用了绝对定位,因为当它展开的时候,不应该影响页面上其他元素的布局
    

    现在这个组件大概长这个样子:

    1.png

    我们继续为这个组件定义属性,很显然,待选菜单应该作为属性传进来,一定不能是内部写死的,属性定义如下:

    这个页面引入并使用了我们的DropDownList组件,:dataList=“dplist” 绑定了当前页面的dplist数组到组件的dataList属性上,这个数组中的对象有一个city字段,我们希望此字段显示在下拉菜单上,因此我们设置组件的labelProperty为city,我们还给这个组件注册了change事件,这个组件内部需要派发这个事件,见下文。

    现在我们回到组件的模板部分,发现它都还是静态内容,我们把这些静态内容修改为通过属性渲染。

    其中待选菜单li的文本是 item[labelProperty] 这样就能正确的显示开发者指定的字段了。
    我们看看选中项的文本表达式:dplLabel,我们并没有定义这个属性,也没有定义这个内部数据,它是哪儿来的?选中项的文本应该是 dataList[activeIndex][labelProperty] (这个很好理解吧,有问题请留言),但这个表达式太长了,写在模板里不利于维护,我们就把它写到计算属性里吧。

    computed:{
    dplLable(){
    return this.dataList[this.activeIndex][this.labelProperty]
    }
    }

    于是才有了上面的dplLabel,计算属性真的很好用呢。

    现在下拉菜单的视图和数据关联部分我们已经写完了,接下来我们要实现它的功能。

    第一步是先让待选菜单默认隐藏起来,这里我们为什么不直接用css的display:none呢,然后鼠标经过的时候display:block不就可以了吗?因为这样的话,我们无法实现点击待选菜单条目的时候让它隐藏,体验不好。我们用js来控制,但vue对直接访问dom元素支持的并不好,我们要想在组件初始化的时候访问dom元素,有一个最方便的做法,那就是:自定义指令。

    我们为下拉菜单组件添加局部自定义指令,代码如下:

    directives:{
    dpl:{
    bind(el){
    el.style.display = “none”;
    }
    }
    },

    这个dpl就是自定义指令啦,请忽略我笨拙的命名哈!然后我们在自定义指令的钩子函数bind方法中,访问el元素,控制它的style属性display:none; 最后,把这个自定义指令加到模板里面的ul标签上。别忘了要加v-,现在看看效果,待选菜单已经隐藏了。

      我们利用自定义指令钩子函数访问dom元素,实现了对dom的控制,这一点非常实用!

      让我们继续实现最开始为下拉菜单定义的鼠标经过和鼠标滑出的监听,实现待选菜单的显示与隐藏。

      onDplOver(event){
      let ul = event.currentTarget.childNodes[1];
      ul.style.display = “block”;
      },
      onDplOut(event){
      let ul = event.currentTarget.childNodes[1];
      ul.style.display = “none”;
      },

      我们在鼠标事件中,访问event的currentTarget对象,为什么不是target?因为下拉菜单的子元素也会触发这个事件,如果访问target,可能不会是我们预期的顶层元素。

      最后一步,我们实现待选菜单条目的点击事件,点击后,待选菜单隐藏,修改内部状态,派发change事件。

      onLiClick(index){
      let path = event.path || (event.composedPath && event.composedPath()) //兼容火狐和safari
      path[1].style.display = “none”;
      this.activeIndex = index;
      this.$emit(“change”, {
      index:index,
      value:this.dataList[index]
      })
      }

      这里有一个细节需要注意,我们要通过li元素找到外层ul元素,但path不支持火狐和safari,好在这两个浏览器支持composedPath,因此才有了第一行代码的兼容写法。然后通过修改内部数据activeIndex实现选中项文本的更新,最后调用emit方法向父元素派发change事件,别忘了把事件对象封装好传出去。

      完整的代码如下:

      以上为大家展示了vue如何实现一个下拉菜单组件,虽然比较简单,但也基本涉及到了组件开发常用的一些特性。

      展开全文
    • <template> <div class="home" v-cloak> <div class="...下拉菜单</p> <div class="main" v-clickoutside=&q
      <template>
          <div class="home" v-cloak>
             
              <div class="menu">
                  <p>下拉菜单</p>
                  <div class="main" v-clickoutside="handleClose">
                      <button @click="show = !show">点击显示下拉菜单</button>
                      <div class="dropdown" v-show = "show"> 
                          <p>下拉框的内容,点击外面区域可以关闭</p>
                      </div>
                  </div>
              </div>
          </div>
      </template>
      <script>
      export default {
          data() {
              return {
                 
                  show:false
              };
          },
         
              // 下拉菜单 
              handleClose(){
                  this.show = false;
              }
          },
               //    自定义指令clickoutside绑定了一个函数handleClose用来关闭菜单
          directives:{
              clickoutside:{
                  bind:function(el,binding,vnode){
                      function documentHandler(e){
                          if(el.contains(e.target)){
                              return false;
                          }
                          if(binding.expression){
                              binding.value(e)
                          }
                      }
                      el._vueClickOutside_ = documentHandler;
                      document.addEventListener('click',documentHandler);
                  },
                  unbind:function(el,binding){
                      document.removeEventListener('click',el._vueClickOutside_);
                      delete el._vueClickOutside_;
                  }
              }
          }
          
      };
      </script>
      <style lang="less" scoped>
          // 作用是取消数据绑定时出现的代码闪烁
          [v-cloak] {
              display: none;
          }
          table {
              border: 1px solid #e9e9e9;
              border-collapse: collapse;
              border-spacing: 0;
              // 隐藏表格中空单元格上的边框和背景:
              empty-cells: show;
          }
          th,
          td {
              padding: 8px 16px;
              border: 1px solid #e9e9e9;
              text-align: left;
          }
          th {
              background: #f7f7f7;
              color: #5c6b77;
              font-weight: 600;
              white-space: nowrap;
          }
          .menu{
              border: 1px solid #ccc;
          }
      </style>
      
      
      
      
      

       

      展开全文
    • vue移动端下拉刷新、上拉加载

      千次阅读 2018-09-07 10:58:53
      由于自身的项目比较简单,只有几个H5页面,用来嵌入app中... 1、下拉刷新DropDownRefresh.vue &lt;template lang="html"&gt; &lt;div class="refreshMoudle" @touchstart="...

      由于自身的项目比较简单,只有几个H5页面,用来嵌入app中,所有没有引入移动端的UI框架,但是介于能让用户在浏览H5页面时有下拉刷新和上拉加载,有更好的用户体验,自己写组件实现。

      Refresh&Load

      1、下拉刷新DropDownRefresh.vue

      <template lang="html">
          <div class="refresh-moudle" @touchstart="touchStart($event)" @touchmove="touchMove($event)" @touchend="touchEnd($event)" :style="{transform: 'translate3d(0,' + top + 'px, 0)'}">
            <header class="pull-refresh">
              <slot name="pull-refresh">
                <div class="down-tip" v-if="dropDownState==1">
                  <img v-if="dropDownInfo.downImg" class="down-img" :src="require('../../assets/images/refreshAndReload/'+dropDownInfo.downImg)">
                  <span class="down-text">{{dropDownInfo.downText}}</span>
                </div>
                <div class="up-tip" v-if="dropDownState==2">
                  <img v-if="dropDownInfo.upImg" class="up-img" :src="require('../../assets/images/refreshAndReload/'+dropDownInfo.upImg)">
                  <span class="up-text">{{dropDownInfo.upText}}</span>
                </div>
                <div class="refresh-tip" v-if="dropDownState==3">
                  <img v-if="dropDownInfo.refreshImg" class="refresh-img" :src="require('../../assets/images/loading/'+dropDownInfo.refreshImg)">
                  <span class="refresh-text">{{dropDownInfo.refreshText}}</span>
                </div>
              </slot>
            </header>
            <slot></slot>
          </div>
      </template>
      <script>
      export default {
        props: {
          onRefresh: {
            type: Function,
            required: false
          }
        },
        data () {
          return {
            defaultOffset: 50, // 默认高度, 相应的修改.releshMoudle的margin-top和.down-tip, .up-tip, .refresh-tip的height
            top: 0,
            scrollIsToTop: 0,
            startY: 0,
            isDropDown: false, // 是否下拉
            isRefreshing: false, // 是否正在刷新
            dropDownState: 1, // 显示1:下拉可以刷新, 2:松开立即刷新, 3:正在刷新数据中...
            dropDownInfo: {
              downText: '下拉可以刷新',
              downImg: 'arrow.png',
              upText: '松开立即刷新',
              upImg: 'arrow.png',
              refreshText: '正在刷新数据...',
              refreshImg: 'loading.png'
            }
          }
        },
        created () {
          if (document.querySelector('.down-tip')) {
            // 获取不同手机的物理像素(dpr),以便适配rem
            this.defaultOffset = document.querySelector('.down-tip').clientHeight || this.defaultOffset
          }
        },
        methods: {
          /**
           * 触摸开始,手指点击屏幕时
           * @param {object} e Touch 对象包含的属性
           */
          touchStart (e) {
            this.startY = e.targetTouches[0].pageY
          },
      
          /**
           * 接触点改变,滑动时
           * @param {object} e Touch 对象包含的属性
           */
          touchMove (e) {
            this.scrollIsToTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop // safari 获取scrollTop用window.pageYOffset
            if (e.targetTouches[0].pageY > this.startY) {
              // 下拉
              this.isDropDown = true
              if (this.scrollIsToTop === 0 && !this.isRefreshing) {
                // 拉动的距离
                let diff = e.targetTouches[0].pageY - this.startY - this.scrollIsToTop
                this.top = Math.pow(diff, 0.8) + (this.dropDownState === 3 ? this.defaultOffset : 0)
                if (this.top >= this.defaultOffset) {
                  this.dropDownState = 2
                  e.preventDefault()
                } else {
                  this.dropDownState = 1
                  // 去掉会导致ios无法刷新
                  e.preventDefault()
                }
              }
            } else {
              this.isDropDown = false
              this.dropDownState = 1
            }
          },
      
          /**
           * 触摸结束,手指离开屏幕时
           * @param {object} e Touch 对象包含的属性
           */
          touchEnd (e) {
            if (this.isDropDown && !this.isRefreshing) {
              if (this.top >= this.defaultOffset) {
                // do refresh
                this.refresh()
                this.isRefreshing = true
              } else {
                // cancel refresh
                this.isRefreshing = false
                this.isDropDown = false
                this.dropDownState = 1
                this.top = 0
              }
            }
          },
      
          /**
           * 刷新
           */
          refresh () {
            this.dropDownState = 3
            this.top = this.defaultOffset
            // 这是全是静态数据,延时1200毫秒,给用户一个刷新的感觉,如果是接口数据的话,直接调接口即可
            setTimeout(() => {
              this.onRefresh(this.refreshDone)
            }, 1200)
          },
      
          /**
           * 刷新完成
           */
          refreshDone () {
            this.isRefreshing = false
            this.isDropDown = false
            this.dropDownState = 1
            this.top = 0
          }
        }
      }
      </script>
      
      <!-- Add "scoped" attribute to limit CSS to this component only -->
      <style scoped>
      .refresh-moudle {
        width: 100%;
        margin-top: -100px;
        -webkit-overflow-scrolling: touch; /* ios5+ */
      }
      
      .pull-refresh {
        width: 100%;
        color: #999;
        transition-duration: 200ms;
        font-size: 24px;
      }
      
      .refresh-moudle .down-tip,
      .up-tip,
      .refresh-tip {
        display: flex;
        align-items: center;
        justify-content: center;
        height: 100px;
      }
      
      .down-img {
        width: 35px;
        height: 35px;
        margin-right: 15px;
        transform: rotate(0deg);
        animation: anticlockwise 0.8s ease;
      }
      
      @keyframes anticlockwise {
        0% {
          transform: rotate(-180deg);
        }
        100% {
          transform: rotate(0deg);
        }
      }
      
      .up-img {
        width: 35px;
        height: 35px;
        margin-right: 15px;
        transform: rotate(180deg);
        animation: clockwise 0.8s ease;
      }
      
      @keyframes clockwise {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(-180deg);
        }
      }
      
      .refresh-img {
        width: 35px;
        height: 35px;
        margin-right: 15px;
        animation: rotating 1.5s linear infinite;
      }
      
      @keyframes rotating {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(1turn);
        }
      }
      </style>
      

      2、上拉加载PullUpReload.vue

      <template lang="html">
        <div class="load-moudle" @touchstart="touchStart($event)" @touchmove="touchMove($event)" @touchend="touchend($event)">
          <slot></slot>
          <footer class="load-more">
            <slot name="load-more">
              <div class="more-tip" v-if="pullUpState==1">
                <span class="more-text">{{pullUpInfo.moreText}}</span>
              </div>
              <div class="loading-tip" v-if="pullUpState==2">
                <span class="loading-icon"></span>
                <span class="loading-text">{{pullUpInfo.loadingText}}</span>
              </div>
              <div class="no-more-tip" v-if="pullUpState==3">
                <span class="connecting-line"></span>
                <span class="no-more-text">{{pullUpInfo.noMoreText}}</span>
                <span class="connecting-line"></span>
              </div>
            </slot>
          </footer>
        </div>
      </template>
      
      <script>
      export default {
        props: {
          parentPullUpState: {
            default: 0
          },
          onInfiniteLoad: {
            type: Function,
            require: false
          }
        },
        data () {
          return {
            top: 0,
            pullUpState: 0, // 1:上拉加载更多, 2:加载中……, 3:我是有底线的
            isLoading: false, // 是否正在加载
            pullUpInfo: {
              moreText: '上拉加载更多',
              loadingText: '数据加载中...',
              noMoreText: '我是有底线的'
            },
            startX: 0,
            startY: 0,
            endX: 0,
            endY: 0
          }
        },
        methods: {
          /**
           * 触摸开始,手指点击屏幕时
           * @param {object} e Touch 对象包含的属性
           */
          touchStart (e) {
            this.startX = e.touches[0].pageX
            this.startY = e.touches[0].pageY
          },
      
          /**
           * 接触点改变,滑动时
           * @param {object} e Touch 对象包含的属性
           */
          touchMove (e) {
            this.endX = e.changedTouches[0].pageX
            this.endY = e.changedTouches[0].pageY
            let direction = this.getSlideDirection(this.startX, this.startY, this.endX, this.endY)
            switch (direction) {
              case 0:
                // console.log('没滑动')
                break
              case 1:
                // console.log('向上')
                this.scrollToTheEnd()
                break
              case 2:
                // console.log('向下')
                break
              case 3:
                // console.log('向左')
                break
              case 4:
                // console.log('向右')
                break
              default:
            }
          },
      
          /**
           * 触摸结束,手指离开屏幕时
           * @param {object} e Touch 对象包含的属性
           */
          touchend (e) {
            this.isLoading = false
          },
      
          /**
           * 判断滚动条是否到底
           */
          scrollToTheEnd () {
            let innerHeight = document.querySelector('.load-moudle').clientHeight
            // 变量scrollTop是滚动条滚动时,距离顶部的距离
            let scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
            // 变量scrollHeight是滚动条的总高度
            let scrollHeight = document.documentElement.clientHeight || document.body.scrollHeight
            // 滚动条到底部的条件
            if (scrollTop + scrollHeight >= innerHeight) {
              if (this.pullUpState !== 3 && !this.isLoading) {
                this.infiniteLoad()
              }
              // console.log('距顶部' + scrollTop + '滚动条总高度' + scrollHeight + '内容高度' + innerHeight)
            }
          },
      
          /**
           * 上拉加载数据
           */
          infiniteLoad () {
            if (this.pullUpState !== 0) {
              this.pullUpState = 2
              this.isLoading = true
              this.onInfiniteLoad(this.infiniteLoadDone)
            }
          },
      
          /**
           * 加载数据完成
           */
          infiniteLoadDone () {
            this.pullUpState = 1
          },
      
          /**
           * 返回角度
           */
          getSlideAngle (dx, dy) {
            return Math.atan2(dy, dx) * 180 / Math.PI
          },
      
          /**
           * 根据起点和终点返回方向 1:向上,2:向下,3:向左,4:向右,0:未滑动
           * @param {number} startX X轴开始位置
           * @param {number} startY X轴结束位置
           * @param {number} endX Y轴开始位置
           * @param {number} endY Y轴结束位置
           */
          getSlideDirection (startX, startY, endX, endY) {
            let dy = startY - endY
            let dx = endX - startX
            let result = 0
            // 如果滑动距离太短
            if (Math.abs(dx) < 2 && Math.abs(dy) < 2) {
              return result
            }
            let angle = this.getSlideAngle(dx, dy)
            if (angle >= -45 && angle < 45) {
              result = 4
            } else if (angle >= 45 && angle < 135) {
              result = 1
            } else if (angle >= -135 && angle < -45) {
              result = 2
            } else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {
              result = 3
            }
            return result
          }
        },
        watch: {
          parentPullUpState (curVal, oldVal) {
            this.pullUpState = curVal
          }
        }
      }
      </script>
      
      <!-- Add "scoped" attribute to limit CSS to this component only -->
      <style scoped>
      .load-more {
        width: 100%;
        color: #c0c0c0;
        background: #fafafa;
        font-size: 24px;
      }
      
      .more-tip,
      .loading-tip,
      .no-more-tip {
        display: flex;
        align-items: center;
        justify-content: center;
        height: 150px;
      }
      
      .load-moudle .loading-icon {
        display: inline-flex;
        width: 35px;
        height: 35px;
        background: url(../../assets/images/refreshAndReload/loading.png) no-repeat;
        background-size: cover;
        margin-right: 5px;
        animation: rotating 2s linear infinite;
      }
      
      @keyframes rotating {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(1turn);
        }
      }
      
      .connecting-line {
        display: inline-flex;
        width: 150px;
        height: 2px;
        background: #ddd;
        margin-left: 20px;
        margin-right: 20px;
      }
      </style>
      

      3、对两个组件的使用

      <template>
        <section class="container">
          <v-refresh :on-refresh="onRefresh">
            <v-reload
              :on-infinite-load="onInfiniteLoad"
              :parent-pull-up-state="infiniteLoadData.pullUpState"
            >
              <div class="bank-box">
                <div class="bank-list" v-for="item in bankList" :key="item.id">
                  <div
                    class="bank-icon"
                    :style="{ 'background': 'url(' + require('../../assets/images/bankIcon/'+item.iconName) + ') no-repeat', 'background-size': '100%' }"
                  ></div>
                  <span class="bank-name">{{item.bankName}}</span>
                </div>
              </div>
              <div class="hot-box">
                <div class="hot-header">
                  <span class="hot-name">热门推荐</span>
                  <div class="more-box">
                    <span class="more-text">查看更多</span>
                    <span class="more-icon"></span>
                  </div>
                </div>
                <div class="hot-centenrt">
                  <div class="hot-left">
                    <span class="left-name">{{hotLeft.name}}</span>
                    <span class="left-desc">{{hotLeft.desc}}</span>
                    <div
                      class="left-img"
                      :style="{ 'background': 'url(' + require('../../assets/images/bank/'+hotLeft.imgName) + ') no-repeat', 'background-size': '100%' }"
                    ></div>
                  </div>
                  <div class="hot-right">
                    <div class="right-top">
                      <div class="text-box">
                        <span class="right-name">{{centenrtOne.name}}</span>
                        <span class="right-desc">{{centenrtOne.desc}}</span>
                      </div>
                      <div
                        class="right-img"
                        :style="{ 'background': 'url(' + require('../../assets/images/bank/'+centenrtOne.imgName) + ') no-repeat', 'background-size': '100%' }"
                      ></div>
                    </div>
                    <div class="hot-right-bottom">
                      <div class="text-box2">
                        <span class="right-name2">{{centenrtTwo.name}}</span>
                        <span class="right-desc2">{{centenrtTwo.desc}}</span>
                      </div>
                      <div
                        class="right-img"
                        :style="{ 'background': 'url(' + require('../../assets/images/bank/'+centenrtTwo.imgName) + ') no-repeat', 'background-size': '100%' }"
                      ></div>
                    </div>
                  </div>
                </div>
              </div>
              <div class="card-state">
                <div class="card-progress border-right">
                  <div class="progress-icon"></div>
                  <div class="card-text">
                    <span class="card-name">办卡进度</span>
                    <span class="card-desc">让等待随处可见</span>
                  </div>
                </div>
                <div class="card-activation">
                  <div class="activation-icon"></div>
                  <div class="card-text">
                    <span class="card-name">办卡激活</span>
                    <span class="card-desc">让等待随处可见</span>
                  </div>
                </div>
              </div>
              <div class="card-order">
                <div class="border-bottom card-bottom">
                  <div class="hot-header">
                    <span class="hot-name">热卡排行</span>
                  </div>
                </div>
                <div slot="load-more">
                  <li
                    class="card-list"
                    v-for="(item,index) in infiniteLoadData.pullUpList"
                    :key="item.id"
                  >
                    <div
                      class="card-content"
                      :class="infiniteLoadData.pullUpList.length - 1 != index? 'card-bottom':''"
                    >
                      <div
                        class="card-img"
                        :style="{ 'background': 'url(' + require('../../assets/images/bank/'+item.imgName) + ') no-repeat', 'background-size': '100%' }"
                      ></div>
                      <div class="card-list-text">
                        <p class="card-name">{{item.cardName}}</p>
                        <p class="card-title">{{item.cardTitle}}</p>
                        <div class="words-lists">
                          <div class="card-words">
                            <p class="card-word">{{item.cardWordOne}}</p>
                          </div>
                          <div v-if="item.cardWordTwo" class="card-words words-two">
                            <p class="card-word">{{item.cardWordTwo}}</p>
                          </div>
                        </div>
                      </div>
                    </div>
                  </li>
                </div>
              </div>
            </v-reload>
          </v-refresh>
        </section>
      </template>
      
      <script>
      import DropDownRefresh from '../common/DropDownRefresh'
      import PullUpReload from '../common/PullUpReload'
      export default {
        data () {
          return {
            bankList: [
              {
                iconName: 'zhaoshang.png',
                bankName: '招商银行'
              },
              {
                iconName: 'minsheng.png',
                bankName: '民生银行'
              },
              {
                iconName: 'pingancar.png',
                bankName: '平安联名'
              },
              {
                iconName: 'xingye.png',
                bankName: '兴业银行'
              },
              {
                iconName: 'shanghai.png',
                bankName: '上海银行'
              },
              {
                iconName: 'jiaotong.png',
                bankName: '交通银行'
              },
              {
                iconName: 'guangda.png',
                bankName: '光大银行'
              },
              {
                iconName: 'more.png',
                bankName: '全部银行'
              }
            ],
            hotLeft: {
              bankName: '交通银行',
              name: '交行Y-POWER黑卡',
              desc: '额度100%取现',
              imgName: 'jiaohangY-POWER.png'
            },
            centenrtOne: {
              bankName: '招商银行',
              name: '招行YOUNG卡',
              desc: '生日月双倍积分',
              imgName: 'zhaohangYOUNG.png'
            },
            centenrtTwo: {
              bankName: '光大银行',
              name: '光大淘票票公仔联名卡',
              desc: '电影达人必备',
              imgName: 'guangdalianming.png'
            },
            cardList: [
              {
                bankName: '平安联名',
                imgName: 'pinganqiche.png',
                cardName: '平安银行信用卡',
                cardTitle: '平安银行汽车之家联名单币卡',
                cardWordOne: '首年免年费',
                cardWordTwo: '加油88折'
              },
              {
                bankName: '上海银行',
                imgName: 'shanghaitaobao.png',
                cardName: '上海银行信用卡',
                cardTitle: '淘宝金卡',
                cardWordOne: '积分抵现',
                cardWordTwo: '首刷有礼'
              },
              {
                bankName: '华夏银行',
                imgName: 'huaxiaiqiyi.png',
                cardName: '华夏银行信用卡',
                cardTitle: '华夏爱奇艺悦看卡',
                cardWordOne: '送爱奇艺会员',
                cardWordTwo: '商城8折'
              },
              {
                bankName: '浦发银行',
                imgName: 'pufajianyue.png',
                cardName: '浦发银行信用卡',
                cardTitle: '浦发银行简约白金卡',
                cardWordOne: '团购立减',
                cardWordTwo: '酒店优惠 免年费'
              },
              {
                bankName: '中信银行',
                imgName: 'zhongxinbaijin.png',
                cardName: '中信银行信用卡',
                cardTitle: '中信银行i白金信用卡',
                cardWordOne: '首刷有礼',
                cardWordTwo: '双倍积分'
              }
            ],
            // 上拉加载的设置
            infiniteLoadData: {
              initialShowNum: 3, // 初始显示多少条
              everyLoadingNum: 3, // 每次加载的个数
              pullUpState: 2, // 子组件的pullUpState状态
              pullUpList: [], // 上拉加载更多数据的数组
              showPullUpListLength: this.initialShowNum // 上拉加载后所展示的个数
            }
          }
        },
        mounted () {
          this.getStartPullUpState()
          this.getPullUpDefData()
        },
        methods: {
          /**
           * 获取上拉加载的初始数据
           */
          getPullUpDefData () {
            this.infiniteLoadData.pullUpList = []
            if (this.cardList.length < this.infiniteLoadData.initialShowNum) {
              for (let i = 0; i < this.cardList.length; i++) {
                this.infiniteLoadData.pullUpList.push(this.cardList[i])
              }
            } else {
              for (let i = 0; i < this.infiniteLoadData.initialShowNum; i++) {
                this.infiniteLoadData.pullUpList.push(this.cardList[i])
              }
            }
            this.getStartPullUpState()
          },
      
          /**
           * 获取上拉加载的pullUpState状态
           */
          getStartPullUpState () {
            if (this.infiniteLoadData.pullUpList.length) {
              if (this.cardList.length <= this.infiniteLoadData.initialShowNum) {
                // 修改子组件的pullUpState状态
                this.infiniteLoadData.pullUpState = 3
              } else {
                this.infiniteLoadData.pullUpState = 1
              }
            } else {
              this.infiniteLoadData.pullUpState = 0
            }
          },
      
          /**
           * 上拉一次加载更多的数据
           */
          getPullUpMoreData () {
            this.showPullUpListLength = this.infiniteLoadData.pullUpList.length
            if (this.infiniteLoadData.pullUpList.length + this.infiniteLoadData.everyLoadingNum > this.cardList.length) {
              for (let i = 0; i < this.cardList.length - this.showPullUpListLength; i++) {
                this.infiniteLoadData.pullUpList.push(this.cardList[i + this.showPullUpListLength])
              }
            } else {
              for (let i = 0; i < this.infiniteLoadData.everyLoadingNum; i++) {
                this.infiniteLoadData.pullUpList.push(this.cardList[i + this.showPullUpListLength])
              }
            }
            if (this.cardList.length === this.infiniteLoadData.pullUpList.length) {
              this.infiniteLoadData.pullUpState = 3
            } else {
              this.infiniteLoadData.pullUpState = 1
            }
          },
      
          /**
           * 下拉刷新
           */
          onRefresh (done) {
            // 如果下拉刷新和上拉加载同时使用,下拉时初始化上拉的数据
            this.getStartPullUpState()
            this.getPullUpDefData()
            done() // call done
          },
      
          /**
           * 上拉加载
           */
          onInfiniteLoad (done) {
            if (this.infiniteLoadData.pullUpState === 1) {
              this.getPullUpMoreData()
            }
            done()
          }
        },
        components: {
          'v-refresh': DropDownRefresh,
          'v-reload': PullUpReload
        }
      }
      </script>
      
      <!-- Add "scoped" attribute to limit CSS to this component only -->
      <style scoped>
      @import "../../assets/css/not2rem.css";
      .container {
        display: flex;
        flex-direction: column;
        width: 750px;
        height: 1334px;
        background-color: #f7f7f7;
      }
      
      .bank-box {
        display: flex;
        flex-wrap: wrap;
        padding: 2px 7px 42px 7px;
        background-color: #fff;
      }
      
      .bank-list {
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        width: 100px;
        height: 98px;
        margin: 40px 42px 0 42px;
      }
      
      .bank-icon {
        width: 56px;
        height: 56px;
        margin: 0 22px 18px;
      }
      
      .bank-name {
        display: flex;
        align-items: center;
        width: 110px;
        height: 24px;
        line-height: 24px;
        font-size: 24px;
        color: #333;
      }
      
      .hot-box {
        width: 100%;
        height: 420px;
        margin-top: 10px;
        background: #fff;
      }
      
      .hot-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        width: 674px;
        height: 80px;
        margin: 0 30px 0 46px;
      }
      
      .hot-name {
        display: flex;
        align-items: center;
        height: 28px;
        font-size: 28px;
        color: #333;
      }
      
      .more-text {
        display: flex;
        align-items: center;
        font-size: 24px;
        color: #999;
      }
      
      .more-box {
        display: flex;
        align-items: center;
      }
      
      .more-icon {
        margin-left: 20px;
        width: 11px;
        height: 20px;
        background: url("../../assets/images/bank/more.png") no-repeat;
        background-size: 100%;
      }
      
      .hot-centenrt {
        display: flex;
        align-items: center;
        width: 710px;
        height: 320px;
        margin: 0 20px 20px 20px;
      }
      
      .hot-left {
        display: flex;
        flex-direction: column;
        width: 350px;
        height: 320px;
        background: #f7f7f7;
      }
      
      .left-name {
        display: flex;
        align-items: center;
        width: 282px;
        height: 24px;
        margin: 50px 34px 0 34px;
        font-size: 24px;
        line-height: 24px;
        color: #333;
      }
      
      .left-desc {
        display: flex;
        align-items: center;
        width: 282px;
        height: 20px;
        margin: 12px 34px 0 34px;
        font-size: 20px;
        line-height: 20px;
        color: #999;
      }
      
      .left-img {
        width: 220px;
        height: 142px;
        margin-left: 34px;
        margin-top: 34px;
      }
      
      .hot-right {
        display: flex;
        flex-direction: column;
        width: 350px;
        height: 320px;
        margin-left: 10px;
      }
      
      .right-top {
        display: flex;
        flex-direction: row;
        width: 100%;
        height: 156px;
        background: #f7f7f7;
      }
      
      .text-box {
        display: flex;
        flex-direction: column;
        width: 180px;
        height: 58px;
        margin: 49px 20px 0 20px;
      }
      
      .right-name {
        display: flex;
        align-items: center;
        width: 100%;
        height: 24px;
        line-height: 24px;
        font-size: 24px;
        color: #333;
      }
      
      .right-desc {
        display: flex;
        align-items: center;
        margin-top: 10px;
        width: 100%;
        height: 24px;
        line-height: 24px;
        font-size: 24px;
        color: #999;
      }
      
      .right-img {
        width: 110px;
        height: 70px;
        margin-top: 43px;
      }
      
      .hot-right-bottom {
        display: flex;
        flex-wrap: wrap;
        width: 100%;
        height: 156px;
        margin-top: 8px;
        background: #f7f7f7;
      }
      
      .text-box2 {
        display: flex;
        flex-direction: column;
        width: 180px;
        margin: 31px 20px 0 20px;
      }
      
      .right-name2 {
        display: flex;
        align-items: center;
        width: 100%;
        height: 58px;
        line-height: 30px;
        font-size: 24px;
        color: #333;
      }
      
      .right-desc2 {
        display: flex;
        align-items: center;
        margin-top: 12px;
        width: 100%;
        height: 24px;
        line-height: 24px;
        font-size: 24px;
        color: #999;
      }
      
      .card-state {
        display: flex;
        flex-direction: row;
        width: 100%;
        height: 128px;
        margin-top: 10px;
        background-color: #fff;
      }
      .card-progress {
        display: flex;
        align-items: center;
        width: 327px;
        height: 88px;
        margin: 20px 0 20px 48px;
      }
      .progress-icon {
        width: 48px;
        height: 48px;
        margin: 20px 0;
        background: url("../../assets/images/bank/search.png") no-repeat;
        background-size: 100%;
      }
      .activation-icon {
        width: 48px;
        height: 48px;
        margin: 20px 0;
        background: url("../../assets/images/bank/activation.png") no-repeat;
        background-size: 100%;
      }
      .card-text {
        display: flex;
        flex-direction: column;
        align-items: center;
        width: 228px;
        height: 66px;
        margin: 11px 20px 11px 30px;
      }
      .card-name {
        display: flex;
        align-items: center;
        width: 100%;
        height: 28px;
        line-height: 28px;
        font-size: 28px;
        color: #333;
      }
      .card-desc {
        display: flex;
        align-items: center;
        width: 100%;
        height: 22px;
        line-height: 22px;
        font-size: 22px;
        margin-top: 16px;
        color: #999;
      }
      .card-activation {
        display: flex;
        align-items: center;
        width: 326px;
        height: 88px;
        margin: 20px 0 20px 48px;
      }
      
      .card-order {
        width: 100%;
        height: auto;
        margin-top: 10px;
        background-color: #fff;
      }
      .border-bottom {
        width: 100%;
        height: 80px;
      }
      .card-list {
        width: 100%;
        height: 228px;
        list-style-type: none;
      }
      .card-content {
        display: flex;
        flex-direction: row;
        width: 700px;
        height: 228px;
        margin-left: 50px;
      }
      .card-img {
        width: 186px;
        height: 120px;
        margin: 54px 0 54px 20px;
      }
      .card-list-text {
        display: flex;
        flex-direction: column;
        width: 386px;
        height: 124px;
        margin: 52px 34px 52px 74px;
      }
      .card-name {
        width: 100%;
        height: 28px;
        line-height: 28px;
        font-size: 28px;
        color: #333;
      }
      .card-title {
        width: 100%;
        height: 24px;
        margin-top: 20px;
        line-height: 24px;
        font-size: 24px;
        color: #666;
      }
      .words-lists {
        display: flex;
        flex-direction: row;
      }
      .card-words {
        height: 36px;
        margin-top: 16px;
        border-radius: 20px;
        background-color: #e8ca88;
      }
      .card-word {
        height: 20px;
        padding: 8px 18px;
        line-height: 20px;
        font-size: 20px;
        color: #4b4b4b;
      }
      .words-two {
        margin-left: 20px;
      }
      </style>
      

      这里只是展示了一下效果,使用的全是静态数据,如果要用接口数据的话,this.getPullUpMoreData()方法直接换成接口数据的方法,并将判断的逻辑移到接口方法里,当然根据实际情况,分页呀啥的做做适当的修改。

      展开全文
    • 今天在项目中遇到一个需求,就是vue下拉树只能选择第三级选项,为了解决这个问题,查阅了官方文档 https://vue-treeselect.js.org/#disable-item-selection 然后看到isDisabled属性可以禁止选择,具体实现代码...

      今天在项目中遇到一个需求,就是vue下拉树只能选择第三级选项,为了解决这个问题,查阅了官方文档

      https://vue-treeselect.js.org/#disable-item-selection

       

      然后看到isDisabled属性可以禁止选择,具体实现代码如下:

      <treeselect 
         :disable-branch-nodes="true"
         :normalizer="normalizer" 
         v-model="formData.goodsTypeId" 
         :multiple="false" 
         :options="goodsType" 
         placeholder='请选择'>
         <label slot="option-label" slot-scope="{ node, shouldShowCount, count, labelClassName, 
           countClassName }">
                {{ node.label }}
          </label>
      </treeselect>

      如何给数据添加isDisabled属性呢?

      自己尝试着在下拉树配置中添加了一下,竟然成功了

      【提示】node这个对象包含的字段打印了一下,(这些数据是后台接口提供的)如下图展示:

      【isLeaf】Y是叶子节点,N是分支节点【typeLevel】第?级

      normalizer(node) {
         //去掉children=[]的children属性
         if(node.children && !node.children.length) {
            delete node.children;
         }
         if(node.isLeaf=='Y' && node.typeLevel!=3){
            node['isDisabled'] = true;
         }
         return {
            id: node.typeId,
            label: node.typeName,
            children: node.children,
         }             
      },

      展开全文
    • 使用vue实现简单的select下拉选择 看看效果 下面看看代码如何实现: 1.代码结构: index.js文件 注册全局组件 TySelect.vue文件 样式代码: 参数说明 1.父组件传递给子组件的参数 optiions ...
    • 下图这种地区搜索方式在很多app中都很常见,今天就使用vue框架中的 better-scroll 第三方包来实现页面滚动和点击侧边栏字母该字母开头的地区列表置顶功能。1、A子组件通过使用 this.$emit(事件名字,事件携带内容) ...
    • 在使用阿里的 Ant Design Vue 框架时,在使用有下拉菜单 a-select 等组件,用在 a-drawer 等组件或者页面上的时候,会发现 a-drawer 等组件能滚动的时候,a-select 等组件的下拉菜单不会跟着走,停留在原地。...
    • 在methods里定义showPicker这个方法,来控制isShowDropdown的值为true或者false,来控制下拉菜单是否展示 3、主要代码 <div> <button @click="showPicker"/> <div id="myDropdown" v-show=...
    • 下拉表格类似于展开是3,合起是2,这样子: 安装 npm install --save vue-table-with-tree-grid 安装完成后,需要进行导入及注册(我放置的位置为main.js): import TreeTable from 'vue-table-with-tree-...
    • 主要介绍了vue实现select下拉显示隐藏功能,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    • 在做项目的路上总是bug不断,上手公司的项目,然后去修改一个功能,需求大概是这个样子的,通过一个下拉菜单实现一个table的显示,并且新建table中的行或者修改table表头的下拉菜单可以将那一行的单价和单位两个属性...
    • 使用antd-desgin-vue框架中的select选择器时,由于要展示给用户是中文,自己筛选条件时需要其中的key值,而官网给出的change事件中返回的value就是中文,这个并不符合我们筛选条件,所以我们需要获取到每个下拉项的...
    • 这个上传资源是为了配合我的博客 http://blog.csdn.net/zhangchao19890805/article/details/72808639。运行项目之前建议使用 yarn install 安装依赖。
    • vue实现select下拉显示隐藏功能【详细&&功能拓展】

      万次阅读 热门讨论 2019-03-11 14:50:19
      需求描述:下拉选择不同的属性,需展示对应的内容; select下拉菜单如下: 当下拉选择【表结构变更】、即展示如下界面: 当下拉选择【接口变更】、即展示如下界面: 代码实现 vue.js: //定义一个select下拉菜单 ...
    • Vue 做新闻展示

      千次阅读 2017-06-12 19:07:22
      需求1. 做一个新闻展示页 ...5. 点击进入详情页,再返回时,定位到原来的位置 图片展示 采用的技术轮播图使用:swiper zepto.js vue.js vue.resource.js vue-router.js 滑动插件:iscroll.j
    • 我总共百度了三种方法,本来使用插件各种报错,不得已自己重写了一个表格,后面有空闲时间了,自己研究了下,这些插件都是可行的,只是要注意下使用的姿势 ...仿照方法,将TreeGrid.vue直接复制进项目里面 ...
    • 需求:等高瀑布流+下拉加载+点击图片放大 老板发布一个需求,需要做一个等高瀑布流图片展示, What?这么简单,还不是随随便便弄个插件就好了,在试过vue-waterfall、vue-waterfall-easy、vue-waterfall2后,我发现...
    • VUE 下拉菜单动态获取

      万次阅读 2018-03-26 11:19:15
      //点击其他地方,隐藏下拉菜单 this.bodyLister=(e)=>{ if(this.isShow==true){ this.isShow=false; } } document.addEventListener('click',this.bodyLister); }, watch:{ name:{...
    • VUE动态展示左侧菜单 在我们实际项目开发中经常会有这样的需求,不同的用户登录系统展示不同的菜单权限, 1.效果图 管理员登录系统 普通用户登录系统 普通用户的权限比管理员的权限小,所有没有展示系统管理、...
    • vue封装select下拉组件

      2020-06-01 10:18:25
      }, //下拉框显示的数据 sel: { type: Array, required: true //必传 } }, data() { return { isshow: true, //控制下拉框显示及隐藏 xz: "请选择" //默认展示的内容 }; }, methods: { //点击换文字的方法 cutValue...
    • vue cascader级联下拉和数据回显

      千次阅读 2020-04-26 18:21:58
      最近在做项目的时候,有个部门级联下拉功能的实现,首次接触,然后根据el-cascader这个组件查看他的属性和方法,在进行数据回显得时候,我用了最笨的方法,就是获取所有的部门数据,然后根据这条数据的部门ID进行...
    • //将想要展示的数据作为value } list = res.content; callback(list); }); }, // 模糊查询下拉框 鼠标键盘选中点击触发 accountHandleSelect(item) { this.accountSearch = item.value; this.accountSearchCode = ...
    • 项目上使用Vue+elementUI-select多选下拉框做数据编辑时,数据回显成功,但是无法对回显成功数据进行操作。 代码片段: <template> <el-form-item label="标签:" prop="values"> <el-select v...
    • vue实现safari下拉选择滚轮效果

      千次阅读 2018-08-17 10:32:50
      vue使用iosSelect实现safari下拉菜单的滚轮效果,这里根据其例子实现地区选择的三级联动滚轮效果 可以不执行npm install --save iosSelect,直接去github上下载源码https://github.com/zhoushengmufc/iosselect 我的...
    • input式的下拉列表的东西 由于极其不爽这个input的下拉列表的样式所以自己写了一个 这个三角的东西是用的图片所以 自己弄一个上去吧 <template> <div class="drop-down-list" :style = "getStyle" ...
    • vue.js 结合 vue-waterfall-easy 实现瀑布流 + 下拉刷新

      千次阅读 热门讨论 2018-10-15 16:48:31
      最近的项目需求首先页面展示是需要瀑布流的,然后还要有下拉刷新功能,我本以为通过 vue-waterfall-easy 实现了瀑布流后再使用其他 ui 库中的下拉刷新就能轻而易举的实现该效果,但事实并非如此,在尝试了多种方法未...
    • 主要介绍了vue实现鼠标移过出现下拉二级菜单功能,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下

    空空如也

    空空如也

    1 2 3 4 5 ... 20
    收藏数 2,855
    精华内容 1,142
    关键字:

    vue点击下拉展示

    vue 订阅