403 lines
8.8 KiB
Vue
403 lines
8.8 KiB
Vue
<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 {String} border = ['auto'|'show'|'none'] 是否显示分隔线
|
||
* @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>
|