tabbar 修改

develop
吴治霖 1 year ago
parent 94f93d2331
commit 989a87aaaa

@ -79,10 +79,6 @@
content: '确认退出登录吗?',
success: (succ) => {
if (succ.confirm) {
that.updateUserInfo({
avatarURL: '',
nickName: ''
})
that.updatetoken('')
that.savetokentostorage()
}

@ -1,51 +1,51 @@
{
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path" : "pages/home/home",
"style" :
{
"path": "pages/home/home",
"style": {
"navigationBarTitleText": "菜品圈",
"enablePullDownRefresh": false
}
},{
}, {
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "uni-app"
}
}
,{
"path" : "pages/search/search",
"style" :
{
}, {
"path": "pages/search/search",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/my/my",
"style" :
{
}, {
"path": "pages/my/my",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}, {
"path": "pages/ranking/ranking",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
,{
"path" : "pages/ranking/ranking",
"style" :
{
}, {
"path": "pages/loginTest/loginTest",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/loginTest/loginTest",
"path" : "pages/recommend/recommend",
"style" :
{
"navigationBarTitleText": "",
@ -61,30 +61,25 @@
"backgroundColor": "#F8F8F8"
},
"subpackages": [{
"root":"subpkg",
"pages" : [
{
"path" : "dishDetail/dishDetail",
"style" :
{
"root": "subpkg",
"pages": [{
"path": "dishDetail/dishDetail",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"path" : "search/search",
"style" :
{
}, {
"path": "search/search",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
},
{
"path" : "searchList/searchList",
"style" :
{
"path": "searchList/searchList",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
@ -98,26 +93,32 @@
"list": [{
"pagePath": "pages/home/home",
"text": "首页",
"iconPath": "static/static/images/home/home2.png",
"selectedIconPath": "static/static/images/home/home1.png"
"iconPath": "static/static/images/home/home1.png",
"selectedIconPath": "static/static/images/home/home2.png"
},
{
"pagePath": "pages/search/search",
"text": "搜索",
"iconPath": "static/static/images/home/search_icon.png",
"selectedIconPath": "static/static/images/home/search_icon.png"
"text": "筛选",
"iconPath": "static/static/images/home/筛选2.png",
"selectedIconPath": "static/static/images/home/筛选1.png"
},
{
"pagePath": "pages/recommend/recommend",
"text": "推荐",
"iconPath": "static/static/images/home/推荐1.png",
"selectedIconPath": "static/static/images/home/推荐2.png"
},
{
"pagePath": "pages/ranking/ranking",
"text": "排行榜",
"iconPath": "static/star0.png",
"selectedIconPath": "static/start1.png"
"iconPath": "static/static/images/home/排行榜2.png",
"selectedIconPath": "static/static/images/home/排行榜1.png"
},
{
"pagePath": "pages/my/my",
"text": "个人中心",
"iconPath": "static/static/images/user/head.png",
"selectedIconPath": "static/static/images/home/my.png"
"iconPath": "/static/static/images/home/我的1.png",
"selectedIconPath": "/static/static/images/home/我的2.png"
}
]
}

@ -27,6 +27,7 @@
</view>
</view>
</view>
<m-tabbar native=""></m-tabbar>
</view>
</template>

@ -3,6 +3,7 @@
<my-login v-if="!token"></my-login>
<my-userinfo v-else></my-userinfo>
<m-tabbar native=""></m-tabbar>
</view>
</template>

@ -1,6 +1,6 @@
<template>
<view>
<m-tabbar native=""></m-tabbar>
</view>
</template>

@ -0,0 +1,37 @@
<template>
<view>
<!-- tabBarShow显示第几个tabbar -->
<cc-myTabbar :tabBarShow="2"></cc-myTabbar>
<!-- 隐藏原生tabbar -->
onReady() {
uni.hideTabBar()
}
<!-- 页面距离底部140rpx(自定义tabbar的高度) -->
page {
padding-bottom: 140rpx;
}
</view>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
}
}
</script>
<style scoped lang="scss">
page {
padding-bottom: 140rpx;
}
</style>

@ -1,6 +1,7 @@
<template>
<view>
<button @click="getDishes()"></button>
<m-tabbar native=""></m-tabbar>
</view>
</template>
@ -10,6 +11,23 @@
return {
};
},
onLoad() {
},
methods: {
async getDishes() {
uniCloud.callFunction({
name:'getDishes',
data:{
api:'getByLabels',
labels:['鸡肉','米饭']
},
success(res) {
console.log(res)
}
})
}
}
}
</script>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 765 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

@ -18,6 +18,25 @@ exports.main = async (event, context) => {
}).get()
return res
}
if (event.api === 'getByLabels') {
let dishes = []
let len = []
for(let i = 0;i < event.labels.length;i++) {
let label = event.labels[i]
const res1 = await db.collection('dish-label').where({label:label}).get()
for(let i = 0;i < res1.data.length;i++) {
dishes.push(res1.data[i])
}
}
let dishRes = []
console.log(dishes.length)
for(let i = 0;i < dishes.length;i++) {
const res = await db.collection('dishes').where({_id:dishes[i].dish_id}).get()
console.log(res)
dishRes.push(res.data[0])
}
return dishRes
}
//返回数据给客户端
return "请求错误"
};

@ -0,0 +1,4 @@
## 1.0.12023-06-22
组件使用说明优化
## 1.0.02023-06-22
组件初始化

@ -0,0 +1,41 @@
/* 主要颜色 */
$base: #fe3b0f; //
.page-total{
position: fixed;
left: 0;
bottom: 0;
width: 100%;
// height: 100rpx;
}
.tab-list{
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 120rpx;
padding-bottom: 10px;
background-color: #FFFFFF;
.list{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 20%;
height: 120rpx;
image{
width: 48rpx;
height: 48rpx;
background-color: white;
}
text{
color: #333333;
font-size: 24rpx;
margin-top: 10rpx;
}
.action{
color: $base;
}
}
}

@ -0,0 +1,107 @@
<template>
<view class="page-total">
<view class="tab-list">
<view class="list" v-for="(item,index) in TabBarList"
@click="onTabBar(item,index)" :style="{marginTop: (index == 2) ? '-18px' : '0px'}"
:key="index">
<image :src="item.acImg" mode="widthFix" v-show="tabBarShow === index" :style="{width: (index == 2) ? '40px' : '24px',borderRadius: (index == 2) ? '12px' : '0px'}"></image>
<image :src="item.img" mode="widthFix" v-show="tabBarShow != index" :style="{width: (index == 2) ? '40px' : '24px',borderRadius: (index == 2) ? '12px' : '0px'}"></image>
<!-- background: (index == 2) ? 'red' : '' -->
<text :class="{'action':tabBarShow===index}" :style="{marginTop: (index == 2) ? '4px' : '0px'}">{{item.name}}</text>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
TabBarList:[
{
index: 0,
name: '首页',
img: "/static/static/images/home/home1.png",
acg: "/static/static/images/home/home2.png"
},
{
index: 1,
name: '筛选',
img: "/static/static/images/home/筛选2.png",
acImg: "/static/static/images/home/筛选1.png"
},
{
index: 2,
name: '推荐',
img: "/static/static/images/home/推荐1.png",
acImg:"/static/static/images/home/推荐2.png"
},
{
index: 3,
name: '排行榜',
img: "/static/static/images/home/排行榜2.png",
acImg: "/static/static/images/home/排行榜1.png"
},
{
index: 4,
name: '个人中心',
img: "/static/static/images/home/我的1.png",
acImg: "/static/static/images/home/我的2.png"
},
],
codeheight: 0,
isOverall: 0,
phoneModel: '',
};
},
props:{
tabBarShow: {
type: Number,
default: 0,
}
},
methods:{
/**
* @param {Object} item
* @param {Number} index
*/
onTabBar(item,index){
// this.tabBarShow = index;
switch (index){
case 0:
uni.switchTab({
url:"/pages/home/home"
})
break;
case 1:
uni.switchTab({
url:"/pages/search/search"
})
break;
case 2:
uni.switchTab({
url:"/pages/recommend/recommend"
})
break;
case 3:
uni.switchTab({
url:"/pages/ranking/ranking"
})
break;
case 4:
uni.switchTab({
url:'/pages/my/my'
})
break;
}
}
}
}
</script>
<style scoped lang="scss">
@import 'cc-myTabbar.scss';
</style>

@ -0,0 +1,85 @@
{
"id": "cc-myTabbar",
"displayName": "自定义tabbar底部tabbar凸起tabbar兼容苹果刘海屏小程序和APP",
"version": "1.0.1",
"description": "自定义tabbar底部tabbar凸起tabbar兼容苹果刘海屏Home键小程序和APP 可自定义tabbar图片文字大小及位置",
"keywords": [
"tabbar",
"自定义tabbar",
"底部导航",
"自定义",
"凸起导航"
],
"repository": "",
"engines": {
"HBuilderX": "^3.8.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "y",
"快手": "y",
"飞书": "y",
"京东": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
}
}
}
}
}

@ -0,0 +1,58 @@
# cc-myTabbar
#### 使用方法
```使用方法
<!-- tabBarShow显示第几个tabbar -->
<cc-myTabbar :tabBarShow="0"></cc-myTabbar>
<!-- 隐藏原生tabbar -->
onReady() {
uni.hideTabBar()
}
<!-- 页面距离底部140rpx(自定义tabbar的高度) -->
page {
padding-bottom: 140rpx;
}
```
#### HTML代码实现部分
```html
<template>
<view class="page">
<!-- tabBarShow显示第几个tabbar -->
<cc-myTabbar :tabBarShow="0"></cc-myTabbar>
</view>
</template>
<script>
export default {
data() {
return {
};
},
onReady() {
uni.hideTabBar()
},
methods: {
}
}
</script>
<style scoped lang="scss">
page {
padding-bottom: 140rpx;
}
</style>
```

@ -0,0 +1,12 @@
## 2.0.22023-08-01
组件优化
## 2.0.12023-08-01
支持单选 多选筛选
## 2.02023-07-22
组件优化 解决样式在小程序不生效问题 适配小程序!
## 1.0.22023-07-19
组件优化
## 1.0.12023-07-15
组件优化
## 1.0.02023-07-15
组件初始化

@ -0,0 +1,335 @@
<template>
<view>
<view class="mask" @tap="hideright" catchtouchmove="true" v-if="popShow == true"></view>
<!-- 点击筛选弹出的右侧弹出窗口 -->
<view :class="popShow == true ? 'right_popup':'hide_popup'" catchtouchmove="true">
<scroll-view :scroll-top="scrollTop" scroll-y="true" class="scroll-Y" @scrolltoupper="upper"
@scrolltolower="lower" @scroll="scroll">
<view v-for="(item, index) in classList" :key="index" class="allClass">
<view class="classtext">{{item.name}}</view>
<view class="class_box">
<!-- 选中分类条件 -->
<view v-for="(subItem, indexs) in item.child" :key="subItem.id" class="class_tag"
:style="{'color':(subItem.current ? '#fff':'#333'),'background':(subItem.current ? colors:''),'border-color':subItem.current ? colors:''}"
@tap="setClass(item,subItem,index,indexs)">{{subItem.name}}</view>
</view>
</view>
</scroll-view>
<view class="bottom_btn">
<view class="reset bottom_btnView" :style="'color:' + colors + ';border-color:' + colors"
@tap="onreset">重置</view>
<view class="enter bottom_btnView" :style="'background:' + colors" @tap="onenter"></view>
</view>
</view>
</view>
</template>
<script>
export default {
mixins: [{
methods: {
setData: function(obj, callback) {
let that = this;
const handleData = (tepData, tepKey, afterKey) => {
tepKey = tepKey.split('.');
tepKey.forEach(item => {
if (tepData[item] === null || tepData[item] === undefined) {
let reg = /^[0-9]+$/;
tepData[item] = reg.test(afterKey) ? [] : {};
tepData = tepData[item];
} else {
tepData = tepData[item];
}
});
return tepData;
};
const isFn = function(value) {
return typeof value == 'function' || false;
};
Object.keys(obj).forEach(function(key) {
let val = obj[key];
key = key.replace(/\]/g, '').replace(/\[/g, '.');
let front, after;
let index_after = key.lastIndexOf('.');
if (index_after != -1) {
after = key.slice(index_after + 1);
front = handleData(that, key.slice(0, index_after), after);
} else {
after = key;
front = that;
}
if (front.$data && front.$data[after] === undefined) {
Object.defineProperty(front, after, {
get() {
return front.$data[after];
},
set(newValue) {
front.$data[after] = newValue;
that.$forceUpdate();
},
enumerable: true,
configurable: true
});
front[after] = val;
} else {
that.$set(front, after, val);
}
});
isFn(callback) && this.$nextTick(callback);
}
}
}],
data() {
return {
paramDict: {
minPrice: '',
maxPrice: ''
}
}
},
props: {
//
isSingleSel: {
type: Boolean
},
//
colors: {
type: String,
default: '#fa436a',
},
//
popShow: {
type: Boolean,
default: false,
},
//
classList: {
type: Array
}
},
methods: {
hideright() {
this.$emit("hideClick");
},
setClass(item, subItem, index, indexs) { //
//
if (this.isSingleSel) {
let data = this.classList;
let count = 0;
data.forEach(e => {
if (count == index) {
e.child.forEach(ele => {
ele.current = false;
});
}
count++;
});
}
subItem.current = !subItem.current;
console.log("选择1 = " + JSON.stringify(subItem));
item.child[indexs] = subItem;
let data = item;
let newdata = 'classList[' + index + ']';
this.setData({
[newdata]: data
});
//
this.$emit("change",this.classList);
},
onreset() { //
let data = this.classList;
data.forEach(e => {
e.child.forEach(ele => {
ele.current = false;
});
});
this.classList = data;
this.paramDict = {};
//
this.$emit("change",this.classList);
},
onenter() { //
let select = []
this.classList.forEach(e => {
e.child.forEach(ele => {
if (ele.current == true) {
select.push(ele)
}
});
})
this.$emit("sureClick", this.paramDict, select)
console.log('选中的项', JSON.stringify(select))
},
}
}
</script>
<style scoped>
.mask {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.6);
z-index: 800;
}
/* 右侧弹出框 */
.right_popup {
width: 85%;
height: 100%;
position: fixed;
right: 0;
top: 0;
background-color: #fff;
z-index: 920;
transition: all 0.3s;
}
.hide_popup {
transition: all 0.3s;
width: 85%;
height: 100%;
position: fixed;
right: -100%;
top: 0;
z-index: 920;
transition: all 0.3s;
background-color: #fff;
}
.pop_scroll {
/* #ifndef H5 */
height: calc(90% - 128upx);
margin-top: 128upx;
/* #endif */
/* #ifdef H5 */
height: calc(90% - 128upx);
margin-top: 128upx;
/* #endif */
}
.top_price {
height: 60upx;
line-height: 60upx;
display: flex;
padding: 0 30upx;
}
.top_priceText {
flex: 1;
text-align: left !important;
font-size: 28upx;
font-weight: bold;
color: #333;
}
.input_box {
padding: 0 30upx;
height: 60upx;
line-height: 60upx;
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 30upx;
}
.input_boxText {
color: #999;
}
.input_boxInput {
flex: 1;
max-width: 45%;
height: 100%;
background-color: #F7F7F7;
border-radius: 40upx;
box-sizing: border-box;
text-align: center;
font-size: 24upx;
}
.allClass {
margin-top: 50upx;
padding: 0 30upx;
}
.allClass .classtext {
font-size: 28upx;
font-weight: bold;
color: #333;
}
.allClass .class_box {
margin-top: 30upx;
overflow: hidden;
}
.class_box .class_tag {
margin-top: 10px;
height: 60upx;
line-height: 60upx;
padding: 0 20upx;
font-size: 26upx;
color: #333;
border: 1upx solid #999;
text-align: center;
border-radius: 10upx;
float: left;
margin-right: 20upx;
}
.bottom_btn {
height: 20%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0px 30upx;
margin-top: -20px;
}
.bottom_btnView {
max-width: 45%;
min-width: 45%;
height: 72upx;
line-height: 72upx;
background-color: #FA436A;
text-align: center;
border-radius: 36upx;
color: #fff;
}
.bottom_btn .reset {
background-color: #fff;
color: #FA436A;
border: 1upx solid #FA436A;
}
</style>

