/** * nx-request * @description api模块管理,loading配置,请求拦截,错误处理 */ import Request from 'luch-request' import { getBaseUrl, getTenantId } from '@/defaultBaseUrl' import $store from '@/nx/store' import { ApiEncryption } from '@/nx/utils/crypto.js' const apiEncryption = new ApiEncryption() const options = { // 显示操作成功消息 默认不显示 showSuccess: false, // 成功提醒 默认使用后端返回值 successMsg: '操作成功', // 显示失败消息 默认显示 showError: true, // 失败提醒 默认使用后端返回信息 errorMsg: '操作失败', // 显示请求时loading模态框 默认显示 showLoading: true, // loading提醒文字 loadingMsg: '加载中', // 需要授权才能请求 默认放开 auth: false, // 是否处理请求结果 isTransformResponse: true } const COMPANY_DEPT_RETRY_HEADER = '__companyDeptRetried' const VISIT_COMPANY_STORAGE_KEY = 'visit-company-info' const VISIT_DEPT_STORAGE_KEY = 'visit-dept-info' // Loading全局实例 let LoadingInstance = { target: null, count: 0 } /** * 关闭loading */ function closeLoading() { if (LoadingInstance.count > 0) LoadingInstance.count-- if (LoadingInstance.count === 0) uni.hideLoading() } /** * @description 请求基础配置 可直接使用访问自定义请求 */ const http = new Request({ timeout: 8000, method: 'GET', header: { 'Content-Type': 'application/json;charset=UTF-8' }, // #ifdef APP-PLUS sslVerify: false, // #endif // #ifdef H5 // 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+) withCredentials: false, // #endif custom: options }) /** * @description 请求拦截器 */ http.interceptors.request.use( config => { if (config.custom.showLoading) { LoadingInstance.count++ LoadingInstance.count === 1 && uni.showLoading({ title: config.custom.loadingMsg, mask: true, fail: () => { uni.hideLoading() } }) } config.baseURL = getBaseUrl() // 可使用async await 做异步操作 config.header['X-TIMESTAMP'] = new Date().getTime() const token = getAccessToken() if (token) config.header['Authorization'] = 'Bearer ' + token config.header['Accept'] = '*/*' config.header['tenant-id'] = getTenantId() const method = config.method?.toUpperCase() // 防止 GET 请求缓存 if (method === 'GET') { config.header['Cache-Control'] = 'no-cache' config.header['Pragma'] = 'no-cache' } let params = config.params || {} let data = config.data || false // if (process.env.NODE_ENV == 'development') console.log('development--params', params) // if (process.env.NODE_ENV == 'development') console.log('development--data', data) // const visitCompanyId = getVisitCompanyId() const visitCompanyId = '101' if (visitCompanyId !== undefined && visitCompanyId !== null && visitCompanyId !== '') { config.header['visit-company-id'] = visitCompanyId // const visitCompanyName = getVisitCompanyName() const visitCompanyName = '"深圳总公司' if (visitCompanyName !== undefined && visitCompanyName !== null) { config.header['visit-company-name'] = encodeURIComponent(visitCompanyName || '') } } // const visitDeptId = getVisitDeptId() const visitDeptId = '103' if (visitDeptId !== undefined && visitDeptId !== null && visitDeptId !== '') { config.header['visit-dept-id'] = visitDeptId // const visitDeptName = getVisitDeptName() const visitDeptName = '研发部门' if (visitDeptName !== undefined && visitDeptName !== null) { config.header['visit-dept-name'] = encodeURIComponent(visitDeptName || '') } } config.header['__companyDeptRetried'] = '1' return config }, error => { return Promise.reject(error) } ) /** * @description 响应拦截器 */ http.interceptors.response.use( response => { // 自动设置登陆令牌 // if (response.header.authorization || response.header.Authorization) { // $store('user').setToken(response.header.authorization || response.header.Authorization) // } const userStore = $store('user') response.config.custom.showLoading && closeLoading() if (!response.config.custom.isTransformResponse) { return Promise.resolve(response.data) } const { code, data, msg } = response.data // 统一处理【公司/部门二次选择】:参考 PC 端逻辑,自动补全或提示选择后重试 if (code === 400 && Array.isArray(data)) { debugger const companyDeptList = data const config = response.config config.header = config.header || {} if (companyDeptList.length === 1) { const item = companyDeptList[0] if (!config.header[COMPANY_DEPT_RETRY_HEADER]) { applyCompanyDeptSelection(item, config) return request(config) } uni.showToast({ title: '公司/部门信息缺失,且自动补全失败,请联系管理员', icon: 'none', mask: true }) return Promise.resolve(data) } else if (companyDeptList.length > 1) { const groupedList = normalizeCompanyDeptList(companyDeptList) const companyDeptDialogStore = $store('company-dept') return new Promise(resolve => { companyDeptDialogStore.open({ companyList: groupedList, onConfirm: ({ companyId, deptId }) => { const selectedCompany = groupedList.find(company => company.companyId === companyId) const selectedDept = selectedCompany?.depts.find(dept => dept.deptId === deptId) applyCompanyDeptSelection( { companyId, companyName: selectedCompany?.companyName || '', deptId, deptName: selectedDept?.deptName || '' }, config ) resolve(request(config)) }, onCancel: () => { uni.showToast({ title: '已取消公司/部门选择', icon: 'none', mask: true }) resolve(data) } }) }) } } if (code !== 0) { uni.showToast({ title: msg || response.config.custom.errorMsg, icon: 'none' }) if (code === 401) { userStore.logout(true) } } if (code === 0 || code === 200) { if (response.config.custom.showSuccess) { setTimeout(() => { uni.showToast({ title: msg || response.config.custom.successMsg, icon: 'none' }) }, 100) } $store('user').updateLastRequestTime() return Promise.resolve(data) } return Promise.reject(response.data) }, error => { console.log('error', error) const userStore = $store('user') const isLogin = userStore.isLogin let errorMessage = '网络请求出错' if (error !== undefined) { switch (error.statusCode) { case 400: errorMessage = '请求错误' break case 401: if (isLogin) { errorMessage = '您的登陆已过期' } else { errorMessage = '请先登录' } userStore.logout(true) break case 403: errorMessage = '拒绝访问' break case 404: errorMessage = '请求出错' break case 408: errorMessage = '请求超时' break case 429: errorMessage = '请求频繁, 请稍后再访问' break case 500: errorMessage = '服务器开小差啦,请稍后再试~' break case 501: errorMessage = '服务未实现' break case 502: errorMessage = '网络错误' break case 503: errorMessage = '服务不可用' break case 504: errorMessage = '网络超时' break case 505: errorMessage = 'HTTP版本不受支持' break } if (error.errMsg.includes('timeout')) errorMessage = '请求超时' // #ifdef H5 if (error.errMsg.includes('Network')) errorMessage = window.navigator.onLine ? '服务器异常' : '请检查您的网络连接' // #endif } if (error && error.config) { if (error.config.custom.showError === true) { uni.showToast({ title: error.data?.msg || errorMessage, icon: 'none', mask: true }) } error.config.custom.showLoading && closeLoading() } return Promise.reject(error) } ) /** 获得访问令牌 */ export const getAccessToken = () => { return uni.getStorageSync('token') } const getStorageObject = key => { const value = uni.getStorageSync(key) if (!value) { return {} } if (typeof value === 'string') { try { return JSON.parse(value) } catch (error) { console.warn(`解析本地存储 ${key} 失败:`, error) return {} } } return value } const setStorageObject = (key, value) => { if (value === undefined || value === null) { uni.removeStorageSync(key) return } uni.setStorageSync(key, value) } export const getVisitCompanyId = () => { const info = getStorageObject(VISIT_COMPANY_STORAGE_KEY) return info?.id ?? info?.companyId ?? null } export const getVisitCompanyName = () => { const info = getStorageObject(VISIT_COMPANY_STORAGE_KEY) return info?.name ?? info?.companyName ?? '' } export const getVisitDeptId = () => { const info = getStorageObject(VISIT_DEPT_STORAGE_KEY) return info?.id ?? info?.deptId ?? null } export const getVisitDeptName = () => { const info = getStorageObject(VISIT_DEPT_STORAGE_KEY) return info?.name ?? info?.deptName ?? '' } export const setVisitCompany = (companyId, companyName) => { if (companyId === undefined || companyId === null || companyId === '') { uni.removeStorageSync(VISIT_COMPANY_STORAGE_KEY) return } setStorageObject(VISIT_COMPANY_STORAGE_KEY, { id: companyId, name: companyName || '' }) } export const setVisitDept = (deptId, deptName) => { if (deptId === undefined || deptId === null || deptId === '') { uni.removeStorageSync(VISIT_DEPT_STORAGE_KEY) return } setStorageObject(VISIT_DEPT_STORAGE_KEY, { id: deptId, name: deptName || '' }) } const applyCompanyDeptSelection = (item, config) => { setVisitCompany(item.companyId, item.companyName) setVisitDept(item.deptId, item.deptName) config.header['visit-company-id'] = item.companyId config.header['visit-company-name'] = encodeURIComponent(item.companyName || '') config.header['visit-dept-id'] = item.deptId config.header['visit-dept-name'] = encodeURIComponent(item.deptName || '') config.header[COMPANY_DEPT_RETRY_HEADER] = '1' } const normalizeCompanyDeptList = (list = []) => { const companyMap = new Map() list.forEach(item => { if (!companyMap.has(item.companyId)) { companyMap.set(item.companyId, { companyId: item.companyId, companyName: item.companyName, depts: [] }) } const company = companyMap.get(item.companyId) if (!company.depts.some(dept => dept.deptId === item.deptId)) { company.depts.push({ deptId: item.deptId, deptName: item.deptName }) } }) return Array.from(companyMap.values()) } const request = config => { return http.middleware(config) } export default request