fix:授权用户定位

cz_dev
wk 2026-01-14 18:55:42 +08:00
parent 15b9d11157
commit ff60fdf732
9 changed files with 935 additions and 731 deletions

View File

@ -11,6 +11,91 @@ let isRefreshing = false
// 等待刷新完成的请求队列 // 等待刷新完成的请求队列
let refreshSubscribers = [] let refreshSubscribers = []
/**
* 检查当前是否在登录页面
* @returns {Boolean} 是否在登录页面
*/
function isInLoginPage() {
try {
const pages = getCurrentPages()
if (pages.length === 0) return false
const currentPage = pages[pages.length - 1]
const currentRoute = currentPage ? currentPage.route : ''
return currentRoute === 'pages/login/login'
} catch (error) {
console.error('检查登录页面状态失败:', error)
return false
}
}
/**
* 检查是否刚刚从登录页面返回页面栈中有登录页面
* @returns {Boolean} 是否刚刚从登录页面返回
*/
function isJustBackFromLogin() {
try {
const pages = getCurrentPages()
if (pages.length <= 1) return false
// 检查页面栈中是否有登录页面(除了当前页面)
const hasLoginPage = pages.some((page, index) => {
// 排除当前页面(最后一个)
if (index === pages.length - 1) return false
const route = page ? page.route : ''
return route === 'pages/login/login'
})
return hasLoginPage
} catch (error) {
console.error('检查登录页面返回状态失败:', error)
return false
}
}
/**
* 跳转到登录页面如果不在登录页面才跳转
* 如果已经在登录页面则不进行跳转
* 如果多次被弹出退出时直接回到根目录
*/
function navigateToLogin() {
// 如果已经在登录页面,不需要再次跳转
if (isInLoginPage()) {
console.log('已在登录页面,无需重复跳转')
return
}
// 获取当前页面栈
const pages = getCurrentPages()
// 如果页面栈中有多个页面,且包含登录页面,说明可能是多次弹出
// 使用 reLaunch 直接回到根目录(首页),然后跳转到登录页
if (pages.length > 1) {
// 检查页面栈中是否有登录页面
const hasLoginPage = pages.some(page => {
const route = page ? page.route : ''
return route === 'pages/login/login'
})
if (hasLoginPage) {
// 多次被弹出,直接回到根目录
uni.reLaunch({
url: '/pages/login/login'
})
return
}
}
// 正常情况,跳转到登录页面
uni.navigateTo({
url: '/pages/login/login',
fail: (err) => {
// 如果 navigateTo 失败(可能因为页面栈已满),使用 reLaunch
console.warn('navigateTo 失败,使用 reLaunch:', err)
uni.reLaunch({
url: '/pages/login/login'
})
}
})
}
/** /**
* 刷新token * 刷新token
* @param {String} refreshToken 刷新令牌 * @param {String} refreshToken 刷新令牌
@ -133,21 +218,36 @@ export function request(options = {}) {
// 构建完整URL处理BASE_URL末尾斜杠问题 // 构建完整URL处理BASE_URL末尾斜杠问题
const fullUrl = url.startsWith('http') ? url : `${BASE_URL}${url.startsWith('/') ? url : '/' + url}` const fullUrl = url.startsWith('http') ? url : `${BASE_URL}${url.startsWith('/') ? url : '/' + url}`
// 构建请求头 // 构建请求头(先添加基础头,再添加传入的 header最后添加 token确保 token 不会被覆盖)
const requestHeader = { const requestHeader = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'tenant-id': '1', // 统一添加租户ID 'tenant-id': '1', // 统一添加租户ID
...header ...header
} }
// 添加token如果需要认证 // 添加token如果需要认证,必须在最后添加,确保不会被 header 参数覆盖
if (needAuth) { if (needAuth) {
const token = uni.getStorageSync('token') const token = uni.getStorageSync('token')
if (token) { if (token) {
requestHeader['Authorization'] = `Bearer ${token}` requestHeader['Authorization'] = `Bearer ${token}`
console.log('[API] 请求已添加 Authorization 头:', url)
console.log('[API] Token 长度:', token.length, 'Token 前10位:', token.substring(0, 10))
} else {
console.error('[API] 需要认证但未找到 token请求可能失败:', url)
console.error('[API] 当前存储的所有 key:', uni.getStorageInfoSync().keys)
// 即使没有 token也尝试发起请求让后端返回 401
} }
} }
// 调试:打印请求信息
console.log('[API] 发起请求:', {
url: fullUrl,
method: method.toUpperCase(),
needAuth: needAuth,
hasAuthHeader: !!requestHeader['Authorization'],
headers: Object.keys(requestHeader)
})
// 发起请求 // 发起请求
uni.request({ uni.request({
url: fullUrl, url: fullUrl,
@ -169,6 +269,12 @@ export function request(options = {}) {
if (res.data.code !== undefined) { if (res.data.code !== undefined) {
// 处理业务错误码401账号未登录 // 处理业务错误码401账号未登录
if (res.data.code === 401) { if (res.data.code === 401) {
// 如果已经在登录页面,不需要显示弹窗和跳转
if (isInLoginPage()) {
reject(new Error('未授权'))
return
}
// 显示登录弹窗 // 显示登录弹窗
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
@ -184,9 +290,7 @@ export function request(options = {}) {
uni.removeStorageSync('userId') uni.removeStorageSync('userId')
uni.removeStorageSync('userInfo') uni.removeStorageSync('userInfo')
// 跳转到登录页面 // 跳转到登录页面
uni.navigateTo({ navigateToLogin()
url: '/pages/login/login'
})
} }
} }
}) })
@ -213,18 +317,13 @@ export function request(options = {}) {
resolve(res.data) resolve(res.data)
} }
} else if (res.statusCode === 401) { } else if (res.statusCode === 401) {
// token过期或未登录尝试使用refreshToken刷新 // HTTP 401 状态码,直接显示登录弹窗
const refreshToken = uni.getStorageSync('refreshToken') // 如果已经在登录页面,不需要显示弹窗和跳转
if (refreshToken) { if (isInLoginPage()) {
// 尝试刷新token reject(new Error('未授权'))
refreshAccessToken(refreshToken) return
.then(() => { }
// 刷新成功,重新发起原请求
return request(options)
})
.then(resolve)
.catch(() => {
// 刷新失败,显示登录弹窗
const errorMsg = res.data?.msg || res.data?.message || '账号未登录,请前往登录' const errorMsg = res.data?.msg || res.data?.message || '账号未登录,请前往登录'
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
@ -239,38 +338,11 @@ export function request(options = {}) {
uni.removeStorageSync('tokenExpiresTime') uni.removeStorageSync('tokenExpiresTime')
uni.removeStorageSync('userId') uni.removeStorageSync('userId')
uni.removeStorageSync('userInfo') uni.removeStorageSync('userInfo')
uni.navigateTo({ navigateToLogin()
url: '/pages/login/login'
})
} }
} }
}) })
reject(new Error('未授权')) reject(new Error('未授权'))
})
} else {
// 没有refreshToken显示登录弹窗
const errorMsg = res.data?.msg || res.data?.message || '账号未登录,请前往登录'
uni.showModal({
title: '提示',
content: errorMsg,
showCancel: false,
confirmText: '去登录',
success: (modalRes) => {
if (modalRes.confirm) {
// 清除token跳转到登录页
uni.removeStorageSync('token')
uni.removeStorageSync('refreshToken')
uni.removeStorageSync('tokenExpiresTime')
uni.removeStorageSync('userId')
uni.removeStorageSync('userInfo')
uni.navigateTo({
url: '/pages/login/login'
})
}
}
})
reject(new Error('未授权'))
}
} else if (res.statusCode >= 500) { } else if (res.statusCode >= 500) {
// 服务器错误 // 服务器错误
uni.showToast({ uni.showToast({

View File

@ -57,7 +57,12 @@
"usingComponents" : true, "usingComponents" : true,
"requiredPrivateInfos" : [ "requiredPrivateInfos" : [
"getLocation" "getLocation"
] ],
"permission" : {
"scope.userLocation" : {
"desc" : "你的位置信息将用于小程序位置接口的效果展示"
}
}
}, },
"mp-alipay" : { "mp-alipay" : {
"usingComponents" : true "usingComponents" : true

View File

@ -19,6 +19,11 @@
"style": { "style": {
"navigationBarTitleText": "服务", "navigationBarTitleText": "服务",
"navigationStyle": "custom" "navigationStyle": "custom"
},
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示"
}
} }
}, },
{ {

View File

@ -32,7 +32,7 @@
<view class="store-image-wrapper"> <view class="store-image-wrapper">
<image <image
class="store-image" class="store-image"
:src="storeInfo.coverUrl || '/static/service/store-default.png'" :src="storeInfo.coverUrl"
mode="aspectFill" mode="aspectFill"
></image> ></image>
</view> </view>

View File

@ -8,7 +8,7 @@
<view class="store-brand"> <view class="store-brand">
<image <image
class="brand-image" class="brand-image"
:src="storeInfo.coverUrl || '/static/service/store-default.png'" :src="storeInfo.coverUrl"
mode="aspectFill" mode="aspectFill"
></image> ></image>
</view> </view>
@ -132,6 +132,8 @@ export default {
categoryLabel: "", categoryLabel: "",
shopId: null, shopId: null,
userInfo: {}, userInfo: {},
hasCheckedLogin: false, //
isLoadingAfterLogin: false, //
}; };
}, },
computed: { computed: {
@ -172,6 +174,43 @@ export default {
}, 1500); }, 1500);
} }
}, },
onShow() {
//
//
const token = uni.getStorageSync('token');
const newUserInfo = uni.getStorageSync('userInfo') || {};
//
if (token && (!this.userInfo || !this.userInfo.id) && newUserInfo && newUserInfo.id) {
this.userInfo = newUserInfo;
this.isLoadingAfterLogin = true; //
// 401
uni.setStorageSync('justBackFromLogin', true);
// 5
setTimeout(() => {
uni.removeStorageSync('justBackFromLogin');
}, 5000);
// token
setTimeout(() => {
// ID
if (this.shopId) {
this.loadStoreData();
}
//
setTimeout(() => {
this.isLoadingAfterLogin = false;
}, 1000);
}, 500); //
} else if (token) {
//
this.userInfo = newUserInfo;
}
//
this.hasCheckedLogin = true;
},
methods: { methods: {
// //
async loadStoreData() { async loadStoreData() {

View File

@ -138,6 +138,12 @@ export default {
const pages = getCurrentPages() const pages = getCurrentPages()
if (pages.length > 1) { if (pages.length > 1) {
this.showBack = true this.showBack = true
//
//
setTimeout(() => {
// uni showModal API
//
}, 100)
} }
this.getSystemInfo() this.getSystemInfo()
}, },
@ -186,9 +192,16 @@ export default {
}) })
// token // token
console.log('[登录] 登录接口返回数据:', res)
const token = res?.accessToken || res?.token || res?.data?.accessToken || res?.data?.token const token = res?.accessToken || res?.token || res?.data?.accessToken || res?.data?.token
if (token) { if (token) {
uni.setStorageSync('token', token) uni.setStorageSync('token', token)
console.log('[登录] Token 已保存,长度:', token.length)
//
const savedToken = uni.getStorageSync('token')
console.log('[登录] 验证保存的 token:', savedToken ? '成功' : '失败')
} else {
console.error('[登录] 未找到 token返回数据:', res)
} }
// refreshTokenaccessToken // refreshTokenaccessToken
@ -235,19 +248,17 @@ export default {
icon: 'success' icon: 'success'
}) })
// //
setTimeout(() => { setTimeout(() => {
//
const pages = getCurrentPages()
if (pages.length > 1) {
//
uni.navigateBack()
} else {
//
uni.switchTab({ uni.switchTab({
url: '/pages/index/index',
fail: () => {
// switchTab tabBar 使 reLaunch
uni.reLaunch({
url: '/pages/index/index' url: '/pages/index/index'
}) })
} }
})
}, 1500) }, 1500)
} catch (error) { } catch (error) {
console.error('登录失败:', error) console.error('登录失败:', error)
@ -305,12 +316,16 @@ export default {
// token // token
// { code: 0, data: { accessToken, refreshToken, expiresTime, userId, ... } } // { code: 0, data: { accessToken, refreshToken, expiresTime, userId, ... } }
// request code === 0 res.data.data || res.data res data // request code === 0 res.data.data || res.data res data
console.log('[登录] 登录接口返回数据:', res)
const token = res?.accessToken || res?.data?.accessToken || res?.token || res?.data?.token const token = res?.accessToken || res?.data?.accessToken || res?.token || res?.data?.token
if (token) { if (token) {
uni.setStorageSync('token', token) uni.setStorageSync('token', token)
console.log('Token 已保存:', token) console.log('[登录] Token 已保存,长度:', token.length)
//
const savedToken = uni.getStorageSync('token')
console.log('[登录] 验证保存的 token:', savedToken ? '成功' : '失败')
} else { } else {
console.error('未找到 token返回数据:', res) console.error('[登录] 未找到 token返回数据:', res)
} }
// refreshTokenaccessToken // refreshTokenaccessToken
@ -358,16 +373,17 @@ export default {
icon: 'success' icon: 'success'
}) })
// //
setTimeout(() => { setTimeout(() => {
const pages = getCurrentPages()
if (pages.length > 1) {
uni.navigateBack()
} else {
uni.switchTab({ uni.switchTab({
url: '/pages/index/index',
fail: () => {
// switchTab tabBar 使 reLaunch
uni.reLaunch({
url: '/pages/index/index' url: '/pages/index/index'
}) })
} }
})
}, 1500) }, 1500)
} catch (error) { } catch (error) {
console.error('一键登录失败:', error) console.error('一键登录失败:', error)

View File

@ -104,7 +104,7 @@
src="/static/service/location-icon.png" src="/static/service/location-icon.png"
mode="aspectFill" mode="aspectFill"
></image> ></image>
<text class="distance-text">{{ item.distance || 0 }}km</text> <text class="distance-text">{{ formatDistance(item.distance) }}</text>
</view> </view>
</view> </view>
@ -222,7 +222,7 @@ export default {
onLoad() { onLoad() {
this.getSystemInfo(); this.getSystemInfo();
this.getServiceCategoryFun(); // this.getServiceCategoryFun(); //
// this.checkAndRequestLocation(); // this.requestUserLocation(); //
}, },
onShow() { onShow() {
// //
@ -242,6 +242,15 @@ export default {
} }
}, },
methods: { methods: {
//
formatDistance(distanceInMeters) {
if (!distanceInMeters || distanceInMeters === 0) {
return '0km';
}
// 1
const distanceInKm = (distanceInMeters / 1000).toFixed(1);
return `${distanceInKm}km`;
},
// //
getSystemInfo() { getSystemInfo() {
const systemInfo = uni.getSystemInfoSync(); const systemInfo = uni.getSystemInfoSync();
@ -318,8 +327,8 @@ export default {
this.loadServiceList(); this.loadServiceList();
}, },
// // 使 wx.getLocation -
checkAndRequestLocation() { requestUserLocation() {
// //
const savedLocation = uni.getStorageSync("userLocation"); const savedLocation = uni.getStorageSync("userLocation");
if (savedLocation && savedLocation.latitude && savedLocation.longitude) { if (savedLocation && savedLocation.latitude && savedLocation.longitude) {
@ -327,12 +336,65 @@ export default {
return; return;
} }
// //
const getLocation = () => {
uni.getLocation({
type: "gcj02", // 使 gcj02
success: (res) => {
console.log("获取位置成功1:", res);
const location = {
latitude: res.latitude,
longitude: res.longitude,
};
//
uni.setStorageSync("userLocation", location);
// 使
if (this.isStoreCategory() && this.currentCategory !== null) {
this.pageNo = 1;
this.serviceList = [];
this.hasMore = true;
this.loadServiceList();
}
},
fail: (err) => {
console.error("获取位置失败:", err);
},
});
};
//
// #ifdef MP-WEIXIN
uni.getSetting({
success: (res) => {
if (res.authSetting["scope.userLocation"] === false) {
//
uni.showModal({
title: "位置权限",
content: "需要获取您的位置信息以提供附近店铺服务,是否前往设置开启?",
confirmText: "去设置",
cancelText: "取消",
success: (modalRes) => {
if (modalRes.confirm) {
uni.openSetting({
success: (settingRes) => {
if (settingRes.authSetting["scope.userLocation"]) {
getLocation();
}
},
});
}
},
});
} else if (res.authSetting["scope.userLocation"] === true) {
//
getLocation();
} else {
//
uni.authorize({ uni.authorize({
scope: "scope.userLocation", scope: "scope.userLocation",
success: () => { success: () => {
// getLocation();
this.getUserLocation();
}, },
fail: () => { fail: () => {
// //
@ -341,13 +403,12 @@ export default {
content: "需要获取您的位置信息以提供附近店铺服务,是否前往设置开启?", content: "需要获取您的位置信息以提供附近店铺服务,是否前往设置开启?",
confirmText: "去设置", confirmText: "去设置",
cancelText: "取消", cancelText: "取消",
success: (res) => { success: (modalRes) => {
if (res.confirm) { if (modalRes.confirm) {
uni.openSetting({ uni.openSetting({
success: (settingRes) => { success: (settingRes) => {
if (settingRes.authSetting["scope.userLocation"]) { if (settingRes.authSetting["scope.userLocation"]) {
// getLocation();
this.getUserLocation();
} }
}, },
}); });
@ -356,24 +417,19 @@ export default {
}); });
}, },
}); });
}
}, },
fail: () => {
//
getLocation();
},
});
// #endif
// // #ifndef MP-WEIXIN
getUserLocation() { //
uni.getLocation({ getLocation();
type: "gcj02", // #endif
success: (res) => {
const location = {
latitude: res.latitude,
longitude: res.longitude,
};
//
uni.setStorageSync("userLocation", location);
},
fail: (err) => {
console.error("获取位置失败:", err);
},
});
}, },
// / // /
@ -481,9 +537,16 @@ export default {
name: this.searchKeyword, name: this.searchKeyword,
}; };
// distance //
const savedLocation = uni.getStorageSync("userLocation");
if (savedLocation && savedLocation.latitude && savedLocation.longitude) {
params.myLatitude = savedLocation.latitude;
params.myLongitude = savedLocation.longitude;
}
// distance
if (this.selectedDistance !== null) { if (this.selectedDistance !== null) {
params.distance = this.selectedDistance; params.distance = this.selectedDistance * 1000; // km m
} }
res = await getGuildStorePage(params); res = await getGuildStorePage(params);
@ -503,6 +566,9 @@ export default {
const newList = res.list || []; const newList = res.list || [];
this.total = res.total || 0; this.total = res.total || 0;
// distance
//
if (isLoadMore) { if (isLoadMore) {
// //
this.serviceList = [...this.serviceList, ...newList]; this.serviceList = [...this.serviceList, ...newList];

View File

@ -78,3 +78,4 @@ export default {