<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> 
 |