列表查詢是很常見的功能,復雜的系統模塊會有很多查詢條件,默認只展示第一行查詢條件是個不錯的方案。
產品的方案是百變的,可能會讓你一行展示固定數目的查詢組件,也可能是根據屏幕寬度動態確定數量。
這個時候就很有必要來一個專門處理展開/折疊功能的組件,專注於展開/折疊功能。內部布局用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可靈活控制內部布局。
復雜效果實現
同一頁面窗口放大時效果