存客宝 React
This commit is contained in:
140
Cunkebao_Uniapp/utils/auth.js
Normal file
140
Cunkebao_Uniapp/utils/auth.js
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* 认证相关工具函数
|
||||
*/
|
||||
import { refreshToken } from '@/api/user';
|
||||
|
||||
const TOKEN_KEY = 'token';
|
||||
const TOKEN_EXPIRES_KEY = 'token_expires';
|
||||
const USER_INFO_KEY = 'user_info';
|
||||
|
||||
/**
|
||||
* 设置Token
|
||||
* @param {string} token 令牌
|
||||
* @param {number} expires 过期时间(秒)
|
||||
*/
|
||||
function setToken(token, expires = 7200) {
|
||||
uni.setStorageSync(TOKEN_KEY, token);
|
||||
|
||||
// 计算过期时间点(当前时间 + 有效期)
|
||||
const expiresTime = Math.floor(Date.now() / 1000) + expires;
|
||||
uni.setStorageSync(TOKEN_EXPIRES_KEY, expiresTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Token
|
||||
* @returns {string} token值
|
||||
*/
|
||||
function getToken() {
|
||||
return uni.getStorageSync(TOKEN_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除Token
|
||||
*/
|
||||
function removeToken() {
|
||||
uni.removeStorageSync(TOKEN_KEY);
|
||||
uni.removeStorageSync(TOKEN_EXPIRES_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户信息
|
||||
* @param {Object} userInfo 用户信息
|
||||
*/
|
||||
function setUserInfo(userInfo) {
|
||||
uni.setStorageSync(USER_INFO_KEY, JSON.stringify(userInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @returns {Object} 用户信息
|
||||
*/
|
||||
function getUserInfo() {
|
||||
const userInfo = uni.getStorageSync(USER_INFO_KEY);
|
||||
return userInfo ? JSON.parse(userInfo) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除用户信息
|
||||
*/
|
||||
function removeUserInfo() {
|
||||
uni.removeStorageSync(USER_INFO_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除所有认证信息
|
||||
*/
|
||||
function removeAll() {
|
||||
removeToken();
|
||||
removeUserInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新Token
|
||||
* @returns {Promise} 刷新结果
|
||||
*/
|
||||
function refreshTokenAsync() {
|
||||
return new Promise((resolve, reject) => {
|
||||
refreshToken()
|
||||
.then(res => {
|
||||
if (res.code === 200) {
|
||||
// 更新Token
|
||||
setToken(res.data.token, res.data.token_expired - Math.floor(Date.now() / 1000));
|
||||
resolve(res);
|
||||
} else {
|
||||
reject(res);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否已登录
|
||||
* @returns {boolean} 是否已登录
|
||||
*/
|
||||
function isLogin() {
|
||||
const token = getToken();
|
||||
// 如果没有token,直接返回未登录
|
||||
if (!token) return false;
|
||||
|
||||
// 检查token是否过期
|
||||
const expiresTime = uni.getStorageSync(TOKEN_EXPIRES_KEY) || 0;
|
||||
const nowTime = Math.floor(Date.now() / 1000);
|
||||
|
||||
// 如果当前时间超过过期时间,则返回未登录
|
||||
return nowTime < expiresTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户类型
|
||||
* @returns {number} 用户类型ID
|
||||
*/
|
||||
function getUserType() {
|
||||
const userInfo = getUserInfo();
|
||||
return userInfo ? userInfo.typeId || 0 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为管理员
|
||||
* @returns {boolean} 是否为管理员
|
||||
*/
|
||||
function isAdmin() {
|
||||
const userInfo = getUserInfo();
|
||||
return userInfo ? !!userInfo.isAdmin : false;
|
||||
}
|
||||
|
||||
export default {
|
||||
setToken,
|
||||
getToken,
|
||||
removeToken,
|
||||
setUserInfo,
|
||||
getUserInfo,
|
||||
removeUserInfo,
|
||||
removeAll,
|
||||
isLogin,
|
||||
refreshToken: refreshTokenAsync,
|
||||
getUserType,
|
||||
isAdmin
|
||||
};
|
||||
141
Cunkebao_Uniapp/utils/common.js
Normal file
141
Cunkebao_Uniapp/utils/common.js
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* 通用工具函数集合
|
||||
*/
|
||||
|
||||
/**
|
||||
* 格式化日期
|
||||
* @param {Date|string|number} date 日期对象/日期字符串/时间戳
|
||||
* @param {string} format 格式化模板,如:YYYY-MM-DD HH:mm:ss
|
||||
* @returns {string} 格式化后的日期字符串
|
||||
*/
|
||||
function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') {
|
||||
if (!date) return '';
|
||||
|
||||
// 如果是时间戳或字符串,转为日期对象
|
||||
if (typeof date === 'string' || typeof date === 'number') {
|
||||
date = new Date(date);
|
||||
}
|
||||
|
||||
// 定义替换规则
|
||||
const rules = {
|
||||
'YYYY': date.getFullYear(),
|
||||
'MM': padZero(date.getMonth() + 1),
|
||||
'DD': padZero(date.getDate()),
|
||||
'HH': padZero(date.getHours()),
|
||||
'mm': padZero(date.getMinutes()),
|
||||
'ss': padZero(date.getSeconds())
|
||||
};
|
||||
|
||||
// 替换
|
||||
return format.replace(/(YYYY|MM|DD|HH|mm|ss)/g, function(key) {
|
||||
return rules[key];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 补零
|
||||
* @param {number} num 数字
|
||||
* @returns {string} 补零后的字符串
|
||||
*/
|
||||
function padZero(num) {
|
||||
return String(num).padStart(2, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化手机号
|
||||
* @param {string} mobile 手机号
|
||||
* @returns {string} 格式化后的手机号,如:138****8888
|
||||
*/
|
||||
function formatMobile(mobile) {
|
||||
if (!mobile) return '';
|
||||
return mobile.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2');
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成UUID
|
||||
* @returns {string} UUID
|
||||
*/
|
||||
function generateUUID() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
const r = Math.random() * 16 | 0;
|
||||
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 深度克隆对象
|
||||
* @param {Object} obj 需要克隆的对象
|
||||
* @returns {Object} 克隆后的对象
|
||||
*/
|
||||
function deepClone(obj) {
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 处理日期
|
||||
if (obj instanceof Date) {
|
||||
return new Date(obj.getTime());
|
||||
}
|
||||
|
||||
// 处理数组
|
||||
if (obj instanceof Array) {
|
||||
return obj.map(item => deepClone(item));
|
||||
}
|
||||
|
||||
// 处理对象
|
||||
const clonedObj = {};
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
clonedObj[key] = deepClone(obj[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return clonedObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 防抖函数
|
||||
* @param {Function} fn 需要防抖的函数
|
||||
* @param {number} delay 延迟时间,单位ms
|
||||
* @returns {Function} 防抖后的函数
|
||||
*/
|
||||
function debounce(fn, delay = 300) {
|
||||
let timer = null;
|
||||
|
||||
return function(...args) {
|
||||
if (timer) clearTimeout(timer);
|
||||
|
||||
timer = setTimeout(() => {
|
||||
fn.apply(this, args);
|
||||
}, delay);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 节流函数
|
||||
* @param {Function} fn 需要节流的函数
|
||||
* @param {number} delay 延迟时间,单位ms
|
||||
* @returns {Function} 节流后的函数
|
||||
*/
|
||||
function throttle(fn, delay = 300) {
|
||||
let lastTime = 0;
|
||||
|
||||
return function(...args) {
|
||||
const now = Date.now();
|
||||
|
||||
if (now - lastTime >= delay) {
|
||||
fn.apply(this, args);
|
||||
lastTime = now;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
formatDate,
|
||||
formatMobile,
|
||||
generateUUID,
|
||||
deepClone,
|
||||
debounce,
|
||||
throttle
|
||||
};
|
||||
185
Cunkebao_Uniapp/utils/request.js
Normal file
185
Cunkebao_Uniapp/utils/request.js
Normal file
@@ -0,0 +1,185 @@
|
||||
import Auth from './auth';
|
||||
|
||||
// 服务器地址
|
||||
const BASE_URL = process.env.VUE_APP_BASE_API || 'http://yishi.com';
|
||||
|
||||
// 请求超时时间
|
||||
const TIMEOUT = 10000;
|
||||
|
||||
/**
|
||||
* 请求拦截器
|
||||
* @param {Object} config 请求配置
|
||||
* @returns {Object} 处理后的请求配置
|
||||
*/
|
||||
function requestInterceptor(config) {
|
||||
// 获取 token
|
||||
const token = uni.getStorageSync('token');
|
||||
|
||||
// 如果有 token,则带上请求头 Authorization: Bearer + token
|
||||
if (token) {
|
||||
config.header = {
|
||||
...config.header,
|
||||
'Authorization': `Bearer ${token}`
|
||||
};
|
||||
}
|
||||
|
||||
// 打印请求日志
|
||||
console.log('请求地址:', `${config.baseURL || BASE_URL}${config.url}`);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应拦截器
|
||||
* @param {Object} response 响应数据
|
||||
* @returns {Object|Promise} 处理后的响应数据或Promise
|
||||
*/
|
||||
function responseInterceptor(response) {
|
||||
// 未登录或token失效 - 取消登录拦截
|
||||
if (response.data.code === 401) {
|
||||
console.log('登录已过期,需要重新登录');
|
||||
|
||||
// 清除登录信息
|
||||
Auth.removeToken();
|
||||
Auth.removeUserInfo();
|
||||
|
||||
// 跳转到登录页
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/index'
|
||||
});
|
||||
|
||||
return Promise.reject(new Error('登录已过期,请重新登录'));
|
||||
}
|
||||
|
||||
// token需要刷新 - 410 状态码
|
||||
if (response.data.code === 410) {
|
||||
// 尝试刷新 token
|
||||
return Auth.refreshToken()
|
||||
.then(res => {
|
||||
if (res.code === 200) {
|
||||
// 更新本地token
|
||||
Auth.setToken(res.data.token, res.data.token_expired - Math.floor(Date.now() / 1000));
|
||||
|
||||
// 使用新token重试原请求
|
||||
const config = response.config;
|
||||
config.header.Authorization = `Bearer ${res.data.token}`;
|
||||
|
||||
// 重新发起请求
|
||||
return request(config);
|
||||
} else {
|
||||
// 刷新失败,跳转到登录页
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/index'
|
||||
});
|
||||
return Promise.reject(new Error('登录已过期,请重新登录'));
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('刷新token失败', err);
|
||||
// 清除登录信息
|
||||
Auth.removeToken();
|
||||
Auth.removeUserInfo();
|
||||
|
||||
// 跳转到登录页
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/index'
|
||||
});
|
||||
return Promise.reject(new Error('登录已过期,请重新登录'));
|
||||
});
|
||||
}
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建完整的URL,包括查询参数
|
||||
* @param {string} baseUrl 基础URL
|
||||
* @param {Object} params 查询参数
|
||||
* @returns {string} 完整的URL
|
||||
*/
|
||||
function buildUrlWithParams(baseUrl, params) {
|
||||
if (!params || Object.keys(params).length === 0) {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
const queryString = Object.keys(params)
|
||||
.filter(key => params[key] !== undefined && params[key] !== null)
|
||||
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
|
||||
.join('&');
|
||||
|
||||
return queryString ? `${baseUrl}${baseUrl.includes('?') ? '&' : '?'}${queryString}` : baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一请求函数
|
||||
* @param {Object} options 请求选项
|
||||
* @returns {Promise} 请求结果
|
||||
*/
|
||||
function request(options) {
|
||||
// 合并请求选项
|
||||
const config = {
|
||||
baseURL: BASE_URL,
|
||||
timeout: TIMEOUT,
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
...options
|
||||
};
|
||||
|
||||
// 请求拦截
|
||||
const interceptedConfig = requestInterceptor(config);
|
||||
|
||||
// 处理GET请求参数
|
||||
let url = `${interceptedConfig.baseURL}${interceptedConfig.url}`;
|
||||
const method = interceptedConfig.method || 'GET';
|
||||
|
||||
// 如果是GET请求并且有params参数,将其转换为URL查询字符串
|
||||
if (method.toUpperCase() === 'GET' && interceptedConfig.params) {
|
||||
url = buildUrlWithParams(url, interceptedConfig.params);
|
||||
|
||||
// 打印完整请求URL(便于调试)
|
||||
console.log('完整请求URL:', url);
|
||||
}
|
||||
|
||||
// 发起请求
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: url,
|
||||
method: method,
|
||||
data: method.toUpperCase() === 'GET' ? undefined : interceptedConfig.data,
|
||||
header: interceptedConfig.header,
|
||||
timeout: interceptedConfig.timeout,
|
||||
success: (res) => {
|
||||
try {
|
||||
const result = responseInterceptor(res);
|
||||
resolve(result);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
// 显示提示
|
||||
uni.showToast({
|
||||
title: '网络请求失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
|
||||
// 增强错误对象,添加更多信息便于调试
|
||||
const enhancedError = {
|
||||
...err,
|
||||
url: url,
|
||||
method: method,
|
||||
params: method.toUpperCase() === 'GET' ? interceptedConfig.params : undefined,
|
||||
data: method.toUpperCase() === 'GET' ? undefined : interceptedConfig.data,
|
||||
message: err.errMsg || '网络请求失败'
|
||||
};
|
||||
|
||||
console.error('请求失败详情:', enhancedError);
|
||||
reject(enhancedError);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default request;
|
||||
Reference in New Issue
Block a user