| | |
| | | import getConfig from "./config.js"; |
| | | const { baseURL } = getConfig(); |
| | | // @/utils/request.js |
| | | let isRedirectingToLogin = false; |
| | | |
| | | /** |
| | | * 显示Toast提示(兼容uview-plus) |
| | | * @param {string} message 提示内容 |
| | | */ |
| | | // 全局 SSO 处理标记(挂载到 uni 上,便于跨模块共享) |
| | | uni.__isSSOHandling = false; |
| | | |
| | | const showToast = (message) => { |
| | | if (uni.$u?.toast) { |
| | | uni.$u.toast(message); |
| | | } else { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | uni.showToast({ title: message, icon: "none" }); |
| | | } |
| | | }; |
| | | console.log(baseURL,'baseURL'); |
| | | |
| | | // 基础配置 - 从环境变量中获取baseURL |
| | | const config = { |
| | | baseURL: "/api", // 使用环境变量中的配置 |
| | | baseURL: baseURL, |
| | | timeout: 60000, |
| | | header: { |
| | | "Content-Type": "application/json", |
| | | "X-Business-System": "medical-system", |
| | | }, |
| | | // 新增配置项 |
| | | loginPage: "/pages/login/Login", // 登录页路径 |
| | | tokenExpiredCode: 401, // 后端返回的Token过期状态码 |
| | | noPermissionCode: 403, // 无权限状态码 |
| | | // 添加白名单配置(不需要校验token的接口) |
| | | whiteList: [ |
| | | "/login", // 登录接口 |
| | | "/getToken", |
| | | // '/dingtalk/auth/login', // 钉钉授权登录接口 |
| | | // '/dingtalk/auth/bind' // 钉钉授权登录绑定接口 |
| | | ], |
| | | loginPage: "/pages/login/Login", |
| | | tokenExpiredCode: 401, |
| | | noPermissionCode: 403, |
| | | whiteList: ["/login", "/getToken", "/GiLink/getCode"], |
| | | }; |
| | | |
| | | /** |
| | | * 检查请求是否在白名单中 |
| | | * @param {string} url 请求URL |
| | | */ |
| | | const isInWhiteList = (url) => { |
| | | // 提取相对路径(移除baseURL)并忽略查询参数 |
| | | const relativeUrl = url.replace(config.baseURL, "").split("?")[0]; |
| | | return config.whiteList.some((path) => { |
| | | // 处理通配符情况 |
| | | if (path.endsWith("/")) { |
| | | return relativeUrl.startsWith(path); |
| | | } |
| | | // 精确匹配路径 |
| | | if (path.endsWith("/")) return relativeUrl.startsWith(path); |
| | | return relativeUrl === path; |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * 跳转到登录页 |
| | | */ |
| | | const navigateToLogin = () => { |
| | | uni.redirectTo({ |
| | | url: |
| | | config.loginPage + |
| | | "?redirect=" + |
| | | encodeURIComponent(getCurrentPagePath()), |
| | | }); |
| | | // SSO 处理中,不重复跳转 |
| | | if (isRedirectingToLogin || uni.__isSSOHandling) { |
| | | console.log("正在跳转登录页或SSO处理中,跳过"); |
| | | return; |
| | | } |
| | | |
| | | isRedirectingToLogin = true; |
| | | |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | const isLoginPage = |
| | | currentPage && |
| | | currentPage.route && |
| | | currentPage.route.includes("login/Login"); |
| | | |
| | | if (isLoginPage) { |
| | | console.log("当前已在登录页,不跳转"); |
| | | isRedirectingToLogin = false; |
| | | return; |
| | | } |
| | | |
| | | console.log("跳转到登录页"); |
| | | setTimeout(() => { |
| | | uni.redirectTo({ |
| | | url: |
| | | config.loginPage + |
| | | "?redirect=" + |
| | | encodeURIComponent(getCurrentPagePath()), |
| | | success: () => { |
| | | console.log("跳转登录页成功"); |
| | | isRedirectingToLogin = false; |
| | | }, |
| | | fail: () => { |
| | | console.log("跳转登录页失败"); |
| | | isRedirectingToLogin = false; |
| | | }, |
| | | }); |
| | | }, 100); |
| | | }; |
| | | |
| | | /** |
| | | * 获取当前页面路径(用于登录后跳回) |
| | | */ |
| | | const getCurrentPagePath = () => { |
| | | const pages = getCurrentPages(); |
| | | return pages[pages.length - 1]?.route || ""; |
| | | }; |
| | | |
| | | /** |
| | | * 检查Token是否过期 |
| | | * @param {number} statusCode HTTP状态码 |
| | | */ |
| | | const checkTokenExpired = (statusCode) => { |
| | | if (statusCode === config.tokenExpiredCode) { |
| | | showToast("登录已过期,请重新登录"); |
| | |
| | | return false; |
| | | }; |
| | | |
| | | /** |
| | | * 请求拦截器 |
| | | */ |
| | | const requestInterceptor = (options) => { |
| | | const token = uni.getStorageSync("token"); |
| | | |
| | | // 如果请求在白名单中,直接放行 |
| | | if (isInWhiteList(options.url)) { |
| | | console.log("白名单接口,跳过token校验:", options.url); |
| | | return options; |
| | | } |
| | | |
| | | // 如果未登录且不是白名单接口,跳转登录 |
| | | // console.log(token,'token'); |
| | | |
| | | if (!token) { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | const isLoginPage = |
| | | currentPage && |
| | | currentPage.route && |
| | | (currentPage.route.includes("login/Login") || |
| | | currentPage.route.includes("login/DingTalkLogin")); |
| | | |
| | | if (isLoginPage) { |
| | | console.log("当前是登录页,允许无token请求"); |
| | | return options; |
| | | } |
| | | |
| | | console.log("未登录且不在登录页,跳转到登录页"); |
| | | navigateToLogin(); |
| | | throw new Error("未登录"); |
| | | } |
| | | |
| | | // 添加Token到Header |
| | | options.header = { |
| | | ...options.header, |
| | | Authorization: `Bearer ${token}`, |
| | | }; |
| | | |
| | | return options; |
| | | }; |
| | | |
| | | /** |
| | | * 响应拦截器 |
| | | */ |
| | | const responseInterceptor = (response) => { |
| | | const { statusCode, data } = response; |
| | | |
| | | // Token过期处理 |
| | | if (checkTokenExpired(statusCode)) { |
| | | return Promise.reject(data); |
| | | } |
| | | |
| | | // 无权限处理 |
| | | if (checkTokenExpired(statusCode)) return Promise.reject(data); |
| | | if (statusCode === config.noPermissionCode) { |
| | | showToast("无权限访问"); |
| | | return Promise.reject(data); |
| | | } |
| | | |
| | | // 其他错误状态码 |
| | | if (statusCode !== 200) { |
| | | showToast(`请求失败: ${statusCode}`); |
| | | return Promise.reject(data); |
| | | } |
| | | |
| | | // 接口自定义错误码处理(假设data.code为0表示成功) |
| | | if (data?.code !== 200) { |
| | | showToast(data?.msg || "操作失败"); |
| | | return Promise.reject(data); |
| | | } |
| | | |
| | | return data; // 返回实际业务数据 |
| | | return data; |
| | | }; |
| | | |
| | | /** |
| | | * 核心请求方法 |
| | | */ |
| | | const request = async (options) => { |
| | | try { |
| | | // 合并配置 |
| | | options = { |
| | | ...config, |
| | | ...options, |
| | | url: config.baseURL + options.url, |
| | | header: { ...config.header, ...options.header }, |
| | | }; |
| | | |
| | | // 请求拦截 |
| | | options = await requestInterceptor(options); |
| | | |
| | | // 发起请求 |
| | | return new Promise((resolve, reject) => { |
| | | uni.request({ |
| | | ...options, |
| | | success: (res) => { |
| | | resolve(responseInterceptor(res)); |
| | | }, |
| | | success: (res) => resolve(responseInterceptor(res)), |
| | | fail: (err) => { |
| | | showToast("网络错误,请重试"); |
| | | reject(err); |
| | |
| | | } |
| | | }; |
| | | |
| | | // 快捷方法封装 |
| | | const http = { |
| | | get(url, data = {}, options = {}) { |
| | | return request({ url, data, method: "GET", ...options }); |
| | |
| | | delete(url, data = {}, options = {}) { |
| | | return request({ url, data, method: "DELETE", ...options }); |
| | | }, |
| | | // 新增:上传文件方法 |
| | | upload(url, filePath, name = "file", formData = {}) { |
| | | return request({ |
| | | url, |
| | |
| | | }, |
| | | }; |
| | | |
| | | // 挂载到全局 |
| | | uni.$uapi = http; |
| | | |
| | | export default http; |