基於@vueuse/core + @vue/composition-api 實現多行元素展開/折疊功能


列表查詢是很常見的功能,復雜的系統模塊會有很多查詢條件,默認只展示第一行查詢條件是個不錯的方案。

產品的方案是百變的,可能會讓你一行展示固定數目的查詢組件,也可能是根據屏幕寬度動態確定數量。

這個時候就很有必要來一個專門處理展開/折疊功能的組件,專注於展開/折疊功能。內部布局用css控制就行了。

實現方案靈感來至於https://segmentfault.com/a/1190000040030723

基於vue2+ @vueuse/core + @vue/composition-api 實現

廢話不多說,直接上代碼

  1 <template>
  2   <!-- 折疊容器
  3        'is-collapse': isCollapse 根據isCollapse判斷是否應用折疊樣式
  4   -->
  5   <div
  6     ref="containerEl"
  7     class="collapse-container"
  8     :class="{
  9       'is-collapse': isCollapse,
 10       'collapse-right-bottom': collapseInRightBottom
 11     }"
 12   >
 13     <!-- 通過控制max-height來達到折疊/展開的效果 -->
 14     <div class="collapse-el" :class="isExpand ? 'expand' : ''">
 15       <!-- 利用css使折疊組始終在最右側 -->
 16       <div class="right-group box-row box-center-end">
 17         <slot name="prefix"></slot>
 18         <div @click="isExpand = !isExpand">
 19           <slot v-if="isCollapse" name="collapse" v-bind="{ isExpand }">
 20             <el-button type="text" class="expand-btn">
 21               {{ isExpand ? '收起' : '展開' }}
 22               <i
 23                 :class="isExpand ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"
 24               ></i>
 25             </el-button>
 26           </slot>
 27         </div>
 28         <slot name="suffix"></slot>
 29       </div>
 30 
 31       <!-- 折疊面板 -->
 32       <div ref="collapsePanel" class="panel-el">
 33         <slot></slot>
 34       </div>
 35     </div>
 36   </div>
 37 </template>
 38 <script>
 39 import { defineComponent, ref } from '@vue/composition-api';
 40 import { useElementSize, useCssVar } from '@vueuse/core';
 41 
 42 import { find } from 'lodash';
 43 /**
 44  *
 45  */
 46 export default defineComponent({
 47   name: 'UxCollapse',
 48   props: {
 49     // 是否默認展開
 50     defaultExpand: {
 51       type: Boolean,
 52       default: false
 53     },
 54     // 折疊容器是否始終在右下角
 55     collapseInRightBottom: {
 56       type: Boolean,
 57       default: true
 58     }
 59   },
 60   setup() {
 61     // 動態獲取collapsePanel的高度
 62     // 要監聽的元素必須是ref引用的元素
 63     const collapsePanel = ref(null);
 64     const { height } = useElementSize(collapsePanel);
 65 
 66     // 支持js設置var變量
 67     // var變量設置元素位置,必須是ref引用的元素
 68     const containerEl = ref(null);
 69     const maxHeight = useCssVar('--max-height', containerEl);
 70     return {
 71       collapsePanel,
 72       height,
 73       containerEl,
 74       maxHeight
 75     };
 76   },
 77   data() {
 78     return {
 79       // 是否開啟折疊/展開功能(自動計算獲取)
 80       isCollapse: false,
 81       // 折疊面板是否展開(isCollapse為true時生效)
 82       isExpand: false
 83     };
 84   },
 85 
 86   watch: {
 87     height() {
 88       // console.log('height', v);
 89       // 高度變化時,重新計算是否開啟折疊功能
 90       this.adjustLayout();
 91     },
 92     isExpand(v) {
 93       // 拋出事件
 94       this.$emit('expandchange', v);
 95     }
 96   },
 97 
 98   methods: {
 99     /**
100      * @description  計算是否開啟折疊/展開功能
101      */
102     adjustLayout() {
103       const me = this;
104       me.$nextTick(() => {
105         // 獲取容器dom
106         const el = me.$el;
107         if (el) {
108           // 延遲執行保證獲取到子元素
109           setTimeout(() => {
110             // 獲取折疊面對dom
111             const collapsePanel = me.$refs.collapsePanel;
112             const children = collapsePanel.children;
113             // 如果折疊面板有子元素
114             if (children.length) {
115               // 獲取第一個元素的offsetTop
116               const firstOffsetTop = collapsePanel.firstChild.offsetTop;
117               // 獲取下一行第一個元素
118               const nextRowChild = find(children, (item) => {
119                 return item.offsetTop > firstOffsetTop;
120               });
121               // 如果找不到下一行第一個元素,說明無需折疊
122               this.isCollapse = !!nextRowChild;
123               if (nextRowChild) {
124                 // 用下一行第一個元素的offsetTop減去第一個元素的offsetTop就是每一行的高
125                 const maxHeight = nextRowChild.offsetTop - firstOffsetTop;
126                 // 設置css變量
127                 this.maxHeight = maxHeight + 'px';
128                 // 設置默認展開狀態
129                 this.isExpand = this.defaultExpand;
130               }
131             }
132           });
133         }
134       });
135     }
136   }
137 });
138 </script>
139 <style scoped lang="scss">
140 @import '~@/assets/sass/all.scss';
141 
142 // 開啟折疊/展開功能后容器樣式
143 .is-collapse {
144   // flex布局高度計算才能使用百分百
145   display: flex;
146   // 收起/展開按鈕
147   .expand-btn {
148     padding: 0;
149   }
150 }
151 .collapse-right-bottom {
152   .collapse-el {
153     // 偽類動態設置高度實現將折疊組擠到最下方的效果
154     &::before {
155       content: '';
156       height: calc(100% - var(--max-height));
157       float: right;
158     }
159   }
160 }
161 
162 // 折疊容器
163 ::v-deep.collapse-container {
164   --max-height: none;
165   width: 100%;
166   .collapse-el {
167     width: 100%;
168     overflow: hidden;
169     // 通過設置最大高度來實現折疊時只顯示一行的效果
170     max-height: var(--max-height);
171     &.expand {
172       // 展開狀態取消最大高度限制,實現展開效果
173       max-height: none;
174     }
175   }
176   .right-group {
177     line-height: var(--max-height);
178     // 保證折疊組始終在最右方
179     float: right;
180     clear: both;
181   }
182 }
183 
184 // 面板默認布局
185 ::v-deep.panel-el {
186   .item-el {
187     display: inline-block;
188   }
189 }
190 </style>

簡單用法

1 <template>
2   <uxCollapse>
3     <div v-for="(item, i) in data" :key="i" class="item-el">{{ item }}</div>
4   </uxCollapse>
5 </template>

 

 1 <script>
 2 import { defineComponent } from '@vue/composition-api';
 3 
 4 import uxCollapse from '@/components/collapse';
 5 export default defineComponent({
 6   name: 'vueuseCollapse',
 7   components: { uxCollapse },
 8   data() {
 9     return {
10       data: ['張三', '李四', '王五', '趙六', '田七', '錢八', '孫九', '周十', '張三', '李四', '王五', '趙六', '田七', '錢八', '孫九', '周十']
11     };
12   }
13 });
14 </script>

 

<style lang="scss">
.item-el {
  margin: 10px;
  padding: 10px;
  border: 1px solid #ccc;
  background-color: #fff;
}
</style>

 

效果

 

每個子元素使用 display: inline-block; 布局即可,通過css可靈活控制內部布局。

復雜效果實現

 

同一頁面窗口放大時效果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM