uniapp开发微信小程序自定义tabbar,自定义导航栏,自定义上拉刷新,下拉加载,封装通知组件等

最近有个需求,需要动态切换tabbar,还有导航栏,刷新等等,看了原生的不是很满足,干脆自己封装了一个
小程序自定义tabbar,用了各种办法,最好的还是通过单页面的形式去做
下面上代码

1. 自定义tabbar

// pages.json
 "pages": [
	...
    {
      "path": "pages/tabbar/tabbar",
      "style": {
        "navigationBarTitleText": "",
        "navigationStyle": "custom", // 这里开启自定义导航栏
        "onReachBottomDistance": 120,
        "usingComponents": {
          "van-tag": "/wxcomponents/vant/tag/index",
          "van-cascader": "/wxcomponents/vant/cascader/index",
          "van-popup": "/wxcomponents/vant/popup/index",
          "van-calendar": "/wxcomponents/vant/calendar/index"
        }
      }
    },
  ],
  "tabBar": {
    "custom": true, // 这里开启自定义tabbar
    "color": "rgba(0, 0, 0, 1)",
    "selectedColor": "#004ed2",
    "borderStyle": "black",
    "backgroundColor": "#ffffff",
    "height": "0",
    "fontSize": "22rpx",
    "iconWidth": "24px",
    "spacing": "3px",
    "list": [ // 这里定义两个页面,空白的就行
      {
        "pagePath": "pages/inspect/inspect",
        "iconPath": "static/imgs/invite.png",
        "selectedIconPath": "static/imgs/invite_active.png",
        "text": "邀约/预约"
      },
      {
        "pagePath": "pages/search/search",
        "iconPath": "static/imgs/record.png",
        "selectedIconPath": "static/imgs/record_active.png",
        "text": "到访记录"
      }
    ]
  }
// pages/tabbar/tabbar 这里开始写tabbar页面
// 这里是自定义tabbar,引入了四个,自行判断是否显示
    <view class="container">
        <invite-tabbar ref="inviteTabbar" v-if="store.tabbarActiveIndex === 'invite'" v-model:triggered="triggered"
            v-model:_freshing="_freshing"></invite-tabbar>
        <record-tabbar v-if="store.tabbarActiveIndex === 'record'" v-model:triggered="triggered"
            v-model:_freshing="_freshing"></record-tabbar>
        <inspect-tabbar v-if="store.tabbarActiveIndex === 'inspect'" v-model:triggered="triggered"
            v-model:_freshing="_freshing"></inspect-tabbar>
        <statistic-tabbar v-if="store.tabbarActiveIndex === 'statistic'"></statistic-tabbar>
        <my-tabbar v-if="store.tabbarActiveIndex === 'my'"></my-tabbar>
    </view>
<Header :title="title" :icon="false"></Header> // 这里是自定义导航栏,后面会写
// 这里是自定义上拉刷新,下拉加载
<scroll-view show-scrollbar="true" class="scroll" :scroll-top="scrollTop" scroll-y="true"
    :refresher-enabled="isOpenRefresh" :refresher-triggered="triggered" :refresher-threshold="100"
    refresher-background="gray" @refresherpulling="onPulling" @refresherrefresh="onRefresh"
    @refresherrestore="onRestore" @refresherabort="onAbort" @scrolltoupper="scrolltoupper"
    @scrolltolower="scrolltolower" @scroll="scroll">
    // 这里是自定义tabbar,引入了四个,自行判断是否显示
    <view class="container">
        <invite-tabbar ref="inviteTabbar" v-if="store.tabbarActiveIndex === 'invite'" v-model:triggered="triggered"
            v-model:_freshing="_freshing"></invite-tabbar>
        <record-tabbar v-if="store.tabbarActiveIndex === 'record'" v-model:triggered="triggered"
            v-model:_freshing="_freshing"></record-tabbar>
        <inspect-tabbar v-if="store.tabbarActiveIndex === 'inspect'" v-model:triggered="triggered"
            v-model:_freshing="_freshing"></inspect-tabbar>
        <statistic-tabbar v-if="store.tabbarActiveIndex === 'statistic'"></statistic-tabbar>
        <my-tabbar v-if="store.tabbarActiveIndex === 'my'"></my-tabbar>
    </view>
</scroll-view>

