美文网首页
Vue造轮子-手风琴组件

Vue造轮子-手风琴组件

作者: Ories | 来源:发表于2020-02-01 15:14 被阅读0次

    一. 大致的使用方法

    <div id="app" style="padding-left: 100px">
      <g-collpase>
        <g-collapse-item title="标题1">内容1</g-collapse-item>
        <g-collapse-item title="标题2">内容2</g-collapse-item>
        <g-collapse-item title="标题3">内容3</g-collapse-item>
      </g-collpase>
    </div>
    

    二. 完成最外部的样式

    //collapse
    <style lang="scss" scoped>
      $grey: #ddd;
      $border-radius: 4px;
      .collapse {
        border: 1px solid $grey;
        border-radius: $border-radius;
      }
    </style>
    
    //collapse-item.vue
    <style lang="scss" scoped>
      $grey: #ddd;
      $border-radius: 4px;
      .collapseItem {
        > .title {
          border: 1px solid $grey;
          margin-top: -1px;
          margin-left: -1px;
          margin-right: -1px;
        }
        &:first-child {
          > .title {
            border-top-left-radius: $border-radius;
            border-top-right-radius: $border-radius;
          }
        }
      }
    </style>
    

    三. 进一步调节样式

    // collapse-item
    <style lang="scss" scoped>
      $grey: #ddd;
      $border-radius: 4px;
      .collapseItem {
        > .title {
          border: 1px solid $grey;
          margin-top: -1px;
          margin-left: -1px;
          margin-right: -1px;
          min-height: 32px;
          display: flex;
          align-items: center;
          padding: 0 8px;
        }
        &:first-child {
          > .title {
            border-top-left-radius: $border-radius;
            border-top-right-radius: $border-radius;
          }
        }
        > .content{
          padding: 8px;
        }
      }
    </style>
    
    

    四. 默认内容折叠起来和点击切换,基本样式和基本功能完成

    // collapse-item
        data (){
          return {
            open: false
          }
        }
    
        <div class="content" v-if="open" @click="open=!open">
          <slot></slot>
        </div>
    

    五. 完善功能第一个点开,第二个就关闭

    • 因为结构比较简单,就父子两个组件,可以用父子通信来做。
        mounted(){
          this.eventBus.$on('update:selected', (vm)=>{
            if (vm !== this){
              this.close()
            }
          })
        },
        methods:{
          toggle(){
            if(this.open) {
              this.open = false
            }else{
              this.open = true
              this.eventBus.$emit('update:selected', this)
            }
          },
          close(){
            this.close()
          }
        }
    

    六. 增加功能是否可以选择多个

    • 方案一.用 single 变量是否需要控制 eventBus
        // 添加single选项,有single就注入,没有就不注入
        // 但是这种方式不太完美会有警告
        props: {
          single: {
            type: Boolean,
            default: false
          }
        },
        provide() {
          if(this.single){
            return {
              eventBus: this.eventBus
            }
          }
        }
    

    七. 可以设置默认 selected

    // index.html

        <div id="app" style="padding: 100px">
          <g-collapse selected="2">
            <g-collapse-item title="标题1" name="1">内容1</g-collapse-item>
            <g-collapse-item title="标题2" name="2">内容2</g-collapse-item>
            <g-collapse-Item title="标题3" name="3">内容3</g-collapse-Item>
          </g-collapse>
        </div>
    
    // collapse-item.vue
        mounted(){
          this.eventBus && this.eventBus.$on('update:selected', (name)=>{
            if (name !== this.name){
              this.close()
            }else{
              this.show()
            }
          })
        },
    

    八. 回头解决子元素是否可以多个打开

    • 通过 collapse.vue 传给 collapse-item
    // index.js
      <div id="app" style="padding: 100px">
        <g-collapse :selected="selectedTab" :selected.sync="selectedTab" single>
          <g-collapse-item title="标题1" name="1">内容1 Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus consequatur </g-collapse-item>
          <g-collapse-item title="标题2" name="2">内容2 Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquid cupiditate dolore d! </g-collapse-item>
          <g-collapse-Item title="标题3" name="3"> 内容3 Lorem ipsum dolor sit amet, consectetur adipisicing elit. Excepturi, magnam. </g-collapse-Item>
          {{selectedTab}}
        </g-collapse>
      </div>
    // collapse.vue
     mounted(){
          this.eventBus.$emit('update:selected',this.selected)
          this.eventBus.$on('update:selected', (name)=>{
            this.$emit('update:selected',name)
          })
          this.$children.forEach((vm)=>{
            vm.single = this.single
            console.log(vm)
          })
        }
    // collapse-item.vue
      mounted(){
          this.eventBus && this.eventBus.$on('update:selected', (name)=>{
            if (name !== this.name){
              if(this.single){
                this.close()
              }
            }else{
              this.show()
            }
          })
        },
    

    9.进一步修改,把选中的值改为数组

        // collapse.vue
        mounted(){
          this.eventBus.$emit('update:selected',this.selected)
          this.eventBus.$on('update:addSelected', (name)=>{
            this.selected.push(name)
            this.eventBus.$emit('update:selected',this.selected)  // 通知儿子
            this.$emit('update:selected',this.selected) // 通知外面
          })
          this.eventBus.$on('update:removeSelected', (name)=>{
            let index = this.selected.indexOf(name)
            this.selected.splice(index,1)
            this.eventBus.$emit('update:selected',this.selected)
            this.$emit('update:selected',this.selected)
          })
          this.$children.forEach((vm)=>{
            vm.single = this.single
            console.log(vm)
          })
        }
        // collapse-item.vue
        methods:{
          toggle(){
            if(this.open) {
              this.open = false
              this.eventBus && this.eventBus.$emit('update:removeSelected', this.name)
              // 移除一个被选中的东西
            }else{
              this.eventBus && this.eventBus.$emit('update:addSelected', this.name)
            }
          },
        }
    

    10. 将所有数据流让父组件统一管理。

    • 不能直接操作 props 要先深拷贝
      // collapse.vue
          mounted(){
          this.eventBus.$emit('update:selected',this.selected)
          this.eventBus.$on('update:addSelected', (name)=>{
            let selectedCopy = JSON.parse(JSON.stringify(this.selected))
            if(this.single){
              selectedCopy = [name]
            }else{
              selectedCopy.push(name)
            }
            this.eventBus.$emit('update:selected',selectedCopy)  // 通知儿子
            this.$emit('update:selected',selectedCopy) // 通知外面
          })
          this.eventBus.$on('update:removeSelected', (name)=>{
            let selectedCopy = JSON.parse(JSON.stringify(this.selected))
            let index = selectedCopy.indexOf(name)
            selectedCopy.splice(index,1)
            this.eventBus.$emit('update:selected',selectedCopy)
            this.$emit('update:selected',selectedCopy)
          })
          this.$children.forEach((vm)=>{
            vm.single = this.single
            console.log(vm)
          })
        }
    
    

    11. 数据流的核心

    • 不要出现两个东西互相让对方更新的状态
      // collapse.vue 爸爸
          mounted(){
          this.eventBus.$emit('update:selected',this.selected) // 一开始就通知所有儿子,该选中就选中
          this.eventBus.$on('update:addSelected', (name)=>{
            let selectedCopy = JSON.parse(JSON.stringify(this.selected))
            // 如果用户添加一个我就把selected拷贝一份,因为vue不支持直接修改props
            if(this.single){
              selectedCopy = [name]
            }else{
              selectedCopy.push(name)
            }
            this.eventBus.$emit('update:selected',selectedCopy)  // 得到最新被选中的item之后,通知儿子
            this.$emit('update:selected',selectedCopy) // 通知外面
          })
    
          this.eventBus.$on('update:removeSelected', (name)=>{
            let selectedCopy = JSON.parse(JSON.stringify(this.selected))
            let index = selectedCopy.indexOf(name)
            selectedCopy.splice(index,1)
            this.eventBus.$emit('update:selected',selectedCopy) // 如果用户想移除,也通知他儿子该移除就移除
            this.$emit('update:selected',selectedCopy)
          })
          this.$children.forEach((vm)=>{
            vm.single = this.single
            console.log(vm)
          })
        }¡
        // collapse-item.vue 儿子
            mounted(){
          this.eventBus && this.eventBus.$on('update:selected', (names)=>{
            // 监听eventBus,只要他爸爸要说更新,他就更新
            console.log(names)
            if (names.indexOf(this.name )>= 0){
              this.open = true
            }else{
                this.open = false
            }
          })
        },
        methods:{
          toggle(){
            if(this.open) {
              // 这里也没有修改自己的OPEN,而是在mounted中等爸爸通知我们修改open,所以他的open永远是爸爸在操作,儿子不操作
              this.eventBus &&this.eventBus.$emit('update:removeSelected', this.name)
              // 他自己触发一个意图,打算移除一个更新
              // 移除一个被选中的东西
            }else{
              this.eventBus && this.eventBus.$emit('update:addSelected', this.name)
              // 他自己触发一个意图,打算添加一个更新
            }
          },
        }
    

    最后,个人微信,欢迎交流!

    相关文章

      网友评论

          本文标题:Vue造轮子-手风琴组件

          本文链接:https://www.haomeiwen.com/subject/bicxxhtx.html