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.

232 lines
6.9 KiB

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