基于@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可灵活控制内部布局。

复杂效果实现

 

同一页面窗口放大时效果

 

posted @ 2022-04-16 10:03  魔狼再世  阅读(1040)  评论(0编辑  收藏  举报