233 lines
6.9 KiB
Vue
233 lines
6.9 KiB
Vue
<template>
|
||
<!-- #ifndef APP-NVUE -->
|
||
<view
|
||
v-if="parentData.col > 0"
|
||
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';
|
||
import mpMixin from '../../libs/mixin/mpMixin';
|
||
import mixin from '../../libs/mixin/mixin';
|
||
import { addStyle, deepMerge } from '../../libs/function/index';
|
||
/**
|
||
* gridItem 提示
|
||
* @description 宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。搭配u-grid使用
|
||
* @tutorial https://ijry.github.io/uview-plus/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: [mpMixin, mixin, props],
|
||
data() {
|
||
return {
|
||
parentData: {
|
||
col: 0, // 父组件划分的宫格数
|
||
border: true, // 是否显示边框,根据父组件决定
|
||
},
|
||
// #ifdef APP-NVUE
|
||
width: 0, // nvue下才这么计算,vue下放到computed中,否则会因为延时造成闪烁
|
||
// #endif
|
||
// #ifdef MP-TOUTIAO
|
||
width: '100%',
|
||
// #endif
|
||
classes: [], // 类名集合,用于判断是否显示右边和下边框
|
||
};
|
||
},
|
||
mounted() {
|
||
this.init()
|
||
},
|
||
emits: ['click'],
|
||
// 微信小程序中 options 选项
|
||
// #ifdef MP-WEIXIN
|
||
options: {
|
||
virtualHost: true ,//将自定义节点设置成虚拟的,更加接近Vue组件的表现。我们不希望自定义组件的这个节点本身可以设置样式、响应 flex 布局等
|
||
},
|
||
// #endif
|
||
computed: {
|
||
// #ifndef APP-NVUE || MP-TOUTIAO
|
||
// vue下放到computed中,否则会因为延时造成闪烁
|
||
width() {
|
||
if (this.parentData.col > 0) {
|
||
return 100 / Number(this.parentData.col) + '%'
|
||
} else {
|
||
return 0;
|
||
}
|
||
},
|
||
// #endif
|
||
itemStyle() {
|
||
const style = {
|
||
background: this.bgColor,
|
||
width: this.width
|
||
}
|
||
return deepMerge(style, 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) {
|
||
let 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
|
||
}
|
||
}
|
||
},
|
||
// #ifdef VUE2
|
||
beforeDestroy() {
|
||
// #endif
|
||
// #ifdef VUE3
|
||
beforeUnmount() {
|
||
// #endif
|
||
// 移除事件监听,释放性能
|
||
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>
|