m-tabbar.vue 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. <template>
  2. <view class="m-tabbar-box" :style="tabbarBoxStyle" v-if="isShowTabBar">
  3. <view class="m-tabbar__fill" v-if="fill || native" :class="{'m-tabbar__safe': (safeBottom || native)}"
  4. :style="tabbarFillStyle"></view>
  5. <view id="m-tabbar" class="m-tabbar"
  6. :class="{'fixed': (fixed || native), 'm-tabbar__safe': (safeBottom || native)}" :style="tabbarStyle">
  7. <view class="m-tabbar__border" v-if="borderStyle === 'black' "></view>
  8. <view class="m-tabbar__flex">
  9. <view @click="tabChange(index)" v-for="(item, index) in tabbarList" :key="index" class="m-tabbar__item"
  10. :class="{
  11. 'm-tabbar__item__active': index === currentIndex,
  12. }">
  13. <slot :name="`tabbar_index_${index}`">
  14. <view class="m-tabbar__icon">
  15. <view class="m-tabbar__badge" v-if="item.dot">{{item.dot}}</view>
  16. <image :src="currentIndex === index ? item.selectedIconPath : item.iconPath"
  17. class="m-tabbar__icon_img" />
  18. </view>
  19. <view class="m-tabbar__label"
  20. :style="{'color': index === currentIndex ? tabbarConfig.selectedColor : tabbarConfig.color }">
  21. {{ item.text }}
  22. </view>
  23. </slot>
  24. </view>
  25. </view>
  26. </view>
  27. </view>
  28. </template>
  29. <script>
  30. const obj2strStyle = (obj) => {
  31. let style = ''
  32. for (let key in obj) {
  33. style += `${key}:${obj[key]};`
  34. }
  35. return style
  36. }
  37. const padFirstSymbol = (str, smb) => {
  38. if (str.startsWith(smb) || str.startsWith('http')) {
  39. return str
  40. }
  41. return `/${str}`
  42. }
  43. const replaceTabbarList = (list) => {
  44. if (!list.length > 0) {
  45. return []
  46. }
  47. return list.map(item => {
  48. if (item.iconPath) {
  49. item.iconPath = padFirstSymbol(item.iconPath, '/')
  50. }
  51. if (item.pagePath) {
  52. item.pagePath = padFirstSymbol(item.pagePath, '/')
  53. }
  54. if (item.selectedIconPath) {
  55. item.selectedIconPath = padFirstSymbol(item.selectedIconPath, '/')
  56. }
  57. return item
  58. })
  59. }
  60. import PageConfig from '@/pages.json'
  61. export default {
  62. emits: ['change', 'click'],
  63. props: {
  64. current: {
  65. type: [Number, String],
  66. default: 0
  67. },
  68. tabbar: {
  69. type: Object,
  70. default () {
  71. return {}
  72. }
  73. },
  74. fixed: {
  75. type: Boolean,
  76. default: false
  77. },
  78. fill: {
  79. type: Boolean,
  80. default: false
  81. },
  82. zIndex: {
  83. type: [Number, String],
  84. default: 9999
  85. },
  86. native: {
  87. type: Boolean,
  88. default: false
  89. },
  90. safeBottom: {
  91. type: Boolean,
  92. default: true
  93. },
  94. beforeChange: {
  95. type: Function,
  96. default: null
  97. },
  98. tabbarHeight: {
  99. type: [Number, String],
  100. default: 100
  101. }
  102. },
  103. data() {
  104. return {
  105. isShowTabBar: false,
  106. currentIndex: 0,
  107. beforeData: {},
  108. reload: false
  109. }
  110. },
  111. watch: {
  112. current(val) {
  113. this.currentIndex = val * 1
  114. }
  115. },
  116. computed: {
  117. tabbarConfig() {
  118. const {
  119. native,
  120. reload
  121. } = this
  122. if (reload) {}
  123. if (native) {
  124. const {
  125. tabBar
  126. } = PageConfig
  127. if (!tabBar) {
  128. console.error('Native mode, Pages.json no tabbar config')
  129. return {
  130. borderStyle: 'black',
  131. list: []
  132. }
  133. }
  134. return tabBar
  135. }
  136. return this.tabbar
  137. },
  138. tabbarList() {
  139. const {
  140. reload
  141. } = this
  142. const {
  143. list
  144. } = this.tabbarConfig
  145. if (reload) {}
  146. if (list) {
  147. return replaceTabbarList(list)
  148. }
  149. console.error('No tabbar config')
  150. return []
  151. },
  152. borderStyle() {
  153. const {
  154. reload
  155. } = this
  156. const {
  157. borderStyle
  158. } = this.tabbarConfig
  159. if (reload) {}
  160. return borderStyle
  161. },
  162. tabbarBoxStyle() {
  163. const {
  164. zIndex,
  165. reload
  166. } = this
  167. if (reload) {}
  168. return obj2strStyle({
  169. 'z-index': zIndex,
  170. })
  171. },
  172. tabbarFillStyle() {
  173. const {
  174. tabbarHeight,
  175. safeBottom,
  176. reload
  177. } = this
  178. if (reload) {}
  179. return obj2strStyle({
  180. 'height': `${tabbarHeight}rpx`
  181. })
  182. },
  183. tabbarStyle() {
  184. const {
  185. tabbarHeight,
  186. reload
  187. } = this
  188. const {
  189. backgroundColor
  190. } = this.tabbarConfig
  191. if (reload) {}
  192. return obj2strStyle({
  193. 'height': `${tabbarHeight}rpx`,
  194. 'background-color': backgroundColor || '#fff',
  195. })
  196. },
  197. tabbarItemStyle() {
  198. const {
  199. currentIndex,
  200. reload
  201. } = this
  202. const {
  203. color,
  204. selectedColor
  205. } = this.tabbarConfig
  206. if (reload) {}
  207. return obj2strStyle({
  208. 'color': currentIndex ? selectedColor : color
  209. })
  210. }
  211. },
  212. mounted() {
  213. this.initTabbar()
  214. },
  215. methods: {
  216. initTabbar() {
  217. const {
  218. current,
  219. fill,
  220. native,
  221. tabbarList
  222. } = this
  223. this.currentIndex = current * 1
  224. if (native) {
  225. const currentPage = `/${getCurrentPages()[0].route}`
  226. const currentIndex = tabbarList.findIndex(item => item.pagePath === currentPage)
  227. this.currentIndex = currentIndex
  228. if (tabbarList.length > 0) {
  229. uni.hideTabBar()
  230. }
  231. }
  232. setTimeout(() => {
  233. this.isShowTabBar = true
  234. })
  235. },
  236. reLoad() {
  237. this.reload = true
  238. setTimeout(() => {
  239. this.reload = false
  240. })
  241. },
  242. checkMaxIndex(index) {
  243. if (!this.tabbarConfig.list[index]) {
  244. console.error('Max tabbar index')
  245. return false
  246. }
  247. return true
  248. },
  249. setTabBarBadge(obj) {
  250. const {
  251. index,
  252. text
  253. } = obj
  254. if (this.checkMaxIndex(index)) {
  255. this.tabbarConfig.list[index].dot = text
  256. this.reLoad()
  257. }
  258. },
  259. setTabBarItem(obj) {
  260. const {
  261. index,
  262. text,
  263. pagePath: newPagePath,
  264. iconPath,
  265. selectedIconPath
  266. } = obj
  267. const {
  268. pagePath: oldPagePath
  269. } = this.tabbarConfig.list[index]
  270. if (this.checkMaxIndex(index)) {
  271. this.tabbarConfig.list[index] = {
  272. pagePath: newPagePath ? newPagePath : oldPagePath,
  273. text,
  274. iconPath,
  275. selectedIconPath
  276. }
  277. this.reLoad()
  278. }
  279. },
  280. showTabBar() {
  281. this.isShowTabBar = true
  282. },
  283. hideTabBar() {
  284. this.isShowTabBar = false
  285. },
  286. tabChange(index) {
  287. const {
  288. currentIndex
  289. } = this
  290. this.$emit('click', index)
  291. if (index === currentIndex) {
  292. return
  293. }
  294. this.beforeData = {
  295. newIndex: index,
  296. oldIndex: currentIndex,
  297. next: this.jumpPage
  298. }
  299. if (this.beforeChange) {
  300. this.beforeChange(this.jumpPage)
  301. } else {
  302. this.jumpPage()
  303. }
  304. },
  305. jumpPage() {
  306. const {
  307. native,
  308. beforeData,
  309. tabbarList: list
  310. } = this
  311. const {
  312. newIndex: index
  313. } = beforeData
  314. const {
  315. pagePath: url,
  316. openType
  317. } = list[index]
  318. if (url) {
  319. if (native) {
  320. uni.switchTab({
  321. url
  322. })
  323. } else {
  324. if (openType !== 'navigate') {
  325. this.currentIndex = index
  326. }
  327. switch (openType) {
  328. case 'navigate':
  329. uni.navigateTo({
  330. url
  331. })
  332. break;
  333. case 'redirect':
  334. uni.redirectTo({
  335. url
  336. })
  337. break;
  338. case 'reLaunch':
  339. uni.reLaunch({
  340. url
  341. })
  342. break;
  343. case 'switchTab':
  344. uni.switchTab({
  345. url
  346. })
  347. break;
  348. case 'navigateBack':
  349. uni.navigateBack({
  350. delta: 1
  351. })
  352. break;
  353. default:
  354. uni.reLaunch({
  355. url
  356. })
  357. }
  358. }
  359. }
  360. this.$emit('change', index)
  361. }
  362. }
  363. };
  364. </script>
  365. <style lang="scss" scoped>
  366. .m-tabbar-box {
  367. position: relative;
  368. z-index: 9999;
  369. }
  370. .m-tabbar {
  371. position: relative;
  372. &.fixed {
  373. position: fixed;
  374. bottom: 0;
  375. left: 0;
  376. width: 100vw;
  377. }
  378. &__safe {
  379. padding-bottom: env(safe-area-inset-bottom);
  380. }
  381. }
  382. .m-tabbar__fill {
  383. pointer-events: none;
  384. opacity: 0;
  385. }
  386. .m-tabbar__flex {
  387. display: flex;
  388. flex-direction: row;
  389. }
  390. .m-tabbar__border {
  391. background-color: rgba(0, 0, 0, 0.33);
  392. width: 100%;
  393. height: 1rpx;
  394. transform: scaleY(0.5);
  395. }
  396. .m-tabbar__item {
  397. display: flex;
  398. flex-direction: column;
  399. align-items: center;
  400. flex: 1;
  401. padding: 4px 0 2px;
  402. }
  403. .m-tabbar__icon {
  404. width: 48rpx;
  405. height: 48rpx;
  406. margin-bottom: 6rpx;
  407. position: relative;
  408. &_img {
  409. display: block;
  410. width: 100%;
  411. height: 100%;
  412. }
  413. .m-tabbar__badge {
  414. color: #fff;
  415. background-color: #f00;
  416. border-radius: 20rpx;
  417. font-size: 22rpx;
  418. position: absolute;
  419. right: -25rpx;
  420. left: 40rpx;
  421. padding: 2rpx 0;
  422. width: 100%;
  423. text-align: center;
  424. white-space: nowrap;
  425. }
  426. }
  427. .m-tabbar__label {
  428. font-size: 24rpx;
  429. }
  430. </style>