Files
QSY-lvjuxcx/utils/request.js
liangjian 445bd850f4 feat: 新增工单中心、房间巡查、失物招领等业务模块
- 新增工单中心(ticket_center)与工单工作台(ticket_workbench)页面
- 新增房间巡查(room_inspection)与失物招领发布(lost_found_publish)页面
- 预约看样流程新增房型/楼栋/楼层选择器组件
- 新增工具模块: booking-entry、privacy、reservation-flow
- api/index.js 补充楼层列表、看样提交等接口
- locales/messages.js 补充多语言文案
- manifest.json 开启微信小程序隐私检查
- 完善登录、注册、申请详情、账单等相关页面交互
2026-07-24 09:35:46 +08:00

229 lines
6.3 KiB
JavaScript

import { config } from '../config.js'
import { clearLoginSession, getAuthToken } from './app-state'
import { getCurrentLanguage } from './i18n'
const API_BASE_URL = config.baseURL
const FILE_BASE_URL = config.filePrefix
const FIXED_PROVIDER_TYPE = 'WechatMiniProgram'
function getRequestText(key = '') {
const language = getCurrentLanguage()
const isEn = language === 'en'
const map = {
requestFailed: isEn ? 'Request failed' : '请求失败',
loginExpired: isEn ? 'Login expired. Please sign in again' : '登录已过期,请重新登录',
tooManyRequests: isEn ? 'Too many requests. Please try again later' : '操作太频繁了,请稍后再试',
networkFailed: isEn ? 'Network request failed' : '网络请求失败'
}
return map[key] || ''
}
function normalizeUrl(url = '') {
if (/^https?:\/\//i.test(url)) {
return url
}
if (!url) {
return API_BASE_URL
}
if (url.startsWith('/')) {
return `${API_BASE_URL}${url}`
}
return `${API_BASE_URL}/${url}`
}
function isPlainRequestObject(value) {
return !!value && typeof value === 'object' && !Array.isArray(value)
}
function appendFixedProviderType(payload) {
if (isPlainRequestObject(payload)) {
return {
...payload,
ProviderType: FIXED_PROVIDER_TYPE
}
}
return {
ProviderType: FIXED_PROVIDER_TYPE
}
}
function hasBusinessCode(payload) {
return !!payload && typeof payload === 'object' && Object.prototype.hasOwnProperty.call(payload, 'code')
}
function isBusinessSuccess(payload) {
if (!hasBusinessCode(payload)) {
return true
}
const code = String(payload.code)
return code === '200' || code === '0'
}
function normalizeBusinessError(payload, fallback = {}) {
const message = (payload && (payload.msg || payload.message)) || fallback.errMsg || getRequestText('requestFailed')
return {
...fallback,
...(payload && typeof payload === 'object' ? payload : {}),
message
}
}
function getCurrentRoute() {
const pages = getCurrentPages()
if (!Array.isArray(pages) || !pages.length) {
return ''
}
const currentPage = pages[pages.length - 1] || {}
return currentPage.route || ''
}
function shouldSkipUnauthorizedRedirect() {
const route = getCurrentRoute()
return route === 'pkgAccount/login/login' || route === 'pages/login/index' || route === 'pages/login/login'
}
function normalizeRequestedLanguageHeaderValue(language) {
return String(language || getCurrentLanguage() || 'zh').trim().toLowerCase() === 'en' ? 'en' : 'zh'
}
function buildRequestHeader(header = {}, token = '') {
const normalizedHeader = { ...(header || {}) }
const requestedLanguageHeaderKey = Object.keys(normalizedHeader).find(key => String(key || '').toLowerCase() === 'x-requested-language')
if (requestedLanguageHeaderKey && requestedLanguageHeaderKey !== 'X-Requested-Language') {
delete normalizedHeader[requestedLanguageHeaderKey]
}
normalizedHeader['X-Requested-Language'] = normalizeRequestedLanguageHeaderValue(
requestedLanguageHeaderKey ? normalizedHeader[requestedLanguageHeaderKey] : normalizedHeader['X-Requested-Language']
)
return {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
...normalizedHeader
}
}
function handleUnauthorized(responseData) {
clearLoginSession()
if (shouldSkipUnauthorizedRedirect()) {
return
}
uni.showToast({
title: getRequestText('loginExpired'),
icon: 'none',
duration: 2000
})
setTimeout(() => {
uni.reLaunch({
url: '/pkgAccount/login/login'
})
}, 500)
}
export function request(options = {}) {
const {
url = '',
method = 'GET',
data,
params,
header = {},
hideErrorToast = false,
withAuth = true
} = options
return new Promise((resolve, reject) => {
const token = withAuth ? getAuthToken() : ''
const uppercaseMethod = String(method || 'GET').toUpperCase()
const normalizedParams = appendFixedProviderType(params)
const normalizedData = appendFixedProviderType(data)
uni.request({
url: normalizeUrl(url),
method: uppercaseMethod,
data: uppercaseMethod === 'GET'
? (params !== undefined ? normalizedParams : normalizedData)
: normalizedData,
header: buildRequestHeader(header, token),
success: (res) => {
const { statusCode, data: responseData } = res
if ((statusCode === 401) || (responseData && Number(responseData.code) === 401)) {
handleUnauthorized(responseData)
reject(normalizeBusinessError(responseData, res))
return
}
if (statusCode === 429) {
if (!hideErrorToast) {
uni.showToast({
title: getRequestText('tooManyRequests'),
icon: 'none',
duration: 2500
})
}
reject({
...normalizeBusinessError(responseData, res),
message: getRequestText('tooManyRequests'),
__toastShown: !hideErrorToast
})
return
}
if (statusCode >= 200 && statusCode < 300) {
if (isBusinessSuccess(responseData)) {
resolve(responseData)
return
}
const businessError = normalizeBusinessError(responseData, res)
if (!hideErrorToast) {
uni.showToast({
title: businessError.message || getRequestText('requestFailed'),
icon: 'none',
duration: 2000
})
}
reject({
...businessError,
__toastShown: !hideErrorToast
})
return
}
const requestError = normalizeBusinessError(responseData, res)
if (!hideErrorToast) {
uni.showToast({
title: requestError.message || getRequestText('requestFailed'),
icon: 'none'
})
}
reject({
...requestError,
__toastShown: !hideErrorToast
})
},
fail: (error) => {
const requestError = normalizeBusinessError(null, error)
if (!hideErrorToast) {
uni.showToast({
title: requestError.message || getRequestText('networkFailed'),
icon: 'none'
})
}
reject({
...requestError,
__toastShown: !hideErrorToast
})
}
})
})
}
export { API_BASE_URL, FILE_BASE_URL }
export default request