<template> 
 | 
    <view class="u-collapse-item"> 
 | 
        <u-cell 
 | 
            :title="title" 
 | 
            :value="value" 
 | 
            :label="label" 
 | 
            :icon="icon" 
 | 
            :isLink="isLink" 
 | 
            :clickable="clickable" 
 | 
            :border="parentData.border && showBorder" 
 | 
            @click="clickHandler" 
 | 
            :arrowDirection="expanded ? 'up' : 'down'" 
 | 
            :disabled="disabled" 
 | 
        > 
 | 
            <!-- #ifndef MP-WEIXIN --> 
 | 
            <!-- 微信小程序不支持,因为微信中不支持 <slot name="title" slot="title" />的写法 --> 
 | 
            <template slot="title"> 
 | 
                <slot name="title"></slot> 
 | 
            </template> 
 | 
            <template slot="icon"> 
 | 
                <slot name="icon"></slot> 
 | 
            </template> 
 | 
            <template slot="value"> 
 | 
                <slot name="value"></slot> 
 | 
            </template> 
 | 
            <template slot="right-icon"> 
 | 
                <slot name="right-icon"></slot> 
 | 
            </template> 
 | 
            <!-- #endif --> 
 | 
        </u-cell> 
 | 
        <view 
 | 
            class="u-collapse-item__content" 
 | 
            :animation="animationData" 
 | 
            ref="animation" 
 | 
        > 
 | 
            <view 
 | 
                class="u-collapse-item__content__text content-class" 
 | 
                :id="elId" 
 | 
                :ref="elId" 
 | 
            ><slot /></view> 
 | 
        </view> 
 | 
        <u-line v-if="parentData.border"></u-line> 
 | 
    </view> 
 | 
</template> 
 | 
  
 | 
