feat: Requirements changed

This commit is contained in:
Mrxtyyp 2024-07-30 10:15:19 +08:00
parent a2a4370327
commit eea5ef4b28
17 changed files with 2623 additions and 925 deletions

View File

@ -619,6 +619,10 @@ radio.white.checked .uni-radio-input {
/* ==================
按钮
==================== */
.cu-tab-icon {
font-size: 28upx;
}
.cu-btn {
position: relative;
@ -1701,6 +1705,14 @@ button.cuIcon.lg {
background-color: inherit;
overflow: initial;
}
/* .cu-bar.tabbar {
flex-direction: column;
} */
.cu-bar.tabbar .title {
color: #C8C9CC;
font-size: 24upx;
}
.cu-bar.tabbar.shop .action {
width: 140upx;
@ -1710,20 +1722,22 @@ button.cuIcon.lg {
.cu-bar.tabbar .action.add-action {
position: relative;
z-index: 2;
padding-top: 50upx;
color: #C8C9CC;
margin-top: -22upx;
font-size: 24upx;
}
.cu-bar.tabbar .action.add-action .bg-main-color {
margin-bottom: 6upx;
}
.cu-bar.tabbar .action.add-action [class*="cuIcon-"] {
position: absolute;
position: relative;
width: 70upx;
z-index: 2;
height: 70upx;
border-radius: 50%;
line-height: 70upx;
font-size: 50upx;
top: -35upx;
left: 0;
right: 0;
margin: auto;
padding: 0;
}
@ -1733,17 +1747,18 @@ button.cuIcon.lg {
position: absolute;
width: 100upx;
height: 100upx;
top: -50upx;
left: 0;
top: -15upx;
left: -15rpx;
right: 0;
margin: auto;
box-shadow: 0 -3upx 8upx rgba(0, 0, 0, 0.08);
border-radius: 50upx;
background-color: inherit;
z-index: 0;
border-bottom: transparent;
}
.cu-bar.tabbar .action.add-action::before {
/* .cu-bar.tabbar .action.add-action::before {
content: "";
position: absolute;
width: 100upx;
@ -1754,7 +1769,7 @@ button.cuIcon.lg {
margin: auto;
background-color: inherit;
z-index: 1;
}
} */
.cu-bar.tabbar .btn-group {
flex: 1;

287
components/CTabbar.vue Normal file
View File

@ -0,0 +1,287 @@
<template>
<view>
<view class="page-content">
<!-- 所有内容的容器 -->
<slot></slot>
</view>
<!-- <module-bar ref="moduleBar" :moduleBarInfos="moduleBarInfos" @getCurPageInfo="getCurPageInfo"></module-bar> -->
<m-tabbar fill fixed safeBottom
:tabbarHeight="140"
:current="current"
:tabbar="tabbar2"
:beforeChange="tabbarBeforeChange">
<template v-slot:tabbar_index_2>
<view class="custom_style" @click="switchToPublish">
<view class="action add-action">
<text class="cuIcon-add bg-main-color"></text>
</view>
<text class="title">发布</text>
</view>
</template>
</m-tabbar>
<!-- 发布 -->
<view class="cu-modal content-mask" :class="isShowPublish?'show':''">
<view class="cu-dialog bottom-dialog margin-bottom-with-bar">
<view class="padding-xl">
<view class="flex justify-start margin-tb-xl">
<view class="cu-avatar round middle-avatar first-avatar">
<view class="cuIcon-formfill"></view>
</view>
<view class="flex flex-column-around text-left margin-left-sm text-white"
@click="goRouter('/pages/publish/publish-task')">
<view class="text-xl">发布任务</view>
<view>公司家居家政雇佣上门服务</view>
</view>
</view>
</view>
</view>
<view class="cu-bar tabbar margin-bottom-xl fixed-bottom-bar">
<view class="action add-action" @click.stop="isShowPublish = false">
<button class="cu-btn bg-gray cuIcon-close"></button>
<text class="mask-close-text">关闭</text>
</view>
</view>
</view>
<!-- 登录校验弹窗 -->
<vertify-login ref="vertifyLogin" @reload="reloadForwardPage"></vertify-login>
<vertify-phone ref="vertifyPhone" @reload="reloadForwardPage"></vertify-phone>
<valid ref="validRef"></valid>
</view>
</template>
<script>
import valid from '@/pages/index/components/valid.vue';
let that
export default {
name: "CTabbar",
components: {
valid
},
props: {
current: {
type: Number,
default: 0
}
},
data() {
return {
tabbar2: {
color: "#aaaaaa",
selectedColor: "#0081ff",
backgroundColor: "#FFFFFF",
list: [{
pagePath: "pages/index/home",
text: "首页",
cuIcon: 'homefill',
openType: 'switchTab'
},
{
pagePath: "pages/index/worker-home",
text: "师傅圈",
cuIcon: 'circle',
openType: 'switchTab'
},
{
cuIcon: 'plus'
},
{
pagePath: "pages/index/msg-home",
text: "消息",
cuIcon: 'message',
openType: 'switchTab'
},
{
pagePath: "pages/index/my-home",
text: "我的",
cuIcon: 'my',
openType: 'switchTab'
}
]
},
isShowPublish: false,
initParam: null
};
},
created() {
uni.hideTabBar();
that = this;
uni.$on('initValid', this.initValid)
},
mounted() {
// this.$refs.vertifyLogin.showModal();
},
methods: {
initValid(initParam) {
this.initParam = initParam
this.$refs.validRef.loadData(initParam)
},
goRouter(path) {
uni.navigateTo({
url: path
})
},
async switchToPublish() {
const res = await this.authLogin()
if(res) {
this.isShowPublish = true
}
},
tabbarBeforeChange: async (next, index) => {
console.log('tabbarBeforeChange==', index);
if (index === 4) {
const res = await that.authLogin()
console.log(res);
if(res){
next()
}
} else {
next()
}
},
async authLogin() {
// userInfo
let res = await this.$request.storageExistUser();
// userInfo
let curUserInfo = this.$request.getCurUserInfo();
//
if (!curUserInfo || !curUserInfo.openId) {
this.$refs.vertifyLogin.showModal();
return false;
} else {
this.$refs.vertifyLogin.hideModal();
}
//
if (!curUserInfo.phone) {
this.$refs.vertifyPhone.showModal();
return false;
} else {
this.$refs.vertifyPhone.hideModal();
}
return true;
},
reloadForwardPage() {
if(this.$refs.validRef.inByShare){
this.$refs.validRef.loadData(this.initParam)
} else {
this.authLogin()
}
},
},
destroyed() {
}
}
</script>
<style lang="scss" scoped>
.page-content {
width: 100vw;
// height: calc(100vh - env(safe-area-inset-bottom) - 100rpx);
background-color: #F7F8FA;
overflow-x: hidden;
}
.custom_style {
position: relative;
z-index: 2;
color: #aaaaaa;
margin-top: -22upx;
font-size: 24upx;
text-align: center;
}
.custom_style .action.add-action {
position: relative;
z-index: 2;
color: #aaaaaa;
margin-top: -22upx;
font-size: 24upx;
width: 70upx;
margin-bottom: 17rpx;
}
.custom_style .action.add-action [class*="cuIcon-"] {
position: relative;
display: inline-block;
width: 70upx;
height: 70upx;
border-radius: 50%;
line-height: 70upx;
font-size: 50upx;
text-align: center;
}
.custom_style .action.add-action .bg-main-color {
margin-bottom: 6upx;
}
.custom_style .action.add-action::after {
content: "";
position: absolute;
width: 100upx;
height: 100upx;
top: -14upx;
left: -14rpx;
right: 0;
margin: auto;
box-shadow: 0 -3upx 8upx rgba(0, 0, 0, 0.08);
border-radius: 50upx;
background-color: inherit;
z-index: -1;
border-bottom: transparent;
background-color: #FFFFFF;
}
.custom_style .title {
color: #aaaaaa;
font-size: 24upx;
}
.content-mask {
z-index: 9999;
background: rgba(0, 0, 0, 0.9);
}
.mask-close-text {
visibility: hidden;
}
.middle-avatar {
width: 120rpx;
height: 120rpx;
}
.middle-avatar [class*="cuIcon-"] {
font-size: 53rpx !important;
}
.cu-avatar.first-avatar {
background-color: #0081ff;
color: #ffffff;
}
.cu-avatar.second-avatar {
background-color: #fbbd08;
color: #ffffff;
}
.cu-avatar.third-avatar {
background-color: #f37b1d;
color: #ffffff;
}
.cu-avatar.fourth-avatar {
background-color: #1cbbb4;
color: #ffffff;
}
.cu-dialog.bottom-dialog {
background-color: unset;
vertical-align: bottom;
}
</style>

View File

@ -3,7 +3,7 @@
<view class="flex justify-start">
<view v-if="vCard.picUrl" class="cu-avatar" :class="avatarPubClass" :style="'background-image:url(' + vCard.picUrl + '); width: ' + avatarWidth + '; height: ' + avatarHeight + ';'">
</view>
<view v-else-if="vCard.workerLogoUrl" class="cu-avatar" :class="avatarPubClass" :style="'background-image:url(' + vCard.workerLogoUrl + '); width: ' + avatarWidth + '; height: ' + avatarHeight + ';'">
<view v-else-if="vCard.workerLogoUrl" class="cu-avatar" :class="avatarPubClass" :style="'background-image:url(' + defaultHead(vCard.workerLogoUrl) + '); width: ' + avatarWidth + '; height: ' + avatarHeight + ';'">
</view>
<view v-else-if="vCard.customerLogoUrl" class="cu-avatar" :class="avatarPubClass" :style="'background-image:url(' + vCard.customerLogoUrl + '); width: ' + avatarWidth + '; height: ' + avatarHeight + ';'">
</view>
@ -69,6 +69,14 @@
return {
}
},
methods: {
defaultHead(url) {
if(url === 'https://thirdwx.qlogo.cn/mmopen/vi_32/POgEwh4mIHO4nibH0KlMECNjjGxQUq24ZEaGT4poC6icRiccVGKSyXwibcPq4BWmiaIGuG1icwxaQX6grC9VemZoJ8rg/132') {
return 'http://gqz.opsoul.com/2741722238771_.pic.jpg'
}
return url
}
}
}
</script>

View File

@ -4,7 +4,7 @@
<view class="cu-modal" :class="isShow?'show':''">
<view class="cu-dialog">
<view class="padding-xl">
<view>授予小程序绑定微信手机号码的权限</view>
<view>授予小程序绑定手机号码的权限</view>
</view>
<view class="cu-bar bg-white">
<navigator class="modal-bottom-oper margin-0 flex-sub text-black solid-right" open-type="exit" target="miniProgram">拒绝授权

View File

@ -1,6 +1,6 @@
{
"name" : "dingdong-mall",
"appid" : "",
"appid" : "__UNI__5E048DE",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
@ -48,7 +48,6 @@
"quickapp" : {},
/* */
"mp-weixin" : {
/* */
"appid" : "wxc39c2af3ea24cd37",
"setting" : {
"urlCheck" : false,

View File

@ -1,6 +1,15 @@
{
"pages": [{
"path": "pages/index/index"
"path": "pages/index/home"
}, {
"path": "pages/index/worker-home"
}, {
"path": "pages/index/msg-home"
}, {
"path": "pages/index/my-home"
}],
"subPackages": [{
"root": "pages/order/",
@ -17,15 +26,15 @@
"path": "product-pick"
}, {
"path": "shop-detail"
}, {
"path": "product-category"
}, {
"path": "filtered-products"
}, {
"path": "product-category"
}, {
"path": "filtered-products"
}]
}, {
"root": "pages/my/",
"pages": [{
"path": "edit-user-basic-info"
"pages": [{
"path": "edit-user-basic-info"
}, {
"path": "my-order"
}, {
@ -46,26 +55,49 @@
"path": "my-team-member"
}, {
"path": "serv-detail"
}, {
"path": "statement-desc"
}, {
"path": "withdraw"
}, {
"path": "b-account-bind"
}, {
"path": "my-money-bag"
}, {
"path": "member-approval"
}, {
"path": "statement-desc"
}, {
"path": "withdraw"
}, {
"path": "b-account-bind"
}, {
"path": "my-money-bag"
}, {
"path": "member-approval"
}]
}, {
"root": "pages/publish/",
"pages": [{
"path": "publish-task"
}]
}, {
"root":"pages/publish/",
"pages": [{
"path": "publish-task"
}]
}],
// "pages": [{
// "path": "pages/my/serv-detail"
// }],
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#3cc51f",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/index/home",
"visible": "false"
},
{
"pagePath": "pages/index/worker-home",
"visible": "false"
},
{
"pagePath": "pages/index/msg-home",
"visible": "false"
},
{
"pagePath": "pages/index/my-home",
"visible": "false"
}
]
},
"globalStyle": {
"navigationStyle": "custom",
"navigationBarTextStyle": "black"
@ -76,4 +108,4 @@
// "background": "#efeff4"
// }
}
}
}

View File

@ -0,0 +1,169 @@
<template>
<view>
<!-- 订阅授权 -->
<vertify-subscribe ref="vertifySubscribe"></vertify-subscribe>
<vertify-settingSubscribe ref="vertifySettingSubscribe"></vertify-settingSubscribe>
</view>
</template>
<script>
export default {
data() {
return {
curPageCode: 'indexPage',
isShowPublish: false,
forwardingPageCode: null,
curUserInfo: null,
inByShare: false,
inParam: null
}
},
// async onLoad(option) {
// this.inParam = option;
// this.loadData(option);
// },
methods: {
async loadData(option) {
this.inParam = option;
this.curUserInfo = this.$request.getCurUserInfo();
//
if (option && option.distributor) {
let loginRes = await this.authLogin();
if (!loginRes) {
this.inByShare = true;
return;
}
this.inByShare = false;
let res = await this.$request.updateUser({
customerPlace: option.distributor,
customerId: this.curUserInfo.customerId
});
if (res && res.code === 0) {
this.inParam = null;
}
}
let _this = this;
//
wx.getSetting({
withSubscriptions: true,
success(res) {
console.log(res.subscriptionsSetting)
if (res.subscriptionsSetting.mainSwitch && (!res.subscriptionsSetting.itemSettings
|| !res.subscriptionsSetting.itemSettings['JtsGFPDjYhL2GbHfKxvTJaR_lLp8xLyw8VeR01Y0JHM'])) {
//
_this.$refs.vertifySubscribe.showModal();
} else if (!res.subscriptionsSetting.mainSwitch
|| res.subscriptionsSetting.itemSettings['JtsGFPDjYhL2GbHfKxvTJaR_lLp8xLyw8VeR01Y0JHM'] !== 'accept') {
_this.$refs.vertifySettingSubscribe.showModal();
}
}
})
},
reloadForwardPage() {
if (this.inByShare) {
this.loadData(this.inParam);
} else {
this.getCurPageInfo({
curPageCode: this.forwardingPageCode
})
}
},
async getCurPageInfo(data) {
let pageCode = data.curPageCode;
this.forwardingPageCode = pageCode;
//
if (['publishPage', 'myPage'].indexOf(pageCode) >= 0) {
let loginRes = await this.authLogin();
if (!loginRes) {
return;
}
}
this.forwardingPageCode = null;
if (pageCode === 'publishPage') {
this.isShowPublish = true;
} else {
this.isShowPublish = false;
this.curPageCode = pageCode;
}
},
hidePublish() {
this.isShowPublish = false;
},
showPage(url) {
uni.navigateTo({
url: url
})
},
async authLogin() {
// userInfo
let res = await this.$request.storageExistUser();
// userInfo
let curUserInfo = this.$request.getCurUserInfo();
//
if (!curUserInfo || !curUserInfo.openId) {
this.$refs.vertifyLogin.showModal();
return false;
} else {
this.$refs.vertifyLogin.hideModal();
}
//
if (!curUserInfo.phone) {
this.$refs.vertifyPhone.showModal();
return false;
} else {
this.$refs.vertifyPhone.hideModal();
}
return true;
}
}
}
</script>
<style scoped>
.content-mask {
z-index: 9999;
background: rgba(0, 0, 0, 0.9);
}
.mask-close-text {
visibility: hidden;
}
.middle-avatar {
width: 120rpx;
height: 120rpx;
}
.middle-avatar [class*="cuIcon-"] {
font-size: 53rpx !important;
}
.cu-avatar.first-avatar {
background-color: #0081ff;
color: #ffffff;
}
.cu-avatar.second-avatar {
background-color: #fbbd08;
color: #ffffff;
}
.cu-avatar.third-avatar {
background-color: #f37b1d;
color: #ffffff;
}
.cu-avatar.fourth-avatar {
background-color: #1cbbb4;
color: #ffffff;
}
.cu-dialog.bottom-dialog {
background-color: unset;
vertical-align: bottom;
}
</style>

View File

@ -1,171 +1,195 @@
<template name="index">
<view>
<!-- 顶部操作条 -->
<!-- <cu-custom class="text-left">
<block slot="left-content">
<image :src="item.url" v-if="item.type=='image'"></image>
<text class="padding-left text-xl" style="float: left;">叮咚到家家政服务</text>
</block>
</cu-custom> -->
<!-- 轮播图-->
<swiper class="screen-swiper" :class="dotStyle?'square-dot':'round-dot'" :indicator-dots="true" :circular="true"
:autoplay="true" interval="5000" duration="500">
<swiper-item v-for="(item,index) in swiperList" :key="item.id">
<image mode="aspectFill" :src="item.url" v-if="item.type=='image'"></image>
<video :src="item.url" autoplay loop muted :show-play-btn="false" :controls="false" objectFit="cover"
v-if="item.type=='video'"></video>
</swiper-item>
</swiper>
<!-- 搜索栏 -->
<view class="cu-bar search bg-white">
<view class="search-form round">
<text class="cuIcon-search"></text>
<input @confirm="searchGoods" v-model="searchInfo.inputGoodsName" :adjust-position="true" type="text"
placeholder="输入搜索内容" confirm-type="search"></input>
</view>
<!-- 区域筛选picker -->
<view class="action">
<picker class="text-black text-bold" :mode="'multiSelector'" @change="regionChange" @columnchange="regionColChange"
:value="areaMultiIndex" :range-key="'areaName'" :range="areaList">
<text>{{searchInfo.area && searchInfo.area.length ? searchInfo.area[1].areaName : areaList[1][0].areaName}}</text>
<text class="text-xl"><text class="cuIcon-triangledownfill"></text></text>
</picker>
</view>
</view>
<c-tabbar :current="0">
<view>
<view class="margin-lr-sm">
<!-- 大类 -->
<view class="flex justify-between text-black">
<view class="padding text-lg" @click="showProductCategories">
<text>分类</text>
<text class="cuIcon-apps text-main-color"></text>
<text class="cuIcon-triangledownfill"></text>
</view>
<view class="flex-twice">
<scroll-view scroll-x class="nav text-right" :scroll-with-animation="true" :scroll-left="scrollLeft">
<view class="cu-item" :class="index==tabCur?'text-main-color cur':''" v-if="index < 2"
v-for="(item,index) in categories" :key="item.goodsCategoryId" @tap="tabSelect($event, item)" :data-index="index">
{{item.goodsCategoryName}}
</view>
</scroll-view>
</view>
</view>
<!-- 热门细类 -->
<view class="cu-list grid no-border hot-sub-category" :class="['col-5']" :style="'padding-top: 10rpx'">
<view class="cu-item align-center" v-for="(item,index) in hotServCategory" :key="index" v-if="tabCur === 0 && index < 15"
@click="chooseCategory(item)">
<!-- <view class="hot-sub-category-icon" :class="['cuIcon-' + item.cuIcon,'text-' + item.color]">
<view class="cu-tag badge" v-if="item.badge!=0">
<block v-if="item.badge!=1">{{item.badge}}</block>
</view>
</view> -->
<view class="cu-avatar round"
:style="'width: 100rpx; height: 100rpx; background-image:url(' + item.cover + ');'"></view>
<text>{{item.name}}</text>
</view>
<view class="cu-item align-center" v-for="(item,index) in hotFittingsCategory" :key="index" v-if="tabCur === 1 && index < 15"
@click="chooseCategory(item)">
<view class="cu-avatar round"
:style="'width: 100rpx; height: 100rpx; background-image:url(' + item.cover + ');'"></view>
<text>{{item.name}}</text>
</view>
</view>
<!-- 细类 -->
<!-- <view class="cu-list grid no-border" :class="['col-5']" v-if="subCategories.length > 5">
<view class="cu-item" v-for="(item,index) in subCategories" :key="index"
v-if="index < subCategories.length && index > 4" @click="chooseCategory(item)">
<view :class="['cuIcon-' + item.cuIcon,'text-' + item.color]">
</view>
<text>{{item.goodsCategoryName}}</text>
</view>
</view> -->
<!-- 超值服务 -->
<!-- <vertical-goods-card ref="discountGoodsCard" :goodsInfos="discountGoods.goodsInfos"
:title="discountGoods.title"></vertical-goods-card> -->
<!-- 热门服务 -->
<!-- <vertical-card :list="hotServCategory" :multiPicMode="true" title="大家都在买"></vertical-card> -->
</view>
<!-- 当前城市服务列表 -->
<view class="text-gray text-lg text-center margin flex align-center">
<view class="divider"/>
<text class="margin-lr-sm" style="flex-basis: 60%; color: #9E9E9E;">当前城市</text>
<view class="divider"/>
</view>
<view>
<view class="margin-top-sm margin-lr-sm waterfall-grid">
<view @click="showDetails(item)" :key="item.goodsId" v-for="(item, index) in productList" class="waterfall-grid-item">
<flow-goods-card :product="item"></flow-goods-card>
</view>
</view>
<load-status-bar ref="loadStatusBar1" @loadMore="loadProductPage"></load-status-bar>
</view>
<!-- 其他城市服务列表 -->
<view class="text-gray text-lg text-center margin flex align-center" style="padding-top: 60rpx;">
<view class="divider"/>
<text class="margin-lr-sm" style="flex-basis: 50%;">其他城市</text>
<view class="divider"/>
</view>
<view>
<view class="margin-top-sm margin-lr-sm waterfall-grid">
<view @click="showDetails(item)" :key="item.goodsId" v-for="(item, index) in otherCityProductList" class="waterfall-grid-item">
<flow-goods-card :product="item"></flow-goods-card>
</view>
</view>
<load-status-bar ref="loadStatusBar2" @loadMore="loadOtherCityProductPage"></load-status-bar>
<!-- 顶部操作条 -->
<!-- <cu-custom class="text-left">
<block slot="left-content">
<image :src="item.url" v-if="item.type=='image'"></image>
<text class="padding-left text-xl" style="float: left;">叮咚到家家政服务</text>
</block>
</cu-custom> -->
<!-- 轮播图-->
<swiper class="screen-swiper" :class="dotStyle?'square-dot':'round-dot'" :indicator-dots="true"
:circular="true" :autoplay="true" interval="5000" duration="500">
<swiper-item v-for="(item,index) in swiperList" :key="item.id">
<image mode="aspectFill" :src="item.url" v-if="item.type=='image'"></image>
<video :src="item.url" autoplay loop muted :show-play-btn="false" :controls="false"
objectFit="cover" v-if="item.type=='video'"></video>
</swiper-item>
</swiper>
<!-- 搜索栏 -->
<view class="cu-bar search bg-white">
<view class="search-form round">
<text class="cuIcon-search"></text>
<input @confirm="searchGoods" v-model="searchInfo.inputGoodsName" :adjust-position="true"
type="text" placeholder="输入搜索内容" confirm-type="search"></input>
</view>
<!-- 区域筛选picker -->
<view class="action">
<picker class="text-black text-bold" :mode="'multiSelector'" @change="regionChange"
@columnchange="regionColChange" :value="areaMultiIndex" :range-key="'areaName'"
:range="areaList">
<text>{{searchInfo.area && searchInfo.area.length ? searchInfo.area[1].areaName : areaList[1][0].areaName}}</text>
<text class="text-xl"><text class="cuIcon-triangledownfill"></text></text>
</picker>
</view>
</view>
<view>
<view class="margin-lr-sm">
<!-- 大类 -->
<view class="flex justify-between text-black">
<view class="padding text-lg" @click="showProductCategories">
<text>分类</text>
<text class="cuIcon-apps text-main-color"></text>
<text class="cuIcon-triangledownfill"></text>
</view>
<view class="flex-twice">
<scroll-view scroll-x class="nav text-right" :scroll-with-animation="true"
:scroll-left="scrollLeft">
<view class="cu-item" :class="index==tabCur?'text-main-color cur':''" v-if="index < 2"
v-for="(item,index) in categories" :key="item.goodsCategoryId"
@tap="tabSelect($event, item)" :data-index="index">
{{item.goodsCategoryName}}
</view>
</scroll-view>
</view>
</view>
<!-- 热门细类 -->
<view class="cu-list grid no-border hot-sub-category" :class="['col-5']"
:style="'padding-top: 10rpx'">
<view class="cu-item align-center" v-for="(item,index) in hotServCategory" :key="index"
v-if="tabCur === 0 && index < 15" @click="chooseCategory(item)">
<!-- <view class="hot-sub-category-icon" :class="['cuIcon-' + item.cuIcon,'text-' + item.color]">
<view class="cu-tag badge" v-if="item.badge!=0">
<block v-if="item.badge!=1">{{item.badge}}</block>
</view>
</view> -->
<view class="cu-avatar round"
:style="'width: 100rpx; height: 100rpx; background-image:url(' + item.cover + ');'">
</view>
<text>{{item.name}}</text>
</view>
<view class="cu-item align-center" v-for="(item,index) in hotFittingsCategory" :key="index"
v-if="tabCur === 1 && index < 15" @click="chooseCategory(item)">
<view class="cu-avatar round"
:style="'width: 100rpx; height: 100rpx; background-image:url(' + item.cover + ');'">
</view>
<text>{{item.name}}</text>
</view>
</view>
<!-- 细类 -->
<!-- <view class="cu-list grid no-border" :class="['col-5']" v-if="subCategories.length > 5">
<view class="cu-item" v-for="(item,index) in subCategories" :key="index"
v-if="index < subCategories.length && index > 4" @click="chooseCategory(item)">
<view :class="['cuIcon-' + item.cuIcon,'text-' + item.color]">
</view>
<text>{{item.goodsCategoryName}}</text>
</view>
</view> -->
<!-- 超值服务 -->
<!-- <vertical-goods-card ref="discountGoodsCard" :goodsInfos="discountGoods.goodsInfos"
:title="discountGoods.title"></vertical-goods-card> -->
<!-- 热门服务 -->
<!-- <vertical-card :list="hotServCategory" :multiPicMode="true" title="大家都在买"></vertical-card> -->
</view>
<!-- 当前城市服务列表 -->
<view class="text-gray text-lg text-center margin flex align-center">
<view class="divider" />
<text class="margin-lr-sm" style="flex-basis: 60%; color: #9E9E9E;">当前城市</text>
<view class="divider" />
</view>
<view>
<view class="margin-top-sm margin-lr-sm waterfall-grid">
<view @click="showDetails(item)" :key="item.goodsId" v-for="(item, index) in productList"
class="waterfall-grid-item">
<flow-goods-card :product="item"></flow-goods-card>
</view>
</view>
<load-status-bar ref="loadStatusBar1" @loadMore="loadProductPage"></load-status-bar>
</view>
<!-- 其他城市服务列表 -->
<view class="text-gray text-lg text-center margin flex align-center" style="padding-top: 60rpx;">
<view class="divider" />
<text class="margin-lr-sm" style="flex-basis: 50%;">其他城市</text>
<view class="divider" />
</view>
<view>
<view class="margin-top-sm margin-lr-sm waterfall-grid">
<view @click="showDetails(item)" :key="item.goodsId"
v-for="(item, index) in otherCityProductList" class="waterfall-grid-item">
<flow-goods-card :product="item"></flow-goods-card>
</view>
</view>
<load-status-bar ref="loadStatusBar2" @loadMore="loadOtherCityProductPage"></load-status-bar>
</view>
</view>
<view class="occupancy-bottom-bar"></view>
<!-- 前往授权设置 -->
<view class="cu-modal" :class="isShowPrivSetting?'show':''">
<view class="cu-dialog">
<view class="padding-xl text-left">
<view>需先授权定位功能才可正常使用功能</view>
</view>
<view class="cu-bar bg-white">
<navigator class="modal-bottom-oper margin-0 flex-sub text-black" open-type="exit"
target="miniProgram">拒绝授权
</navigator>
<button class="cu-btn modal-bottom-oper margin-0 flex-sub text-main-color bg-white solid-left"
open-type="openSetting" @opensetting="authLocationCallback">前往授权</button>
</view>
</view>
</view>
</view>
<view class="occupancy-bottom-bar"></view>
<!-- 前往授权设置 -->
<view class="cu-modal" :class="isShowPrivSetting?'show':''">
<view class="cu-dialog">
<view class="padding-xl text-left">
<view>需先授权定位功能才可正常使用功能</view>
</view>
<view class="cu-bar bg-white">
<navigator class="modal-bottom-oper margin-0 flex-sub text-black" open-type="exit" target="miniProgram">拒绝授权
</navigator>
<button class="cu-btn modal-bottom-oper margin-0 flex-sub text-main-color bg-white solid-left" open-type="openSetting" @opensetting="authLocationCallback">前往授权</button>
</view>
</view>
</view>
</view>
<!-- <valid ref="validRef"></valid> -->
</c-tabbar>
</template>
<script>
import verticalCard from '@/components/common-card/vertical-card.vue';
import loadStatusBar from '@/components/custom-bar/load-status-bar.vue';
import verticalCard from '@/components/common-card/vertical-card.vue';
import loadStatusBar from '@/components/custom-bar/load-status-bar.vue';
import flowGoodsCard from '@/components/goods-card/flow-goods-card.vue';
import CTabbar from '@/components/CTabbar.vue';
// import valid from './components/valid.vue';
export default {
name: 'index',
components: {
verticalCard,
loadStatusBar,
flowGoodsCard
verticalCard,
loadStatusBar,
flowGoodsCard,
CTabbar,
// valid
},
data() {
return {
return {
scrollLeft: 0,
dotStyle: true,
swiperList: [],
tabCur: 0,
categories: [],
subCategories: [],
hotServCategory: [],
hotServCategory: [],
hotFittingsCategory: [],
discountGoods: {},
InputBottom: 0,
searchInfo: {},
areaList: [],
areaMultiIndex: [0, 0],
productList: [],
otherCityProductList: [],
pageNum: 0,
pageSize: 0,
otherCityProductPageNum: 0,
otherCityProductPageSize: 0,
isShowPrivSetting: false
areaMultiIndex: [0, 0],
productList: [],
otherCityProductList: [],
pageNum: 0,
pageSize: 0,
otherCityProductPageNum: 0,
otherCityProductPageSize: 0,
isShowPrivSetting: false,
initParam: null
}
},
onLoad(option) {
this.initParam = option
uni.hideTabBar();
},
mounted() {
this.bindEvent();
@ -173,31 +197,56 @@
beforeDestroy() {
this.offBindEvent();
},
async onReady() {
// await this.$request.authAndGetLocation();
//
await this.getCurAreaArr();
async onReady() {
// await this.$request.authAndGetLocation();
//
await this.getCurAreaArr();
uni.$emit('initValid', this.initParam)
},
async onShareAppMessage(e) {
let shareInfo = null;
if (e && e.target && e.target.dataset) {
shareInfo = e.target.dataset.shareInfo;
}
if (!this.curUserInfo) {
this.curUserInfo = await this.$request.getCurUserNoCache();
if (!this.curUserInfo) {
uni.showToast({
title: '请前往“我的”完成登录',
icon: 'none'
})
return;
}
}
if (!shareInfo) {
shareInfo = {
title: '家政服务就找工圈子',
path: '/pages/index/home?distributor=' + this.curUserInfo.customerId,
imageUrl: 'http://gqz.opsoul.com/sys/group-selfie.png'
}
}
return shareInfo;
},
methods: {
async loadData() {
async loadData() {
this.initBasicData();
//
await this.loadRegionList();
//
let hotServCategoryRes = await this.$request.getHotCategory({
type: 1
});
this.hotServCategory = hotServCategoryRes.data;
//
let hotFittingsCategoryRes = await this.$request.getHotCategory({
type: 2
});
this.hotFittingsCategory = hotFittingsCategoryRes.data;
//
this.loadCategoryList();
//
this.loadProductPage();
//
let hotServCategoryRes = await this.$request.getHotCategory({
type: 1
});
this.hotServCategory = hotServCategoryRes.data;
//
let hotFittingsCategoryRes = await this.$request.getHotCategory({
type: 2
});
this.hotFittingsCategory = hotFittingsCategoryRes.data;
//
this.loadCategoryList();
//
this.loadProductPage();
//
this.loadOtherCityProductPage();
this.swiperList = await this.$api.data('swiperList');
@ -206,117 +255,123 @@
this.moduleBarInfos = await this.$api.data('moduleBarInfos');
// this.hotGoods = await this.$api.data('hotGoods');
// this.discountGoods = await this.$api.data('discountGoods');
},
},
// authCallback() {
// this.$refs.validRef.loadData(this.initParam)
// },
bindEvent() {
uni.$on('clickCard', this.clickHotCategory);
uni.$on('clickCard', this.clickHotCategory);
// uni.$on('authCallback', this.authCallback);
},
offBindEvent() {
uni.$off('clickCard');
},
async authLocationCallback(res) {
if (res.detail.authSetting['scope.userLocation']) {
this.isShowPrivSetting = false;
await this.getCurAreaArr();
}
},
async wxGetLocation() {
let _this = this;
// wx.getSetting authScope
let res1 = await wx.getSetting();
if (res1) {
if (!res1.authSetting['scope.userLocation']) {
let res2 = await wx.getLocation({
type: 'gcj02',
fail: async (result) => {
console.log(result)
if (result.errno === 103 || result.errMsg === 'getLocation:fail auth deny') {
this.isShowPrivSetting = true;
} else {
//
uni.showToast({
title: '请确认是否开启定位',
icon: 'none',
duration: 1500
})
}
},
success: async (result) => {
await _this.$request.storageLocation(result);
let areaArr = await _this.getCurAreaArr();
if (areaArr) {
_this.searchInfo.area = areaArr;
await _this.loadData();
}
}
})
} else {
wx.getLocation({
type: 'gcj02',
fail: async (result) => {
console.log(result)
if (result.errCode === 2 || result.errCode === 404) {
uni.showToast({
title: '定位获取失败,请确认是否开启定位',
icon: 'none',
duration: 2500
})
} else {
uni.showToast({
title: '定位获取失败,请稍后重试',
icon: 'none',
duration: 2500
})
}
},
success: async (result) => {
await _this.$request.storageLocation(result);
let areaArr = await _this.getCurAreaArr();
if (areaArr) {
_this.searchInfo.area = areaArr;
await _this.loadData();
}
}
})
}
}
},
async getCurAreaArr() {
let curLocation = uni.getStorageSync('curLocation');
if (curLocation && curLocation.updateTimes) {
let curTimes = new Date().getTime();
let deltaSeconds = curTimes - curLocation.updateTimes;
if (deltaSeconds <= 15 * 60 * 1000) {
this.searchInfo.area = curLocation.area;
this.loadData();
return;
}
}
await this.wxGetLocation();
},
async loadCategoryList() {
let res = await this.$request.getProductCategories();
this.categories = res[1].data.data;
this.tabSelect(null, this.categories[0]);
uni.$off('clickCard');
// uni.$off('authCallback');
},
async loadRegionList() {
async authLocationCallback(res) {
if (res.detail.authSetting['scope.userLocation']) {
this.isShowPrivSetting = false;
await this.getCurAreaArr();
}
},
async wxGetLocation() {
let _this = this;
// wx.getSetting authScope
let res1 = await wx.getSetting();
if (res1) {
if (!res1.authSetting['scope.userLocation']) {
let res2 = await wx.getLocation({
type: 'gcj02',
fail: async (result) => {
console.log(result)
if (result.errno === 103 || result.errMsg ===
'getLocation:fail auth deny') {
this.isShowPrivSetting = true;
} else {
//
uni.showToast({
title: '请确认是否开启定位',
icon: 'none',
duration: 1500
})
}
},
success: async (result) => {
await _this.$request.storageLocation(result);
let areaArr = await _this.getCurAreaArr();
if (areaArr) {
_this.searchInfo.area = areaArr;
await _this.loadData();
}
}
})
} else {
wx.getLocation({
type: 'gcj02',
fail: async (result) => {
console.log(result)
if (result.errCode === 2 || result.errCode === 404) {
uni.showToast({
title: '定位获取失败,请确认是否开启定位',
icon: 'none',
duration: 2500
})
} else {
uni.showToast({
title: '定位获取失败,请稍后重试',
icon: 'none',
duration: 2500
})
}
},
success: async (result) => {
await _this.$request.storageLocation(result);
let areaArr = await _this.getCurAreaArr();
if (areaArr) {
_this.searchInfo.area = areaArr;
await _this.loadData();
}
}
})
}
}
},
async getCurAreaArr() {
let curLocation = uni.getStorageSync('curLocation');
if (curLocation && curLocation.updateTimes) {
let curTimes = new Date().getTime();
let deltaSeconds = curTimes - curLocation.updateTimes;
if (deltaSeconds <= 15 * 60 * 1000) {
this.searchInfo.area = curLocation.area;
this.loadData();
return;
}
}
await this.wxGetLocation();
},
async loadCategoryList() {
let res = await this.$request.getProductCategories();
this.categories = res[1].data.data;
this.tabSelect(null, this.categories[0]);
},
async loadRegionList() {
let area = this.searchInfo.area && this.searchInfo.area.length ? this.searchInfo.area : null
let regionList = await this.$request.areaListByStep({
areaId: area == null ? null : area[0].areaId
let regionList = await this.$request.areaListByStep({
areaId: area == null ? null : area[0].areaId
});
regionList = regionList.data;
let subRegionList = [];
let subSubRegionList = [];
if (regionList && regionList.length > 0) {
let params = {};
if (area != null) {
params = {
areaId: area[1].areaId,
parentCode: area[0].areaId
}
} else {
params = {
parentCode: regionList[0].areaCode
}
if (regionList && regionList.length > 0) {
let params = {};
if (area != null) {
params = {
areaId: area[1].areaId,
parentCode: area[0].areaId
}
} else {
params = {
parentCode: regionList[0].areaCode
}
}
subRegionList = await this.$request.areaListByStep(params);
subRegionList = subRegionList.data;
@ -325,23 +380,23 @@
subSubRegionList = await this.$request.areaListByStep({
parentCode: subRegionList[0].areaCode
});
subSubRegionList = subSubRegionList.data;
let allAreaIdsOfSubSubRegionList = [];
subSubRegionList.forEach(region => {
allAreaIdsOfSubSubRegionList.push(region.areaId)
})
let subSubRegionListWithAll = [{
areaIds: allAreaIdsOfSubSubRegionList,
areaId: subRegionList[0].areaId,
isAll: true,
areaName: subRegionList[0].areaName
}].concat(subSubRegionList);
subSubRegionList = subSubRegionList.data;
let allAreaIdsOfSubSubRegionList = [];
subSubRegionList.forEach(region => {
allAreaIdsOfSubSubRegionList.push(region.areaId)
})
let subSubRegionListWithAll = [{
areaIds: allAreaIdsOfSubSubRegionList,
areaId: subRegionList[0].areaId,
isAll: true,
areaName: subRegionList[0].areaName
}].concat(subSubRegionList);
subSubRegionList = subSubRegionListWithAll;
}
}
this.areaList = [];
// this.areaList.push(regionList);
this.areaList.push(subRegionList);
this.areaList.push(subSubRegionList);
this.areaList.push(subSubRegionList);
this.searchInfo.area = [this.areaList[0][0], this.areaList[1][0]];
},
regionChange(e) {
@ -350,7 +405,7 @@
for (let i = 0; i < this.areaList.length; i++) {
chosenArea.push(this.areaList[i][this.areaMultiIndex[i]]);
}
this.searchInfo.area = chosenArea;
this.searchInfo.area = chosenArea;
this.reloadProductPage();
},
async regionColChange(e) {
@ -384,38 +439,38 @@
// this.areaMultiIndex = [this.areaMultiIndex[0], colObj.value, 0];
// }
},
async tabSelect(e, item) {
this.tabCur = e == null ? 0 : e.currentTarget.dataset.index;
async tabSelect(e, item) {
this.tabCur = e == null ? 0 : e.currentTarget.dataset.index;
this.scrollLeft = (this.tabCur - 1) * 60;
let subSubTypeList = [];
for (let i = 0; i < item.child.length; i++) {
let stopFlag = false;
let secondList = item.child[i].child;
if (secondList && secondList.length) {
for (let k = 0; k < secondList.length; k++) {
let thirdList = secondList[k].child;
if (thirdList && thirdList.length) {
for (let j = 0; j < thirdList.length; j++) {
subSubTypeList = subSubTypeList.concat(thirdList[j]);
if (subSubTypeList.length >= 15) {
subSubTypeList = subSubTypeList.slice(0, 15);
stopFlag = true;
break;
}
}
}
if (stopFlag) break;
}
}
if (stopFlag) break;
}
let subCategoryOpt = await this.$api.data('subCategories');
for(let i = 0; i < subSubTypeList.length; i++) {
subSubTypeList[i]['cuIcon'] = subCategoryOpt[i].cuIcon;
subSubTypeList[i]['color'] = subCategoryOpt[i].color;
}
let subSubTypeList = [];
for (let i = 0; i < item.child.length; i++) {
let stopFlag = false;
let secondList = item.child[i].child;
if (secondList && secondList.length) {
for (let k = 0; k < secondList.length; k++) {
let thirdList = secondList[k].child;
if (thirdList && thirdList.length) {
for (let j = 0; j < thirdList.length; j++) {
subSubTypeList = subSubTypeList.concat(thirdList[j]);
if (subSubTypeList.length >= 15) {
subSubTypeList = subSubTypeList.slice(0, 15);
stopFlag = true;
break;
}
}
}
if (stopFlag) break;
}
}
if (stopFlag) break;
}
let subCategoryOpt = await this.$api.data('subCategories');
for (let i = 0; i < subSubTypeList.length; i++) {
subSubTypeList[i]['cuIcon'] = subCategoryOpt[i].cuIcon;
subSubTypeList[i]['color'] = subCategoryOpt[i].color;
}
this.subCategories = subSubTypeList;
},
chooseCategory(item) {
@ -433,87 +488,90 @@
})
},
clickHotCategory(item) {
let params = {
category: item
};
uni.navigateTo({
url: '/pages/product/filtered-products?params=' + encodeURIComponent(JSON.stringify(params))
let params = {
category: item
};
uni.navigateTo({
url: '/pages/product/filtered-products?params=' + encodeURIComponent(JSON.stringify(params))
})
},
showProductCategories() {
uni.navigateTo({
url: '/pages/product/product-category'
})
},
/* 底部当前城市服务列表 start */
initBasicData() {
this.pageNum = this.$globalData.initPageNum;
this.pageSize = this.$globalData.initPageSize;
this.otherCityProductPageNum = this.$globalData.initPageNum;
this.otherCityProductPageSize = this.$globalData.initPageSize;
this.productList = [];
this.otherCityProductList = [];
},
showDetails(productItem) {
let params = {
goodsId: productItem.goodsId
}
uni.navigateTo({
url: '/pages/product/product-detail?params=' + encodeURIComponent(JSON.stringify(params))
});
},
async reloadProductPage() {
this.initBasicData();
this.loadProductPage();
this.loadOtherCityProductPage();
},
async loadProductPage(params = {
areaId: this.searchInfo.area && this.searchInfo.area.length ? this.searchInfo.area[this.searchInfo.area.length - 1].areaId : null
}, loadStatusBarRefName = "loadStatusBar1", resContainer = "productList", pageNumName = "pageNum", pageSizeName = "pageSize") {
if (!params.exceptParentAreaId && this.searchInfo.area
&& this.searchInfo.area.length && this.searchInfo.area[this.searchInfo.area.length - 1].isAll) {
params.areaIds = this.searchInfo.area[this.searchInfo.area.length - 1].areaIds;
params.areaId = null;
}
params.pageNum = this[pageNumName];
params.pageSize = this[pageSizeName];
params.status = 0;
this.$refs[loadStatusBarRefName].showLoading();
try {
let res = await this.$request.qryProductPage(params);
let rowsLength = res[1].data.rows.length;
if (rowsLength > 0) {
this[resContainer] = this[resContainer].concat(res[1].data.rows);
console.log(this[resContainer])
this[pageNumName]++;
if (rowsLength === this[pageSizeName]) {
this.$refs[loadStatusBarRefName].showLoadMore();
return;
}
}
this.$refs[loadStatusBarRefName].showLoadOver();
} catch (e) {
console.error(e)
this.$refs[loadStatusBarRefName].showLoadErr();
}
},
/* 底部当前城市服务列表 end */
/* 其他城市服务列表 start */
loadOtherCityProductPage() {
this.loadProductPage({
exceptParentAreaId: this.searchInfo.area && this.searchInfo.area.length ? this.searchInfo.area[1].areaId : null
}, "loadStatusBar2", "otherCityProductList", "otherCityProductPageNum", "otherCityProductPageSize")
}
},
/* 底部当前城市服务列表 start */
initBasicData() {
this.pageNum = this.$globalData.initPageNum;
this.pageSize = this.$globalData.initPageSize;
this.otherCityProductPageNum = this.$globalData.initPageNum;
this.otherCityProductPageSize = this.$globalData.initPageSize;
this.productList = [];
this.otherCityProductList = [];
},
showDetails(productItem) {
let params = {
goodsId: productItem.goodsId
}
uni.navigateTo({
url: '/pages/product/product-detail?params=' + encodeURIComponent(JSON.stringify(params))
});
},
async reloadProductPage() {
this.initBasicData();
this.loadProductPage();
this.loadOtherCityProductPage();
},
async loadProductPage(params = {
areaId: this.searchInfo.area && this.searchInfo.area.length ? this.searchInfo.area[this.searchInfo
.area.length - 1].areaId : null
}, loadStatusBarRefName = "loadStatusBar1", resContainer = "productList", pageNumName = "pageNum",
pageSizeName = "pageSize") {
if (!params.exceptParentAreaId && this.searchInfo.area &&
this.searchInfo.area.length && this.searchInfo.area[this.searchInfo.area.length - 1].isAll) {
params.areaIds = this.searchInfo.area[this.searchInfo.area.length - 1].areaIds;
params.areaId = null;
}
params.pageNum = this[pageNumName];
params.pageSize = this[pageSizeName];
params.status = 0;
this.$refs[loadStatusBarRefName].showLoading();
try {
let res = await this.$request.qryProductPage(params);
let rowsLength = res[1].data.rows.length;
if (rowsLength > 0) {
this[resContainer] = this[resContainer].concat(res[1].data.rows);
console.log(this[resContainer])
this[pageNumName]++;
if (rowsLength === this[pageSizeName]) {
this.$refs[loadStatusBarRefName].showLoadMore();
return;
}
}
this.$refs[loadStatusBarRefName].showLoadOver();
} catch (e) {
console.error(e)
this.$refs[loadStatusBarRefName].showLoadErr();
}
},
/* 底部当前城市服务列表 end */
/* 其他城市服务列表 start */
loadOtherCityProductPage() {
this.loadProductPage({
exceptParentAreaId: this.searchInfo.area && this.searchInfo.area.length ? this.searchInfo.area[
1].areaId : null
}, "loadStatusBar2", "otherCityProductList", "otherCityProductPageNum", "otherCityProductPageSize")
}
/* 其他城市服务列表 end */
}
}
</script>
<style scoped>
.screen-swiper {
height: 500rpx;
}
<style scoped>
.screen-swiper {
height: 500rpx;
}
.cu-list+.cu-list {
margin-top: 0;
}
@ -524,27 +582,27 @@
.hot-sub-category .cu-item .hot-sub-category-icon {
font-size: 80rpx;
}
}
.modal-bottom-oper {
height: 70rpx;
display: inline-flex;
align-items: center;
justify-content: center;
}
.waterfall-grid {
/* column-count: 2;
column-gap: 15rpx; */
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.waterfall-grid-item {
/* break-inside: avoid;
margin-bottom: 15rpx; */
width: 49%;
margin-bottom: 15rpx;
}
</style>
.waterfall-grid {
/* column-count: 2;
column-gap: 15rpx; */
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.waterfall-grid-item {
/* break-inside: avoid;
margin-bottom: 15rpx; */
width: 49%;
margin-bottom: 15rpx;
}
</style>

View File

@ -1,4 +1,5 @@
<template>
<c-tabbar :current="3">
<view class="margin-bottom-lg">
<!-- 顶部操作条 -->
<cu-custom :bgColor="'bg-main-color'">
@ -6,21 +7,48 @@
</cu-custom>
<bg-toast :icon="'repair'" :msg="'开发中'"></bg-toast>
</view>
</c-tabbar>
</template>
<script>
import bgToast from '@/components/default-toast/bg-toast.vue';
import bgToast from '@/components/default-toast/bg-toast.vue';
import CTabbar from '@/components/CTabbar.vue';
export default {
name: 'msgPage',
components: {
bgToast
bgToast,
CTabbar
},
data() {
return {
}
},
async onShareAppMessage(e) {
let shareInfo = null;
if (e && e.target && e.target.dataset) {
shareInfo = e.target.dataset.shareInfo;
}
if (!this.curUserInfo) {
this.curUserInfo = await this.$request.getCurUserNoCache();
if (!this.curUserInfo) {
uni.showToast({
title: '请前往“我的”完成登录',
icon: 'none'
})
return;
}
}
if (!shareInfo) {
shareInfo = {
title: '家政服务就找工圈子',
path: '/pages/index/home?distributor=' + this.curUserInfo.customerId,
imageUrl: 'http://gqz.opsoul.com/sys/group-selfie.png'
}
}
return shareInfo;
},
methods: {
}

View File

@ -1,5 +1,5 @@
<template>
<view>
<c-tabbar :current="4">
<!-- 顶部头像栏 -->
<view class="padding bg-gradual-color"
:style="'padding-top: ' + pageContentTop + 'px; padding-bottom: 100rpx;'">
@ -301,17 +301,18 @@
</view>
</view>
</view>
</view>
</c-tabbar>
</template>
<script>
// import appInviteQrcode from '@/pages/my/app-invite-qrcode.vue';
import CTabbar from '@/components/CTabbar.vue';
export default {
name: 'personal-center',
// components: {
// appInviteQrcode
// },
components: {
CTabbar
},
data() {
return {
pageContentTop: this.CustomBar,
@ -387,6 +388,30 @@
},
onReady() {
this.loadData();
},
async onShareAppMessage(e) {
let shareInfo = null;
if (e && e.target && e.target.dataset) {
shareInfo = e.target.dataset.shareInfo;
}
if (!this.curUserInfo) {
this.curUserInfo = await this.$request.getCurUserNoCache();
if (!this.curUserInfo) {
uni.showToast({
title: '请前往“我的”完成登录',
icon: 'none'
})
return;
}
}
if (!shareInfo) {
shareInfo = {
title: '家政服务就找工圈子',
path: '/pages/index/home?distributor=' + this.curUserInfo.customerId,
imageUrl: 'http://gqz.opsoul.com/sys/group-selfie.png'
}
}
return shareInfo;
},
methods: {
async loadData() {

File diff suppressed because it is too large Load Diff

View File

@ -179,7 +179,7 @@
<view class="margin-lr-sm margin-top-sm padding bg-white">
<view class="flex justify-between align-center">
<view class="flex justify-start align-center">
<view class="cu-avatar round" :style="'background-image:url(' + shopInfo.workerLogoUrl + ');'"></view>
<view class="cu-avatar round" :style="'background-image:url(' + defaultHead(shopInfo.workerLogoUrl) + ');'"></view>
<view class="content flex-sub margin-lr-sm">
<view class="text-black">{{shopInfo.name}}</view>
<uni-rate :size="15" :readonly="true" allow-half :value="5" />
@ -196,7 +196,7 @@
</view>
</view>
<!-- 底部操作条 -->
<view class="cu-bar bg-white tabbar border shop fixed-bottom-bar">
<view class="cu-bar bg-white tabbar border shop fixed-bottom-bar" style="flex-direction: row;">
<view class="action" @click="showIndex">
<view class="cuIcon-homefill"></view>
首页
@ -443,6 +443,12 @@
uni.navigateTo({
url: '/pages/index/index'
});
},
defaultHead(url) {
if(url === 'https://thirdwx.qlogo.cn/mmopen/vi_32/POgEwh4mIHO4nibH0KlMECNjjGxQUq24ZEaGT4poC6icRiccVGKSyXwibcPq4BWmiaIGuG1icwxaQX6grC9VemZoJ8rg/132') {
return 'http://gqz.opsoul.com/2741722238771_.pic.jpg'
}
return url
}
}
}

View File

@ -9,7 +9,7 @@
<view class="bg-white padding solid-bottom">
<view class="flex justify-start">
<view class="cu-avatar round"
:style="'background-image:url(' + shopInfo.workerLogoUrl + '); width: 130rpx; height: 130rpx;'">
:style="'background-image:url(' + defaultHead(shopInfo.workerLogoUrl) + '); width: 130rpx; height: 130rpx;'">
</view>
<view class="margin-left-sm flex-column-between text-beside-avatar">
<view class="text-black text-xl">{{shopInfo.name}}</view>
@ -286,6 +286,13 @@
},
showAllArea(index) {
this.isShowAllAreaCurCity = !this.isShowAllAreaCurCity;
},
defaultHead(url) {
if(url === 'https://thirdwx.qlogo.cn/mmopen/vi_32/POgEwh4mIHO4nibH0KlMECNjjGxQUq24ZEaGT4poC6icRiccVGKSyXwibcPq4BWmiaIGuG1icwxaQX6grC9VemZoJ8rg/132') {
return 'http://gqz.opsoul.com/2741722238771_.pic.jpg'
}
return url
}
}
}

View File

@ -0,0 +1,76 @@
## 2.0.02023-11-14
1、新增切换拦截返回当前点击index
2、新增自动读取主页面配置功能
3、优化部分代码结构实现
注意代码重构优化2.0.0版本不支持老版本兼容
## 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版本

View File

@ -0,0 +1,525 @@
<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 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'" />
<!-- tabbar border -->
<view class="m-tabbar__flex">
<view class="m-tabbar__item" @tap="_tabChange(index)" v-for="(item, index) in tabbarList" :key="index"
: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" /> -->
<text :class="['cuIcon-' + item.cuIcon, currentIndex === index ? 'text-main-color' : 'text-gray']"></text>
</view>
<view class="m-tabbar__label"
:style="{'color': index === currentIndex ? tabbarConfig.selectedColor : tabbarConfig.color }">
{{ item.text }}
</view>
</slot>
</view>
<!-- tabbar item -->
</view>
<!-- tabbar item box -->
</view>
<!-- tabbar block -->
</view>
<!-- tabbar box -->
</template>
<script>
// stylestring
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
})
}
// 便
const setTabbarStorage = (key, value, name) => {
try {
uni.setStorageSync(`mTab_${key}_${name}`, value)
} catch (e) {
console.error(e)
}
}
//
const getTabbarStorage = (key, name) => {
try {
const value = uni.getStorageSync(`mTab_${key}_${name}`)
if (value) {
return value
}
return ''
} catch (e) {
console.error(e)
}
}
import PageConfig from '@/pages.json'
export default {
emits: ['change', 'click'],
props: {
// 使
native: {
type: Boolean,
default: false
},
// 0
current: {
type: [Number, String],
default: 0
},
// tabbar
tabbar: {
type: Object,
default () {
return {}
}
},
//
fixed: {
type: Boolean,
default: false
},
//
fill: {
type: Boolean,
default: false
},
//
zIndex: {
type: [Number, String],
default: 9999
},
//
safeBottom: {
type: Boolean,
default: true
},
// ()
beforeChange: {
type: Function,
default: null
},
// tabbarrpx
tabbarHeight: {
type: [Number, String],
default: 100
},
// tabbarname,便tabbar
name: {
type: String,
default: 'custom'
}
},
data() {
return {
isShowTabBar: false, //
currentIndex: 0, //
beforeData: {}, //
initTabbarConfig: {},
isReload: false //unicomputedget set使reload
}
},
watch: {
current(val) {
this.currentIndex = val * 1
}
},
computed: {
tabbarConfig() {
const {
isReload,
native,
initTabbarConfig: tabbar
} = this
if(isReload){}
// native
if (native) {
if (!tabbar) {
console.error('Native mode, Pages.json no tabbar config')
return {
borderStyle: 'black',
list: []
}
}
return tabbar
}
// tabbar
if (!tabbar.color) {
return this._getDefaultTabbar()
}
return tabbar
},
tabbarList() {
const {
isReload,
tabbarConfig: {
list
}
} = this
if(isReload){}
if (!list) {
console.error('No tabbar config')
return []
}
return replaceTabbarList(list)
},
borderStyle() {
const {
isReload,
borderStyle
} = this.tabbarConfig
if(isReload){}
return borderStyle
},
tabbarBoxStyle() {
const {
isReload,
zIndex
} = this
if(isReload){}
return obj2strStyle({
'z-index': zIndex,
})
},
tabbarFillStyle() {
const {
isReload,
tabbarHeight
} = this
if(isReload){}
return obj2strStyle({
'height': `${tabbarHeight}rpx`
})
},
tabbarStyle() {
const {
isReload,
tabbarHeight,
tabbarConfig: {
backgroundColor
}
} = this
if(isReload){}
return obj2strStyle({
'height': `${tabbarHeight}rpx`,
'background-color': backgroundColor || '#fff',
})
},
tabbarItemStyle() {
const {
isReload,
currentIndex,
tabbarConfig: {
color,
selectedColor
}
} = this
if(isReload){}
return obj2strStyle({
'color': currentIndex ? selectedColor : color
})
}
},
created() {
const {
tabbar,
native
} = this
const pageTabbar = PageConfig.tabBar
this.initTabbarConfig = native ? pageTabbar : tabbar
console.log(this.name)
this._initTabbar()
},
mounted() {
console.log(this.currentIndex);
},
methods: {
_initTabbar() {
const {
current,
fill,
native,
tabbarList
} = this
this.currentIndex = current * 1
if (native && tabbarList.length > 0) {
const currentPage = `/${getCurrentPages()[0].route}`
const currentIndex = tabbarList.findIndex(item => item.pagePath === currentPage)
this.currentIndex = currentIndex
uni.hideTabBar()
}
setTimeout(() => {
this.isShowTabBar = true
})
this._setTabbarStorage('tabbarConfig')
},
_setTabbarStorage(key){
setTabbarStorage(key, this.tabbarConfig, this.name)
this._setReload()
},
_getDefaultTabbar() {
return getTabbarStorage('tabbarConfig', this.name)
},
_checkMaxIndex(index) {
const {
tabbarConfig: {
list
}
} = this
if (list.length < 1) {
return false
}
if (!list[index]) {
console.error('Max tabbar index')
return false
}
return true
},
_setReload(){
this.isReload = true
setTimeout(()=>{
this.isReload = false
})
},
setTabBarBadge(obj) {
const {
index,
text
} = obj
if (this._checkMaxIndex(index)) {
this.tabbarConfig.list[index].dot = text
this._setTabbarStorage('tabbarConfig')
}
},
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._setTabbarStorage('tabbarConfig')
}
},
showTabBar() {
this.isShowTabBar = true
},
hideTabBar() {
this.isShowTabBar = false
},
reload(){
this.initTabbarConfig = this._getDefaultTabbar()
},
_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, index)
} else {
this._jumpPage()
}
},
_jumpPage() {
const {
native,
beforeData: {
newIndex: index
},
tabbarList: list
} = this
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._initTabbar()
}
}
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;
z-index: 9999;
}
&__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;
font-size: 40upx;
text-align: center;
&_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>

View File

@ -0,0 +1,81 @@
{
"id": "m-tabbar",
"displayName": "自定义tabbar、tabbar路由守卫、零配置tabbar、凸起导航",
"version": "2.0.0",
"description": "自定义tabbar超高还原原生配置模式一行代码自定义导航自带tabbar路由守卫功能",
"keywords": [
"自定义tabbar,tabbar,自定义标签栏,tabbar路由守卫,零配置tabbar,路由守卫、凸起导航"
],
"repository": "",
"engines": {
"HBuilderX": "^3.5.4"
},
"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"
}
}
}
}
}

