zjk
昨天 3e813eb0c5b3314a2799014db813814d9e5fd0dc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/**
 * 公共请求方法封装(用户端小程序)
 * 统一处理:地址拼接、token 注入、响应解包、异常提示、登录失效跳转
 */
import config from '@/config/index.js'
 
// 登录失效提示开关:防止多个并发请求同时弹窗
let isReloginTipShowing = false
 
// ======================== 登录令牌读写 ========================
 
/** 读取本地缓存的 token */
export function getToken() {
    return uni.getStorageSync(config.tokenKey) || ''
}
 
/** 写入 token 到本地缓存 */
export function setToken(token) {
    uni.setStorageSync(config.tokenKey, token)
}
 
/** 清除本地登录态 */
export function clearToken() {
    uni.removeStorageSync(config.tokenKey)
}
 
/**
 * 页面登录态守卫:无 token 时重定向至登录页
 * 供各页面 onShow 调用,避免登录态校验逻辑重复散落
 * @returns {Boolean} 已登录返回 true,未登录(已触发跳转)返回 false
 */
export function ensureLogin() {
    if (getToken()) return true
    uni.reLaunch({ url: config.loginPage })
    return false
}
 
// ======================== 核心请求方法 ========================
 
/**
 * 统一请求方法
 * @param {Object}   options              请求配置
 * @param {String}   options.url          接口路径(不含根地址;传完整 http 地址则直接使用)
 * @param {String}   [options.method]     请求方法,默认 GET
 * @param {Object}   [options.data]       请求参数
 * @param {Object}   [options.header]     自定义请求头
 * @param {Boolean}  [options.auth]       是否需要携带 token,默认 true
 * @param {Boolean}  [options.loading]    是否显示加载中提示,默认 false
 * @param {String}   [options.loadingText] 加载提示文案
 * @returns {Promise<Object>} 解析后的响应体 { code, msg, data }
 */
export function request(options = {}) {
    const {
        url = '',
        method = 'GET',
        data = {},
        header = {},
        auth = true,
        loading = false,
        loadingText = '加载中...'
    } = options
 
    // 拼接完整请求地址
    const fullUrl = /^https?:\/\//i.test(url) ? url : config.baseUrl + url
 
    // 组装请求头
    const finalHeader = {
        'Content-Type': 'application/json;charset=utf-8',
        ...header
    }
    // 鉴权接口自动注入 token
    const token = getToken()
    if (auth && token) {
        finalHeader.Authorization = 'Bearer ' + token
    }
 
    if (loading) {
        uni.showLoading({ title: loadingText, mask: true })
    }
 
    return new Promise((resolve, reject) => {
        uni.request({
            url: fullUrl,
            method: method.toUpperCase(),
            data,
            header: finalHeader,
            timeout: config.timeout,
            success: (res) => {
                const { statusCode, data: body } = res
 
                // 非 HTTP 200 视为网络异常
                if (statusCode !== 200) {
                    reject(new Error('网络请求异常:' + statusCode))
                    return
                }
 
                // 登录失效:清理登录态并跳转登录页
                if (body && body.code === config.unauthorizedCode) {
                    _handleUnauthorized()
                    reject(new Error(body.msg || '登录状态已过期'))
                    return
                }
 
                // 业务成功:返回完整响应体,调用方按需取 data
                if (body && body.code === config.successCode) {
                    resolve(body)
                    return
                }
 
                // 业务失败:统一抛出错误信息
                reject(new Error((body && body.msg) || '请求失败'))
            },
            fail: (err) => {
                reject(new Error((err && err.errMsg) || '网络连接失败'))
            },
            complete: () => {
                if (loading) {
                    uni.hideLoading()
                }
            }
        })
    })
}
 
/**
 * 处理登录态失效:清缓存 + 提示 + 延时跳转登录页
 */
function _handleUnauthorized() {
    if (isReloginTipShowing) return
    isReloginTipShowing = true
    clearToken()
    uni.showToast({ title: '登录状态已过期,请重新登录', icon: 'none' })
    setTimeout(() => {
        isReloginTipShowing = false
        uni.reLaunch({ url: config.loginPage })
    }, 1500)
}
 
// ======================== 快捷方法 ========================
 
/** GET 请求 */
export const get = (url, data = {}, options = {}) =>
    request({ url, method: 'GET', data, ...options })
 
/** POST 请求 */
export const post = (url, data = {}, options = {}) =>
    request({ url, method: 'POST', data, ...options })
 
/** PUT 请求 */
export const put = (url, data = {}, options = {}) =>
    request({ url, method: 'PUT', data, ...options })
 
/** DELETE 请求 */
export const del = (url, data = {}, options = {}) =>
    request({ url, method: 'DELETE', data, ...options })
 
export default request