import config from '@/config/index.js' function buildUrl(url, baseURL = config.baseURL) { const normalizedBaseURL = baseURL.endsWith('/') ? baseURL.slice(0, -1) : baseURL const path = url.startsWith('/') ? url : `/${url}` return `${normalizedBaseURL}${path}` } function getNetworkErrorMessage(error) { const errorMessage = String(error?.errMsg || '') if (errorMessage.toLowerCase().includes('timeout')) { return '请求超时,请稍后重试' } return '无法连接服务器,请检查网络后重试' } function getHeaders(headers = {}, includeJsonContentType = true) { const token = uni.getStorageSync(config.tokenKey) const result = { ...(includeJsonContentType ? { 'Content-Type': 'application/json' } : {}), [config.channelHeaderKey]: config.channel, ...headers } if (token) { result[config.tokenHeaderKey] = String(token).startsWith('Bearer ') ? token : `Bearer ${token}` } return result } async function refreshAccessToken() { const refreshToken = uni.getStorageSync('scanapp_refresh_token') if (!refreshToken) return '' const response = await uniRequest('/auth/refresh', 'POST', { refresh_token: refreshToken }, { baseURL: config.apiBaseURL, showError: false }) const accessToken = response?.access_token if (!accessToken) return '' uni.setStorageSync(config.tokenKey, accessToken) if (response.refresh_token) uni.setStorageSync('scanapp_refresh_token', response.refresh_token) return accessToken } function uniRequest(url, method, data = {}, options = {}) { return new Promise((resolve, reject) => { const requestUrl = buildUrl(url, options.baseURL || config.baseURL) uni.request({ url: requestUrl, method, data, header: getHeaders(options.headers), timeout: options.timeout || 15000, success: (res) => { let body = res.data || {} if (typeof body === 'string') { try { body = JSON.parse(body) } catch { body = { msg: body } } } if (Number(body.code) === 0) { resolve(body.data) return } reject(new Error(body.msg || '请求失败')) }, fail: () => reject(new Error('网络请求失败')) }) }) } function request(url, method, data = {}, options = {}) { const baseURL = options.baseURL || config.baseURL return new Promise((resolve, reject) => { const requestUrl = buildUrl(url, baseURL) uni.request({ url: requestUrl, method, data, header: getHeaders(options.headers), timeout: options.timeout || 15000, success: async (res) => { let body = res.data || {} if (typeof body === 'string') { try { body = JSON.parse(body) } catch (error) { body = { msg: body } } } if (Number(body.code) === 401 && !options._retry) { try { const token = await refreshAccessToken() if (token) { resolve(request(url, method, data, { ...options, _retry: true })) return } } catch (error) { console.error('[scanapp] 刷新登录令牌失败', error) } } if (Number(body.code) === 0) { resolve(options.returnFullResponse ? body : body.data) return } const message = body.msg || `请求失败(HTTP ${res.statusCode || 0},code ${body.code ?? 'unknown'})` console.error('[scanapp] 请求失败', { url: requestUrl, statusCode: res.statusCode, code: body.code, message, body }) if (options.showError !== false) { uni.showToast({ title: message, icon: 'none' }) } reject(new Error(message)) }, fail: (error) => { const message = getNetworkErrorMessage(error) console.error('[scanapp] 网络请求失败', { url: requestUrl, rawMessage: error?.errMsg, message, error }) if (options.showError !== false) { uni.showToast({ title: message, icon: 'none' }) } reject(new Error(message)) } }) }) } export function uploadFile(url, filePath, formData = {}, options = {}) { const baseURL = options.baseURL || config.baseURL return new Promise((resolve, reject) => { const requestUrl = buildUrl(url, baseURL) const token = uni.getStorageSync(config.tokenKey) const header = getHeaders(options.headers, false) if (token) { header[config.tokenHeaderKey] = String(token).startsWith('Bearer ') ? token : `Bearer ${token}` } uni.uploadFile({ url: requestUrl, filePath, name: options.name || 'file', formData, header, timeout: options.timeout || 30000, success: (res) => { let body = res.data || {} if (typeof body === 'string') { try { body = JSON.parse(body) } catch (error) { body = { msg: body } } } if (Number(body.code) === 0) { resolve(options.returnFullResponse ? body : body.data) return } const message = body.msg || `上传失败(HTTP ${res.statusCode || 0},code ${body.code ?? 'unknown'})` if (options.showError !== false) { uni.showToast({ title: message, icon: 'none' }) } reject(new Error(message)) }, fail: (error) => { const message = getNetworkErrorMessage(error) if (options.showError !== false) { uni.showToast({ title: message, icon: 'none' }) } reject(new Error(message)) } }) }) } export default { get(url, data = {}, options = {}) { return request(url, 'GET', data, options) }, post(url, data = {}, options = {}) { return request(url, 'POST', data, options) }, put(url, data = {}, options = {}) { return request(url, 'PUT', data, options) }, delete(url, data = {}, options = {}) { return request(url, 'DELETE', data, options) } }