View File

@ -0,0 +1,312 @@
# 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`参数,用户可根据自己情况自行扩展页面打开方式,详细看下方配置
> 闪烁的话,可以使用一个页面,多个组件,如果是`if切换组件`的话,就是一个页面控制多个组件显示隐藏来实现。 如果组件封装的有问题,会出现组件之间的协调问题,请看情况使用。 还有一些原生的交互没有办法达到预期,会影响到原生体验。 比如下拉刷新滚动加载更多切换tabbar后滚动位置不能固定等
### 1、提取tabbar配置2.0.0版本支持自动读取配置,主页面配置,内页可以不配置)
新建文件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 2.0.0版本支持自动读取配置,主页面配置,内页可以不引入)
#### 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, index){
//index为当前点击的是第几项0开始
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高度没啥用,如果非要更改,布局有影响,请使用样式覆盖) |
| name | string | custom | 否 | 用于内部调用页面时,自动读取前一个页面的配置,无需二次配置 |
### 属性说明(普通模式)
| 属性名 | 类型 | 默认值 | 必填 | 说明 |
| ------------ | ------------- | ------ | ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 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高度没啥用,如果非要更改,布局有影响,请使用样式覆盖) |
| name | string | custom | 否 | 用于内部调用页面时,自动读取前一个页面的配置,无需二次配置 |
### 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'
})
})
//reload重载整个组件
this.$nextTick(()=>{
this.$refs.tabbar.reload()
})
```
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
问题描述: 提示什么什么错误
```