123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 |
- <!-- 导航工具栏 -->
- <template>
- <view :style="{
- width: position != 'static' ? wininfo.windowWidth - offsetLeft * 2 + 'px' : 'auto',
- left: position != 'static' ? offsetLeft + 'px' : '0px',
- top: position == 'top' ? top_px + 'px' : 'auto',
- bottom: position == 'bottom' ? bottom_px + 'px' : 'auto'
- }" class="tm-bottomnavigation " :class="[
- black_tmeme ? 'grey-darken-5' : '',
- black_tmeme ? 'bk' : '',
- 'round-' + round,
- bgTheme,
- position,
- border === 'top' ? 'border-t-1' : '',
- border === 'bottom' ? 'border-b-1' : '',
- 'sheetIDX'
- ]">
- <view :style="{ background: bgColor }" class=" flex-between py-10">
- <block v-for="(item, index) in listDate" :key="index">
- <view :style="{
- width: listLen + '%'
- }" class="flex-center" :key="index" :class="[active_selecindex == index && ani == true ? ` ani ` : ``]">
-
- <tm-button :key="index" titl height="100%" :width="btnwidth"
- :iconSize="item['iconSize'] ? item['iconSize'] : 38"
- :fontSize="item['fontSize'] ? item['fontSize'] : 20" :vertical="vertical" :icon="active_selecindex==index?item.icon:(item['noIcon']||item.icon)"
- :label="item.value" block :theme="active_selecindex == index ? iconColor : iconColorGrey"
- :black="black_tmeme" @click.stop="onclick($event, index)" item-class="noGutter "
- :font-color="active_selecindex == index ? iconColor : iconColorGrey">
- <template v-slot:icon="{ data }">
- <!-- #ifdef MP -->
- <text v-if="false">{{offsetLeft}}</text>
- <!-- #endif -->
- <view style="height: 52rpx;">
- <view v-if="!item['custom']" >
- <view v-if="item.showDot" class="relative fulled" style="z-index: 10;">
- <tm-badges :color="item.dot['color']||iconColor" v-if="!item.dot['dot']&&!item.dot['icon']" :label="item.dot.num"
- :offset="[10, 0]" :dot="item.dot.dot"></tm-badges>
- <tm-badges :color="item.dot['color']||iconColor" v-if="item.dot['dot']&&!item.dot['icon']"></tm-badges>
- <tm-badges :color="item.dot['color']||iconColor" :offset="[10,0]" :icon="item.dot['icon']" v-if="item.dot['icon']"></tm-badges>
-
- </view>
-
- <tm-icons :black="black_tmeme"
- :color="active_selecindex == index ? iconColor : iconColorGrey" :size="data.size"
- :name="data.icon"></tm-icons>
- </view>
- <view v-if="item['custom'] === true"
- class="tm-bottomnavigation-custom absolute flex-center" :style="{
- width: '100%',
- height: '100%',
- minHeight: '100%',
- top: '-50%',
- left: '0',
- flexShrink: 0
- }">
- <view
- :class="[item['customColor'] ? item['customColor'] : (bgColor?'':'red'), black_tmeme ? 'bk' : '']"
- :style="{width: '90rpx',height: '90rpx',background:bgColor?bgColor:''}" class="rounded flex-center">
- <tm-icons :black="black_tmeme" :color="item['customFontColor'] || iconColorGrey"
- :size="data.size" :name="data.icon"></tm-icons>
- </view>
- </view>
- </view>
- </template>
- <template v-slot:default="{data}">
- <text :class="[active_selecindex == index ? 'ani' : '']">{{ data }}</text>
- </template>
- </tm-button>
-
-
- </view>
- </block>
- </view>
- <!-- 安全区域的高度。 -->
- <view v-if="safe" :style="{ height: safeBottomeHeight + 'px',background: bgColor }"></view>
- </view>
- <!--
- list:[{...}],基础属性为:{icon:"图标或者图片",value:"标题"}
- 全部属性为(除了上述基本两个属性,其它全为可选。):
- {
- icon: 'icon-user-fill',
- noIcon:'',//未选中时的图标,不提供默认使用相同的图标icon
- value: '个人中心',
- iconSize: 38,
- fontSize: 20,
- showDot:false,//是否显示角标。
- dot:{
- dot:false,//是否显示点。如果是点,则不显示Num
- num:"",//是否显示数字。
- ico:"",//是否显示图标,优先级高于dot,num.
- },
- path: ''
- }
- -->
- </template>
- <script>
- /**
- * 底部导航工具条
- * @property {Array} list = [] 默认:[],基本属性:{icon:"图标或者图片",value:"标题"},
- * @property {String} bg-theme = [] 默认:"white",背景颜色主题名称
- * @property {String | Boolean} black = [true|false] 默认:null,暗黑模式。
- * @property {String} bg-color = [] 默认:'',自定义背景颜色。
- * @property {String} icon-color = [] 默认:使用主题色,项目默认激活色。
- * @property {String} icon-color-grey = [] 默认:使用主题色,项目失去焦点时的颜色。
- * @property {String} position = [bottom|top|static] 默认:bottom,可选位置:bottom|top|static
- * @property {Number|String} top = [] 默认:0,距离顶部的距离:只有在position=='top'使用
- * @property {Number|String} bottom = [] 默认:0,距离底部的距离:只有在position=='bottom'使用
- * @property {String} border = [top|bottom] 默认:top,显示上边线还是下边线。可选top / bottom
- * @property {String|Boolean} vertical = [true|false] 默认:true,文字方向:默认是竖向。否则为横向。
- * @property {String|Number} offset-left = [] 默认:0,偏移量。即离左边的间距。如果提供,自动居中出现两边间隙。
- * @property {String|Boolean} safe = [true|false] 默认:true,// 是否开启底部安全区域。
- * @property {String|Boolean} auto-selected = [true|false] 默认:true,// 是否开启自动匹配页面选中底部按钮。
- * @property {Function} change 切换按钮时触发。
- *
- */
- import tmButton from "@/tm-vuetify/components/tm-button/tm-button.vue"
- import tmIcons from "@/tm-vuetify/components/tm-icons/tm-icons.vue"
- import tmBadges from "@/tm-vuetify/components/tm-badges/tm-badges.vue"
- export default {
- components: {
- tmButton,
- tmIcons,
- tmBadges
- },
- name: 'tm-bottomnavigation',
- props: {
- list: {
- Array,
- default: () => {
- return [];
- }
- },
- // 背景颜色主题名称
- bgTheme: {
- type: String,
- default: 'black'
- },
- // 是否启用暗黑主题。
- black: {
- type: String | Boolean,
- default: null
- },
- // 背景颜色,自定义。
- bgColor: {
- type: String,
- default: ''
- },
- // 自定义项目文字默认激活色。
- iconColor: {
- type: String,
- default: 'primary'
- },
- // 自定义项目文字默认失去焦点色。
- iconColorGrey: {
- type: String,
- default: 'grey-lighten-1'
- },
- // 可选bootom / top / static
- position: {
- type: String,
- default: 'bottom'
- },
- // 距离顶部的距离。默认是0,只有在position=='top'使用
- top: {
- type: Number | String,
- default: 0
- },
- // 距离顶部的距离。默认是0,只有在position=='bottom'使用
- bottom: {
- type: Number | String,
- default: 0
- },
- // 显示上边线还是下边线。可选top / bottom
- border: {
- type: String,
- default: 'top'
- },
- // 文字方向:默认是竖向。否则为横向。
- vertical: {
- type: String | Boolean,
- default: true
- },
- // 只支持圆角主题。如round-5
- round: {
- type: String | Number,
- default: 0
- },
- // 偏移量。即离左边的间距。如果提供,整个navbar的宽度会是100% - offsetLeft*2。
- offsetLeft: {
- type: String | Number,
- default: 0
- },
- // 是否开启底部安全区域。
- safe: {
- type: String | Boolean,
- default: true
- },
- // 是否开启点按动画,默认开启。
- ani: {
- type: String | Boolean,
- default: true
- },
- // 是否自动匹配页面选中底部按钮?
- autoSelected: {
- type: String | Boolean,
- default: true
- },
- activeIndex:{
- type:Number,
- default:0
- }
- },
- computed: {
- black_tmeme: function() {
- if (this.black !== null) return this.black;
- return this.$tm.vx.state().tmVuetify.black;
- },
- top_px: function() {
- return this.top;
- },
- bottom_px: function() {
- return this.bottom;
- },
- wininfo: function() {
- return uni.getSystemInfoSync();
- },
- listLen: function() {
- if (this.listDate.length == 0) return 1;
- return 100 / this.listDate.length;
- },
- active_selecindex:function(){
- if(!this.autoSelected) return this.slectedIndx;
- return this.$tm.vx.state().tmVuetify.tmVueTifly_pagesIndex;
- }
- },
- created() {
- uni.hideTabBar({animation:false})
- },
- data() {
- return {
- slectedIndx: -1, //默认激活的值。
- btnwidth: 0,
- safeBottomeHeight: 0,
- listDate: [],
- pages:[]
- };
- },
- watch:{
- list:{
- deep:true,
- handler(){
- let t = this;
- this.$nextTick(function() {
- let qr = uni.createSelectorQuery().in(this);
- qr.select('.sheetIDX')
- .boundingClientRect()
- .exec(e => {
- t.btnwidth = e[0].width / 5 + 'px';
- t.listItem();
- });
- });
- }
- },
- activeIndex:function(){
- if(!this.autoSelected) this.slectedIndx = this.activeIndex;
- }
- },
-
- mounted() {
- if(!this.autoSelected) this.slectedIndx = this.activeIndex;
- setTimeout(function(){
- uni.hideTabBar({animation:false})
- },350)
- let t = this;
- this.$nextTick(function() {
- uni.hideTabBar({animation:false})
- let qr = uni.createSelectorQuery().in(this);
- qr.select('.sheetIDX')
- .boundingClientRect()
- .exec(e => {
- t.btnwidth = e[0].width / 5 + 'px';
- t.listItem();
- });
- });
-
- },
- methods: {
- autoxz(){
-
- let sy = uni.getSystemInfoSync();
- // #ifdef MP
- this.safeBottomeHeight = sy.screenHeight - sy.safeArea.bottom;
- // #endif
- // #ifdef H5
- this.safeBottomeHeight = sy.windowHeight - sy.safeArea.height;
- // #endif
- // #ifdef APP
- this.safeBottomeHeight = Math.abs(sy.safeArea.bottom - sy.safeArea.height);
- // #endif
- if(!this.autoSelected){
- return;
- }
- let pageRoute = this.$tm.vx.state().tmVuetify.tmVueTifly_pages;
-
-
- let index = this.listDate.findIndex((el,index)=>{
- let url = el?.path||"";
- url = url.split('?')[0]||"";
- return url == pageRoute
- })
-
- if(index>-1){
- this.slectedIndx = index;
- this.$tm.vx.commit('setPageNowIndex',index)
- this.$emit('change', {
- index: index,
- item: this.listDate[index]
- });
-
- }
- },
- listItem() {
- let mod = {
- icon: 'icon-user-fill',
- value: '个人中心',
- iconSize: 38,
- fontSize: 20,
- showDot: false, //是否显示角标。
- dot: {
- dot: false, //是否显示点。如果是点,则不显示Num
- num: '', //是否显示数字。
- ico: '' //是否显示图标,优先级高于dot,num.
- },
- path: '',
- openType:'switchTab'
- };
- this.$nextTick(function() {
- let tm = [];
- let ls = this.$tm.deepClone(this.list);
- ls.forEach((item, index) => {
- tm.push({
- ...mod,
- ...item
- });
- });
- this.listDate = tm;
- this.autoxz();
- });
- },
- onclick(e, index) {
- let t = this;
- this.slectedIndx = index;
- this.$tm.vx.commit('setPageNowIndex',index)
- let item = this.listDate[index];
- this.$emit('change', {
- index: index,
- item: item
- });
- if (item?.path) {
- let oldPath = item['path']||"";
- oldPath = oldPath.split("?")[0]||"";
- let pages = getCurrentPages();
- let url = pages[pages.length-1].route;
- if(url[0]!='/') url = '/' + url;
- url = url.split('?')[0]||"";
- if(url==oldPath) return;
- // #ifdef MP || APP
- try{
- uni.vibrateShort()
- }catch(e){
- // ...
- }
- // #endif
- switch (item['openType']) {
- case 'switchTab':
- uni.switchTab({
- url: item.path,
- fail: (e) => {
- console.log(e);
- }
- })
- break;
- case 'redirect':
- uni.redirectTo({
- url: item.path
- })
- break;
- case 'reLaunch':
- uni.reLaunch({
- url: item.path
- })
- break;
- case 'navigateBack':
- uni.navigateBack({
- url: item.path
- })
- break;
- default:
- uni.navigateTo({
- url: item.path
- })
- break;
- }
- }
-
-
- }
- }
- };
- </script>
- <style>
- page,
- body {
- /* padding-bottom: 167rpx; */
- height: 100%;
- }
- </style>
- <style lang="scss" scoped>
- .tm-bottomnavigation {
- // animation: scalse 0.4s linear;
- &.bottom {
- position: fixed;
- bottom: 0;
- left: 0;
- z-index: 450;
- }
- &.top {
- position: fixed;
- z-index: 450;
- top: 0;
- left: 0;
- }
- }
- .ani {
- animation: scalse 0.4s linear;
- }
- @keyframes scalse {
- 0% {
- transform: scale(0.9);
- }
- 50% {
- transform: scale(1.1);
- }
- 100% {
- transform: scale(1);
- }
- }
- </style>
|