su-tabbar-item.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <!-- 自定义底部导航项 -->
  2. <template>
  3. <view class="u-tabbar-item" :style="[addStyle(customStyle)]">
  4. <view v-if="isCenter" class="tabbar-center-item">
  5. <image class="center-image" :src="centerImage" mode="aspectFill"></image>
  6. </view>
  7. <template v-else>
  8. <view class="u-tabbar-item__icon">
  9. <uni-badge absolute="rightTop" size="small" :text="badge || (dot ? 1 : null)" :customStyle="badgeStyle"
  10. :isDot="dot">
  11. <image v-if="icon" :name="icon" :color="isActive ? parentData.activeColor : parentData.inactiveColor"
  12. :size="20"></image>
  13. <block v-else>
  14. <slot v-if="isActive" name="active-icon" />
  15. <slot v-else name="inactive-icon" />
  16. </block>
  17. </uni-badge>
  18. </view>
  19. <slot name="text">
  20. <text class="u-tabbar-item__text" :style="{
  21. color: isActive ? parentData.activeColor : parentData.inactiveColor,
  22. }">
  23. {{ text }}
  24. </text>
  25. </slot>
  26. </template>
  27. </view>
  28. </template>
  29. <script>
  30. /**
  31. * TabbarItem 底部导航栏子组件
  32. * @description 此组件提供了自定义tabbar的能力。
  33. * @property {String | Number} name item标签的名称,作为与u-tabbar的value参数匹配的标识符
  34. * @property {String} icon uView内置图标或者绝对路径的图片
  35. * @property {String | Number} badge 右上角的角标提示信息
  36. * @property {Boolean} dot 是否显示圆点,将会覆盖badge参数(默认 false )
  37. * @property {String} text 描述文本
  38. * @property {Object | String} badgeStyle 控制徽标的位置,对象或者字符串形式,可以设置top和right属性(默认 'top: 6px;right:2px;' )
  39. * @property {Object} customStyle 定义需要用到的外部样式
  40. *
  41. */
  42. import { $parent, addStyle } from '@/sheep/helper';
  43. export default {
  44. name: 'su-tabbar-item',
  45. props: {
  46. customStyle: {
  47. type: [Object, String],
  48. default: () => ({}),
  49. },
  50. customClass: {
  51. type: String,
  52. default: '',
  53. },
  54. // 跳转的页面路径
  55. url: {
  56. type: String,
  57. default: '',
  58. },
  59. // 页面跳转的类型
  60. linkType: {
  61. type: String,
  62. default: 'navigateTo',
  63. },
  64. // item标签的名称,作为与u-tabbar的value参数匹配的标识符
  65. name: {
  66. type: [String, Number, null],
  67. default: '',
  68. },
  69. // uView内置图标或者绝对路径的图片
  70. icon: {
  71. icon: String,
  72. default: '',
  73. },
  74. // 右上角的角标提示信息
  75. badge: {
  76. type: [String, Number, null],
  77. default: '',
  78. },
  79. // 是否显示圆点,将会覆盖badge参数
  80. dot: {
  81. type: Boolean,
  82. default: false,
  83. },
  84. // 描述文本
  85. text: {
  86. type: String,
  87. default: '',
  88. },
  89. // 控制徽标的位置,对象或者字符串形式,可以设置top和right属性
  90. badgeStyle: {
  91. type: Object,
  92. default: () => { },
  93. },
  94. isCenter: {
  95. type: Boolean,
  96. default: false,
  97. },
  98. centerImage: {
  99. type: String,
  100. default: '',
  101. },
  102. },
  103. data() {
  104. return {
  105. isActive: false, // 是否处于激活状态
  106. addStyle,
  107. parentData: {
  108. value: null,
  109. activeColor: '', // 选中标签的颜色
  110. inactiveColor: '', // 未选中标签的颜色
  111. },
  112. parent: {},
  113. };
  114. },
  115. created() {
  116. this.init();
  117. },
  118. methods: {
  119. getParentData(parentName = '') {
  120. // 避免在created中去定义parent变量
  121. if (!this.parent) this.parent = {};
  122. // 这里的本质原理是,通过获取父组件实例(也即类似u-radio的父组件u-radio-group的this)
  123. // 将父组件this中对应的参数,赋值给本组件(u-radio的this)的parentData对象中对应的属性
  124. // 之所以需要这么做,是因为所有端中,头条小程序不支持通过this.parent.xxx去监听父组件参数的变化
  125. // 此处并不会自动更新子组件的数据,而是依赖父组件u-radio-group去监听data的变化,手动调用更新子组件的方法去重新获取
  126. this.parent = $parent.call(this, parentName);
  127. if (this.parent.children) {
  128. // 如果父组件的children不存在本组件的实例,才将本实例添加到父组件的children中
  129. this.parent.children.indexOf(this) === -1 && this.parent.children.push(this);
  130. }
  131. if (this.parent && this.parentData) {
  132. // 历遍parentData中的属性,将parent中的同名属性赋值给parentData
  133. Object.keys(this.parentData).map((key) => {
  134. this.parentData[key] = this.parent[key];
  135. });
  136. }
  137. },
  138. init() {
  139. // 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用
  140. this.updateParentData();
  141. if (!this.parent) {
  142. console.log('u-tabbar-item必须搭配u-tabbar组件使用');
  143. }
  144. // 本子组件在u-tabbar的children数组中的索引
  145. const index = this.parent.children.indexOf(this);
  146. // 判断本组件的name(如果没有定义name,就用index索引)是否等于父组件的value参数
  147. this.isActive = (this.name.split('?')[0] || index) === this.parentData.value;
  148. },
  149. updateParentData() {
  150. // 此方法在mixin中
  151. this.getParentData('su-tabbar');
  152. },
  153. // 此方法将会被父组件u-tabbar调用
  154. updateFromParent() {
  155. // 重新初始化
  156. this.init();
  157. },
  158. clickHandler() {
  159. this.$nextTick(() => {
  160. const index = this.parent.children.indexOf(this);
  161. const name = this.name || index;
  162. // 点击的item为非激活的item才发出change事件
  163. if (name !== this.parent.value) {
  164. this.parent.$emit('change', name);
  165. }
  166. this.$emit('click', name);
  167. });
  168. },
  169. },
  170. };
  171. </script>
  172. <style lang="scss" scoped>
  173. .tabbar-center-item {
  174. height: 40px;
  175. width: 40px;
  176. display: flex;
  177. align-items: center;
  178. justify-content: center;
  179. border-radius: 50%;
  180. background-color: rebeccapurple;
  181. transform: scale(1.3) translateY(-6px);
  182. position: absolute;
  183. z-index: 2;
  184. .center-image {
  185. width: 25px;
  186. height: 25px;
  187. }
  188. }
  189. .u-tabbar-item {
  190. display: flex;
  191. flex-direction: column;
  192. align-items: center;
  193. justify-content: center;
  194. flex: 1;
  195. position: relative;
  196. z-index: 1;
  197. &__icon {
  198. display: flex;
  199. position: relative;
  200. width: 150rpx;
  201. justify-content: center;
  202. }
  203. &__text {
  204. margin-top: 2px;
  205. font-size: 12px;
  206. color: var(--textSize);
  207. }
  208. }
  209. /* #ifdef MP */
  210. // 由于小程序都使用shadow DOM形式实现,需要给影子宿主设置flex: 1才能让其撑开
  211. :host {
  212. flex: 1;
  213. }
  214. /* #endif */
  215. </style>