@mirari/mp-common
Version:
小程序公共库
309 lines (281 loc) • 8.88 kB
JavaScript
import utils from './utils'
import throttle from '../lib/lodash/throttle'
const DEFAULTS = {
debug: false,
autoLogin: true, // token过期时自动尝试一次登录
withCredentials: true, // 是否附带用户登录信息凭据
baseURL: '',
method: 'GET', // 请求方式
showLoading: false, // 是否遮罩
showError: true, // 显示错误信息
resultHandler: null // 查询结果处理
}
class APIError extends Error {
constructor (result) {
super(result.errmsg)
this.name = 'APIError'
this.code = result.code
this.data = result.data
}
}
const methodsWithData = ['POST', 'PUT', 'PATCH']
const methodsNoData = ['DELETE', 'GET', 'HEAD', 'OPTIONS']
// 将相对URL与baseURL合并
function combineURLs (baseURL, relativeURL) {
return relativeURL
? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
: baseURL
}
// 是否绝对URL
function isAbsoluteURL (url) {
// A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
// RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
// by any combination of letters, digits, plus, period, or hyphen.
return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url)
}
function encode (val) {
return encodeURIComponent(val).replace(/%40/gi, '@').replace(/%3A/gi, ':').replace(/%24/g, '$').replace(/%2C/gi, ',').replace(/%20/g, '+').replace(/%5B/gi, '[').replace(/%5D/gi, ']')
}
function buildURL (url, params, paramsSerializer) {
/*eslint no-param-reassign:0*/
if (!params) {
return url
}
var serializedParams
if (paramsSerializer) {
serializedParams = paramsSerializer(params)
} else if (utils.isURLSearchParams(params)) {
serializedParams = params.toString()
} else {
var parts = []
utils.forEach(params, function serialize (val, key) {
if (val === null || typeof val === 'undefined') {
return
}
if (utils.isArray(val)) {
key = key + '[]'
}
if (!utils.isArray(val)) {
val = [val]
}
utils.forEach(val, function parseValue (v) {
if (utils.isDate(v)) {
v = v.toISOString()
} else if (utils.isObject(v)) {
v = JSON.stringify(v)
}
parts.push(encode(key) + '=' + encode(v))
})
})
serializedParams = parts.join('&')
}
if (serializedParams) {
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
}
return url
}
// 删除空参数
function deleteEmptyData (data) {
if (data) {
for (const key in data) {
const val = data[key]
if ((typeof val === 'undefined') || val === '' || val === null) {
delete data[key]
}
}
}
return data
}
class API {
constructor ({config, auth}) {
this.defaults = Object.assign({}, DEFAULTS, config)
this.auth = auth // 鉴权实例
// 节流封装,避免重复跳转
this.showLogin = throttle(() => {
auth.showLogin()
}, 1000)
}
// 添加通用参数
appendData (config) {
if (!config.params) {
config.params = {}
}
if (config.withCredentials) {
if (!config.params.userId) {
config.params.userId = this.auth.userId
}
if (!config.params.token) {
config.params.token = this.auth.token
}
}
return config
}
action (config) {
config = Object.assign({}, this.defaults, config)
// 删除空参数
deleteEmptyData(config.params)
deleteEmptyData(config.data)
// 添加公共参数
this.appendData(config)
let {url, baseURL, params, data, method, header} = config
// 拼接完整URL
if (baseURL && !isAbsoluteURL(url)) {
url = combineURLs(baseURL, url)
}
// POST方法时,需要将params拼到URL里
// if (methodsWithData.includes(method) && params) {
if (methodsWithData.indexOf(method) > -1 && params) {
url = buildURL(url, params, config.paramsSerializer)
}
// GET方法时,需要将params改成data
// if (methodsNoData.includes(method) && params) {
if (methodsNoData.indexOf(method) > -1 && params) {
data = params
params = null
}
if (config.showLoading) {
const {title='请稍候...', mask=true} = config.showLoading
wx.showLoading({
title,
mask
})
}
return new Promise((resolve, reject) => {
wx.request({
url,
data,
method,
header,
success: res => {
if (config.showLoading) {
wx.hideLoading()
}
if (res.statusCode >= 400) {
console.error('wx.request fail [business]', config, res.statusCode, res.data)
res.message = '网络貌似不给力~请检查你的网络后重试'
reject(res)
} else {
let result = res.data
const {code} = result
if (code === '00') {
resolve(result)
} else {
const e = new APIError(result)
if (code === '01') {
if (config.autoLogin) {
config.autoLogin = false
// 先尝试一次登录
this.auth.relogin().then(() => {
resolve(this.action(config))
})
} else {
this.showLogin()
reject(e)
}
} else {
if (config.showError) {
wx.showToast({
icon: 'none',
title: e.message
})
}
reject(e)
}
}
}
},
fail: e => {
if (config.showLoading) {
wx.hideLoading()
}
// 格式化成标准error,便于异常处理
if (e.errMsg) {
e = new Error(e.errMsg)
}
console.error('wx.request fail [network]', config, e)
if (config.showError) {
wx.showToast({
icon: 'none',
title: e.message
})
}
reject(e)
},
complete: () => {
}
})
})
}
request (config) {
return new Promise((resolve, reject) => {
this.action(config).then(result => {
let data = result.data
if (config.resultHandler) {
data = config.resultHandler(data)
}
resolve(data)
}).catch(error => {
reject(error)
})
})
}
paginationQuery (config, {pageSize, pageNum}) {
return new Promise((resolve, reject) => {
config.params = Object.assign({}, {
pageSize: pageSize || 10,
pageNum: pageNum || 1
}, config.params)
this.action(config).then((result) => {
const {data, pagination} = result
// 后端分页属性解耦
let paging
if (pagination) {
paging = {
list: data, // 分页数据(required)
total: pagination.totalSize, // 总数(required)
pageNum: pagination.pageNum || 1, // 当前页码(required) element分页插件的页码不允许为0,否则会无限触发跳页动作
pageSize: pagination.pageSize, // 分页数设定值
length: data.length, // 分页数实际值(比如设定pageSize为10,但最后一页只有7条记录,size为7)
pages: pagination.pages || 1, // 总页数
startRow: pagination.startRow, // 当前起始索引
endRow: pagination.endRow // 当前结尾索引
}
} else {
paging = {
list: data, // 分页数据(required)
total: 0, // 总数(required)
pageNum: 1, // 当前页码(required) element分页插件的页码不允许为0,否则会无限触发跳页动作
pageSize: 0, // 分页数设定值
length: 0, // 分页数实际值(比如设定pageSize为10,但最后一页只有7条记录,size为7)
pages: 1, // 总页数
startRow: 0, // 当前起始索引
endRow: 0 // 当前结尾索引
}
}
if (config.resultHandler) {
paging.list = config.resultHandler(paging.list)
}
resolve(paging)
}).catch(error => {
reject(error)
})
})
}
getUrl (config) {
config = Object.assign({}, this.defaults, config)
// 删除空参数
deleteEmptyData(config.params)
// 添加公共参数
this.appendData(config)
let {url, baseURL, params, paramsSerializer} = config
// 拼接完整URL
if (baseURL && !isAbsoluteURL(url)) {
url = combineURLs(baseURL, url)
}
// 将params拼到URL里
url = buildURL(url, params, paramsSerializer)
return url
}
}
API.buildURL = buildURL
export default API