su-tabbar-item.vue 7.2 KB

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