VUE 實現 Studio 管理後臺(五):手風琴式摺疊元件(Accordion)

idlewater發表於2020-03-06

作為一個有目標的人(目標是做一個好用的Bootstrap視覺化編輯器,第一個版本已經實現,演示地址:https://vular.cn/rxeditor/,程式碼地址:https://github.com/vularsoft/rxeditor),工作比較積極,思維也比較活躍,睡眠相對較少。今早6:30就起床了,吃早飯前,實現了一個手風琴式摺疊元件,具體效果如下:

一般情況,這樣的控制元件有兩種表現形式:

1、排他展開,也就是一次只有一個專案展開,其它閉合,類似QQ的好友分組。

2、隨意展開,不拍它,可以同時展開多個

只實現第二種方式,相對比較容易,每個子專案控制自己的狀態就可以,不用跟其它專案互動。我的目標式做一個控制元件,兩種方式都支援,通過屬性引數multiple確定是否可以同時展開多個。

新增新的檔案跟目錄:

跟上次實現Tabs控制元件一樣,有一個通用的子元件:CollapsibleItem,基於這個元件實現摺疊元件:SimpleAccordion。如果以後像實現其它風格的摺疊元件,可以共用CollapsibleItem元件。根據我個人的習慣,首先設想這個元件的使用方式,應該是這樣:

<SimpleAccordion :multiple='true'> 
    <CollapsibleItem>
    <template #heading>標題1</template>
    <template #body>內容1....</template>
    </CollapsibleItem>
    <CollapsibleItem>
    <template #heading>標題2</template>
    <template #body>內容2....</template>
    </CollapsibleItem>
</SimpleAccordion>

按照這樣的需求,先寫SimpleAccordion 的模板程式碼:

<template>
  <div class="simple-accordion">
    <slot></slot>
  </div>
</template>

看起來非常簡單,接下來再寫CollapsibleItem的模板程式碼:

<template>
  <div class="collapsible-item" :class="!isActive ? 'item-collapse' : ''">
    <div class="item-heading" @click="click">
      <slot name="heading"></slot>
    </div>
    <div class="item-body">
      <slot name="body"></slot>
    </div>
  </div>
</template>

兩個slot分別對應每個摺疊項的標題跟內容。如果該項被摺疊的話,那麼放置css class: item-collapse。標題容器DIV接收click事件。指令碼程式碼:

export default {
  name: 'CollapsibleItem',
  props: {
    selected: { default: false}
  },
  data() { return {
      isActive: false };
  },

  methods: {
    click() { this.$emit('itemClick', this)
    }
  },

  mounted() { this.isActive = this.selected;
  }
}

其他部分跟tabs控制元件一樣,並不難理解,需要注意的是click方法,這個方法接收到滑鼠點選事件後,在分發給它的父元件,本例中父元件就是SimpleAccordion。

再看SimpleAccordion的指令碼程式碼:

export default {
  name: 'SimpleAccordion',
  props: {
    multiple: { default: false}
  },
  data() { return {items: [] }
  },
  created() { this.items = this.$children
  },
  mounted () { this.items.forEach(item=>{
      item.$on('itemClick', this.itemClick)
    })
  },
  methods: {
    itemClick(clickedItem) {
      clickedItem.isActive = !clickedItem.isActive if(!this.multiple){ this.items.forEach(item => { if(item !== clickedItem){
            item.isActive = false }
        })
      }
    }
  }
}

在mounted的方法中,在所有的子項上註冊事件“itemClick”,這個事件在子元件中分發。事件處理方法itemClick根據需要,啟用相應的專案。

這樣這個控制元件就完成了,對應的CSS:

  .simple-accordion{
    flex: 1;
    width: 100%;
    overflow: auto;
    height: 0;
    display: flex;
    flex-flow: column;
    margin-top: 2px;
  }
  .simple-accordion .collapsible-item{
    display: flex;
    flex-flow: column;
    width: 100%;
  }
  .simple-accordion .item-heading{
    display: flex;
    flex-flow: row;
    flex-wrap: wrap;
    align-items: center;
    font-size: 12px;
    color:#f0f1ef;
    padding-left: 10px;
    padding-top:10px;
    padding-bottom:10px;
    padding-right:20px;
    position: relative;
    border-top:#484848 solid 1px;
    border-bottom:#282828 solid 1px;
    cursor: default;
  }

  .simple-accordion .item-heading:hover{
    background: #383838;
  }

  .simple-accordion .collapsible-item .item-heading span{
    margin-right: 5px;
  }

  .simple-accordion .collapsible-item .item-heading small{
    white-space:nowrap;
    color: #aaa;
    font-size: 11px;
  }


  .simple-accordion .collapsible-item .item-heading::after{
    position: absolute;
    content: '';
    width: 0; 
    height: 0;
    top: calc(50% -1px);
    right: 19px;
    border-width: 4px;
    border-style: solid;
    border-color: #f0f1ef transparent transparent  transparent;
  }

  .simple-accordion .item-body{
    border-top:#282828 solid 1px;
    padding: 10px;
  }

  .simple-accordion .collapsible-item.item-collapse .item-heading::after{
    position: absolute;
    content: '';
    width: 0; 
    height: 0;
    top: calc(50% -1px);
    right: 17px;
    border-width: 4px;
    border-style: solid;
    border-color:transparent transparent  transparent #f0f1ef;
  }

  .simple-accordion .collapsible-item.item-collapse .item-body{
    display: none;
  }

  .simple-accordion .item-body .element{
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    cursor: move;
    color: #c2c2c2;
    font-size: 13px;
    padding:5px 5px;
    margin:3px;
    display: flex;
    flex-flow: row;
    justify-content: space-between;
    align-items: center;
  }

  .simple-accordion .item-body .element:hover{
    color: #75b325;
    box-shadow: 2px 2px 5px 0px rgba(0, 0, 0, 0.4); 
  }

做這個控制元件時,碰到了一些細小問題,調整了專案的CSS,其中比較重要的有以下幾點:

1、視窗拖動時,拖動把手會被擠沒,在把手的CCS中新增:

flex-shrink: 0;

這樣可以避免handle被擠小擠沒

2、SimpleAccordion寬度設為100%,他會把父元件WidgetTabs撐大,失去拖放效果。後來追蹤到是WidgetTabs的父元素沒有設定寬度,從widgettabs到固定寬度的父容器,全部設定寬度100%:

.left-inner{
  width:100%}
.top-area{
  width:100%}

3、用於顯示展開閉合的箭頭,使用css繪製,具體繪製原理,請自行百度。

整個專案在這個歷史節點的程式碼,請到我的Github上檢視:https://github.com/vularsoft/studio-ui

找到該歷史節點的方法:

RXEditor是一個Boostrap程式碼視覺化編輯工具,本系列記錄了該軟體的開發過程,有問題的朋友請在ithub上給我留言。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章