feat: 优化页面未认证状态的显示逻辑

This commit is contained in:
davy 2025-04-04 13:00:55 +08:00
parent ce75ba93a4
commit 17cb5edb5c
5 changed files with 285 additions and 147 deletions

View File

@ -4,6 +4,14 @@ import {
const timeout = 5000;
let showModal = false
// 定义错误码
const ErrorCode = {
SUCCESS: 200,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
SERVER_ERROR: 500
}
// 定义公开API白名单
const publicApis = [
'/beer/list', // 啤酒列表
@ -24,19 +32,60 @@ const publicApis = [
'/bar/brewery/getBreweryInfo', // 获取品牌详情
]
// 检查是否是公开API
// 检查是否是公开API - 使用更精确的匹配
const isPublicApi = (url) => {
// 使用更精确的匹配方式
return publicApis.some(api => {
// 如果是完整路径匹配
if (api.includes('/')) {
return url.includes(api);
if (api.endsWith('/')) {
return url.startsWith(api);
}
// 如果是通配符匹配
return url.split('/').includes(api);
return url === api || url.startsWith(api + '/');
});
}
// 处理错误提示
const handleErrorMessage = (code, msg, url) => {
// 如果是公开API不显示错误提示
if (isPublicApi(url)) return;
switch (code) {
case ErrorCode.UNAUTHORIZED:
if (!showModal) {
showModal = true;
// 直接触发登录事件,显示登录弹窗
uni.$emit('needLogin');
setTimeout(() => {
showModal = false;
}, 100);
}
break;
case ErrorCode.FORBIDDEN:
uni.showToast({
title: msg || '暂无权限',
icon: 'none',
duration: 2000
});
break;
case ErrorCode.SERVER_ERROR:
uni.showToast({
title: '服务器异常,请稍后重试',
icon: 'none',
duration: 2000
});
break;
default:
if (msg) {
uni.showToast({
title: msg,
icon: 'none',
duration: 2000
});
}
}
}
export default (params) => {
let url = params.url;
let method = params.method || "get";
@ -65,50 +114,34 @@ export default (params) => {
if (res.statusCode == 200) {
console.log(res.data, '接口返回值')
if (res.data.code == 200) {
if (res.data.code == ErrorCode.SUCCESS) {
resolve(res.data);
} else if (res.data.code == 401 && !isPublicApi(url)) {
uni.clearStorageSync()
if (showModal) return
showModal = true
uni.showModal({
title: "提示",
content: res.data.msg || "身份已过期,请重新登录",
showCancel: false,
success() {
showModal = false
uni.navigateTo({
url: '/pages/index/chooseLogin'
})
},
});
} else {
// 对于公开接口的401错误不显示错误提示
if (!isPublicApi(url)) {
uni.showToast({
title: res.data.msg || '请求失败',
icon: 'none',
duration: 3000,
})
// 处理401未授权错误
if (res.data.code == ErrorCode.UNAUTHORIZED) {
// 只有非公开API才清除token
if (!isPublicApi(url)) {
uni.removeStorageSync('token')
}
}
// 显示错误提示
handleErrorMessage(res.data.code, res.data.msg, url);
reject(res.data)
}
} else {
uni.showToast({
title: '服务器异常',
icon: 'none',
duration: 2000
})
handleErrorMessage(res.statusCode, '服务器异常', url);
reject(response)
}
},
fail(err) {
uni.showToast({
title: '网络异常',
icon: 'none',
duration: 2000
})
if (!isPublicApi(url)) {
uni.showToast({
title: '网络异常,请检查网络连接',
icon: 'none',
duration: 2000
})
}
reject(err)
},
// complete() {

View File

@ -313,11 +313,6 @@
})
break;
case 4: //
const token = uni.getStorageSync('token')
if (!token) {
this.$refs.loginRef.open()
return
}
uni.navigateTo({
url: '/pagesMy/myAttention'
})

View File

@ -22,12 +22,12 @@
<text>登录/认证</text>
</block>
<block v-else-if="userStatus === 'unverified'">
<text>您的门店未认证</text>
<!-- <text>您的门店未认证</text> -->
<text>请点击认证门店信息</text>
</block>
<block v-else-if="userStatus === 'verifying'">
<text>正在认证审核中</text>
<text>请耐心等待</text>
<!-- <text>请耐心等待</text> -->
</block>
</view>
<text class="cuIcon-right" style="color: #999999; font-size: 32rpx;"></text>

View File

@ -638,33 +638,35 @@
padding: 0 32rpx;
background: #FFFFFF;
height: 88rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
.nav-item {
position: relative;
padding: 28rpx 0;
margin: 0 48rpx;
color: #606060;
width: calc((100% - 56rpx) / 2); //
margin: 0 14rpx; //
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 400;
// margin-right: 24rpx;
color: #1A1A1A;
font-weight: normal;
transition: all 0.3s ease;
&.nav-active {
font-size: 32rpx;
color: #4E63E0;
font-weight: 600;
// padding: 32rpx;
color: #19367A;
// font-weight: 600;
}
.nav-line {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 48rpx;
height: 4rpx;
background: #19367A;
border-radius: 2rpx;
.nav-line {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 48rpx;
height: 4rpx;
background: #4E63E0;
border-radius: 2rpx;
}
}
}
}

View File

@ -10,57 +10,72 @@
</view>
</view>
</scroll-view>
<view v-if="tabCur == 0" class="my-container">
<template v-if="favoriteBeerList.length > 0">
<scroll-view style="height: 100%;" enable-flex scroll-y @scrolltolower="changeBeerPage">
<view class="beer-grid">
<view class="beer-box" v-for="(item, index) in favoriteBeerList" :key="index" @click="toBeer(item)">
<view class="cover-box">
<image :src="item.cover" class="cover" mode="aspectFill"></image>
<view class="like">
<text class="cuIcon-likefill" style="color: #4E63E0"
@click.stop="cancelFavBeer(item)"></text>
<view v-if="!isLogin" class="flex align-center justify-center" style="height: calc(100vh - 100rpx);">
<view class="flex flex-col align-center">
<text style="color: #747783;font-size: 28rpx;margin-bottom: 30rpx;">登录后查看我的关注</text>
<button class="cu-btn" style="color: #FFFFFF; background-color: #4E63E0;" @click="toLogin">去登录</button>
</view>
</view>
<template v-else>
<view v-if="!isVerified" class="flex align-center justify-center" style="height: calc(100vh - 100rpx);">
<view class="flex flex-col align-center">
<text style="color: #747783;font-size: 28rpx;margin-bottom: 30rpx;">请先完成门店认证</text>
<button class="cu-btn" style="color: #FFFFFF; background-color: #4E63E0;" @click="toVerify">去认证</button>
</view>
</view>
<template v-else>
<view v-if="tabCur == 0" class="my-container">
<template v-if="favoriteBeerList.length > 0">
<scroll-view style="height: 100%;" enable-flex scroll-y @scrolltolower="changeBeerPage">
<view class="beer-grid">
<view class="beer-box" v-for="(item, index) in favoriteBeerList" :key="index" @click="toBeer(item)">
<view class="cover-box">
<image :src="item.cover" class="cover" mode="aspectFill"></image>
<view class="like">
<text class="cuIcon-likefill" style="color: #4E63E0"
@click.stop="cancelFavBeer(item)"></text>
</view>
</view>
<view class="info-box">
<view class="title">{{ item.beerName }}</view>
<view class="rating">
<text class="score">{{ item.beerOverallRating || '5' }}</text>
<text class="reviews">({{ item.beerReviewsCount || '0' }})</text>
</view>
</view>
</view>
</view>
<view class="info-box">
<view class="title">{{ item.beerName }}</view>
<view class="rating">
<text class="score">{{ item.beerOverallRating || '5' }}</text>
<text class="reviews">({{ item.beerReviewsCount || '0' }})</text>
<view class="cu-load" :class="favoriteBeerList.length == totalBeer ? 'over' :'more'"></view>
</scroll-view>
</template>
<template v-else>
<view class="flex align-center justify-center"
style="height: 140rpx;width: 100%;color: #747783;font-size: 24rpx;">暂无关注的酒款</view>
</template>
</view>
<view v-if="tabCur == 1" class="my-brandSide">
<template v-if="favoriteBreweryList.length > 0">
<scroll-view style="height: 100%;" scroll-y="true" @scrolltolower="changeBreweryPage">
<view class="brandSide-box" v-for="(item, index) in favoriteBreweryList" :key="index"
@click="toBrand(item)">
<view class="flex align-center justify-start">
<image :src="item.brandLogo" class="logo"></image>
<view>
<view class="title">{{ item.brandName }}</view>
</view>
</view>
<view class="cu-btn radius bg-gray" @click.stop="cancelFavBrewery(item)">取消关注</view>
</view>
</view>
</view>
<view class="cu-load" :class="favoriteBeerList.length == totalBeer ? 'over' :'more'"></view>
</scroll-view>
</scroll-view>
</template>
<template v-else>
<view class="flex align-center justify-center"
style="height: 140rpx;width: 100%;color: #747783;font-size: 24rpx;">暂无关注的品牌</view>
</template>
</view>
</template>
<template v-else>
<view class="flex align-center justify-center"
style="height: 140rpx;width: 100%;color: #747783;font-size: 24rpx;">暂无关注的酒款</view>
</template>
</view>
<view v-if="tabCur == 1" class="my-brandSide">
<template v-if="favoriteBreweryList.length > 0">
<scroll-view style="height: 100%;" scroll-y="true" @scrolltolower="changeBreweryPage">
<view class="brandSide-box" v-for="(item, index) in favoriteBreweryList" :key="index"
@click="toBrand(item)">
<view class="flex align-center justify-start">
<image :src="item.brandLogo" class="logo"></image>
<view>
<view class="title">{{ item.brandName }}</view>
<!-- <view class="desc">20241223</view> -->
</view>
</view>
<view class="cu-btn radius bg-gray" @click.stop="cancelFavBrewery(item)">取消关注</view>
</view>
<!-- <view class="cu-load" :class="favoriteBreweryList.length == totalBrewery ? 'over' :'more'"></view> -->
</scroll-view>
</template>
<template v-else>
<view class="flex align-center justify-center"
style="height: 140rpx;width: 100%;color: #747783;font-size: 24rpx;">暂无关注的品牌</view>
</template>
</view>
</template>
<loginPopup ref="loginRef" @loginSuccess="loginSuccess"></loginPopup>
</view>
</template>
@ -73,9 +88,15 @@
favorBeer,
favorBrewery
} from '@/api/bar.js'
import loginPopup from '@/components/loginPopup.vue'
export default {
components: {
loginPopup
},
data() {
return {
isLogin: false,
isVerified: false,
tabCur: 0,
favoriteBeerList: [], //
favoriteBreweryList: [], //
@ -91,11 +112,60 @@
totalBrewery: 0
};
},
onShow() {
this.checkLoginStatus()
},
onLoad() {
this.getFavoriteBeerList()
this.getFavoriteBreweryList()
//
uni.$on('needLogin', () => {
this.toLogin()
})
},
onUnload() {
//
uni.$off('needLogin')
},
methods: {
//
checkLoginStatus() {
const token = uni.getStorageSync('token')
const userInfo = uni.getStorageSync('userInfo')
this.isLogin = !!token
this.isVerified = userInfo?.isVerified === 1
if (this.isLogin && this.isVerified) {
this.getFavoriteBeerList()
this.getFavoriteBreweryList()
} else {
this.favoriteBeerList = []
this.favoriteBreweryList = []
}
},
//
toLogin() {
this.$refs.loginRef.open()
},
//
toVerify() {
uni.navigateTo({
url: '/pages/index/registration'
})
},
//
loginSuccess() {
this.isLogin = true
const userInfo = uni.getStorageSync('userInfo')
this.isVerified = userInfo?.isVerified === 1
if (this.isVerified) {
this.favoriteBeerList = []
this.favoriteBreweryList = []
this.beerQuery.pageNum = 1
this.breweryQuery.pageNum = 1
this.getFavoriteBeerList()
this.getFavoriteBreweryList()
}
},
tabSelect(e) {
console.log(e)
this.tabCur = e.currentTarget.dataset.id;
@ -111,6 +181,8 @@
},
//
getFavoriteBeerList() {
if (!this.isLogin) return
listMyFavoriteBeer(this.beerQuery).then(res => {
this.totalBeer = res.total
if (res.rows && res.rows.length > 0) {
@ -118,9 +190,8 @@
this.favoriteBeerList.push(it)
})
}
// for(let i = 0; i < 29; i++) {
// this.favoriteBeerList.push(res.rows[0])
// }
}).catch(() => {
//
})
},
//
@ -133,6 +204,8 @@
},
//
getFavoriteBreweryList() {
if (!this.isLogin) return
listMyFavoriteBrewery(this.breweryQuery).then(res => {
this.totalBrewery = res.total
if (res.rows && res.rows.length > 0) {
@ -140,6 +213,8 @@
this.favoriteBreweryList.push(it)
})
}
}).catch(() => {
//
})
},
//
@ -164,52 +239,48 @@
},
//
cancelFavBeer(item) {
if (!this.isLogin) {
this.toLogin()
return
}
let data = {
beerId: item.beerId,
status: 2
}
favorBeer(data).then(res => {
// if (status == 1) {
// uni.showToast({
// title: '',
// icon: 'success'
// })
// } else {
uni.showToast({
title: '取消收藏',
icon: 'none'
})
// }
/* */
this.favoriteBeerList = []
this.beerQuery.pageNum = 1
this.getFavoriteBeerList()
}).catch(() => {
//
})
},
//
cancelFavBrewery(item) {
if (!this.isLogin) {
this.toLogin()
return
}
let data = {
breweryId: item.breweryId,
status: 2
}
favorBrewery(data).then(res => {
// if (status == 1) {
// uni.showToast({
// title: '',
// icon: 'success'
// })
// } else {
uni.showToast({
title: '取消收藏',
icon: 'none'
})
// }
this.favoriteBreweryList = []
this.breweryQuery.pageNum = 1
this.getFavoriteBreweryList()
}).catch(() => {
//
})
},
@ -222,19 +293,56 @@
background: #F9F9F9;
height: 100vh;
.tab {
color: #1A1A1A;
font-size: 32rpx;
font-weight: normal;
}
.nav {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
padding: 0 32rpx;
background: #FFFFFF;
height: 88rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
.active {
color: #4E63E0;
.tab {
position: relative;
width: calc((100% - 56rpx) / 2); //
margin: 0 14rpx; //
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: #1A1A1A;
font-weight: normal;
transition: all 0.3s ease;
&.active {
color: #4E63E0;
font-weight: 600;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 48rpx;
height: 4rpx;
background: #4E63E0;
border-radius: 2rpx;
}
}
}
}
.my-container {
padding: 28rpx 36rpx;
height: calc(100vh - 100rpx);
margin-top: 88rpx; //
height: calc(100vh - 88rpx);
.beer-grid {
display: flex;