| <template> | 
|     <view class="uni-collapse-item"> | 
|         <!-- onClick(!isOpen) --> | 
|         <view @click="onClick(!isOpen)" class="uni-collapse-item__title" | 
|             :class="{'is-open':isOpen &&titleBorder === 'auto' ,'uni-collapse-item-border':titleBorder !== 'none'}"> | 
|             <view class="uni-collapse-item__title-wrap"> | 
|                 <slot name="title"> | 
|                     <view class="uni-collapse-item__title-box" :class="{'is-disabled':disabled}"> | 
|                         <image v-if="thumb" :src="thumb" class="uni-collapse-item__title-img" /> | 
|                         <text class="uni-collapse-item__title-text">{{ title }}</text> | 
|                     </view> | 
|                 </slot> | 
|             </view> | 
|             <view v-if="showArrow" | 
|                 :class="{ 'uni-collapse-item__title-arrow-active': isOpen, 'uni-collapse-item--animation': showAnimation === true }" | 
|                 class="uni-collapse-item__title-arrow"> | 
|                 <uni-icons :color="disabled?'#ddd':'#bbb'" size="14" type="bottom" /> | 
|             </view> | 
|         </view> | 
|         <view class="uni-collapse-item__wrap" :class="{'is--transition':showAnimation}" | 
|             :style="{height: (isOpen?height:0) +'px'}"> | 
|             <view :id="elId" ref="collapse--hook" class="uni-collapse-item__wrap-content" | 
|                 :class="{open:isheight,'uni-collapse-item--border':border&&isOpen}"> | 
|                 <slot></slot> | 
|             </view> | 
|         </view> | 
|   | 
|     </view> | 
| </template> | 
|   | 
| <script> | 
|     // #ifdef APP-NVUE | 
|     const dom = weex.requireModule('dom') | 
|     // #endif | 
|     /** | 
|      * CollapseItem 折叠面板子组件 | 
|      * @description 折叠面板子组件 | 
|      * @property {String} title 标题文字 | 
|      * @property {String} thumb 标题左侧缩略图 | 
|      * @property {String} name 唯一标志符 | 
|      * @property {Boolean} open = [true|false] 是否展开组件 | 
|      * @property {Boolean} titleBorder = [true|false] 是否显示标题分隔线 | 
|      * @property {Boolean} border = [true|false] 是否显示分隔线 | 
|      * @property {Boolean} disabled = [true|false] 是否展开面板 | 
|      * @property {Boolean} showAnimation = [true|false] 开启动画 | 
|      * @property {Boolean} showArrow = [true|false] 是否显示右侧箭头 | 
|      */ | 
|     export default { | 
|         name: 'uniCollapseItem', | 
|         props: { | 
|             // 列表标题 | 
|             title: { | 
|                 type: String, | 
|                 default: '' | 
|             }, | 
|             name: { | 
|                 type: [Number, String], | 
|                 default: '' | 
|             }, | 
|             // 是否禁用 | 
|             disabled: { | 
|                 type: Boolean, | 
|                 default: false | 
|             }, | 
|             // #ifdef APP-PLUS | 
|             // 是否显示动画,app 端默认不开启动画,卡顿严重 | 
|             showAnimation: { | 
|                 type: Boolean, | 
|                 default: false | 
|             }, | 
|             // #endif | 
|             // #ifndef APP-PLUS | 
|             // 是否显示动画 | 
|             showAnimation: { | 
|                 type: Boolean, | 
|                 default: true | 
|             }, | 
|             // #endif | 
|             // 是否展开 | 
|             open: { | 
|                 type: Boolean, | 
|                 default: false | 
|             }, | 
|             // 缩略图 | 
|             thumb: { | 
|                 type: String, | 
|                 default: '' | 
|             }, | 
|             // 标题分隔线显示类型 | 
|             titleBorder: { | 
|                 type: String, | 
|                 default: 'auto' | 
|             }, | 
|             border: { | 
|                 type: Boolean, | 
|                 default: true | 
|             }, | 
|             showArrow: { | 
|                 type: Boolean, | 
|                 default: true | 
|             } | 
|         }, | 
|         data() { | 
|             // TODO 随机生生元素ID,解决百度小程序获取同一个元素位置信息的bug | 
|             const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}` | 
|             return { | 
|                 isOpen: false, | 
|                 isheight: null, | 
|                 height: 0, | 
|                 elId, | 
|                 nameSync: 0 | 
|             } | 
|         }, | 
|         watch: { | 
|             open(val) { | 
|                 this.isOpen = val | 
|                 this.onClick(val, 'init') | 
|             } | 
|         }, | 
|         updated(e) { | 
|             this.$nextTick(() => { | 
|                 this.init(true) | 
|             }) | 
|         }, | 
|         created() { | 
|             this.collapse = this.getCollapse() | 
|             this.oldHeight = 0 | 
|             this.onClick(this.open, 'init') | 
|         }, | 
|         // #ifndef VUE3 | 
|         // TODO vue2 | 
|         destroyed() { | 
|             if (this.__isUnmounted) return | 
|             this.uninstall() | 
|         }, | 
|         // #endif | 
|         // #ifdef VUE3 | 
|         // TODO vue3 | 
|         unmounted() { | 
|             this.__isUnmounted = true | 
|             this.uninstall() | 
|         }, | 
|         // #endif | 
|         mounted() { | 
|             if (!this.collapse) return | 
|             if (this.name !== '') { | 
|                 this.nameSync = this.name | 
|             } else { | 
|                 this.nameSync = this.collapse.childrens.length + '' | 
|             } | 
|             if (this.collapse.names.indexOf(this.nameSync) === -1) { | 
|                 this.collapse.names.push(this.nameSync) | 
|             } else { | 
|                 console.warn(`name 值 ${this.nameSync} 重复`); | 
|             } | 
|             if (this.collapse.childrens.indexOf(this) === -1) { | 
|                 this.collapse.childrens.push(this) | 
|             } | 
|             this.init() | 
|         }, | 
|         methods: { | 
|             init(type) { | 
|                 // #ifndef APP-NVUE | 
|                 this.getCollapseHeight(type) | 
|                 // #endif | 
|                 // #ifdef APP-NVUE | 
|                 this.getNvueHwight(type) | 
|                 // #endif | 
|             }, | 
|             uninstall() { | 
|                 if (this.collapse) { | 
|                     this.collapse.childrens.forEach((item, index) => { | 
|                         if (item === this) { | 
|                             this.collapse.childrens.splice(index, 1) | 
|                         } | 
|                     }) | 
|                     this.collapse.names.forEach((item, index) => { | 
|                         if (item === this.nameSync) { | 
|                             this.collapse.names.splice(index, 1) | 
|                         } | 
|                     }) | 
|                 } | 
|             }, | 
|             onClick(isOpen, type) { | 
|                 if (this.disabled) return | 
|                 this.isOpen = isOpen | 
|                 if (this.isOpen && this.collapse) { | 
|                     this.collapse.setAccordion(this) | 
|                 } | 
|                 if (type !== 'init') { | 
|                     this.collapse.onChange(isOpen, this) | 
|                 } | 
|             }, | 
|             getCollapseHeight(type, index = 0) { | 
|                 const views = uni.createSelectorQuery().in(this) | 
|                 views | 
|                     .select(`#${this.elId}`) | 
|                     .fields({ | 
|                         size: true | 
|                     }, data => { | 
|                         // TODO 百度中可能获取不到节点信息 ,需要循环获取 | 
|                         if (index >= 10) return | 
|                         if (!data) { | 
|                             index++ | 
|                             this.getCollapseHeight(false, index) | 
|                             return | 
|                         } | 
|                         // #ifdef APP-NVUE | 
|                         this.height = data.height + 1 | 
|                         // #endif | 
|                         // #ifndef APP-NVUE | 
|                         this.height = data.height | 
|                         // #endif | 
|                         this.isheight = true | 
|                         if (type) return | 
|                         this.onClick(this.isOpen, 'init') | 
|                     }) | 
|                     .exec() | 
|             }, | 
|             getNvueHwight(type) { | 
|                 const result = dom.getComponentRect(this.$refs['collapse--hook'], option => { | 
|                     if (option && option.result && option.size) { | 
|                         // #ifdef APP-NVUE | 
|                         this.height = option.size.height + 1 | 
|                         // #endif | 
|                         // #ifndef APP-NVUE | 
|                         this.height = option.size.height | 
|                         // #endif | 
|                         this.isheight = true | 
|                         if (type) return | 
|                         this.onClick(this.open, 'init') | 
|                     } | 
|                 }) | 
|             }, | 
|             /** | 
|              * 获取父元素实例 | 
|              */ | 
|             getCollapse(name = 'uniCollapse') { | 
|                 let parent = this.$parent; | 
|                 let parentName = parent.$options.name; | 
|                 while (parentName !== name) { | 
|                     parent = parent.$parent; | 
|                     if (!parent) return false; | 
|                     parentName = parent.$options.name; | 
|                 } | 
|                 return parent; | 
|             } | 
|         } | 
|     } | 
| </script> | 
|   | 
| <style lang="scss"> | 
|     .uni-collapse-item { | 
|         /* #ifndef APP-NVUE */ | 
|         box-sizing: border-box; | 
|   | 
|         /* #endif */ | 
|         &__title { | 
|             /* #ifndef APP-NVUE */ | 
|             display: flex; | 
|             width: 100%; | 
|             box-sizing: border-box; | 
|             /* #endif */ | 
|             flex-direction: row; | 
|             align-items: center; | 
|             transition: border-bottom-color .3s; | 
|   | 
|             // transition-property: border-bottom-color; | 
|             // transition-duration: 5s; | 
|             &-wrap { | 
|                 width: 100%; | 
|                 flex: 1; | 
|   | 
|             } | 
|   | 
|             &-box { | 
|                 padding: 0 15px; | 
|                 /* #ifndef APP-NVUE */ | 
|                 display: flex; | 
|                 width: 100%; | 
|                 box-sizing: border-box; | 
|                 /* #endif */ | 
|                 flex-direction: row; | 
|                 justify-content: space-between; | 
|                 align-items: center; | 
|                 height: 48px; | 
|                 line-height: 48px; | 
|                 background-color: #fff; | 
|                 color: #303133; | 
|                 font-size: 13px; | 
|                 font-weight: 500; | 
|                 /* #ifdef H5 */ | 
|                 cursor: pointer; | 
|                 outline: none; | 
|   | 
|                 /* #endif */ | 
|                 &.is-disabled { | 
|                     .uni-collapse-item__title-text { | 
|                         color: #999; | 
|                     } | 
|                 } | 
|   | 
|             } | 
|   | 
|             &.uni-collapse-item-border { | 
|                 border-bottom: 1px solid #ebeef5; | 
|             } | 
|   | 
|             &.is-open { | 
|                 border-bottom-color: transparent; | 
|             } | 
|   | 
|             &-img { | 
|                 height: 22px; | 
|                 width: 22px; | 
|                 margin-right: 10px; | 
|             } | 
|   | 
|             &-text { | 
|                 flex: 1; | 
|                 font-size: 14px; | 
|                 /* #ifndef APP-NVUE */ | 
|                 white-space: nowrap; | 
|                 color: inherit; | 
|                 /* #endif */ | 
|                 /* #ifdef APP-NVUE */ | 
|                 lines: 1; | 
|                 /* #endif */ | 
|                 overflow: hidden; | 
|                 text-overflow: ellipsis; | 
|             } | 
|   | 
|             &-arrow { | 
|                 /* #ifndef APP-NVUE */ | 
|                 display: flex; | 
|                 box-sizing: border-box; | 
|                 /* #endif */ | 
|                 align-items: center; | 
|                 justify-content: center; | 
|                 width: 20px; | 
|                 height: 20px; | 
|                 margin-right: 10px; | 
|                 transform: rotate(0deg); | 
|   | 
|                 &-active { | 
|                     transform: rotate(-180deg); | 
|                 } | 
|             } | 
|   | 
|   | 
|         } | 
|   | 
|         &__wrap { | 
|             /* #ifndef APP-NVUE */ | 
|             will-change: height; | 
|             box-sizing: border-box; | 
|             /* #endif */ | 
|             background-color: #fff; | 
|             overflow: hidden; | 
|             position: relative; | 
|             height: 0; | 
|   | 
|             &.is--transition { | 
|                 // transition: all 0.3s; | 
|                 transition-property: height, border-bottom-width; | 
|                 transition-duration: 0.3s; | 
|                 /* #ifndef APP-NVUE */ | 
|                 will-change: height; | 
|                 /* #endif */ | 
|             } | 
|   | 
|   | 
|   | 
|             &-content { | 
|                 position: absolute; | 
|                 font-size: 13px; | 
|                 color: #303133; | 
|                 // transition: height 0.3s; | 
|                 border-bottom-color: transparent; | 
|                 border-bottom-style: solid; | 
|                 border-bottom-width: 0; | 
|   | 
|                 &.uni-collapse-item--border { | 
|                     border-bottom-width: 1px; | 
|                     border-bottom-color: red; | 
|                     border-bottom-color: #ebeef5; | 
|                 } | 
|   | 
|                 &.open { | 
|                     position: relative; | 
|                 } | 
|             } | 
|         } | 
|   | 
|         &--animation { | 
|             transition-property: transform; | 
|             transition-duration: 0.3s; | 
|             transition-timing-function: ease; | 
|         } | 
|   | 
|     } | 
| </style> |