您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

247 行
5.6 KiB

  1. <template>
  2. <view v-if="show" class="u-tabbar" @touchmove.stop.prevent>
  3. <view class="u-tabbar__content safe-area-inset-bottom" :style="{
  4. height: $u.addUnit(height),
  5. backgroundColor: bgColor,
  6. }" :class="{
  7. 'u-border-top': borderTop
  8. }">
  9. <view class="u-tabbar__content__item" v-for="(item, index) in list" :key="index" :class="{
  10. 'u-tabbar__content__circle': midButton &&item.midButton
  11. }" @tap.stop="clickHandler(index)" :style="{
  12. backgroundColor: bgColor
  13. }">
  14. <view :class="[
  15. midButton && item.midButton ? 'u-tabbar__content__circle__button' : 'u-tabbar__content__item__button'
  16. ]">
  17. <u-icon
  18. :size="midButton && item.midButton ? midButtonSize : iconSize"
  19. :name="index == value ? item.selectedIconPath : item.iconPath"
  20. :color="index == value ? activeColor : inactiveColor"
  21. :custom-prefix="item.customIcon ? 'custom-icon' : 'uicon'"
  22. ></u-icon>
  23. <u-badge :count="item.count" :is-dot="item.isDot"
  24. v-if="item.count > 0"
  25. :offset="[-2, getOffsetRight(item.count, item.isDot)]"
  26. ></u-badge>
  27. </view>
  28. <view class="u-tabbar__content__item__text" :style="{
  29. color: index == value ? activeColor : inactiveColor
  30. }">
  31. <text class="u-line-1">{{item.text}}</text>
  32. </view>
  33. </view>
  34. <view v-if="midButton" class="u-tabbar__content__circle__border" :class="{
  35. 'u-border': borderTop,
  36. }" :style="{
  37. backgroundColor: bgColor
  38. }">
  39. </view>
  40. </view>
  41. <!-- 这里加上一个48rpx的高度,是为了增高有凸起按钮时的防塌陷高度(也即按钮凸出来部分的高度) -->
  42. <view class="u-fixed-placeholder safe-area-inset-bottom" :style="{
  43. height: `calc(${$u.addUnit(height)} + ${midButton ? 48 : 0}rpx)`,
  44. }"></view>
  45. </view>
  46. </template>
  47. <script>
  48. export default {
  49. props: {
  50. // 显示与否
  51. show: {
  52. type: Boolean,
  53. default: true
  54. },
  55. // 通过v-model绑定current值
  56. value: {
  57. type: [String, Number],
  58. default: 0
  59. },
  60. // 整个tabbar的背景颜色
  61. bgColor: {
  62. type: String,
  63. default: '#ffffff'
  64. },
  65. // tabbar的高度,默认50px,单位任意,如果为数值,则为rpx单位
  66. height: {
  67. type: [String, Number],
  68. default: '50px'
  69. },
  70. // 非凸起图标的大小,单位任意,数值默认rpx
  71. iconSize: {
  72. type: [String, Number],
  73. default: 40
  74. },
  75. // 凸起的图标的大小,单位任意,数值默认rpx
  76. midButtonSize: {
  77. type: [String, Number],
  78. default: 90
  79. },
  80. // 激活时的演示,包括字体图标,提示文字等的演示
  81. activeColor: {
  82. type: String,
  83. default: '#303133'
  84. },
  85. // 未激活时的颜色
  86. inactiveColor: {
  87. type: String,
  88. default: '#606266'
  89. },
  90. // 是否显示中部的凸起按钮
  91. midButton: {
  92. type: Boolean,
  93. default: false
  94. },
  95. // 配置参数
  96. list: {
  97. type: Array,
  98. default () {
  99. return []
  100. }
  101. },
  102. // 切换前的回调
  103. beforeSwitch: {
  104. type: Function,
  105. default: null
  106. },
  107. // 是否显示顶部的横线
  108. borderTop: {
  109. type: Boolean,
  110. default: true
  111. },
  112. },
  113. data() {
  114. return {
  115. }
  116. },
  117. methods: {
  118. async clickHandler(index) {
  119. if(this.beforeSwitch && typeof(this.beforeSwitch) === 'function') {
  120. // 执行回调,同时传入索引当作参数
  121. let beforeSwitch = this.beforeSwitch(index);
  122. // 判断是否返回了promise
  123. if (!!beforeSwitch && typeof beforeSwitch.then === 'function') {
  124. await beforeSwitch.then(res => {
  125. // promise返回成功,
  126. this.switchTab(index);
  127. }).catch(err => {
  128. })
  129. } else if(beforeSwitch === true) {
  130. // 如果返回true
  131. this.switchTab(index);
  132. }
  133. } else {
  134. this.switchTab(index);
  135. }
  136. },
  137. // 切换tab
  138. switchTab(index) {
  139. // 发出事件和修改v-model绑定的值
  140. this.$emit('change', index);
  141. this.$emit('input', index);
  142. },
  143. // 计算角标的right值
  144. getOffsetRight(count, isDot) {
  145. // 点类型,count大于9(两位数),分别设置不同的right值,避免位置太挤
  146. if(isDot) {
  147. return -20;
  148. } else if(count > 9) {
  149. return -40;
  150. } else {
  151. return -30;
  152. }
  153. }
  154. }
  155. }
  156. </script>
  157. <style scoped lang="scss">
  158. .u-fixed-placeholder {
  159. box-sizing: content-box;
  160. }
  161. .u-tabbar {
  162. &__content {
  163. display: flex;
  164. align-items: center;
  165. position: relative;
  166. position: fixed;
  167. bottom: 0;
  168. left: 0;
  169. width: 100%;
  170. z-index: 998;
  171. box-sizing: content-box;
  172. &__circle__border {
  173. border-radius: 100%;
  174. width: 110rpx;
  175. height: 110rpx;
  176. top: -48rpx;
  177. left: 50%;
  178. transform: translateX(-50%);
  179. position: absolute;
  180. z-index: 4;
  181. background-color: #ffffff;
  182. &:after {
  183. border-radius: 100px;
  184. }
  185. }
  186. &__item {
  187. flex: 1;
  188. justify-content: center;
  189. height: 100%;
  190. padding: 12rpx 0;
  191. display: flex;
  192. flex-direction: column;
  193. align-items: center;
  194. position: relative;
  195. &__button {
  196. position: absolute;
  197. top: 10rpx;
  198. }
  199. &__text {
  200. color: $u-content-color;
  201. font-size: 26rpx;
  202. line-height: 28rpx;
  203. position: absolute;
  204. bottom: 12rpx;
  205. left: 50%;
  206. transform: translateX(-50%);
  207. }
  208. }
  209. &__circle {
  210. position: relative;
  211. display: flex;
  212. flex-direction: column;
  213. justify-content: space-between;
  214. z-index: 10;
  215. height: calc(100% - 1px);
  216. &__button {
  217. width: 90rpx;
  218. height: 90rpx;
  219. border-radius: 100%;
  220. display: flex;
  221. justify-content: center;
  222. align-items: center;
  223. position: absolute;
  224. background-color: #ffffff;
  225. top: -40rpx;
  226. left: 50%;
  227. z-index: 6;
  228. transform: translateX(-50%);
  229. }
  230. }
  231. }
  232. }
  233. </style>