u-grid-item.vue 6.2 KB
   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>