<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> 
 |