Vue 基於 ant-design-vue 封裝 TabBar
阿新 • • 發佈:2021-01-01
Vue 基於 ant-design-vue 封裝 TabBar
目錄
BaseTabBarItem
<template> <a-badge v-if="true" :class="[ isCenter ? 'base-tabbar-center-item-badge-wrap' : 'base-tabbar-item-badge-wrap', 'animate__animated', isActive ? 'animate__zoomIn' : '' ]" :style="{ '--animate-duration': '.35s', 'animation-fill-mode': 'forwards' }" :color="badge.color" :count="badge.count" :overflow-count="badge.overflowCount" :dot="badge.dot" @click="clickTabBar" > <div :class="[ isCenter ? 'base-tabbar-center-item-wrap' : 'base-tabbar-item-wrap', isActive ? 'base-tabbar-item-wrap-active' : 'base-tabbar-item-wrap-unactive' ]" > <div class="base-tabbar-item-icon" :style=" isCenter ? { 'background-color': activeColor, 'border-radius': !item.centerShape || item.centerShape == 'circle' ? '50%' : '15%' } : {} " > <slot name="icon" ><icon :icon="item.icon" :color="isActive && !isCenter ? activeColor : color" ></icon ></slot> </div> <div :class="[ 'base-tabbar-item-text', 'animate__animated', !isActive && showTextOnActive ? 'animate__zoomOut base-tabbar-item-text-unactive' : '' ]" > <slot name="text" ><span v-text="item.text" :style="{ color: isActive ? activeColor : color }" ></span ></slot> </div> </div> </a-badge> </template> <script> function isString(string) { return typeof string === "string"; } function pushRoute(route) { // Vue重新整理頁面的三種方式 // https://blog.csdn.net/weixin_43885417/article/details/91310674 if (!route) { return; } let currentRoutePath = router.currentRoute.path; if ( route && router.currentRoute && isString(route) ? currentRoutePath == route : currentRoutePath == route.path ) { return; } router.push(route); } export default { name: "BaseTabBarItem", data() { return {}; }, props: { // 中間是否隱藏 是則不是中間的樣式 centerVisible: { type: [Boolean], default: true, required: false }, // 啟用時才顯示文字 showTextOnActive: { type: [Boolean], default: false, required: false }, color: { type: [String], default: "", required: false }, activeColor: { type: [String], default: "", required: false }, itemKey: { type: [String, Number], default: "", required: false }, activeItemKey: { type: [String, Number], default: "", required: false }, item: { type: [Object], default() { // index: 1, // icon: "Antd_home", // text: "首頁", // route: { path: "/pvtnote/home" }, // badge: JSON.stringify({ count: 0 }), // isCenter: true, // // circle square // centerShape: "circle" return {}; }, required: false } }, computed: { badge() { let badge = this.item.badge; return badge ? isString(badge) ? JSON.parse(badge) : badge : { count: 0 }; }, isActive() { let activeItemKey = this.activeItemKey; return activeItemKey && activeItemKey == this.itemKey; }, isCenter() { return this.item.isCenter && this.centerVisible; } }, methods: { pushRoute, clickTabBar() { this.$emit("click", this.itemKey); this.$emit("change", this.itemKey); let route = this.item.route; if (route) { this.pushRoute(this.item.route); } } } }; </script> <style> .base-tabbar-item-badge-wrap { cursor: pointer; width: 3.3rem; padding: 0 0 0.1rem; } .base-tabbar-item-badge-wrap sup, .base-tabbar-center-item-badge-wrap sup { animation: none; position: absolute; } /* ant-design-vue 修改 badge 樣式 避免徽章在底部導航欄突出顯示 */ .base-tabbar-item-badge-wrap .ant-badge-count, .base-tabbar-item-badge-wrap .ant-badge-dot, .base-tabbar-item-badge-wrap .ant-badge .ant-scroll-number-custom-component { transform: translate(30%, 10%); } .base-tabbar-item-wrap, .base-tabbar-center-item-wrap { display: flex; flex-direction: column; align-items: center; justify-content: space-around; } .base-tabbar-center-item-wrap .icon, .base-tabbar-item-wrap .icon { margin: 0.3rem 0; } .base-tabbar-center-item-wrap .icon, .base-tabbar-center-item-wrap .icon svg { width: 1.2rem; height: 1.2rem; } .base-tabbar-item-wrap .icon, .base-tabbar-item-wrap .icon svg { width: 1.1rem; height: 1.1rem; } .base-tabbar-center-item-wrap .base-tabbar-item-text, .base-tabbar-item-wrap .base-tabbar-item-text { font-size: 0.5rem; color: black; } .base-tabbar-center-item-wrap .base-tabbar-item-text { font-size: 0.75rem; margin-top: 0.45rem; } .base-tabbar-center-item-wrap .base-tabbar-item-text-unactive, .base-tabbar-item-wrap .base-tabbar-item-text-unactive { display: none; } /* Center */ .base-tabbar-center-item-badge-wrap { cursor: pointer; width: 3.5rem; position: relative; top: -1.3rem; } .base-tabbar-center-item-wrap .base-tabbar-item-icon { display: flex; justify-content: center; align-items: center; width: 3.7rem; height: 3.7rem; /* border-radius: 20%; */ border: 0.5rem solid #ffffff; box-shadow: 0 0 0.35rem 0 #e2e1e1; box-shadow: 0 0 6px rgba(180, 160, 120, 0.8); } </style>
BaseTabBar
<template> <div class="base-tabbar-wrap" :style="{ bottom }"> <slot name="baseTabBarItem"> <base-tab-bar-item v-for="(baseTabBar, index) in baseTabBars" :item="baseTabBar" :key="index" :item-key="index" :active-item-key="realActiveItemKey" :centerVisible="centerVisible" @click="clickTabBar" @change="changeTabBar(index)" v-bind="$props" ></base-tab-bar-item> </slot> </div> </template> <script> import BaseTabBarItem from "./BaseTabBarItem"; export default { name: "BaseTabBar", components: { BaseTabBarItem }, data() { return { inActiveItemKey: "" }; }, props: { centerVisible: { type: [Boolean], default: true, required: false }, visible: { type: [Boolean], default: true, required: false }, showTextOnActive: { type: [Boolean], default: false, required: false }, color: { type: [String], default: "", required: false }, activeColor: { type: [String], default: "", required: false }, activeItemKey: { type: [String, Number], default: "", required: false }, baseTabBars: { type: [Array], default: function() { return [ { icon: "Antd_home", text: "首頁", route: {}, badge: JSON.stringify({ color: "red", count: 12, overflowCount: 99, dot: false }) } ]; }, required: false } }, computed: { realActiveItemKey() { let activeItemKey = this.activeItemKey; let isNotNull = activeItemKey | (activeItemKey == 0); return isNotNull ? activeItemKey : this.inActiveItemKey; }, bottom() { return this.visible ? 0 : "-7.25%"; } }, methods: { changeTabBar(key) { this.inActiveItemKey = key; this.$emit("change", key); }, clickTabBar() { this.$emit("click"); } }, mounted() { let activeItemKey = this.activeItemKey; let isNotNull = activeItemKey | (activeItemKey == 0); return isNotNull ? activeItemKey : this.inActiveItemKey; } }; </script> <style> .base-tabbar-wrap { height: 7.25%; max-height: 7.25%; display: flex; flex-direction: row; justify-content: space-around; align-items: center; background-color: white; position: fixed; width: 100%; bottom: 0; transition: bottom 0.25s; } </style>
API
BaseTabBarItem
引數 | 說明 | 型別 | 預設值 |
---|---|---|---|
centerVisible | 是否中間隱藏 是則不是中間的樣式 用於底部工具欄出現時隱藏中間樣式 | Boolean | false |
showTextOnActive | 是否啟用時才顯示文字 | Boolean | false |
color | 文字顏色 | String | |
activeColor | 啟用時文字顏色 | String | |
itemKey | 項鍵 | String,Number | |
activeItemKey | 啟用項鍵 用於啟用時顯示 | String,Number | |
icon | 圖示元件 - 圖示元件需要自己實現 | Slot | |
text | 文字 | Slot | |
item | 項 | Object |
事件
事件名稱 | 說明 | 型別 | 預設值 |
---|---|---|---|
click | 點選回撥 | function(itemKey){} | |
change | 改變回調 | function(itemKey){} |
Item
引數 | 說明 | 型別 | 預設值 |
---|---|---|---|
icon | 圖示 - 這裡是自己實現的圖示元件所需引數 | String | |
text | 文字 | String | |
route | 路由 | Object,String | |
badge | 徽章 | Object,String | |
isCenter | 是否中間樣式 | Boolean | |
centerShape | 中間形狀 | String | circle,square |
BaseTabBar
引數 | 說明 | 型別 | 預設值 |
---|---|---|---|
centerVisible | 是否中間隱藏 是則不是中間的樣式 用於底部工具欄出現時隱藏中間樣式 | Boolean | true |
showTextOnActive | 是否啟用時才顯示文字 | Boolean | false |
visible | 是否隱藏 是則不顯示 | Boolean | true |
color | 文字顏色 | String | |
activeColor | 啟用時文字顏色 | String | |
activeItemKey | 啟用項鍵 用於啟用時顯示 | String,Number | |
baseTabBars | tab項陣列 | Array |
事件
事件名稱 | 說明 | 型別 | 預設值 |
---|---|---|---|
click | 點選回撥 | function(){} | |
change | 改變回調 | function(itemKey){} |
示例