You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
6.2 KiB
209 lines
6.2 KiB
<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>
|
|
|