@ -0,0 +1,86 @@
{
"id": "cc-rightPopup",
"displayName": "仿美团右侧侧边栏弹框筛选框popup alert 支持单选 多选",
"version": "2.0.2",
"description": "仿美团右侧侧边栏弹框筛选框popup alert 支持单选 多选",
"keywords": [
"alert",
"",
"侧边栏",
"弹框",
"popup",
"筛选框"
],
"repository": "",
"engines": {
"HBuilderX": "^3.8.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "y",
"快手": "y",
"飞书": "y",
"京东": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
}
}
}
}
}

@ -0,0 +1,154 @@
# cc-rightPopup
#### 使用方法
```使用方法
<!-- pop-show:是否显示弹框 isSingleSel是否单选 color:主题色 classList分类数组 @changeClick:数据发生变化 @sureClick:筛选确认 @hideClick:隐藏事件 -->
<cc-rightPopup :pop-show="popShow" :colors="colors" :classList="classList" @sureClick="sureClick"
@hideClick="hideright"></cc-rightPopup>
// 数据发生变化
changeClick(arr) {
// 适配小程序 样式需要在父级改变
this.classList = arr;
},
// 隐藏处理
hideright() {
this.popShow = false;
},
// 筛选确认
sureClick(paramDict, selArr) {
this.popShow = false;
uni.showModal({
title: '筛选数据',
content: '筛选价格数据 = ' + JSON.stringify(paramDict) + ' 筛选按钮数据= ' + JSON.stringify(selArr)
})
}
```
#### HTML代码实现部分
```html
<template>
<view class="content">
<button style="margin: 60px 90px;" @click="showPopClick">点击弹框</button>
<!-- pop-show:是否显示弹框 color:主题色 classList分类数组 @changeClick:数据发生变化 @sureClick:筛选确认 @hideClick:隐藏事件 -->
<cc-rightPopup :pop-show="popShow" :colors="colors" :classList="classList" @change="changeClick"
@sureClick="sureClick" @hideClick="hideright"></cc-rightPopup>
</view>
</template>
<script>
export default {
data() {
return {
colors: '#fa436a',
popShow: false,
classList: [{
name: '营业事件',
id: 1,
child: [{
name: '0-5时',
id: 10,
current: true
}, {
name: '5-10时',
id: 11,
current: false
}, {
name: '10-14时',
id: 12,
current: false
}, {
name: '14-18时',
id: 13,
current: false
}, {
name: '18-22时',
id: 14,
current: false
}, {
name: '22-24时',
id: 15,
current: false
}]
}, {
name: '用餐人数',
id: 2,
child: [{
name: '单人餐',
id: 20,
current: false
}, {
name: '双人餐',
id: 21,
current: false
}, {
name: '3-4人餐',
id: 22,
current: false
},
{
name: '5-10人餐',
id: 23,
current: false
}
]
}],
}
},
methods: {
changeClick(arr) {
// 适配小程序 样式需要在父级改变
this.classList = arr;
},
showPopClick() {
this.popShow = true;
},
hideright() {
this.popShow = false;
},
sureClick(paramDict, selArr) {
this.popShow = false;
uni.showModal({
title: '筛选数据',
content: '筛选价格数据 = ' + JSON.stringify(paramDict) + ' 筛选按钮数据= ' + JSON.stringify(selArr)
})
}
}
}
</script>
<style>
page {
background-color: #f2f2f2;
margin-bottom: 50px;
}
</style>
```

