|
|
@@ -0,0 +1,402 @@
|
|
|
+<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 bold">
|
|
|
+ <uni-icons :color="disabled?'#ddd':'#333'" size="38rpx" type="right" />
|
|
|
+ </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(90deg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ &__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>
|