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