uv-grid-item.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <template>
  2. <!-- #ifndef APP-NVUE -->
  3. <view
  4. class="uv-grid-item"
  5. hover-class="uv-grid-item--hover-class"
  6. :hover-stay-time="200"
  7. @tap="clickHandler"
  8. :class="classes"
  9. :style="[itemStyle]"
  10. >
  11. <slot />
  12. </view>
  13. <!-- #endif -->
  14. <!-- #ifdef APP-NVUE -->
  15. <view
  16. class="uv-grid-item"
  17. :hover-stay-time="200"
  18. @tap="clickHandler"
  19. :class="classes"
  20. :style="[itemStyle]"
  21. >
  22. <slot />
  23. </view>
  24. <!-- #endif -->
  25. </template>
  26. <script>
  27. import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
  28. import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
  29. import props from './props.js';
  30. /**
  31. * gridItem 提示
  32. * @description 宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。搭配uv-grid使用
  33. * @tutorial https://www.uvui.cn/components/grid.html
  34. * @property {String | Number} name 宫格的name ( 默认 null )
  35. * @property {String} bgColor 宫格的背景颜色 (默认 'transparent' )
  36. * @property {Object} customStyle 自定义样式,对象形式
  37. * @event {Function} click 点击宫格触发
  38. * @example <uv-grid-item></uv-grid-item>
  39. */
  40. export default {
  41. name: "uv-grid-item",
  42. mixins: [mpMixin, mixin, props],
  43. emits: ['$uvGridItem','click'],
  44. data() {
  45. return {
  46. parentData: {
  47. col: 3, // 父组件划分的宫格数
  48. border: true, // 是否显示边框,根据父组件决定
  49. },
  50. // #ifdef APP-NVUE
  51. width: 0, // nvue下才这么计算,vue下放到computed中,否则会因为延时造成闪烁
  52. // #endif
  53. classes: [], // 类名集合,用于判断是否显示右边和下边框
  54. };
  55. },
  56. created() {
  57. // 父组件的实例
  58. this.updateParentData()
  59. },
  60. mounted() {
  61. this.init()
  62. },
  63. computed: {
  64. // #ifndef APP-NVUE
  65. // vue下放到computed中,否则会因为延时造成闪烁
  66. width() {
  67. return 100 / Number(this.parentData.col) + '%'
  68. },
  69. // #endif
  70. itemStyle() {
  71. const style = {
  72. background: this.bgColor,
  73. width: this.width
  74. }
  75. return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
  76. }
  77. },
  78. methods: {
  79. init() {
  80. // 用于在父组件uv-grid的children中被添加入子组件时,
  81. // 重新计算item的边框
  82. uni.$on('$uvGridItem', () => {
  83. this.gridItemClasses()
  84. })
  85. // #ifdef APP-NVUE
  86. // 获取元素该有的长度,nvue下要延时才准确
  87. this.$nextTick(function(){
  88. this.getItemWidth()
  89. })
  90. // #endif
  91. // 发出事件,通知所有的grid-item都重新计算自己的边框
  92. uni.$emit('$uvGridItem')
  93. this.gridItemClasses()
  94. },
  95. // 获取父组件的参数
  96. updateParentData() {
  97. // 此方法写在mixin中
  98. this.getParentData('uv-grid');
  99. },
  100. clickHandler() {
  101. let name = this.name
  102. // 如果没有设置name属性,历遍父组件的children数组,判断当前的元素是否和本实例this相等,找出当前组件的索引
  103. const children = this.parent?.children
  104. if(children && this.name === null) {
  105. name = children.findIndex(child => child === this)
  106. }
  107. // 调用父组件方法,发出事件
  108. this.parent && this.parent.childClick(name)
  109. this.$emit('click', name)
  110. },
  111. async getItemWidth() {
  112. // 如果是nvue,不能使用百分比,只能使用固定宽度
  113. let width = 0
  114. if(this.parent) {
  115. // 获取父组件宽度后,除以栅格数,得出每个item的宽度
  116. const parentWidth = await this.getParentWidth()
  117. width = parentWidth / Number(this.parentData.col) + 'px'
  118. }
  119. this.width = width
  120. },
  121. // 获取父元素的尺寸
  122. getParentWidth() {
  123. // #ifdef APP-NVUE
  124. // 返回一个promise,让调用者可以用await同步获取
  125. const dom = uni.requireNativePlugin('dom')
  126. return new Promise(resolve => {
  127. // 调用父组件的ref
  128. dom.getComponentRect(this.parent.$refs['uv-grid'], res => {
  129. resolve(res.size.width)
  130. })
  131. })
  132. // #endif
  133. },
  134. gridItemClasses() {
  135. if(this.parentData.border) {
  136. let classes = []
  137. this.parent.children.map((child, index) =>{
  138. if(this === child) {
  139. const len = this.parent.children.length
  140. // 贴近右边屏幕边沿的child,并且最后一个(比如只有横向2个的时候),无需右边框
  141. if((index + 1) % this.parentData.col !== 0 && index + 1 !== len) {
  142. classes.push('uv-border-right')
  143. }
  144. // 总的宫格数量对列数取余的值
  145. // 如果取余后,值为0,则意味着要将最后一排的宫格,都不需要下边框
  146. const lessNum = len % this.parentData.col === 0 ? this.parentData.col : len % this.parentData.col
  147. // 最下面的一排child,无需下边框
  148. if(index < len - lessNum) {
  149. classes.push('uv-border-bottom')
  150. }
  151. }
  152. })
  153. // 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
  154. // #ifdef MP-ALIPAY || MP-TOUTIAO
  155. classes = classes.join(' ')
  156. // #endif
  157. this.classes = classes
  158. }
  159. }
  160. },
  161. beforeDestroy() {
  162. // 移除事件监听,释放性能
  163. uni.$off('$uvGridItem')
  164. }
  165. };
  166. </script>
  167. <style lang="scss" scoped>
  168. $show-border: 1;
  169. $show-border-right: 1;
  170. $show-border-bottom: 1;
  171. @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
  172. @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
  173. @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
  174. $uv-grid-item-hover-class-opcatiy:.5 !default;
  175. $uv-grid-item-margin-top:1rpx !default;
  176. $uv-grid-item-border-right-width:0.5px !default;
  177. $uv-grid-item-border-bottom-width:0.5px !default;
  178. $uv-grid-item-border-right-color:$uv-border-color !default;
  179. $uv-grid-item-border-bottom-color:$uv-border-color !default;
  180. .uv-grid-item {
  181. align-items: center;
  182. justify-content: center;
  183. position: relative;
  184. flex-direction: column;
  185. /* #ifndef APP-NVUE */
  186. box-sizing: border-box;
  187. display: flex;
  188. /* #endif */
  189. /* #ifdef MP */
  190. position: relative;
  191. float: left;
  192. /* #endif */
  193. /* #ifdef MP-WEIXIN */
  194. margin-top:$uv-grid-item-margin-top;
  195. /* #endif */
  196. &--hover-class {
  197. opacity:$uv-grid-item-hover-class-opcatiy;
  198. }
  199. }
  200. /* #ifdef APP-NVUE */
  201. // 由于nvue不支持组件内引入app.vue中再引入的样式,所以需要写在这里
  202. .uv-border-right {
  203. border-right-width:$uv-grid-item-border-right-width;
  204. border-color: $uv-grid-item-border-right-color;
  205. }
  206. .uv-border-bottom {
  207. border-bottom-width:$uv-grid-item-border-bottom-width;
  208. border-color:$uv-grid-item-border-bottom-color;
  209. }
  210. /* #endif */
  211. </style>