学员端小程序
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

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