diff --git a/api/index.js b/api/index.js
index 5151b5f..fa4434f 100644
--- a/api/index.js
+++ b/api/index.js
@@ -11,6 +11,91 @@ let isRefreshing = false
// 等待刷新完成的请求队列
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
* @param {String} refreshToken 刷新令牌
@@ -133,21 +218,36 @@ export function request(options = {}) {
// 构建完整URL(处理BASE_URL末尾斜杠问题)
const fullUrl = url.startsWith('http') ? url : `${BASE_URL}${url.startsWith('/') ? url : '/' + url}`
- // 构建请求头
+ // 构建请求头(先添加基础头,再添加传入的 header,最后添加 token,确保 token 不会被覆盖)
const requestHeader = {
'Content-Type': 'application/json',
'tenant-id': '1', // 统一添加租户ID
...header
}
- // 添加token(如果需要认证)
+ // 添加token(如果需要认证,必须在最后添加,确保不会被 header 参数覆盖)
if (needAuth) {
const token = uni.getStorageSync('token')
if (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({
url: fullUrl,
@@ -169,6 +269,12 @@ export function request(options = {}) {
if (res.data.code !== undefined) {
// 处理业务错误码401(账号未登录)
if (res.data.code === 401) {
+ // 如果已经在登录页面,不需要显示弹窗和跳转
+ if (isInLoginPage()) {
+ reject(new Error('未授权'))
+ return
+ }
+
// 显示登录弹窗
uni.showModal({
title: '提示',
@@ -184,9 +290,7 @@ export function request(options = {}) {
uni.removeStorageSync('userId')
uni.removeStorageSync('userInfo')
// 跳转到登录页面
- uni.navigateTo({
- url: '/pages/login/login'
- })
+ navigateToLogin()
}
}
})
@@ -213,64 +317,32 @@ export function request(options = {}) {
resolve(res.data)
}
} else if (res.statusCode === 401) {
- // token过期或未登录,尝试使用refreshToken刷新
- const refreshToken = uni.getStorageSync('refreshToken')
- if (refreshToken) {
- // 尝试刷新token
- refreshAccessToken(refreshToken)
- .then(() => {
- // 刷新成功,重新发起原请求
- return request(options)
- })
- .then(resolve)
- .catch(() => {
- // 刷新失败,显示登录弹窗
- 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 {
- // 没有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'
- })
- }
- }
- })
+ // HTTP 401 状态码,直接显示登录弹窗
+ // 如果已经在登录页面,不需要显示弹窗和跳转
+ if (isInLoginPage()) {
reject(new Error('未授权'))
+ return
}
+
+ 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')
+ navigateToLogin()
+ }
+ }
+ })
+ reject(new Error('未授权'))
} else if (res.statusCode >= 500) {
// 服务器错误
uni.showToast({
diff --git a/manifest.json b/manifest.json
index 8ca22dc..a2ccaed 100644
--- a/manifest.json
+++ b/manifest.json
@@ -57,7 +57,12 @@
"usingComponents" : true,
"requiredPrivateInfos" : [
"getLocation"
- ]
+ ],
+ "permission" : {
+ "scope.userLocation" : {
+ "desc" : "你的位置信息将用于小程序位置接口的效果展示"
+ }
+ }
},
"mp-alipay" : {
"usingComponents" : true
diff --git a/pages.json b/pages.json
index 84d6a36..4ed235b 100644
--- a/pages.json
+++ b/pages.json
@@ -19,6 +19,11 @@
"style": {
"navigationBarTitleText": "服务",
"navigationStyle": "custom"
+ },
+ "permission": {
+ "scope.userLocation": {
+ "desc": "你的位置信息将用于小程序位置接口的效果展示"
+ }
}
},
{
diff --git a/pages/detail/mapDetail.vue b/pages/detail/mapDetail.vue
index 8435892..2b099a2 100644
--- a/pages/detail/mapDetail.vue
+++ b/pages/detail/mapDetail.vue
@@ -32,7 +32,7 @@
diff --git a/pages/detail/serviceDetail.vue b/pages/detail/serviceDetail.vue
index 7173f9e..8dfdd21 100644
--- a/pages/detail/serviceDetail.vue
+++ b/pages/detail/serviceDetail.vue
@@ -8,7 +8,7 @@
@@ -132,6 +132,8 @@ export default {
categoryLabel: "",
shopId: null,
userInfo: {},
+ hasCheckedLogin: false, // 是否已经检查过登录状态
+ isLoadingAfterLogin: false, // 是否正在登录后重新加载
};
},
computed: {
@@ -172,6 +174,43 @@ export default {
}, 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: {
// 加载店铺数据
async loadStoreData() {
diff --git a/pages/index/index.vue b/pages/index/index.vue
index 40acc78..8228e00 100644
--- a/pages/index/index.vue
+++ b/pages/index/index.vue
@@ -1,596 +1,596 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ item.label }}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ item.title }}
- {{ item.summary }}
- 结束时间:{{ formatTime(item.endTime) }}
-
- 立即参与
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+ {{ item.summary }}
+ 结束时间:{{ formatTime(item.endTime) }}
+
+ 立即参与
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/login/login.vue b/pages/login/login.vue
index 3981003..fa5b522 100644
--- a/pages/login/login.vue
+++ b/pages/login/login.vue
@@ -138,6 +138,12 @@ export default {
const pages = getCurrentPages()
if (pages.length > 1) {
this.showBack = true
+ // 从其他页面跳转过来,说明可能有弹窗需要关闭
+ // 延迟一下,确保页面已经加载完成,弹窗会自动关闭
+ setTimeout(() => {
+ // 尝试隐藏可能存在的弹窗(虽然 uni 没有直接关闭 showModal 的 API)
+ // 但页面跳转后,弹窗应该会自动关闭
+ }, 100)
}
this.getSystemInfo()
},
@@ -186,9 +192,16 @@ export default {
})
// 登录成功,保存token(兼容不同的响应格式)
+ console.log('[登录] 登录接口返回数据:', res)
const token = res?.accessToken || res?.token || res?.data?.accessToken || res?.data?.token
if (token) {
uni.setStorageSync('token', token)
+ console.log('[登录] Token 已保存,长度:', token.length)
+ // 验证保存是否成功
+ const savedToken = uni.getStorageSync('token')
+ console.log('[登录] 验证保存的 token:', savedToken ? '成功' : '失败')
+ } else {
+ console.error('[登录] 未找到 token,返回数据:', res)
}
// 保存refreshToken(用于刷新accessToken)
@@ -235,19 +248,17 @@ export default {
icon: 'success'
})
- // 延迟跳转,让用户看到成功提示
+ // 延迟跳转,让用户看到成功提示,统一跳转到首页
setTimeout(() => {
- // 检查是否有返回路径
- const pages = getCurrentPages()
- if (pages.length > 1) {
- // 有上一页,返回上一页
- uni.navigateBack()
- } else {
- // 没有上一页,跳转到首页
- uni.switchTab({
- url: '/pages/index/index'
- })
- }
+ uni.switchTab({
+ url: '/pages/index/index',
+ fail: () => {
+ // 如果 switchTab 失败(可能不在 tabBar 页面),使用 reLaunch
+ uni.reLaunch({
+ url: '/pages/index/index'
+ })
+ }
+ })
}, 1500)
} catch (error) {
console.error('登录失败:', error)
@@ -305,12 +316,16 @@ export default {
// 登录成功,保存token
// 根据接口返回:{ code: 0, data: { accessToken, refreshToken, expiresTime, userId, ... } }
// 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
if (token) {
uni.setStorageSync('token', token)
- console.log('Token 已保存:', token)
+ console.log('[登录] Token 已保存,长度:', token.length)
+ // 验证保存是否成功
+ const savedToken = uni.getStorageSync('token')
+ console.log('[登录] 验证保存的 token:', savedToken ? '成功' : '失败')
} else {
- console.error('未找到 token,返回数据:', res)
+ console.error('[登录] 未找到 token,返回数据:', res)
}
// 保存refreshToken(用于刷新accessToken)
@@ -358,16 +373,17 @@ export default {
icon: 'success'
})
- // 延迟跳转
+ // 延迟跳转,让用户看到成功提示,统一跳转到首页
setTimeout(() => {
- const pages = getCurrentPages()
- if (pages.length > 1) {
- uni.navigateBack()
- } else {
- uni.switchTab({
- url: '/pages/index/index'
- })
- }
+ uni.switchTab({
+ url: '/pages/index/index',
+ fail: () => {
+ // 如果 switchTab 失败(可能不在 tabBar 页面),使用 reLaunch
+ uni.reLaunch({
+ url: '/pages/index/index'
+ })
+ }
+ })
}, 1500)
} catch (error) {
console.error('一键登录失败:', error)
diff --git a/pages/service/service.vue b/pages/service/service.vue
index f754614..1189760 100644
--- a/pages/service/service.vue
+++ b/pages/service/service.vue
@@ -104,7 +104,7 @@
src="/static/service/location-icon.png"
mode="aspectFill"
>
- {{ item.distance || 0 }}km
+ {{ formatDistance(item.distance) }}
@@ -222,7 +222,7 @@ export default {
onLoad() {
this.getSystemInfo();
this.getServiceCategoryFun(); // 获取服务分类
- // this.checkAndRequestLocation(); // 检查并请求位置权限
+ this.requestUserLocation(); // 请求用户位置授权并获取位置
},
onShow() {
// 每次显示页面时刷新数据
@@ -242,6 +242,15 @@ export default {
}
},
methods: {
+ // 格式化距离显示:将米转换成千米
+ formatDistance(distanceInMeters) {
+ if (!distanceInMeters || distanceInMeters === 0) {
+ return '0km';
+ }
+ // 将米转换成千米,保留1位小数
+ const distanceInKm = (distanceInMeters / 1000).toFixed(1);
+ return `${distanceInKm}km`;
+ },
// 获取系统信息
getSystemInfo() {
const systemInfo = uni.getSystemInfoSync();
@@ -318,8 +327,8 @@ export default {
this.loadServiceList();
},
- // 检查并请求位置权限
- checkAndRequestLocation() {
+ // 请求用户位置授权并获取位置(使用 wx.getLocation - 精确位置)
+ requestUserLocation() {
// 先检查是否已有存储的位置信息
const savedLocation = uni.getStorageSync("userLocation");
if (savedLocation && savedLocation.latitude && savedLocation.longitude) {
@@ -327,53 +336,100 @@ export default {
return;
}
- // 如果没有位置信息,请求授权并获取位置
- uni.authorize({
- scope: "scope.userLocation",
- success: () => {
- // 授权成功,获取位置
- this.getUserLocation();
- },
- fail: () => {
- // 授权失败,提示用户
- uni.showModal({
- title: "位置权限",
- content: "需要获取您的位置信息以提供附近店铺服务,是否前往设置开启?",
- confirmText: "去设置",
- cancelText: "取消",
- success: (res) => {
- if (res.confirm) {
- uni.openSetting({
- success: (settingRes) => {
- if (settingRes.authSetting["scope.userLocation"]) {
- // 用户开启了位置权限,获取位置
- this.getUserLocation();
+ // 获取位置的统一方法
+ 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({
+ scope: "scope.userLocation",
+ success: () => {
+ getLocation();
+ },
+ fail: () => {
+ // 授权失败,提示用户
+ uni.showModal({
+ title: "位置权限",
+ content: "需要获取您的位置信息以提供附近店铺服务,是否前往设置开启?",
+ confirmText: "去设置",
+ cancelText: "取消",
+ success: (modalRes) => {
+ if (modalRes.confirm) {
+ uni.openSetting({
+ success: (settingRes) => {
+ if (settingRes.authSetting["scope.userLocation"]) {
+ getLocation();
+ }
+ },
+ });
}
},
});
- }
- },
- });
- },
- });
- },
-
- // 获取用户当前位置
- getUserLocation() {
- uni.getLocation({
- type: "gcj02",
- success: (res) => {
- const location = {
- latitude: res.latitude,
- longitude: res.longitude,
- };
- // 存储位置信息
- uni.setStorageSync("userLocation", location);
- },
- fail: (err) => {
- console.error("获取位置失败:", err);
+ },
+ });
+ }
+ },
+ fail: () => {
+ // 获取设置失败,直接尝试获取位置
+ getLocation();
},
});
+ // #endif
+
+ // #ifndef MP-WEIXIN
+ // 非微信小程序环境,直接获取位置
+ getLocation();
+ // #endif
},
// 选择位置/距离
@@ -481,9 +537,16 @@ export default {
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) {
- params.distance = this.selectedDistance;
+ params.distance = this.selectedDistance * 1000; // 将 km 转换成 m
}
res = await getGuildStorePage(params);
@@ -503,6 +566,9 @@ export default {
const newList = res.list || [];
this.total = res.total || 0;
+ // 处理列表数据:将接口返回的 distance(米)转换成千米用于显示
+ // 注意:这里不修改原始数据,只在显示时转换
+
if (isLoadMore) {
// 加载更多,追加数据
this.serviceList = [...this.serviceList, ...newList];
diff --git a/pages/webview/webview.vue b/pages/webview/webview.vue
index 5e6cf18..b86999f 100644
--- a/pages/webview/webview.vue
+++ b/pages/webview/webview.vue
@@ -78,3 +78,4 @@ export default {
+