<script> 
 | 
    import props from './props.js'; 
 | 
    // #ifdef APP-NVUE 
 | 
    const animation = uni.requireNativePlugin('animation') 
 | 
    const dom = uni.requireNativePlugin('dom') 
 | 
    // #endif 
 | 
    /** 
 | 
     * collapseItem 折叠面板Item 
 | 
     * @description 通过折叠面板收纳内容区域(搭配u-collapse使用) 
 | 
     * @tutorial https://www.uviewui.com/components/collapse.html 
 | 
     * @property {String}            title         标题 
 | 
     * @property {String}            value         标题右侧内容 
 | 
     * @property {String}            label         标题下方的描述信息 
 | 
     * @property {Boolean}            disbled     是否禁用折叠面板 ( 默认 false ) 
 | 
     * @property {Boolean}            isLink         是否展示右侧箭头并开启点击反馈 ( 默认 true ) 
 | 
     * @property {Boolean}            clickable    是否开启点击反馈 ( 默认 true ) 
 | 
     * @property {Boolean}            border        是否显示内边框 ( 默认 true ) 
 | 
     * @property {String}            align        标题的对齐方式 ( 默认 'left' ) 
 | 
     * @property {String | Number}    name        唯一标识符 
 | 
     * @property {String}            icon        标题左侧图片,可为绝对路径的图片或内置图标 
 | 
     * @event {Function}            change             某个item被打开或者收起时触发 
 | 
     * @example <u-collapse-item :title="item.head" v-for="(item, index) in itemList" :key="index">{{item.body}}</u-collapse-item> 
 | 
     */ 
 | 
    export default { 
 | 
        name: "u-collapse-item", 
 | 
        mixins: [uni.$u.mpMixin, uni.$u.mixin, props], 
 | 
        data() { 
 | 
            return { 
 | 
                elId: uni.$u.guid(), 
 | 
                // uni.createAnimation的导出数据 
 | 
                animationData: {}, 
 | 
                // 是否展开状态 
 | 
                expanded: false, 
 | 
                // 根据expanded确定是否显示border,为了控制展开时,cell的下划线更好的显示效果,进行一定时间的延时 
 | 
                showBorder: false, 
 | 
                // 是否动画中,如果是则不允许继续触发点击 
 | 
                animating: false, 
 | 
                // 父组件u-collapse的参数 
 | 
                parentData: { 
 | 
                    accordion: false, 
 | 
                    border: false 
 | 
                } 
 | 
            }; 
 | 
        }, 
 | 
        watch: { 
 | 
            expanded(n) { 
 | 
                clearTimeout(this.timer) 
 | 
                this.timer = null 
 | 
                // 这里根据expanded的值来进行一定的延时,是为了cell的下划线更好的显示效果 
 | 
                this.timer = setTimeout(() => { 
 | 
                    this.showBorder = n 
 | 
                }, n ? 10 : 290) 
 | 
            } 
 | 
        }, 
 | 
        mounted() { 
 | 
            this.init() 
 | 
        }, 
 | 
        methods: { 
 | 
            // 异步获取内容,或者动态修改了内容时,需要重新初始化 
 | 
            init() { 
 | 
                // 初始化数据 
 | 
                this.updateParentData() 
 | 
                if (!this.parent) { 
 | 
                    return uni.$u.error('u-collapse-item必须要搭配u-collapse组件使用') 
 | 
                } 
 | 
                const { 
 | 
                    value, 
 | 
                    accordion, 
 | 
                    children = [] 
 | 
                } = this.parent 
 | 
  
 | 
                if (accordion) { 
 | 
                    if (uni.$u.test.array(value)) { 
 | 
                        return uni.$u.error('手风琴模式下,u-collapse组件的value参数不能为数组') 
 | 
                    } 
 | 
                    this.expanded = this.name == value 
 | 
                } else { 
 | 
                    if (!uni.$u.test.array(value) && value !== null) { 
 | 
                        return uni.$u.error('非手风琴模式下,u-collapse组件的value参数必须为数组') 
 | 
                    } 
 | 
                    this.expanded = (value || []).some(item => item == this.name) 
 | 
                } 
 | 
                // 设置组件的展开或收起状态 
 | 
                this.$nextTick(function() { 
 | 
                    this.setContentAnimate() 
 | 
                }) 
 | 
            }, 
 | 
            updateParentData() { 
 | 
                // 此方法在mixin中 
 | 
                this.getParentData('u-collapse') 
 | 
            }, 
 | 
            async setContentAnimate() { 
 | 
                // 每次面板打开或者收起时,都查询元素尺寸 
 | 
                // 好处是,父组件从服务端获取内容后,变更折叠面板后可以获得最新的高度 
 | 
                const rect = await this.queryRect() 
 | 
                const height = this.expanded ? rect.height : 0 
 | 
                this.animating = true 
 | 
                // #ifdef APP-NVUE 
 | 
                const ref = this.$refs['animation'].ref 
 | 
                animation.transition(ref, { 
 | 
                    styles: { 
 | 
                        height: height + 'px' 
 | 
                    }, 
 | 
                    duration: this.duration, 
 | 
                    // 必须设置为true,否则会到面板收起或展开时,页面其他元素不会随之调整它们的布局 
 | 
                    needLayout: true, 
 | 
                    timingFunction: 'ease-in-out', 
 | 
                }, () => { 
 | 
                    this.animating = false 
 | 
                }) 
 | 
                // #endif 
 | 
  
 | 
                // #ifndef APP-NVUE 
 | 
                const animation = uni.createAnimation({ 
 | 
                    timingFunction: 'ease-in-out', 
 | 
                }); 
 | 
                animation 
 | 
                    .height(height) 
 | 
                    .step({ 
 | 
                        duration: this.duration, 
 | 
                    }) 
 | 
                    .step() 
 | 
                // 导出动画数据给面板的animationData值 
 | 
                this.animationData = animation.export() 
 | 
                // 标识动画结束 
 | 
                uni.$u.sleep(this.duration).then(() => { 
 | 
                    this.animating = false 
 | 
                }) 
 | 
                // #endif 
 | 
            }, 
 | 
            // 点击collapsehead头部 
 | 
            clickHandler() { 
 | 
                if (this.disabled && this.animating) return 
 | 
                // 设置本组件为相反的状态 
 | 
                this.parent && this.parent.onChange(this) 
 | 
            }, 
 | 
            // 查询内容高度 
 | 
            queryRect() { 
 | 
                // #ifndef APP-NVUE 
 | 
                // $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://www.uviewui.com/js/getRect.html 
 | 
                // 组件内部一般用this.$uGetRect,对外的为uni.$u.getRect,二者功能一致,名称不同 
 | 
                return new Promise(resolve => { 
 | 
                    this.$uGetRect(`#${this.elId}`).then(size => { 
 | 
                        resolve(size) 
 | 
                    }) 
 | 
                }) 
 | 
                // #endif 
 | 
  
 | 
                // #ifdef APP-NVUE 
 | 
                // nvue下,使用dom模块查询元素高度 
 | 
                // 返回一个promise,让调用此方法的主体能使用then回调 
 | 
                return new Promise(resolve => { 
 | 
                    dom.getComponentRect(this.$refs[this.elId], res => { 
 | 
                        resolve(res.size) 
 | 
                    }) 
 | 
                }) 
 | 
                // #endif 
 | 
            } 
 | 
        }, 
 | 
    }; 
 | 
</script> 
 | 
  
 | 
<style lang="scss" scoped> 
 | 
    @import "../../libs/css/components.scss"; 
 | 
  
 | 
    .u-collapse-item { 
 | 
  
 | 
        &__content { 
 | 
            overflow: hidden; 
 | 
            height: 0; 
 | 
  
 | 
            &__text { 
 | 
                padding: 12px 15px; 
 | 
                color: $u-content-color; 
 | 
                font-size: 14px; 
 | 
                line-height: 18px; 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
</style> 
 |