// 这里引入tabbar具体业务组件
import InviteTabbar from "@/components/invite/invite.vue";
import RecordTabbar from "@/components/record/record.vue";
import InspectTabbar from "@/components/inspect/inspect.vue";
import StatisticTabbar from "@/components/statistic/statistic.vue";
import MyTabbar from "@/components/my/my.vue";
// 这里是tabbar列表
import TabbarList from "@/components/tabbar/tabbar.vue";
// 这里引入的封装自定义导航栏
import Header from "@/components/header/header.vue";
import Bus from "@/utils/bus"; // 使用mitt封装一下引入就行

下面是tabbar封装组件列表,会引入到上面

// /components/tabbar/tabbar.vue
<template>
    <view class="tabbar">
        <template v-for="item in tabbarList" :key="item.id">
            <view :class="{ 'tabbar-item': true, active: store.tabbarActiveIndex === item.id }"
                v-if="!(item.id === 'inspect' && !store.approvalAuthority)" @tap="handleChangeTabbar(item)">
                <img class="icon" :src="store.tabbarActiveIndex === item.id ? item.selectedIconPath : item.iconPath" alt="">
                <view>{{ item.text }}</view>
            </view>
        </template>
    </view>
</template>
<script setup lang="ts">
import type { ITabbarList } from "@/types/common";
import { tabbarList } from '@/utils/tabbar'
import useUser from '@/store/user'

const store = useUser()

const handleChangeTabbar = (item: ITabbarList) => {
    // console.log(item);
    // 这里切换tabbar,通过pinia存储
    store.SET_tabbarActiveIndex(item.id)
}
</script>

// /utils/tabbar.ts 这里存放有哪些tabbar页面
import type { ITabbarList } from "@/types/common";
export const tabbarList: ITabbarList[] = [
  {
    id: "invite",
    pagePath: "/pages/invite/invite",
    iconPath: "/static/imgs/invite.png",
    selectedIconPath: "/static/imgs/invite_active.png",
    text: "邀约/预约",
  },
  ...自行添加即可
];

2. 自定义导航栏

// 要引入的页面
<Header :title="title" :icon="false"></Header> // 这里是自定义导航栏,后面会写

//  封装导航栏
<template>
    <!-- <van-sticky class="sticky"> -->
    <view class="header">
        <img src="/static/imgs/right_arrow.png" alt="" v-if="props.icon" class="icon" @tap="goBack">
        <!-- <van-icon v-if="props.icon" class="icon arrow-left" name="arrow-left" @tap="goBack" /> -->
        <text class="title">{{ title }}</text>
    </view>
    <!-- </van-sticky> -->
    <view class="header-height"></view>
    <van-notify class="van-notify" />
</template>

<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import useUser from '@/store/user'
import Notify from '@/wxcomponents/vant/notify/notify';
import Bus from '@/utils/bus'

const store = useUser()
// 使用uni.getMenuButtonBoundingClientRect()获取高度,存储到store
const top = ref<string>(store.menuButtonBoundingClientRect.top + 'px')
const height = ref<string>(store.menuButtonBoundingClientRect.height + 'px')

interface IHeader {
    title: string, // 标题
    icon: boolean, // 是否需要返回上一层图标
    color: string, // 标题栏颜色
    goBack?: Function // 点击返回上一层传递的函数
}
const props = withDefaults(defineProps<IHeader>(), {
    icon: false,
    color: '#fff'
})
const goBack = () => {
    props.goBack && props.goBack() // 这里可以执行接收到的函数
    uni.navigateBack({
        delta: 1
    })
}
</script>

<style scoped lang="scss">
.sticky {
    z-index: 999999;
}

.header {
    position: fixed;
    top: 0;
    right: 0;
    left: 0;
    display: flex;
    justify-content: center;
    padding-top: v-bind(top);
    padding-bottom: 26rpx;
    height: v-bind(height);
    line-height: v-bind(height);
    background-color: #fff;
    font-weight: 700;
    font-size: 36rpx;
    z-index: 999999;

    .icon {
        position: absolute;
        left: 18rpx;
        bottom: 26rpx;
        width: 64rpx;
        height: 64rpx;
    }
}

.header-height {
    padding-top: v-bind(top);
    padding-bottom: 26rpx;
    height: v-bind(height);
    line-height: v-bind(height);
    background-color: #fff;
    z-index: 999999;
}
</style>

3. 自定义上拉刷新,下拉加载

