上传
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 认证工具 - Token 和用户信息管理
|
||||
*/
|
||||
import config from '@/api/config.js'
|
||||
import {
|
||||
getStorage,
|
||||
setStorage,
|
||||
removeStorage
|
||||
} from './storage'
|
||||
|
||||
/**
|
||||
* 获取 Token
|
||||
* @returns {string|null}
|
||||
*/
|
||||
export function getToken() {
|
||||
return getStorage(config.tokenKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Token
|
||||
* @param {string} token
|
||||
*/
|
||||
export function setToken(token) {
|
||||
setStorage(config.tokenKey, token)
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除 Token
|
||||
*/
|
||||
export function removeToken() {
|
||||
removeStorage(config.tokenKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否已登录
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isLoggedIn() {
|
||||
return !!getToken()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @returns {Object|null}
|
||||
*/
|
||||
export function getUserInfo() {
|
||||
return getStorage(config.userInfoKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户信息
|
||||
* @param {Object} userInfo
|
||||
*/
|
||||
export function setUserInfo(userInfo) {
|
||||
setStorage(config.userInfoKey, userInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有认证信息
|
||||
*/
|
||||
export function clearAuthInfo() {
|
||||
removeToken()
|
||||
removeStorage(config.userInfoKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录检查,未登录则跳转登录页
|
||||
* @param {string} [redirectUrl] 登录后重定向地址
|
||||
* @returns {boolean} 是否已登录
|
||||
*/
|
||||
export function checkLogin(redirectUrl) {
|
||||
if (isLoggedIn()) {
|
||||
return true
|
||||
}
|
||||
const url = redirectUrl ?
|
||||
`/pages/login/login?redirect=${encodeURIComponent(redirectUrl)}` :
|
||||
'/pages/login/login'
|
||||
uni.navigateTo({
|
||||
url
|
||||
})
|
||||
return false
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
export * from './storage'
|
||||
export * from './auth'
|
||||
export * from './platform'
|
||||
export {
|
||||
default as request, get, post, put, del, upload
|
||||
}
|
||||
from './request'
|
||||
|
||||
/**
|
||||
* 防抖函数
|
||||
* @param {Function} fn 目标函数
|
||||
* @param {number} [delay=300] 延迟毫秒
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function debounce(fn, delay = 300) {
|
||||
let timer = null
|
||||
return function(...args) {
|
||||
if (timer) clearTimeout(timer)
|
||||
timer = setTimeout(() => {
|
||||
fn.apply(this, args)
|
||||
timer = null
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 节流函数
|
||||
* @param {Function} fn 目标函数
|
||||
* @param {number} [interval=300] 间隔毫秒
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function throttle(fn, interval = 300) {
|
||||
let lastTime = 0
|
||||
return function(...args) {
|
||||
const now = Date.now()
|
||||
if (now - lastTime >= interval) {
|
||||
lastTime = now
|
||||
fn.apply(this, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 深拷贝
|
||||
* @param {*} obj
|
||||
* @returns {*}
|
||||
*/
|
||||
export function deepClone(obj) {
|
||||
if (obj === null || typeof obj !== 'object') return obj
|
||||
if (obj instanceof Date) return new Date(obj)
|
||||
if (obj instanceof RegExp) return new RegExp(obj)
|
||||
const clone = Array.isArray(obj) ? [] : {}
|
||||
for (const key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
clone[key] = deepClone(obj[key])
|
||||
}
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟执行(Promise 版 setTimeout)
|
||||
* @param {number} ms 毫秒
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一 ID
|
||||
* @returns {string}
|
||||
*/
|
||||
export function generateId() {
|
||||
return Date.now().toString(36) + Math.random().toString(36).substring(2)
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面跳转封装
|
||||
*/
|
||||
export const router = {
|
||||
push(url, params) {
|
||||
const query = params ? '?' + objectToQuery(params) : ''
|
||||
uni.navigateTo({
|
||||
url: url + query
|
||||
})
|
||||
},
|
||||
replace(url, params) {
|
||||
const query = params ? '?' + objectToQuery(params) : ''
|
||||
uni.redirectTo({
|
||||
url: url + query
|
||||
})
|
||||
},
|
||||
reLaunch(url, params) {
|
||||
const query = params ? '?' + objectToQuery(params) : ''
|
||||
uni.reLaunch({
|
||||
url: url + query
|
||||
})
|
||||
},
|
||||
switchTab(url) {
|
||||
uni.switchTab({
|
||||
url
|
||||
})
|
||||
},
|
||||
back(delta = 1) {
|
||||
uni.navigateBack({
|
||||
delta
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象转查询字符串
|
||||
* @param {Object} obj
|
||||
* @returns {string}
|
||||
*/
|
||||
export function objectToQuery(obj) {
|
||||
return Object.keys(obj)
|
||||
.filter((key) => obj[key] !== undefined && obj[key] !== null)
|
||||
.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`)
|
||||
.join('&')
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示消息提示
|
||||
*/
|
||||
export const toast = {
|
||||
success(title, duration = 1500) {
|
||||
uni.showToast({
|
||||
title,
|
||||
icon: 'success',
|
||||
duration
|
||||
})
|
||||
},
|
||||
error(title, duration = 2000) {
|
||||
uni.showToast({
|
||||
title,
|
||||
icon: 'none',
|
||||
duration
|
||||
})
|
||||
},
|
||||
loading(title = '加载中...') {
|
||||
uni.showLoading({
|
||||
title,
|
||||
mask: true
|
||||
})
|
||||
},
|
||||
hideLoading() {
|
||||
uni.hideLoading()
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认弹窗(Promise 版)
|
||||
* @param {string} content 内容
|
||||
* @param {string} [title='提示'] 标题
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
export function confirm(content, title = '提示') {
|
||||
return new Promise((resolve) => {
|
||||
uni.showModal({
|
||||
title,
|
||||
content,
|
||||
success: (res) => resolve(!!res.confirm),
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* 获取系统信息
|
||||
* @param {boolean} [forceRefresh=false] 强制重新获取(折叠屏场景使用)
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function getSystemInfo() {
|
||||
return uni.getSystemInfoSync()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态栏高度
|
||||
* @param {boolean} [forceRefresh=false]
|
||||
* @returns {number}
|
||||
*/
|
||||
export function getStatusBarHeight() {
|
||||
return getSystemInfo().statusBarHeight || 0
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/**
|
||||
* 网络请求封装 - 基于 uni.request 二次封装
|
||||
*/
|
||||
import config from '@/api/config.js';
|
||||
import { getToken, clearAuthInfo, getUserInfo } from './auth';
|
||||
|
||||
// 请求队列(用于防重复请求)
|
||||
const pendingRequests = new Map();
|
||||
|
||||
/**
|
||||
* 生成请求唯一标识
|
||||
*/
|
||||
function generateRequestKey(options) {
|
||||
return `${options.method}_${options.url}_${JSON.stringify(options.data || {})}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心请求方法
|
||||
* @param {Object} options 请求配置
|
||||
* @param {string} options.url 请求路径(不含 baseUrl)
|
||||
* @param {string} [options.method='GET'] 请求方法
|
||||
* @param {Object} [options.data] 请求参数
|
||||
* @param {Object} [options.header] 自定义请求头
|
||||
* @param {boolean} [options.loading=false] 是否显示加载提示
|
||||
* @param {string} [options.loadingText='加载中...'] 加载提示文字
|
||||
* @param {boolean} [options.showError=true] 是否显示错误提示
|
||||
* @param {boolean} [options.preventDuplicate=false] 是否防止重复请求
|
||||
* @param {boolean} [options.isUnitId=true] 是否需要unitId参数
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function request(options) {
|
||||
const { url, method = 'GET', data = {}, header = {}, loading = false, loadingText = '加载中...', showError = true, preventDuplicate = false, isUnitId = true } = options;
|
||||
|
||||
const fullUrl = url.startsWith('http') ? url : `${config.baseUrl}${url}`;
|
||||
|
||||
// 防重复请求
|
||||
const requestKey = generateRequestKey({
|
||||
method,
|
||||
url: fullUrl,
|
||||
data
|
||||
});
|
||||
if (preventDuplicate && pendingRequests.has(requestKey)) {
|
||||
return pendingRequests.get(requestKey);
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
if (loading) {
|
||||
uni.showLoading({
|
||||
title: loadingText,
|
||||
mask: true
|
||||
});
|
||||
}
|
||||
|
||||
// 构建请求头
|
||||
const token = getToken();
|
||||
const requestHeader = {
|
||||
'Content-Type': 'application/json',
|
||||
...header
|
||||
};
|
||||
if (token) {
|
||||
requestHeader['token'] = token;
|
||||
}
|
||||
//获取用户的unitld
|
||||
const userInfo = getUserInfo();
|
||||
if (userInfo?.UnitId && isUnitId) {
|
||||
console.log('用户id的get', userInfo.UnitId);
|
||||
data['unitId'] = userInfo.UnitId;
|
||||
}
|
||||
const requestPromise = new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: fullUrl,
|
||||
method,
|
||||
data,
|
||||
header: requestHeader,
|
||||
timeout: config.timeout,
|
||||
success: (res) => {
|
||||
const { statusCode, data: responseData } = res;
|
||||
// HTTP 状态码判断
|
||||
if (statusCode === 200) {
|
||||
// 根据业务状态码处理(按实际后端接口约定调整)
|
||||
if (responseData.code === 1 || responseData.code === 200) {
|
||||
resolve(responseData.data);
|
||||
} else if (responseData.code === 401) {
|
||||
// Token 过期/无效
|
||||
handleUnauthorized();
|
||||
reject(responseData);
|
||||
} else {
|
||||
// 业务错误
|
||||
if (showError) {
|
||||
uni.showToast({
|
||||
title: responseData.message || '请求失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
reject(responseData);
|
||||
}
|
||||
} else if (statusCode === 401) {
|
||||
handleUnauthorized();
|
||||
reject(res);
|
||||
} else {
|
||||
if (showError) {
|
||||
uni.showToast({
|
||||
title: `请求错误 (${statusCode})`,
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
reject(res);
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
if (showError) {
|
||||
uni.showToast({
|
||||
title: '网络异常,请检查网络连接',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
reject(err);
|
||||
},
|
||||
complete: () => {
|
||||
if (loading) {
|
||||
uni.hideLoading();
|
||||
}
|
||||
pendingRequests.delete(requestKey);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (preventDuplicate) {
|
||||
pendingRequests.set(requestKey, requestPromise);
|
||||
}
|
||||
|
||||
return requestPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 401 未授权
|
||||
*/
|
||||
function handleUnauthorized() {
|
||||
clearAuthInfo();
|
||||
uni.showToast({
|
||||
title: '登录已过期,请重新登录',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/login'
|
||||
});
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
* @param {string} filePath 文件本地路径
|
||||
* @param {Object} [formData] 额外表单数据
|
||||
* @param {string} [name='file'] 文件字段名
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function upload(filePath, formData = {}, name = 'file') {
|
||||
const token = getToken();
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.showLoading({
|
||||
title: '上传中...',
|
||||
mask: true
|
||||
});
|
||||
uni.uploadFile({
|
||||
url: config.uploadUrl || `${config.baseUrl}/upload`,
|
||||
filePath,
|
||||
name,
|
||||
formData,
|
||||
header: token
|
||||
? {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
: {},
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
const data = JSON.parse(res.data);
|
||||
if (data.code === 0 || data.code === 200) {
|
||||
resolve(data.data);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: data.message || '上传失败',
|
||||
icon: 'none'
|
||||
});
|
||||
reject(data);
|
||||
}
|
||||
} else {
|
||||
reject(res);
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.showToast({
|
||||
title: '上传失败',
|
||||
icon: 'none'
|
||||
});
|
||||
reject(err);
|
||||
},
|
||||
complete: () => {
|
||||
uni.hideLoading();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 快捷方法
|
||||
/**
|
||||
* 核心请求方法
|
||||
* @param {Object} options 请求配置
|
||||
* @param {string} options.url 请求路径(不含 baseUrl)
|
||||
* @param {string} [options.method='GET'] 请求方法
|
||||
* @param {Object} [options.data] 请求参数
|
||||
* @param {Object} [options.header] 自定义请求头
|
||||
* @param {boolean} [options.loading=false] 是否显示加载提示
|
||||
* @param {string} [options.loadingText='加载中...'] 加载提示文字
|
||||
* @param {boolean} [options.showError=true] 是否显示错误提示
|
||||
* @param {boolean} [options.preventDuplicate=false] 是否防止重复请求
|
||||
* @param {boolean} [options.isUnitId=true] 是否需要unitId参数
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const get = (url, data, options = {}) =>
|
||||
request({
|
||||
url,
|
||||
method: 'GET',
|
||||
data,
|
||||
...options
|
||||
});
|
||||
|
||||
export const post = (url, data, options = {}) =>
|
||||
request({
|
||||
url,
|
||||
method: 'POST',
|
||||
data,
|
||||
...options
|
||||
});
|
||||
|
||||
export const put = (url, data, options = {}) =>
|
||||
request({
|
||||
url,
|
||||
method: 'PUT',
|
||||
data,
|
||||
...options
|
||||
});
|
||||
|
||||
export const del = (url, data, options = {}) =>
|
||||
request({
|
||||
url,
|
||||
method: 'DELETE',
|
||||
data,
|
||||
...options
|
||||
});
|
||||
|
||||
export default request;
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* 本地存储封装 - 兼容微信小程序和 App
|
||||
*/
|
||||
|
||||
/**
|
||||
* 设置本地存储(同步)
|
||||
* @param {string} key 键名
|
||||
* @param {*} value 值(自动序列化)
|
||||
*/
|
||||
export function setStorage(key, value) {
|
||||
try {
|
||||
uni.setStorageSync(key, JSON.stringify(value))
|
||||
} catch (e) {
|
||||
console.error(`setStorage [${key}] 失败:`, e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地存储(同步)
|
||||
* @param {string} key 键名
|
||||
* @param {*} defaultValue 默认值
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getStorage(key, defaultValue = null) {
|
||||
try {
|
||||
const value = uni.getStorageSync(key)
|
||||
if (value) {
|
||||
return JSON.parse(value)
|
||||
}
|
||||
return defaultValue
|
||||
} catch (e) {
|
||||
console.error(`getStorage [${key}] 失败:`, e)
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除本地存储
|
||||
* @param {string} key 键名
|
||||
*/
|
||||
export function removeStorage(key) {
|
||||
try {
|
||||
uni.removeStorageSync(key)
|
||||
} catch (e) {
|
||||
console.error(`removeStorage [${key}] 失败:`, e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有本地存储
|
||||
*/
|
||||
export function clearStorage() {
|
||||
try {
|
||||
uni.clearStorageSync()
|
||||
} catch (e) {
|
||||
console.error('clearStorage 失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置带过期时间的存储
|
||||
* @param {string} key 键名
|
||||
* @param {*} value 值
|
||||
* @param {number} expire 过期时间(秒)
|
||||
*/
|
||||
export function setStorageWithExpire(key, value, expire) {
|
||||
const data = {
|
||||
value,
|
||||
expire: expire ? Date.now() + expire * 1000 : null,
|
||||
}
|
||||
setStorage(key, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取带过期时间的存储
|
||||
* @param {string} key 键名
|
||||
* @param {*} defaultValue 默认值
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getStorageWithExpire(key, defaultValue = null) {
|
||||
const data = getStorage(key)
|
||||
if (!data) return defaultValue
|
||||
if (data.expire && Date.now() > data.expire) {
|
||||
removeStorage(key)
|
||||
return defaultValue
|
||||
}
|
||||
return data.value
|
||||
}
|
||||
Reference in New Issue
Block a user