@ -0,0 +1,71 @@
## 1.3.32023-02-27
修复openType类型为navigateTo时页面返回重新点击不响应问题
## 1.3.22023-02-27
修改占位空间点击事件遮挡问题
## 1.3.12023-02-27
1、新增页面打开方式,可以自定义每个tabbarItem的点击打开方式
2、新增tabbarHeight参数,方便你直接使用参数控制tabbar高度
## 1.3.02023-02-21
新增ref方法若干优化使用说明文档
## 1.2.92023-02-21
修改safe计算使用原生css抛弃原始计算耗时能力
## 1.2.82023-02-20
修正文档使用说明
## 1.2.72023-02-20
1、新增角标显示
2、新增setTabBarBadgereLoad方法
## 1.2.62023-02-13
修复判断safebottom底部安全距离数值写死问题
## 1.2.52023-01-12
1、修复默认初始高度闪烁问题不在使用计算直接默认
2、修复上个版本遗留current选中问题
3、修复safeBottom底部判断错误问题
4、更新使用说明文档
## 1.2.42023-01-09
新增文档说明,增加示例项目页面
## 1.2.32022-12-14
修复外部不跳转页面修改current组件不响应bug
## 1.2.22022-12-14
新增click事件click事件会优先于任何事件
## 1.2.12022-12-09
修改文档使用说明,路由拦截有平台兼容性,暂时无法解决,平台原生不支持
## 1.2.02022-12-09
新增插槽,可以自定义凸起导航,优化文档使用说明
## 1.1.92022-12-06
修复iconPath图片路径为网络路径判断错误问题
## 1.1.72022-12-01
优化部分机器计算错误问题
## 1.1.62022-12-01
修改高度计算错误问题
## 1.1.52022-12-01
修复各别设备上高度计算有误问题
## 1.1.42022-11-25
修复vue2版本在微信小程序上不支持:style传入参数bug优化参数错误逻辑
## 1.1.32022-11-24
修复native模式borderStyle样式丢失问题
## 1.1.22022-11-24
修改描述文档说明,修改插件描述信息
## 1.1.12022-11-24
优化使用说明文档
## 1.1.02022-11-24
极度简化native模式页面只需要引入组件即可任何操作都不需要
## 1.0.92022-11-24
修复native模式下fill忘记计算高度.
## 1.0.82022-11-24
优化native模式简化参数数量使用更简单
## 1.0.72022-11-24
新增native配置可以兼容原生tabbar新增beforeChange可自行根据要求自己兼容路由守卫
## 1.0.62022-11-23
修改文档描述错误
## 1.0.52022-11-23
修复fill高度遗漏安全距离问题文档使用说明优化更新
## 1.0.42022-11-23
优化配置选项提取当前选中项新增fixed配置
## 1.0.32022-11-14
添加上阴影效果修复由于去除了上线条造成如果内容如果是白色tabbar会和内容高度重合的问题
## 1.0.22022-11-14
修改说明文档,更加详细备注说明
## 1.0.12022-11-14
新增当前选中项class名方便用户直接样式覆盖
## 1.0.02022-11-14
第一个自定义tabbar版本

