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; const timeout = 5000;
let showModal = false let showModal = false
// 定义错误码
const ErrorCode = {
SUCCESS: 200,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
SERVER_ERROR: 500
}
// 定义公开API白名单 // 定义公开API白名单
const publicApis = [ const publicApis = [
'/beer/list', // 啤酒列表 '/beer/list', // 啤酒列表
@ -24,19 +32,60 @@ const publicApis = [
'/bar/brewery/getBreweryInfo', // 获取品牌详情 '/bar/brewery/getBreweryInfo', // 获取品牌详情
] ]
// 检查是否是公开API // 检查是否是公开API - 使用更精确的匹配
const isPublicApi = (url) => { const isPublicApi = (url) => {
// 使用更精确的匹配方式
return publicApis.some(api => { return publicApis.some(api => {
// 如果是完整路径匹配 if (api.endsWith('/')) {
if (api.includes('/')) { return url.startsWith(api);
return url.includes(api);
} }
// 如果是通配符匹配 return url === api || url.startsWith(api + '/');
return url.split('/').includes(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) => { export default (params) => {
let url = params.url; let url = params.url;
let method = params.method || "get"; let method = params.method || "get";
@ -65,50 +114,34 @@ export default (params) => {
if (res.statusCode == 200) { if (res.statusCode == 200) {
console.log(res.data, '接口返回值') console.log(res.data, '接口返回值')
if (res.data.code == 200) { if (res.data.code == ErrorCode.SUCCESS) {
resolve(res.data); 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 { } else {
// 对于公开接口的401错误不显示错误提示 // 处理401未授权错误
if (!isPublicApi(url)) { if (res.data.code == ErrorCode.UNAUTHORIZED) {
uni.showToast({ // 只有非公开API才清除token
title: res.data.msg || '请求失败', if (!isPublicApi(url)) {
icon: 'none', uni.removeStorageSync('token')
duration: 3000, }
})
} }
// 显示错误提示
handleErrorMessage(res.data.code, res.data.msg, url);
reject(res.data) reject(res.data)
} }
} else { } else {
uni.showToast({ handleErrorMessage(res.statusCode, '服务器异常', url);
title: '服务器异常',
icon: 'none',
duration: 2000
})
reject(response) reject(response)
} }
}, },
fail(err) { fail(err) {
uni.showToast({ if (!isPublicApi(url)) {
title: '网络异常', uni.showToast({
icon: 'none', title: '网络异常,请检查网络连接',
duration: 2000 icon: 'none',
}) duration: 2000
})
}
reject(err) reject(err)
}, },
// complete() { // complete() {

View File

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

View File

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

View File

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

View File

@ -10,57 +10,72 @@
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
<view v-if="tabCur == 0" class="my-container"> <view v-if="!isLogin" class="flex align-center justify-center" style="height: calc(100vh - 100rpx);">
<template v-if="favoriteBeerList.length > 0"> <view class="flex flex-col align-center">
<scroll-view style="height: 100%;" enable-flex scroll-y @scrolltolower="changeBeerPage"> <text style="color: #747783;font-size: 28rpx;margin-bottom: 30rpx;">登录后查看我的关注</text>
<view class="beer-grid"> <button class="cu-btn" style="color: #FFFFFF; background-color: #4E63E0;" @click="toLogin">去登录</button>
<view class="beer-box" v-for="(item, index) in favoriteBeerList" :key="index" @click="toBeer(item)"> </view>
<view class="cover-box"> </view>
<image :src="item.cover" class="cover" mode="aspectFill"></image> <template v-else>
<view class="like"> <view v-if="!isVerified" class="flex align-center justify-center" style="height: calc(100vh - 100rpx);">
<text class="cuIcon-likefill" style="color: #4E63E0" <view class="flex flex-col align-center">
@click.stop="cancelFavBeer(item)"></text> <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> </view>
<view class="info-box"> <view class="cu-load" :class="favoriteBeerList.length == totalBeer ? 'over' :'more'"></view>
<view class="title">{{ item.beerName }}</view> </scroll-view>
<view class="rating"> </template>
<text class="score">{{ item.beerOverallRating || '5' }}</text> <template v-else>
<text class="reviews">({{ item.beerReviewsCount || '0' }})</text> <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>
<view class="cu-btn radius bg-gray" @click.stop="cancelFavBrewery(item)">取消关注</view>
</view> </view>
</view> </scroll-view>
</view> </template>
<view class="cu-load" :class="favoriteBeerList.length == totalBeer ? 'over' :'more'"></view> <template v-else>
</scroll-view> <view class="flex align-center justify-center"
style="height: 140rpx;width: 100%;color: #747783;font-size: 24rpx;">暂无关注的品牌</view>
</template>
</view>
</template> </template>
<template v-else> </template>
<view class="flex align-center justify-center" <loginPopup ref="loginRef" @loginSuccess="loginSuccess"></loginPopup>
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>
</view> </view>
</template> </template>
@ -73,9 +88,15 @@
favorBeer, favorBeer,
favorBrewery favorBrewery
} from '@/api/bar.js' } from '@/api/bar.js'
import loginPopup from '@/components/loginPopup.vue'
export default { export default {
components: {
loginPopup
},
data() { data() {
return { return {
isLogin: false,
isVerified: false,
tabCur: 0, tabCur: 0,
favoriteBeerList: [], // favoriteBeerList: [], //
favoriteBreweryList: [], // favoriteBreweryList: [], //
@ -91,11 +112,60 @@
totalBrewery: 0 totalBrewery: 0
}; };
}, },
onShow() {
this.checkLoginStatus()
},
onLoad() { onLoad() {
this.getFavoriteBeerList() //
this.getFavoriteBreweryList() uni.$on('needLogin', () => {
this.toLogin()
})
},
onUnload() {
//
uni.$off('needLogin')
}, },
methods: { 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) { tabSelect(e) {
console.log(e) console.log(e)
this.tabCur = e.currentTarget.dataset.id; this.tabCur = e.currentTarget.dataset.id;
@ -111,6 +181,8 @@
}, },
// //
getFavoriteBeerList() { getFavoriteBeerList() {
if (!this.isLogin) return
listMyFavoriteBeer(this.beerQuery).then(res => { listMyFavoriteBeer(this.beerQuery).then(res => {
this.totalBeer = res.total this.totalBeer = res.total
if (res.rows && res.rows.length > 0) { if (res.rows && res.rows.length > 0) {
@ -118,9 +190,8 @@
this.favoriteBeerList.push(it) this.favoriteBeerList.push(it)
}) })
} }
// for(let i = 0; i < 29; i++) { }).catch(() => {
// this.favoriteBeerList.push(res.rows[0]) //
// }
}) })
}, },
// //
@ -133,6 +204,8 @@
}, },
// //
getFavoriteBreweryList() { getFavoriteBreweryList() {
if (!this.isLogin) return
listMyFavoriteBrewery(this.breweryQuery).then(res => { listMyFavoriteBrewery(this.breweryQuery).then(res => {
this.totalBrewery = res.total this.totalBrewery = res.total
if (res.rows && res.rows.length > 0) { if (res.rows && res.rows.length > 0) {
@ -140,6 +213,8 @@
this.favoriteBreweryList.push(it) this.favoriteBreweryList.push(it)
}) })
} }
}).catch(() => {
//
}) })
}, },
// //
@ -164,52 +239,48 @@
}, },
// //
cancelFavBeer(item) { cancelFavBeer(item) {
if (!this.isLogin) {
this.toLogin()
return
}
let data = { let data = {
beerId: item.beerId, beerId: item.beerId,
status: 2 status: 2
} }
favorBeer(data).then(res => { favorBeer(data).then(res => {
// if (status == 1) {
// uni.showToast({
// title: '',
// icon: 'success'
// })
// } else {
uni.showToast({ uni.showToast({
title: '取消收藏', title: '取消收藏',
icon: 'none' icon: 'none'
}) })
// }
/* */
this.favoriteBeerList = [] this.favoriteBeerList = []
this.beerQuery.pageNum = 1 this.beerQuery.pageNum = 1
this.getFavoriteBeerList() this.getFavoriteBeerList()
}).catch(() => {
//
}) })
}, },
// //
cancelFavBrewery(item) { cancelFavBrewery(item) {
if (!this.isLogin) {
this.toLogin()
return
}
let data = { let data = {
breweryId: item.breweryId, breweryId: item.breweryId,
status: 2 status: 2
} }
favorBrewery(data).then(res => { favorBrewery(data).then(res => {
// if (status == 1) {
// uni.showToast({
// title: '',
// icon: 'success'
// })
// } else {
uni.showToast({ uni.showToast({
title: '取消收藏', title: '取消收藏',
icon: 'none' icon: 'none'
}) })
// }
this.favoriteBreweryList = [] this.favoriteBreweryList = []
this.breweryQuery.pageNum = 1 this.breweryQuery.pageNum = 1
this.getFavoriteBreweryList() this.getFavoriteBreweryList()
}).catch(() => {
//
}) })
}, },
@ -222,19 +293,56 @@
background: #F9F9F9; background: #F9F9F9;
height: 100vh; height: 100vh;
.tab { .nav {
color: #1A1A1A; display: flex;
font-size: 32rpx; justify-content: center;
font-weight: normal; 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 { .tab {
color: #4E63E0; 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 { .my-container {
padding: 28rpx 36rpx; padding: 28rpx 36rpx;
height: calc(100vh - 100rpx); margin-top: 88rpx; //
height: calc(100vh - 88rpx);
.beer-grid { .beer-grid {
display: flex; display: flex;