139 lines
3.6 KiB
JavaScript
139 lines
3.6 KiB
JavaScript
import { useUserStore } from '@/store'
|
||
|
||
export const baseUrl = 'https://lygcgs.com.cn:8078/shjapi/api/'
|
||
export const baseFileUrl = 'https://lygcgs.com.cn:8078/shj/'
|
||
|
||
/**
|
||
* 将参数对象序列化为 URL query string
|
||
* 手动构建确保跨平台(小程序 / H5 / App)行为一致
|
||
* - 过滤 undefined 和 null
|
||
* - encodeURIComponent 编码中文和特殊字符
|
||
*/
|
||
function toQueryString(params) {
|
||
if (!params) return ''
|
||
const parts = Object.keys(params)
|
||
.filter((k) => params[k] !== undefined && params[k] !== null)
|
||
.map((k) => `${encodeURIComponent(k)}=${encodeURIComponent(params[k])}`)
|
||
return parts.length ? '?' + parts.join('&') : ''
|
||
}
|
||
|
||
/**
|
||
* 核心请求方法(兼容 小程序 / H5 / App)
|
||
*
|
||
* @param {string} url - API 路径(不含 baseUrl)
|
||
* @param {object} options - 可选配置
|
||
* {string} method - GET | POST,默认 GET
|
||
* {object} data - GET 时自动拼入 query string,POST 时作为请求体
|
||
* {object} header - 自定义请求头
|
||
* {boolean} loading - 是否显示 loading,默认 true
|
||
* {boolean} toast - 是否在失败时弹 toast,默认 true
|
||
*/
|
||
export function request(url, options = {}) {
|
||
const {
|
||
method = 'GET',
|
||
data,
|
||
header = {},
|
||
loading = true,
|
||
toast = true
|
||
} = options
|
||
|
||
const userStore = useUserStore()
|
||
const headers = {
|
||
'content-type': 'application/json',
|
||
...header
|
||
}
|
||
if (userStore.token) {
|
||
headers.token = userStore.token
|
||
}
|
||
|
||
// GET: 手动拼 query string(不依赖 uni.request 各平台差异)
|
||
let fullUrl = `${baseUrl}${url}`
|
||
if (method === 'GET' && data) {
|
||
fullUrl += toQueryString(data)
|
||
}
|
||
|
||
if (loading) {
|
||
uni.showLoading({ title: '加载中...', mask: true })
|
||
}
|
||
|
||
return new Promise((resolve, reject) => {
|
||
uni.request({
|
||
url: fullUrl,
|
||
method,
|
||
data: method === 'POST' ? data : undefined,
|
||
header: headers,
|
||
success: (res) => {
|
||
if (loading) uni.hideLoading()
|
||
const body = res.data
|
||
if (body.code !== 1) {
|
||
if (toast) {
|
||
uni.showToast({
|
||
title: body.message || '请求失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
if (body.code === 401) {
|
||
userStore.logout()
|
||
uni.reLaunch({ url: '/pages/login/index' })
|
||
}
|
||
reject(new Error(body.message || '请求失败'))
|
||
return
|
||
}
|
||
resolve(body)
|
||
},
|
||
fail: (err) => {
|
||
if (loading) uni.hideLoading()
|
||
uni.showToast({ title: '网络连接失败', icon: 'none' })
|
||
reject(err)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* GET 快捷方法
|
||
* 兼容两种调用方式:
|
||
* get('/path?key=val') 旧风格(URL 自带参数)
|
||
* get('/path', { key: 'val' }) 新风格(参数对象,自动编码)
|
||
*/
|
||
export const get = (url, params) => {
|
||
if (params) {
|
||
url += toQueryString(params)
|
||
}
|
||
return request(url, { method: 'GET' })
|
||
}
|
||
|
||
/**
|
||
* POST 快捷方法
|
||
*/
|
||
export const post = (url, data) => {
|
||
return request(url, { method: 'POST', data })
|
||
}
|
||
|
||
/**
|
||
* 解析附件 URL 字符串为可展示的照片列表
|
||
* @param {string} urlStr - 逗号分隔的附件路径,如 "FileUpLoad/xxx.png,FileUpLoad/yyy.png"
|
||
* @param {number} startId - 起始 ID,用于生成唯一标识
|
||
* @returns {Array<{_id: number, src: string, uploaded: boolean}>}
|
||
*/
|
||
export const parseAttachUrls = (urlStr, startId = 0) => {
|
||
if (!urlStr) return []
|
||
let id = startId
|
||
return urlStr.split(',').filter(Boolean).map((url) => ({
|
||
_id: ++id,
|
||
src: `${baseFileUrl}${url}`,
|
||
_attachUrl: url,
|
||
uploaded: true
|
||
}))
|
||
}
|
||
|
||
export const getUrlParam = (url, name) => {
|
||
if (!url || !name) return null
|
||
const regex = new RegExp(`[?&]${name}=([^&]*)`, 'i')
|
||
const match = url.match(regex)
|
||
if (match && match[1]) {
|
||
return decodeURIComponent(match[1])
|
||
}
|
||
return null
|
||
}
|