@ -0,0 +1,446 @@
<template>
<view class="m-tabbar-box" :style="tabbarBoxStyle" v-if="isShowTabBar">
<view class="m-tabbar__fill" v-if="fill || native" :class="{'m-tabbar__safe': (safeBottom || native)}"
:style="tabbarFillStyle"></view>
<view id="m-tabbar" class="m-tabbar"
:class="{'fixed': (fixed || native), 'm-tabbar__safe': (safeBottom || native)}" :style="tabbarStyle">
<view class="m-tabbar__border" v-if="borderStyle === 'black' "></view>
<view class="m-tabbar__flex">
<view @click="tabChange(index)" v-for="(item, index) in tabbarList" :key="index" class="m-tabbar__item"
:class="{
'm-tabbar__item__active': index === currentIndex,
}">
<slot :name="`tabbar_index_${index}`">
<view class="m-tabbar__icon">
<view class="m-tabbar__badge" v-if="item.dot">{{item.dot}}</view>
<image :src="currentIndex === index ? item.selectedIconPath : item.iconPath"
class="m-tabbar__icon_img" />
</view>
<view class="m-tabbar__label"
:style="{'color': index === currentIndex ? tabbarConfig.selectedColor : tabbarConfig.color }">
{{ item.text }}
</view>
</slot>
</view>
</view>
</view>
</view>
</template>
<script>
const obj2strStyle = (obj) => {
let style = ''
for (let key in obj) {
style += `${key}:${obj[key]};`
}
return style
}
const padFirstSymbol = (str, smb) => {
if (str.startsWith(smb) || str.startsWith('http')) {
return str
}
return `/${str}`
}
const replaceTabbarList = (list) => {
if (!list.length > 0) {
return []
}
return list.map(item => {
if (item.iconPath) {
item.iconPath = padFirstSymbol(item.iconPath, '/')
}
if (item.pagePath) {
item.pagePath = padFirstSymbol(item.pagePath, '/')
}
if (item.selectedIconPath) {
item.selectedIconPath = padFirstSymbol(item.selectedIconPath, '/')
}
return item
})
}
import PageConfig from '@/pages.json'
export default {
emits: ['change', 'click'],
props: {
current: {
type: [Number, String],
default: 0
},
tabbar: {
type: Object,
default () {
return {}
}
},
fixed: {
type: Boolean,
default: false
},
fill: {
type: Boolean,
default: false
},
zIndex: {
type: [Number, String],
default: 9999
},
native: {
type: Boolean,
default: false
},
safeBottom: {
type: Boolean,
default: true
},
beforeChange: {
type: Function,
default: null
},
tabbarHeight: {
type: [Number, String],
default: 100
}
},
data() {
return {
isShowTabBar: false,
currentIndex: 0,
beforeData: {},
reload: false
}
},
watch: {
current(val) {
this.currentIndex = val * 1
}
},
computed: {
tabbarConfig() {
const {
native,
reload
} = this
if (reload) {}
if (native) {
const {
tabBar
} = PageConfig
if (!tabBar) {
console.error('Native mode, Pages.json no tabbar config')
return {
borderStyle: 'black',
list: []
}
}
return tabBar
}
return this.tabbar
},
tabbarList() {
const {
reload
} = this
const {
list
} = this.tabbarConfig
if (reload) {}
if (list) {
return replaceTabbarList(list)
}
console.error('No tabbar config')
return []
},
borderStyle() {
const {
reload
} = this
const {
borderStyle
} = this.tabbarConfig
if (reload) {}
return borderStyle
},
tabbarBoxStyle() {
const {
zIndex,
reload
} = this
if (reload) {}
return obj2strStyle({
'z-index': zIndex,
})
},
tabbarFillStyle() {
const {
tabbarHeight,
safeBottom,
reload
} = this
if (reload) {}
return obj2strStyle({
'height': `${tabbarHeight}rpx`
})
},
tabbarStyle() {
const {
tabbarHeight,
reload
} = this
const {
backgroundColor
} = this.tabbarConfig
if (reload) {}
return obj2strStyle({
'height': `${tabbarHeight}rpx`,
'background-color': backgroundColor || '#fff',
})
},
tabbarItemStyle() {
const {
currentIndex,
reload
} = this
const {
color,
selectedColor
} = this.tabbarConfig
if (reload) {}
return obj2strStyle({
'color': currentIndex ? selectedColor : color
})
}
},
mounted() {
this.initTabbar()
},
methods: {
initTabbar() {
const {
current,
fill,
native,
tabbarList
} = this
this.currentIndex = current * 1
if (native) {
const currentPage = `/${getCurrentPages()[0].route}`
const currentIndex = tabbarList.findIndex(item => item.pagePath === currentPage)
this.currentIndex = currentIndex
if (tabbarList.length > 0) {
uni.hideTabBar()
}
}
setTimeout(() => {
this.isShowTabBar = true
})
},
reLoad() {
this.reload = true
setTimeout(() => {
this.reload = false
})
},
checkMaxIndex(index) {
if (!this.tabbarConfig.list[index]) {
console.error('Max tabbar index')
return false
}
return true
},
setTabBarBadge(obj) {
const {
index,
text
} = obj
if (this.checkMaxIndex(index)) {
this.tabbarConfig.list[index].dot = text
this.reLoad()
}
},
setTabBarItem(obj) {
const {
index,
text,
pagePath: newPagePath,
iconPath,
selectedIconPath
} = obj
const {
pagePath: oldPagePath
} = this.tabbarConfig.list[index]
if (this.checkMaxIndex(index)) {
this.tabbarConfig.list[index] = {
pagePath: newPagePath ? newPagePath : oldPagePath,
text,
iconPath,
selectedIconPath
}
this.reLoad()
}
},
showTabBar() {
this.isShowTabBar = true
},
hideTabBar() {
this.isShowTabBar = false
},
tabChange(index) {
const {
currentIndex
} = this
this.$emit('click', index)
if (index === currentIndex) {
return
}
this.beforeData = {
newIndex: index,
oldIndex: currentIndex,
next: this.jumpPage
}
if (this.beforeChange) {
this.beforeChange(this.jumpPage)
} else {
this.jumpPage()
}
},
jumpPage() {
const {
native,
beforeData,
tabbarList: list
} = this
const {
newIndex: index
} = beforeData
const {
pagePath: url,
openType
} = list[index]
if (url) {
if (native) {
uni.switchTab({
url
})
} else {
if (openType !== 'navigate') {
this.currentIndex = index
}
switch (openType) {
case 'navigate':
uni.navigateTo({
url
})
break;
case 'redirect':
uni.redirectTo({
url
})
break;
case 'reLaunch':
uni.reLaunch({
url
})
break;
case 'switchTab':
uni.switchTab({
url
})
break;
case 'navigateBack':
uni.navigateBack({
delta: 1
})
break;
default:
uni.reLaunch({
url
})
}
}
}
this.$emit('change', index)
}
}
};
</script>
<style lang="scss" scoped>
.m-tabbar-box {
position: relative;
z-index: 9999;
}
.m-tabbar {
position: relative;
&.fixed {
position: fixed;
bottom: 0;
left: 0;
width: 100vw;
}
&__safe {
padding-bottom: env(safe-area-inset-bottom);
}
}
.m-tabbar__fill {
pointer-events: none;
opacity: 0;
}
.m-tabbar__flex {
display: flex;
flex-direction: row;
}
.m-tabbar__border {
background-color: rgba(0, 0, 0, 0.33);
width: 100%;
height: 1rpx;
transform: scaleY(0.5);
}
.m-tabbar__item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
padding: 4px 0 2px;
}
.m-tabbar__icon {
width: 48rpx;
height: 48rpx;
margin-bottom: 6rpx;
position: relative;
&_img {
display: block;
width: 100%;
height: 100%;
}
.m-tabbar__badge {
color: #fff;
background-color: #f00;
border-radius: 20rpx;
font-size: 22rpx;
position: absolute;
right: -25rpx;
left: 40rpx;
padding: 2rpx 0;
width: 100%;
text-align: center;
white-space: nowrap;
}
}
.m-tabbar__label {
font-size: 24rpx;
}
</style>

