You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
customAgent/src/utils/https.js

469 lines
13 KiB

'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)
})
})
}