'use strict' //引入axios import axios from 'axios' import QS from 'qs' import router from '@/router' import config from './config' import utils from './utils' import { useAppStore } from '@/store/app.js' import { showToast, showConfirmDialog, allowMultipleToast, setToastDefaultOptions } from 'vant' // 惰性获取 Pinia store(替代原 Vuex 的 import store from "@/store/index") const appStore = () => useAppStore() allowMultipleToast() setToastDefaultOptions({ forbidClick: true }) const base = { baseUrl: config.baseUrl, timeout: 300000, } //关闭页面 const closeWindow = () => { var userAgent = navigator.userAgent if (userAgent.indexOf('Firefox') != -1 || userAgent.indexOf('Chrome') != -1) { window.location.href = 'about:blank' window.close() } else { window.opener = null window.open('', '_self') window.close() } // 微信中关闭 if (WeixinJSBridge) { WeixinJSBridge.call('closeWindow') } } /** * 提示函数 * 禁止点击蒙层、显示3秒后关闭 */ const toast = (msg) => { showToast({ message: msg, duration: 4000, forbidClick: true, }) } /** * 跳转登录页 * 携带当前页面路由,以期在登录页面完成登录后返回当前页面 */ const toLogin = () => { router.replace({ path: '/liics/login', query: { redirect: router.currentRoute.fullPath, }, }) } /** * 请求失败后的错误统一处理 * @param {Number} status 请求失败的状态码 */ const errorHandle = (status, msg) => { // 状态码判断 switch (status) { //未登录状态,跳转登录页 跳转登录 case 401: // toLogin(); // 清除token toast('【401】当前会话超时,请刷新页面') appStore().del_token() sessionStorage.removeItem('token') break // 403 token过期 // 清除token并跳转登录页 case 403: toast('【403】当前会话超时,请刷新页面') // 清除token appStore().del_token() sessionStorage.removeItem('token') setTimeout(() => { // toLogin(); }, 1000) break // 404请求不存在 case 404: toast('哎呦~404了~休息一下') break case 408: toast('【408】请求超时') break case 500: toast('【500】当前网络不给力,请检查你的网络设置') break case 503: toast('【503】当前服务器不可用,请稍后重试') break // 其他错误,直接抛出错误提示 default: toast(`连接错误${msg}`) } } // 请求超时时间 axios.defaults.timeout = base.timeout //设置默认请求头 // axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'; axios.defaults.headers.post['Content-Type'] = 'application/json' // 请求拦截器 axios.interceptors.request.use( (config) => { // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了 // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断 const token = appStore().token // console.log("https===token:", token) token && (config.headers.token = token) return config }, (error) => { return Promise.reject(error) }, ) // 响应拦截器 axios.interceptors.response.use( // 请求成功 (res) => { if (res.data.code === 10001) { appStore().del_token() sessionStorage.removeItem('token') // 删除已经失效或过期的token(不删除也可以,因为登录后覆盖) // 如果没有展示返回头部的话去掉弹窗按钮 if (appStore().notitle === '1') { toast('当前会话已结束,请刷新页面或关闭页面重新进入') return Promise.resolve(res) } showConfirmDialog({ title: '提示', message: '当前会话已结束,请刷新页面或关闭页面重新进入', confirmButtonText: '取消', cancelButtonText: '确认', confirmButtonColor: '#000000', cancelButtonColor: '#000000', }) .then(() => { // on cancel (现在是右边的取消按钮) // utils.closeWindow(); // router.replace({ // path: '/404' // 到登录页重新获取token // }) }) .catch(() => { // on confirm (现在是左边的确认按钮) //确认重新刷新页面获取token // window.location.reload(); utils.AppReloadHtml() }) return Promise.resolve(res) } res.status === 200 ? Promise.resolve(res) : Promise.reject(res) return res }, // 服务器状态码不是200的情况 // 请求失败 async (error) => { const { response } = error if (response) { //过滤排队接口弹窗问题 if ( response.config.url.indexOf('HumanChat/queueNumbers') != -1 || response.config.url.indexOf('HumanChat/msgCallBack') != -1 ) { return Promise.reject(response) } if (response.data.code === 10001) { appStore().del_token() sessionStorage.removeItem('token') // 删除已经失效或过期的token(不删除也可以,因为登录后覆盖) // 如果没有展示返回头部的话去掉弹窗按钮 if (appStore().notitle === '1') { toast('当前会话已结束,请刷新页面或关闭页面重新进入') return Promise.reject(response) } showConfirmDialog({ title: '提示', message: '当前会话已结束,请刷新页面或关闭页面重新进入', confirmButtonText: '取消', cancelButtonText: '确认', confirmButtonColor: '#000000', cancelButtonColor: '#000000', }) .then(() => { // on cancel (现在是右边的取消按钮) // utils.closeWindow(); // router.replace({ // path: '/404' // 到登录页重新获取token // }) }) .catch(() => { // on confirm (现在是左边的确认按钮) //确认重新刷新页面获取token // window.location.reload(); utils.AppReloadHtml() }) return Promise.reject(response) } // 请求已发出,但是不在2xx的范围 errorHandle(response.status, response.statusText) return Promise.reject(response) } else { // 处理断网的情况 // eg:请求超时或断网时,更新state的network状态 // network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏 // 关于断网组件中的刷新重新获取数据,会在断网组件中说明 // store.commit('changeNetwork', false); if (error?.message?.includes('timeout')) { // 判断请求异常信息中是否含有超时timeout字符串 toast('请求超时,稍后重试!') return Promise.reject(error) } toast('当前网络不给力,请检查你的网络设置') return Promise.reject(error) } }, ) /** * get方法,对应get请求 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */ export function Get(url, params) { return new Promise((resolve, reject) => { url = base.baseUrl + url axios .get(url, QS.stringify(params)) .then((res) => { resolve(res.data) }) .catch((err) => { reject(err.status) }) }) } /** * post方法,对应post请求 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */ export function Post(url, params) { return new Promise((resolve, reject) => { url = base.baseUrl + url console.log('url', url, base); axios .post(url, params) .then((res) => { resolve(res.data) }) .catch((err) => { reject(err.status) }) }) } /** * post方法,对应post请求 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */ export function fetchPost(url, params) { return new Promise((resolve, reject) => { url = base.baseUrl + url axios .post(url, params, { headers: { 'Content-Type': 'application/json', token: appStore().token, }, }) .then((res) => { resolve(res.data) }) .catch((err) => { reject(err.status) }) }) } /** * post方法,对应post请求 上传图片专用 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */ export function upLoadImgHttp(url, params) { return new Promise((resolve, reject) => { url = base.baseUrl + url const instance = axios.create({ withCredentials: true, }) instance .post(url, params, { headers: { 'Content-Type': 'multipart/form-data', token: appStore().token, }, }) .then((res) => { resolve(res.data) }) .catch((err) => { reject(err.status) }) }) } /** * 获取图片数据(支持base64格式) * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] * @param {Object} options [额外配置选项] */ export function fetchImageData(url, params, options = {}) { return new Promise((resolve, reject) => { url = base.baseUrl + url const { timeout = 10000 } = options const instance = axios.create({ timeout, withCredentials: true, }) instance .post(url, params, { headers: { 'Content-Type': 'application/json', token: appStore().token, }, }) .then((res) => { // 检查响应格式 if (res.data.code !== 0) { reject(new Error(res.data.msg || 'Request failed')) return } const imageData = res.data.data if (!imageData || !imageData.content) { reject(new Error('Invalid response: missing image content')) return } // 验证文件扩展名是否为图片格式 const validImageExts = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'bmp'] const fileExt = imageData.fileNameExt?.toLowerCase() if (!validImageExts.includes(fileExt)) { reject(new Error(`Unsupported image format: ${fileExt}`)) return } resolve({ fileName: imageData.fileName, fileCrc: imageData.fileCrc, fileNameExt: imageData.fileNameExt, content: imageData.content, // 构建完整的data URL dataUrl: `data:image/${fileExt};base64,${imageData.content}`, }) }) .catch((err) => { reject(err) }) }) } /** * 获取图片文件流(保持向后兼容) * @deprecated 使用 fetchImageData 替代 */ export function fetchImageStream(url, params, options = {}) { return fetchImageData(url, params, options).then((imageData) => { // 将base64转换为blob以保持兼容性 return base64ToBlob(imageData.content, `image/${imageData.fileNameExt}`) }) } /** * 将base64字符串转换为Blob对象 * @param {String} base64Data - base64编码的数据(不包含前缀) * @param {String} contentType - MIME类型 * @returns {Blob} Blob对象 */ export function base64ToBlob(base64Data, contentType = 'image/png') { try { // 解码base64数据 const byteCharacters = atob(base64Data) const byteNumbers = new Array(byteCharacters.length) for (let i = 0; i < byteCharacters.length; i++) { byteNumbers[i] = byteCharacters.charCodeAt(i) } const byteArray = new Uint8Array(byteNumbers) return new Blob([byteArray], { type: contentType }) } catch (error) { throw new Error(`Failed to convert base64 to blob: ${error.message}`) } } /** * 通用文件下载方法 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] * @param {Object} options [配置选项] */ export function downloadFile(url, params, options = {}) { return new Promise((resolve, reject) => { url = base.baseUrl + url const { timeout = 30000, responseType = 'blob', method = 'POST' } = options const config = { url, method, headers: { 'Content-Type': 'application/json', token: appStore().token, }, responseType, timeout, } if (method.toUpperCase() === 'POST') { config.data = params } else { config.params = params } axios(config) .then((res) => { resolve(res.data) }) .catch((err) => { reject(err) }) }) }