审查视图

node_modules/uview-ui/components/u-grid-item/u-grid-item.vue 6.2 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
<template>
	<!-- #ifndef APP-NVUE -->
	<view
	    class="u-grid-item"
	    hover-class="u-grid-item--hover-class"
	    :hover-stay-time="200"
	    @tap="clickHandler"
	    :class="classes"
	    :style="[itemStyle]"
	>
		<slot />
	</view>
	<!-- #endif -->
	<!-- #ifdef APP-NVUE -->
	<view
	    class="u-grid-item"
	    :hover-stay-time="200"
	    @tap="clickHandler"
	    :class="classes"
	    :style="[itemStyle]"
	>
		<slot />
	</view>
	<!-- #endif -->
</template>

<script>
	import props from './props.js';
	/**
	 * gridItem 提示
	 * @description 宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。搭配u-grid使用
	 * @tutorial https://www.uviewui.com/components/grid.html
	 * @property {String | Number}	name		宫格的name ( 默认 null )
	 * @property {String}			bgColor		宫格的背景颜色 (默认 'transparent' )
	 * @property {Object}			customStyle	自定义样式,对象形式
	 * @event {Function} click 点击宫格触发
	 * @example <u-grid-item></u-grid-item>
	 */
	export default {
		name: "u-grid-item",
		mixins: [uni.$u.mpMixin, uni.$u.mixin,props],
		data() {
			return {
				parentData: {
					col: 3, // 父组件划分的宫格数
					border: true, // 是否显示边框,根据父组件决定
				},
				// #ifdef APP-NVUE
				width: 0, // nvue下才这么计算,vue下放到computed中,否则会因为延时造成闪烁
				// #endif
				classes: [], // 类名集合,用于判断是否显示右边和下边框
			};
		},
		mounted() {
			this.init()
		},
		computed: {
			// #ifndef APP-NVUE
			// vue下放到computed中,否则会因为延时造成闪烁
			width() {
				return 100 / Number(this.parentData.col) + '%'
			},
			// #endif
			itemStyle() {
				const style = {
					background: this.bgColor,
					width: this.width
				}
				return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle))
			}
		},
		methods: {
			init() {
				// 用于在父组件u-grid的children中被添加入子组件时,
				// 重新计算item的边框
				uni.$on('$uGridItem', () => {
					this.gridItemClasses()
				})
				// 父组件的实例
				this.updateParentData()
				// #ifdef APP-NVUE
				// 获取元素该有的长度,nvue下要延时才准确
				this.$nextTick(function(){
					this.getItemWidth()
				})
				// #endif
				// 发出事件,通知所有的grid-item都重新计算自己的边框
				uni.$emit('$uGridItem')
				this.gridItemClasses()
			},
			// 获取父组件的参数
			updateParentData() {
				// 此方法写在mixin中
				this.getParentData('u-grid');
			},
			clickHandler() {
				let name = this.name
				// 如果没有设置name属性,历遍父组件的children数组,判断当前的元素是否和本实例this相等,找出当前组件的索引
				const children = this.parent?.children
				if(children && this.name === null) {
					name = children.findIndex(child => child === this)
				}
				// 调用父组件方法,发出事件
				this.parent && this.parent.childClick(name)
				this.$emit('click', name)
			},
			async getItemWidth() {
				// 如果是nvue,不能使用百分比,只能使用固定宽度
				let width = 0
				if(this.parent) {
					// 获取父组件宽度后,除以栅格数,得出每个item的宽度
					const parentWidth = await this.getParentWidth()
					width = parentWidth / Number(this.parentData.col) + 'px'
				}
				this.width = width
			},
			// 获取父元素的尺寸
			getParentWidth() {
				// #ifdef APP-NVUE
				// 返回一个promise,让调用者可以用await同步获取
				const dom = uni.requireNativePlugin('dom')
				return new Promise(resolve => {
					// 调用父组件的ref
					dom.getComponentRect(this.parent.$refs['u-grid'], res => {
						resolve(res.size.width)
					})
				})
				// #endif
			},
			gridItemClasses() {
				if(this.parentData.border) {
					const classes = []
					this.parent.children.map((child, index) =>{
						if(this === child) {
							const len = this.parent.children.length
							// 贴近右边屏幕边沿的child,并且最后一个(比如只有横向2个的时候),无需右边框
							if((index + 1) % this.parentData.col !== 0 && index + 1 !== len) {
								classes.push('u-border-right')
							}
							// 总的宫格数量对列数取余的值
							// 如果取余后,值为0,则意味着要将最后一排的宫格,都不需要下边框
							const lessNum = len % this.parentData.col === 0 ? this.parentData.col : len % this.parentData.col
							// 最下面的一排child,无需下边框
							if(index < len - lessNum) {
								classes.push('u-border-bottom')
							}
						}
					})
					// 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
					// #ifdef MP-ALIPAY || MP-TOUTIAO
					classes = classes.join(' ')
					// #endif
					this.classes = classes
				}
			}
		},
		beforeDestroy() {
			// 移除事件监听,释放性能
			uni.$off('$uGridItem')
		}
	};
</script>

<style lang="scss" scoped>
	@import "../../libs/css/components.scss";
      $u-grid-item-hover-class-opcatiy:.5 !default;
      $u-grid-item-margin-top:1rpx !default;
      $u-grid-item-border-right-width:0.5px !default;
      $u-grid-item-border-bottom-width:0.5px !default;
      $u-grid-item-border-right-color:$u-border-color !default;
      $u-grid-item-border-bottom-color:$u-border-color !default;
	.u-grid-item {
		align-items: center;
		justify-content: center;
		position: relative;
		flex-direction: column;
		/* #ifndef APP-NVUE */
		box-sizing: border-box;
		display: flex;
		/* #endif */

		/* #ifdef MP */
		position: relative;
		float: left;
		/* #endif */

		/* #ifdef MP-WEIXIN */
		margin-top:$u-grid-item-margin-top;
		/* #endif */

		&--hover-class {
			opacity:$u-grid-item-hover-class-opcatiy;
		}
	}

	/* #ifdef APP-NVUE */
	// 由于nvue不支持组件内引入app.vue中再引入的样式,所以需要写在这里
	.u-border-right {
		border-right-width:$u-grid-item-border-right-width;
		border-color: $u-grid-item-border-right-color;
	}

	.u-border-bottom {
		border-bottom-width:$u-grid-item-border-bottom-width;
		border-color:$u-grid-item-border-bottom-color;
	}

	/* #endif */
</style>