WXL
12 小时以前 05c363fdd7ab04e3bd9a753e2c5d5bfff04d681c
utils/request.js
@@ -1,210 +1,196 @@
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)
    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',
    "Content-Type": "application/json",
    "X-Business-System": "medical-system",
  },
  // 新增配置项
  loginPage: '/pages/login/Login', // 登录页路径
  tokenExpiredCode: 401, // 后端返回的Token过期状态码
  noPermissionCode: 403, // 无权限状态码
  // 添加白名单配置(不需要校验token的接口)
  whiteList: [
    '/login', // 登录接口
    // '/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)
    }
    // 精确匹配路径
    return relativeUrl === path
  })
}
  const relativeUrl = url.replace(config.baseURL, "").split("?")[0];
  return config.whiteList.some((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 || ''
}
  const pages = getCurrentPages();
  return pages[pages.length - 1]?.route || "";
};
/**
 * 检查Token是否过期
 * @param {number} statusCode HTTP状态码
 */
const checkTokenExpired = (statusCode) => {
  if (statusCode === config.tokenExpiredCode) {
    showToast('登录已过期,请重新登录')
    navigateToLogin()
    return true
    showToast("登录已过期,请重新登录");
    navigateToLogin();
    return true;
  }
  return false
}
  return false;
};
/**
 * 请求拦截器
 */
const requestInterceptor = (options) => {
  const token = uni.getStorageSync('token')
  // 如果请求在白名单中,直接放行
  const token = uni.getStorageSync("token");
  if (isInWhiteList(options.url)) {
    return options
    console.log("白名单接口,跳过token校验:", options.url);
    return options;
  }
  // 如果未登录且不是白名单接口,跳转登录
  console.log(token,'token');
  if (!token) {
    navigateToLogin()
    throw new Error('未登录')
    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
}
    Authorization: `Bearer ${token}`,
  };
  return options;
};
/**
 * 响应拦截器
 */
const responseInterceptor = (response) => {
  const { statusCode, data } = response
  // Token过期处理
  if (checkTokenExpired(statusCode)) {
    return Promise.reject(data)
  }
  // 无权限处理
  const { statusCode, data } = response;
  if (checkTokenExpired(statusCode)) return Promise.reject(data);
  if (statusCode === config.noPermissionCode) {
    showToast('无权限访问')
    return Promise.reject(data)
    showToast("无权限访问");
    return Promise.reject(data);
  }
  // 其他错误状态码
  if (statusCode !== 200) {
    showToast(`请求失败: ${statusCode}`)
    return Promise.reject(data)
    showToast(`请求失败: ${statusCode}`);
    return Promise.reject(data);
  }
  // 接口自定义错误码处理(假设data.code为0表示成功)
  if (data?.code !== 200) {
    showToast(data?.msg || '操作失败')
    return Promise.reject(data)
    showToast(data?.msg || "操作失败");
    return Promise.reject(data);
  }
  return data;
};
  return data.data || data // 返回实际业务数据
}
/**
 * 核心请求方法
 */
const request = async (options) => {
  try {
    // 合并配置
    options = {
      ...config,
      ...options,
      url: config.baseURL + options.url,
      header: { ...config.header, ...options.header }
    }
    // 请求拦截
    options = await requestInterceptor(options)
    // 发起请求
      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)
        }
      })
    })
          showToast("网络错误,请重试");
          reject(err);
        },
      });
    });
  } catch (err) {
    return Promise.reject(err)
    return Promise.reject(err);
  }
}
};
// 快捷方法封装
const http = {
  get(url, data = {}, options = {}) {
    return request({ url, data, method: 'GET', ...options })
    return request({ url, data, method: "GET", ...options });
  },
  post(url, data = {}, options = {}) {
    return request({ url, data, method: 'POST', ...options })
    return request({ url, data, method: "POST", ...options });
  },
  put(url, data = {}, options = {}) {
    return request({ url, data, method: 'PUT', ...options })
    return request({ url, data, method: "PUT", ...options });
  },
  delete(url, data = {}, options = {}) {
    return request({ url, data, method: 'DELETE', ...options })
    return request({ url, data, method: "DELETE", ...options });
  },
  // 新增:上传文件方法
  upload(url, filePath, name = 'file', formData = {}) {
  upload(url, filePath, name = "file", formData = {}) {
    return request({
      url,
      method: 'POST',
      method: "POST",
      filePath,
      name,
      formData,
      header: { 'Content-Type': 'multipart/form-data' }
    })
  }
}
      header: { "Content-Type": "multipart/form-data" },
    });
  },
};
// 挂载到全局
uni.$uapi = http
export default http
uni.$uapi = http;
export default http;