| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- /**
- * 报销/备用金模块 HTTP(bx 信封:code === 0)
- * 对齐 bx-mobile/src/api/bxClient.ts
- */
- import { TOKEN_KEY, REFRESH_TOKEN_KEY, USER_INFO_KEY, resolveApiBase } from '@/config/api'
- const BX_COLLECTION_ROOT = /^\/(reimburses|advances|payments|workflows|expense-items)$/
- function normalizeBxUrl(url) {
- if (!url || url.endsWith('/')) return url
- return BX_COLLECTION_ROOT.test(url) ? `${url}/` : url
- }
- function buildQuery(params) {
- if (!params || typeof params !== 'object') return ''
- const parts = []
- Object.keys(params).forEach((key) => {
- const val = params[key]
- if (val === undefined || val === null || val === '') return
- parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(val))}`)
- })
- return parts.length ? `?${parts.join('&')}` : ''
- }
- function parseDetailMessage(data) {
- if (!data || typeof data !== 'object') return ''
- if (typeof data.msg === 'string' && data.msg) return data.msg
- const detail = data.detail
- if (typeof detail === 'string') return detail
- if (Array.isArray(detail)) {
- return detail
- .map((x) => (x && x.msg) || '')
- .filter(Boolean)
- .join(';')
- }
- return data.message || ''
- }
- async function onUnauthorized() {
- uni.removeStorageSync(TOKEN_KEY)
- uni.removeStorageSync(REFRESH_TOKEN_KEY)
- uni.removeStorageSync(USER_INFO_KEY)
- try {
- const { useUserStore } = await import('@/stores/user')
- useUserStore().clearLoginInfo()
- } catch (e) {
- // ignore
- }
- uni.showToast({ title: '登录已过期,请重新登录', icon: 'none' })
- setTimeout(() => {
- uni.reLaunch({ url: '/pages/login/login' })
- }, 800)
- }
- /**
- * @param {{ url: string, method?: string, data?: any, params?: any, showError?: boolean }} options
- * @returns {Promise<{ code: number, data: any, msg?: string }>}
- */
- export function bxRequest(options = {}) {
- const method = String(options.method || 'GET').toUpperCase()
- const showError = options.showError !== false
- const token = uni.getStorageSync(TOKEN_KEY) || ''
- let path = normalizeBxUrl(options.url || '')
- const query = buildQuery(options.params)
- const rawUrl = `${path}${query}`
- const url = rawUrl.startsWith('http')
- ? rawUrl
- : `${resolveApiBase()}${rawUrl.startsWith('/') ? rawUrl : `/${rawUrl}`}`
- return new Promise((resolve, reject) => {
- uni.request({
- url,
- method,
- data: method === 'GET' ? undefined : options.data,
- header: {
- 'Content-Type': 'application/json',
- ...(token ? { Authorization: `Bearer ${token}` } : {})
- },
- success: (res) => {
- const status = res.statusCode
- const body = res.data
- if (status === 401) {
- onUnauthorized()
- reject(new Error('登录已过期'))
- return
- }
- if (status < 200 || status >= 300) {
- const message = parseDetailMessage(body) || `请求失败(${status})`
- if (showError) {
- uni.showToast({ title: String(message).slice(0, 40), icon: 'none' })
- }
- reject(new Error(message))
- return
- }
- if (body && body.code !== undefined && body.code !== 0) {
- const message = body.msg || '请求失败'
- if (showError) {
- uni.showToast({ title: String(message).slice(0, 40), icon: 'none' })
- }
- reject(new Error(message))
- return
- }
- resolve(body)
- },
- fail: (err) => {
- if (showError) {
- uni.showToast({ title: '网络异常', icon: 'none' })
- }
- reject(err)
- }
- })
- })
- }
- /**
- * 附件 multipart 上传(uni.uploadFile)
- * @param {string} filePath
- * @param {{ bizType?: string, bizId?: string|number, showError?: boolean }} opts
- */
- export function bxUploadFile(filePath, opts = {}) {
- const showError = opts.showError !== false
- const token = uni.getStorageSync(TOKEN_KEY) || ''
- const query = buildQuery({
- biz_type: opts.bizType || '',
- biz_id: opts.bizId != null && opts.bizId !== '' ? opts.bizId : 0
- })
- const url = `${resolveApiBase()}/attachments/upload${query}`
- return new Promise((resolve, reject) => {
- uni.uploadFile({
- url,
- filePath,
- name: 'file',
- header: {
- ...(token ? { Authorization: `Bearer ${token}` } : {})
- },
- success: (res) => {
- const status = res.statusCode
- let body = res.data
- if (typeof body === 'string') {
- try {
- body = JSON.parse(body)
- } catch (e) {
- body = {}
- }
- }
- if (status === 401) {
- onUnauthorized()
- reject(new Error('登录已过期'))
- return
- }
- if (status < 200 || status >= 300) {
- const message = parseDetailMessage(body) || `上传失败(${status})`
- if (showError) uni.showToast({ title: String(message).slice(0, 40), icon: 'none' })
- reject(new Error(message))
- return
- }
- if (body && body.code !== undefined && body.code !== 0) {
- const message = body.msg || '上传失败'
- if (showError) uni.showToast({ title: String(message).slice(0, 40), icon: 'none' })
- reject(new Error(message))
- return
- }
- resolve(body)
- },
- fail: (err) => {
- if (showError) uni.showToast({ title: '上传失败', icon: 'none' })
- reject(err)
- }
- })
- })
- }
- /**
- * 发票 multipart 上传(POST /invoices,name=files)
- * 对齐 bx-mobile FormData:amount / remark / files / targetUserId
- * @param {{ amount: number|string, remark?: string, filePath: string, targetUserId?: string|number, showError?: boolean }} opts
- */
- export function bxUploadInvoice(opts = {}) {
- const showError = opts.showError !== false
- const token = uni.getStorageSync(TOKEN_KEY) || ''
- const filePath = opts.filePath
- if (!filePath) {
- return Promise.reject(new Error('请选择发票文件'))
- }
- const formData = {
- amount: String(opts.amount ?? '')
- }
- if (opts.remark != null && String(opts.remark).trim()) {
- formData.remark = String(opts.remark).trim()
- }
- if (opts.targetUserId != null && opts.targetUserId !== '') {
- formData.targetUserId = String(opts.targetUserId)
- }
- const url = `${resolveApiBase()}/invoices`
- return new Promise((resolve, reject) => {
- uni.uploadFile({
- url,
- filePath,
- name: 'files',
- formData,
- header: {
- ...(token ? { Authorization: `Bearer ${token}` } : {})
- },
- success: (res) => {
- const status = res.statusCode
- let body = res.data
- if (typeof body === 'string') {
- try {
- body = JSON.parse(body)
- } catch (e) {
- body = {}
- }
- }
- if (status === 401) {
- onUnauthorized()
- reject(new Error('登录已过期'))
- return
- }
- if (status < 200 || status >= 300) {
- const message = parseDetailMessage(body) || `上传失败(${status})`
- if (showError) uni.showToast({ title: String(message).slice(0, 40), icon: 'none' })
- reject(new Error(message))
- return
- }
- if (body && body.code !== undefined && body.code !== 0) {
- const message = body.msg || '上传失败'
- if (showError) uni.showToast({ title: String(message).slice(0, 40), icon: 'none' })
- reject(new Error(message))
- return
- }
- resolve(body)
- },
- fail: (err) => {
- if (showError) uni.showToast({ title: '上传失败', icon: 'none' })
- reject(err)
- }
- })
- })
- }
- export default bxRequest
|