审查视图

node_modules/uview-plus/components/u-collapse-item/u-collapse-item.vue 6.6 KB
韩昌 authored
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
<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 #title>
				<slot name="title"></slot>
			</template>
			<template #icon>
				<slot name="icon"></slot>
			</template>
			<template #value>
				<slot name="value"></slot>
			</template>
			<template #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';
	import mpMixin from '../../libs/mixin/mpMixin.js';
	import mixin from '../../libs/mixin/mixin.js';
	import { nextTick } from 'vue'
	// #ifdef APP-NVUE
	const animation = uni.requireNativePlugin('animation')
	const dom = uni.requireNativePlugin('dom')
	// #endif
	/**
	 * collapseItem 折叠面板Item
	 * @description 通过折叠面板收纳内容区域(搭配u-collapse使用)
	 * @tutorial https://ijry.github.io/uview-plus/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: [mpMixin, 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: {
			// 异步获取内容,或者动态修改了内容时,需要重新初始化
			async 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)
				}
				// 设置组件的展开或收起状态
				await nextTick()
				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://ijry.github.io/uview-plus/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>