当前位置:   article > 正文

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

u-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": "到访记录"
      }
    ]
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
// 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封装一下引入就行
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

下面是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: "邀约/预约",
  },
  ...自行添加即可
];
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84

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 // 置顶
        });
    }
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85

封装通知组件

// 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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134

使用通知组件

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

import Bus from '@/utils/bus'
let notify = {
	message: '要发送的消息',
    background: '通知的背景色',
}
Bus.emit('notify', notify)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/651849
推荐阅读
相关标签
  

闽ICP备14008679号