// 在tabbar界面使用
<scroll-view show-scrollbar="true" class="scroll" :scroll-top="scrollTop" scroll-y="true"
    :refresher-enabled="isOpenRefresh" :refresher-triggered="triggered" :refresher-threshold="100"
    refresher-background="gray" @refresherpulling="onPulling" @refresherrefresh="onRefresh"
    @refresherrestore="onRestore" @refresherabort="onAbort" @scrolltoupper="scrolltoupper"
    @scrolltolower="scrolltolower" @scroll="scroll">
    // 这里是自定义tabbar,引入了四个,自行判断是否显示
    <view class="container">
        <invite-tabbar ref="inviteTabbar" v-if="store.tabbarActiveIndex === 'invite'" v-model:triggered="triggered"
            v-model:_freshing="_freshing"></invite-tabbar>
        <record-tabbar v-if="store.tabbarActiveIndex === 'record'" v-model:triggered="triggered"
            v-model:_freshing="_freshing"></record-tabbar>
        <inspect-tabbar v-if="store.tabbarActiveIndex === 'inspect'" v-model:triggered="triggered"
            v-model:_freshing="_freshing"></inspect-tabbar>
        <statistic-tabbar v-if="store.tabbarActiveIndex === 'statistic'"></statistic-tabbar>
        <my-tabbar v-if="store.tabbarActiveIndex === 'my'"></my-tabbar>
    </view>
</scroll-view>

// scroll view
const isOpenRefresh = ref(true);
const triggered = ref<string | boolean>(false);
const _freshing = ref(false);
const dataList = ref<Array<number>>([]);
const isAllowRefresh = ref(false);
const scrollTop = ref(0) // 设置滚动条位置
const scrollTopOld = ref(0) // 设置滚动条位置

// 自定义下拉刷新控件被下拉
const onPulling = (e: any) => {
    // console.log("onpulling", e);
    if (e.detail.dy < 0) return; // 防止上滑页面也触发下拉
    if (isAllowRefresh.value) return;
    triggered.value = true;
};
// 自定义下拉刷新被触发
const onRefresh = () => {
    if (_freshing.value) return;
    _freshing.value = true;
    // 这里触发刷新数据,具体到业务组件接收
    Bus.emit(`refresh-${store.tabbarActiveIndex}`);
    setTimeout(() => {
        triggered.value = false;
        _freshing.value = false;
    }, 500);
};
// 自定义下拉刷新被复位
const onRestore = () => {
    triggered.value = "restore"; // 需要重置
    console.error("onRestore");
};
// 自定义下拉刷新被中止
const onAbort = () => {
    console.error("onAbort");
    setTimeout(() => {
        triggered.value = false;
        _freshing.value = false;
    }, 0);
};
// 自定义上拉加载
const scrolltolower = (e: any) => {
    // console.log(e);
    // 这里触发上拉加载,添加数据,具体到业务组件接收
    Bus.emit(`push-${store.tabbarActiveIndex}`);
};
// 触顶操作-准入
const scrolltoupper = () => {
    isAllowRefresh.value = true;
};
const scroll = (e: any) => {
    // console.log(e)
    scrollTopOld.value = e.detail.scrollTop // 赋值滚动条位置
}

watch(
    () => store.tabbarActiveIndex,
    (newVal) => {
        // console.log(newVal);
        // 注意!!切换tabbar的时候滚动条会保持原位,下面恢复到最顶端
        scrollTop.value = scrollTopOld.value
        nextTick(function () {
            scrollTop.value = 0 // 置顶
        });
    }
);

封装通知组件

// app.vue
<template>
  <van-notify class="van-notify" />
</template>
<script setup lang="ts">
import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
import useUser from '@/store/user'
import Notify from '@/wxcomponents/vant/notify/notify';
import Bus from '@/utils/bus'
import { ref, reactive, onMounted } from 'vue'

const store = useUser()

// 通知数据
interface INotify {
  message?: string,
  background?: string,
}
onMounted(() => {
  // console.log('app');
// 这里是通知组件
  Bus.on('notify', (notifyData) => {
    console.log('app notify');
    handleRunNotify(notifyData as INotify)
  })
})

const handleRunNotify = (notifyData?: INotify) => {
  Notify({
    message: '请阅读并勾选同意下方协议22',
    duration: 3000,
    top: (store.menuButtonBoundingClientRect?.top + store.menuButtonBoundingClientRect.height + 24),
    selector: '.van-notify',
    background: '#fe6262',
    ...notifyData
  })
}

