210 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Vue
		
	
	
	
		
		
			
		
	
	
			210 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Vue
		
	
	
	
|  | <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> |