@ -0,0 +1,81 @@
{
"id": "m-tabbar",
"displayName": "自定义tabbar、tabbar路由守卫、零配置tabbar、凸起导航",
"version": "1.3.3",
"description": "自定义tabbar超高还原原生配置模式一行代码自定义导航自带tabbar路由守卫功能",
"keywords": [
"自定义tabbar,tabbar,自定义标签栏,tabbar路由守卫,零配置tabbar,路由守卫、凸起导航"
],
"repository": "",
"engines": {
"HBuilderX": "^3.3.10"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "y",
"快手": "y",
"飞书": "y",
"京东": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
}
}
}
}
}

@ -0,0 +1,302 @@
# m-tabbar自定义
## 使用说明,注意事项(必看)
> 我配套上传了一个案例包,如果不会使用的,建议下载阅读使用
---
> 1、自定义tabbar的情况下不建议在一个页面内通过几个组件用v-if切换去模拟各个页面会存在各种不可控bug
> 如果是`if切换组件`的话,就是一个页面控制多个组件显示隐藏来实现。 如果组件封装的有问题,会出现组件之间的协调问题,请看情况使用。 还有一些原生的交互没有办法达到预期,会影响到原生体验。 比如下拉刷新滚动加载更多切换tabbar后滚动位置不能固定等
>2、在pages.json中正常定义tabbar配置和字段使用`native`模式组件会自动加载pages.json配置项并自动判断当前选中项并自动隐藏原生的tabbar 但是有个闪烁问题,暂时无解,如果你有好的方案,欢迎指正
> 3、如果出现tabbar不显示但是控制台无任何报错信息应该就是样式布局影响了请自行排查
> 4、因为是自定义导航所以原生方法是不支持的只能通过 ref 可以获取到 tabbar 实例并调用插件的实例方法,详细请看页面最下方
---
# 快速使用
## 方式一、Native模式使用
```
// native模式无需配置其他项
<m-tabbar native></m-tabbar>
```
在各个tabbar页面引入tabbar组件传入属性`native``native`模式下无需任何配置
组件会默认自动通过`uni.hideTabBar()`隐藏系统tabbar
## 方式二、页面使用(current默认从0开始)(强烈推荐)
```
// 普通页面模式
<m-tabbar fixed fill current="1" :tabbar="tabbar"></m-tabbar>
```
配置选项和`uniapp`的配置完全相同,直接复制过来, 默认传入`pagePath`后,直接使用`reLaunch`跳转
插件支持扩展`openType`参数,用户可根据自己情况自行扩展页面打开方式,详细看下方配置
### 1、提取tabbar配置
新建文件config/tabbar.js(默认你有config目录,根据自己情况而定)
```
export default {
color: "#161616",
selectedColor: "#161616",
borderStyle: "black",
backgroundColor: "#ffffff",
list: [{
pagePath: "/pages/index/index",
iconPath: "/static/tabbar/index.png",
selectedIconPath: "/static/tabbar/index_active.png",
text: "首页",
openType: 'navigate', //新版本新增页面被打开方式默认为reLaunch
dot: 1 //新版本新增参数,详细看页面最下方使用说明
}, {
pagePath: "/pages/shop/index",
iconPath: "/static/tabbar/shop.png",
selectedIconPath: "/static/tabbar/shop_active.png",
text: "门店"
}, {
pagePath: "/pages/my/index",
iconPath: "/static/tabbar/my.png",
selectedIconPath: "/static/tabbar/my_active.png",
text: "我的"
}]
}
```
### 2、引入tabbar
#### VUE2引入
```
import TabbarConfig from '@/config/tabbar.js'
export default {
data(){
return {
tabbar: TabbarConfig
}
},
onLoad(){
// 没有开启native模式下使用reLaunch跳转会存在首页标志需要隐藏
#ifdef MP-JD || MP-WEIXIN
uni.hideHomeButton()
#endif
}
}
```
#### VUE3 setup引入
```
import TabbarConfig from '@/config/tabbar.js'
import { reactive } from 'vue'
// 没有开启native模式下使用reLaunch跳转会存在首页标志需要隐藏
#ifdef MP-JD || MP-WEIXIN
uni.hideHomeButton()
#endif
const tabbar = reactive(TabbarConfig)
```
### 3、页面使用
```
<m-tabbar fixed fill current="1" :tabbar="tabbar"></m-tabbar>
```
## 高级用法beforeChange路由守卫
有些特殊需求我们在点击一个tabbar其他一项的时候可能需要判断权限是否可以进入那么我们在切换前做一下路由拦截`beforeChange`,如果达到自己的预期,就进行跳转
> uniapp 微信小程序不支持$listeners,只能使用prop方式传入, 部分平台不支持prop传入方法有平台限制详细请看(问题解答)[https://ask.dcloud.net.cn/question/70659]
### 页面使用传入beforeChange
```
// native模式无需传入 fixed fill
<m-tabbar native :beforeChange="onBeforeChange"></m-tabbar>
// 普通页面模式
<m-tabbar fixed fill current="1" :tabbar="tabbar" :beforeChange="onBeforeChange"></m-tabbar>
```
### 进行事件判断监听
函数必选参数 next当判断逻辑执行完毕后满足条件的情况下执行 `next()`
```
methods: {
onBeforeChange(next){
console.log('before page2 switch')
setTimeout(()=>{
console.log('switch page2 end')
next()
}, 1000)
}
}
```
## 自定义凸起导航(插槽使用)
```
<m-tabbar native>
<template v-slot:tabbar_index_1> //插槽详细看文档,样式你自己写
<view class="custom_style">
<view class="custom_style_icon">+</view>
</view>
</template>
</m-tabbar>
<style lang="scss">
.custom_style{
color: #fff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 24rpx;
&_icon{
background-color: #15357A;
font-size: 80rpx;
width: 120rpx;
height: 120rpx;
border-radius: 100%;
display: flex;
justify-content: center;
align-items: center;
margin-top: -40rpx;
}
}
</style>
```
### 属性说明(Native模式)
| 属性名 | 类型 | 默认值 | 必填 | 说明 |
| ------- | ------- | ------- | ------- | ------- |
| zIndex | Number,String | 999 | 否 | 当前处于z-index层级 |
| native | Boolean | false | 否 | native模式当前页面是系统原生tabbar页面(pages.json里面配置了tabBar) |
| beforeChange | Function | null | 否 | 导航切换前事件hooks,用于判断点击tabbar的时候可以先执行自己定义的事件达到预期后在跳转类似router的路由守卫,方法需要调用next参数回调部分平台不支持存在兼容性 |
| tabbarHeight | Number,String | 100 | 否 | 默认tabbar高度有些时候你可能想控制tabbar高度没啥用,如果非要更改,布局有影响,请使用样式覆盖) |
### 属性说明(普通模式)
| 属性名 | 类型 | 默认值 | 必填 | 说明 |
| ------- | ------- | ------- | ------- | ------- |
| current | Number,String | 0 | 是 | 默认选中第几项,0开始 |
| tabbar | Object | {} | 是 | tabbar配置项(新增dot参数,详细看下方使用说明) |
| fixed | Boolean | false | 否 | 是否定位在底部 |
| fill | Boolean | false | 否 | 是否填充底部高度如果开启fixed后会出现tabbar遮盖内容问题开启此属性会自动填充高度可单独使用 |
| safeBottom | Boolean | true | 否 | 是否自动规避iphoneX\XR等底部安全距离 |
| zIndex | Number,String | 999 | 否 | 当前处于z-index层级 |
| beforeChange | Function | null | 否 | 导航切换前事件hooks,用于判断点击tabbar的时候可以先执行自己定义的事件达到预期后在跳转类似router的路由守卫,方法需要调用next参数回调部分平台不支持存在兼容性 |
| tabbarHeight | Number,String | 100 | 否 | 默认tabbar高度有些时候你可能想控制tabbar高度没啥用,如果非要更改,布局有影响,请使用样式覆盖) |
### openType对应值(默认reLaunch跳转)
| 方法名 | 返回值说明 |
| ------- | ------- |
| navigate | [对应navigateTo](https://uniapp.dcloud.io/api/router?id=navigateto) |
| redirect | [对应redirectTo](https://uniapp.dcloud.io/api/router?id=redirectto) |
| reLaunch | [对应reLaunch](https://uniapp.dcloud.io/api/router?id=relaunch) |
| switchTab | [对应switchTab](https://uniapp.dcloud.io/api/router?id=switchtab) 注意此方法需要你有原生tabbar比如内页使用了自定义导航想回tabbar的指定页面 |
| navigateBack | [对应navigateBack](https://uniapp.dcloud.io/api/router?id=navigateback) 只能后退一步 |
### 方法说明
| 方法名 | 返回值说明 |
| ------- | ------- |
| click | 当前选中index,无论什么情况下都会先触发click事件方便自由定制更多方法 |
| change | 当前选中index(beforeChange会在change之前执行只有执行next才会返回) |
### 插槽 (注意Vue3存在跨断不兼容问题)
| 插槽名 | 返回值说明 |
| ------- | ------- |
| tabbar_index_{index} | 插槽名字为tabbar_index_你要变化的index, 可以做到任意控制自己的导航比如中心凸起比如你想让第一个变化index就是0比如你tabbarList里面有5个item,你想让中间的凸起那么index就是2取下标 |
### 方法 (通过 ref 可以获取到 tabbar 实例并调用实例方法)
> 注意,由于是使用了自定义,所以原生的方法是不能使用的,只能通过 ref 可以获取到 tabbar 实例并调用实例方法目前只有下面4个方法而且方法是没有`success,fail,complete`回掉的,考虑到跨平台型,如果原生方法有平台差异,插件也是不考虑支持的
| 事件名 | 参数 | 参数说明|
| ------- | ------- | ------- |
| setTabBarBadge | object | [为 tabBar 某一项的右上角添加文本](https://uniapp.dcloud.io/api/ui/tabbar?id=settabbarbadge) |
| setTabBarItem | object | [动态设置 tabBar 某一项的内容](https://uniapp.dcloud.io/api/ui/tabbar?id=settabbaritem) |
| reLoad | 无 | 有特殊情况下你可能需要调用重新载入tabbar基本没啥用 |
| showTabBar | 无 | 显示tabbar默认显示无动画效果 |
| hideTabBar | 无 | 隐藏tabbar无动画效果 |
```
//页面调用组件添加ref
<m-tabbar ref="tabbar" native></m-tabbar>
// setTabBarBadge ,为 tabBar 某一项的右上角添加文本
this.$refs.tabbar.setTabBarBadge({
index: 0,
text: '10'
})
//setTabBarItem 动态设置 tabBar 某一项的内容
this.$refs.tabbar.setTabBarBadge({
index: 0,
text: 'text',
pagePath: 'newPagePath',
//插件新增pagePath注意native模式下如果更改了pagePath,可能存在选中项自动选中失败问题
iconPath: '/path/to/iconPath',
selectedIconPath: '/path/to/selectedIconPath'
})
//如果直接在onload或者onshow等组件还在加载中的特殊情况下由于加载比较慢
// 方法可能会失效建议放在nextTick函数里面
this.$nextTick(()=>{
this.$refs.tabbar.setTabBarBadge({
index: 0,
text: '10'
})
})
```
tabbarConfig参数新增dot配置项可以单独配置每一项的右上角角标可传入任意类型不显示为空即可或者不填写 默认为红色,如果想更改样式,请使用样式覆盖`m-tabbar__badge`
```
list: [{
pagePath: "/pages/index/index",
iconPath: "/static/tabbar/index.png",
selectedIconPath: "/static/tabbar/index_active.png",
text: "首页",
dot: 1
}, {
pagePath: "/pages/shop/index",
iconPath: "/static/tabbar/shop.png",
selectedIconPath: "/static/tabbar/shop_active.png",
text: "门店",
dot: ''
}, {
pagePath: "/pages/my/index",
iconPath: "/static/tabbar/my.png",
selectedIconPath: "/static/tabbar/my_active.png",
text: "我的",
dot: ''
}]
```
### 目前已知问题
- 1、在全局加样式 filter: grayscale(1) 后tabbar组件的fixed样式失效排版在页面最底部无法修复
- 2、微信小程序native模式首次进入的时候底部tabbar会闪动一下无法修复 建议全局页面做一个延迟加载效果,等全部加载完成后,在显示
### 插件写的时候,没办法照顾到所有平台,欢迎点评指正,如有问题欢迎给我留言
#### 例如:
```
设备iphone13
系统: ios13
使用环境平台: 微信小程序、app
使用vue版本 vue3
问题描述: 提示什么什么错误
```
Loading…
Cancel
Save