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