- 新增工单中心(ticket_center)与工单工作台(ticket_workbench)页面 - 新增房间巡查(room_inspection)与失物招领发布(lost_found_publish)页面 - 预约看样流程新增房型/楼栋/楼层选择器组件 - 新增工具模块: booking-entry、privacy、reservation-flow - api/index.js 补充楼层列表、看样提交等接口 - locales/messages.js 补充多语言文案 - manifest.json 开启微信小程序隐私检查 - 完善登录、注册、申请详情、账单等相关页面交互
229 lines
6.3 KiB
JavaScript
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
|