123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- <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.js';
- import mpMixin from '../../libs/mixin/mpMixin.js';
- import mixin from '../../libs/mixin/mixin.js';
- /**
- * 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
- classes: [], // 类名集合,用于判断是否显示右边和下边框
- };
- },
- mounted() {
- this.init()
- },
- emits: ['click'],
- // 微信小程序中 options 选项
- options: {
- virtualHost: true //将自定义节点设置成虚拟的,更加接近Vue组件的表现。我们不希望自定义组件的这个节点本身可以设置样式、响应 flex 布局等
- },
- computed: {
- // #ifndef APP-NVUE
- // 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 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) {
- 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
- }
- }
- },
- 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>
|