onLaunch(() => {
  console.log("App Launch");
  console.log(store);
  if (store.acces_token) {
    console.log('acces_token', store.acces_token);
    store.SET_tabbarActiveIndex('invite')
    uni.redirectTo({
      url: '/pages/tabbar/tabbar',
    });
  }
// 进入页面开启监听,
  Bus.on('notify', (notifyData) => {
    console.log('app notify');
    handleRunNotify(notifyData as INotify)
  })
  // 有的页面返回上一层并通知,所以这里需要做一个转发,也可以上面做一个延迟,最好是分开
  Bus.on('invite-again', (notify) => {
    console.log('invite-again');
    setTimeout(() => {
      Bus.emit('notify', notify)
    }, 500);
  })
});
onShow(() => {
  console.log("App Show");
});
onHide(() => {
  console.log("App Hide");
});
</script>
<style lang="scss">
@import '@/wxcomponents/vant/common/index.wxss';

page {
  font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica,
    Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB', 'Microsoft Yahei',
    sans-serif;
  font-family: 'PingFang SC';
  background-color: #f4f5fa;
  height: 100vh;
}

.container {
  padding: 24rpx 32rpx 120rpx;
  box-sizing: border-box;
}

:deep(.van-notify.van-notify--danger) {
  // position: absolute;
  display: flex;
  justify-content: center;
  align-items: center;
  width: calc(100% - 64rpx) !important;
  margin: 0 32rpx !important;
  height: 88rpx;
  // line-height: 88rpx;
  border-radius: 16rpx;
  opacity: 0.8;

  // transform: translateY(32rpx);
  box-sizing: border-box;
}

.input-placeholder,
.textarea-placeholder {
  font-size: 28rpx;
  color: #ccc;
}

/* dialog弹窗 */
.dialog {
  :deep(.van-scale-enter-to) {
    width: 622rpx !important;
  }

  :deep(.van-dialog__message) {
    padding: 72rpx 88rpx 50rpx !important;
    line-height: 50.4rpx !important;
    color: #333 !important;
    font-size: 36rpx !important;
  }

  :deep(.van-dialog__footer) {
    button {
      color: #004ed2;
      font-size: 32rpx !important;
    }

    .van-hairline--right {
      button {
        color: #333;
      }
    }
  }
}
</style>

使用通知组件

// 需要使用通知组件的页面
<van-notify class="van-notify" />

import Bus from '@/utils/bus'
let notify = {
	message: '要发送的消息',
    background: '通知的背景色',
}
Bus.emit('notify', notify)

到这里就全部实现了uniapp开发微信小程序自定义tabbar,自定义导航栏,自定义上拉刷新,下拉加载,封装通知组件等功能,有问题可以留言交流~

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用UniApp框架开发微信小程序时,可以通过自定义组件来实现自定义TabBar。下面是一个简单的示例: 1. 在UniApp项目的`components`目录下创建一个名为`custom-tabbar`的文件夹,并在该文件夹下创建`index.vue`文件。 2. 在`index.vue`文件中编写自定义TabBar的代码,以下是一个示例: ```vue <template> <view class="custom-tabbar"> <view class="tabbar-item" :class="{ active: activeTab === 'home' }" @click="switchTab('home')"> <text>首页</text> </view> <view class="tabbar-item" :class="{ active: activeTab === 'category' }" @click="switchTab('category')"> <text>分类</text> </view> <view class="tabbar-item" :class="{ active: activeTab === 'mine' }" @click="switchTab('mine')"> <text>我的</text> </view> </view> </template> <script> export default { data() { return { activeTab: 'home' // 默认选中的Tab } }, methods: { switchTab(tab) { // 切换Tab this.activeTab = tab; // 触发父组件的事件,实现页面切换 this.$emit('switchTab', tab); } } } </script> <style> .custom-tabbar { display: flex; justify-content: space-around; align-items: center; height: 100px; background-color: #f5f5f5; } .tabbar-item { display: flex; justify-content: center; align-items: center; flex-direction: column; } .tabbar-item.active { color: #007aff; } </style> ``` 3. 在需要使用自定义TabBar的页面中引入`custom-tabbar`组件,并使用`switchTab`事件进行页面切换,以下是一个示例: ```vue <template> <view class="page"> <custom-tabbar @switchTab="onSwitchTab"></custom-tabbar> <!-- 其他页面内容 --> </view> </template> <script> export default { methods: { onSwitchTab(tab) { // 切换页面 uni.switchTab({ url: `/pages/${tab}/index` }); } } } </script> <style> .page { position: fixed; top: 0; left: 0; right: 0; bottom: 100px; /* 留出TabBar的高度 */ } </style> ``` 通过以上步骤,你就可以在UniApp自定义微信小程序TabBar了。记得根据自己的需求进行样式和页面切换逻辑的修改。希望对你有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值