提交新的移动端框架

This commit is contained in:
zlwu committed 2025-08-07 13:19:12 +08:00
1 parent 134f711349
commit 9bda3717fe
75 files changed
+5283 -9011

No files matched your search

+394
View File
@@ -0,0 +1,394 @@
// 移动端统一请求工具 - 与mobile工程保持一致
import {
generateAesKey,
encryptBase64,
decryptBase64,
encryptWithAes,
decryptWithAes,
encrypt,
decrypt,
} from './crypto.js';
import { ENCRYPT_CONFIG, REQUEST_CONFIG } from './config.js';
import { sessionCache, userStorage } from './storage.js';
/**
* 获取token
*/
function getToken() {
try {
const token = uni.getStorageSync('access_token');
console.log(
'🔍 获取Token:',
token ? `Bearer ${token.substring(0, 20)}...` : '❌ Token为空',
);
return token ? `Bearer ${token}` : '';
} catch (e) {
console.error('❌ 获取Token失败:', e);
return '';
}
}
/**
* 处理401未授权响应
*/
function handleUnauthorized(message = '登录已失效,请重新登录') {
console.log('🔒 处理401未授权响应:', message);
// 清除登录信息
try {
uni.removeStorageSync('access_token');
uni.removeStorageSync('userInfo');
uni.removeStorageSync('isLoggedIn');
console.log('📤 已清除本地登录信息');
} catch (e) {
console.error('❌ 清除登录信息失败:', e);
}
// 显示提示信息
uni.showToast({
title: message,
icon: 'none',
duration: 2000,
});
// 延迟跳转,确保Toast显示
setTimeout(() => {
// 获取当前页面路径
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const currentPath = '/' + currentPage.route;
// 如果当前不是登录页,则跳转到登录页
if (currentPath !== '/pages/login/index') {
console.log('🔄 从', currentPath, '跳转到登录页');
uni.reLaunch({
url: '/pages/login/index',
fail: (error) => {
console.error('❌ 跳转登录页失败:', error);
// 如果reLaunch失败,尝试其他方式
uni.redirectTo({
url: '/pages/login/index',
});
},
});
}
}, 500);
}
/**
* 统一请求方法
*/
export const request = (options) => {
return new Promise((resolve, reject) => {
// 获取token
const token = getToken();
// 处理请求配置
const config = {
url: REQUEST_CONFIG.baseURL + options.url,
method: options.method || 'GET',
data: options.data || {},
timeout: options.timeout || REQUEST_CONFIG.timeout,
header: {
'Content-Type': 'application/json',
authorization: token,
clientid: options.clientId || REQUEST_CONFIG.clientId,
// 注意:在H5环境中,浏览器会自动设置referer,手动设置会报错
// referer: options.referer || REQUEST_CONFIG.referer,
...options.header,
},
};
// 检查特殊header配置
const isToken = options.header?.isToken === false;
const isRepeatSubmit = options.header?.repeatSubmit === false;
const isEncrypt = options.header?.isEncrypt === 'true';
// 如果不需要token,移除authorization
if (isToken) {
delete config.header.authorization;
}
// 防重复提交检查
if (!isRepeatSubmit && (config.method === 'POST' || config.method === 'PUT')) {
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime(),
};
const sessionObj = sessionCache.get('sessionObj');
if (sessionObj) {
const s_url = sessionObj.url;
const s_data = sessionObj.data;
const s_time = sessionObj.time;
const interval = REQUEST_CONFIG.repeatSubmitInterval;
if (
s_data === requestObj.data &&
requestObj.time - s_time < interval &&
s_url === requestObj.url
) {
const message = '数据正在处理,请勿重复提交';
console.warn(`[${s_url}]: ` + message);
uni.showToast({
title: message,
icon: 'none',
});
return reject(new Error(message));
}
}
sessionCache.set('sessionObj', requestObj);
}
// 加密处理
if (
REQUEST_CONFIG.encryptEnabled &&
isEncrypt &&
(config.method === 'POST' || config.method === 'PUT')
) {
try {
console.log('🔐 开始请求加密处理...');
// 生成AES密钥
const aesKey = generateAesKey();
// 使用RSA加密AES密钥
const encryptedKey = encrypt(encryptBase64(aesKey));
if (!encryptedKey) {
throw new Error('RSA加密AES密钥失败');
}
// 设置加密头
config.header[ENCRYPT_CONFIG.ENCRYPT_HEADER] = encryptedKey;
// 加密请求数据
const dataToEncrypt =
typeof config.data === 'object' ? JSON.stringify(config.data) : config.data;
config.data = encryptWithAes(dataToEncrypt, aesKey);
console.log('✅ 请求数据加密完成');
} catch (error) {
console.error('❌ 请求加密失败:', error);
uni.showToast({
title: '请求加密失败',
icon: 'none',
});
return reject(error);
}
}
// FormData处理
if (config.data instanceof globalThis.FormData) {
delete config.header['Content-Type'];
}
console.log('📤 发送请求:', {
url: config.url,
method: config.method,
hasToken: !!token,
isEncrypt: isEncrypt,
});
// 发送请求
uni.request({
...config,
success: (res) => {
console.log('📥 收到响应:', {
statusCode: res.statusCode,
url: config.url,
hasEncryptHeader: !!res.header[ENCRYPT_CONFIG.ENCRYPT_HEADER],
});
// 解密处理
if (REQUEST_CONFIG.encryptEnabled && res.header[ENCRYPT_CONFIG.ENCRYPT_HEADER]) {
try {
console.log('🔓 开始响应解密处理...');
// 获取加密的AES密钥
const keyStr = res.header[ENCRYPT_CONFIG.ENCRYPT_HEADER];
// RSA解密获取AES密钥
const base64Str = decrypt(keyStr);
if (!base64Str) {
throw new Error('RSA解密AES密钥失败');
}
// Base64解码获取AES密钥
const aesKey = decryptBase64(base64Str);
// AES解密响应数据
const decryptedData = decryptWithAes(res.data, aesKey);
// 解析JSON
res.data = JSON.parse(decryptedData);
console.log('✅ 响应数据解密完成');
} catch (error) {
console.error('❌ 响应解密失败:', error);
uni.showToast({
title: '响应解密失败',
icon: 'none',
});
return reject(error);
}
}
// 处理响应
if (res.statusCode === 200) {
// 检查业务状态码
if (res.data && res.data.code !== undefined) {
if (res.data.code === 200) {
resolve(res.data);
} else if (res.data.code === 401) {
// 401未授权,跳转到登录页
console.log('🔒 Token失效,自动跳转到登录页');
handleUnauthorized(res.data.msg || '登录已失效,请重新登录');
reject(res.data);
} else {
// 其他业务错误
const errorMsg = res.data.msg || '请求失败';
if (res.data.code !== 404) {
// 404错误静默处理
uni.showToast({
title: errorMsg,
icon: 'none',
});
}
reject(res.data);
}
} else {
resolve(res.data);
}
} else if (res.statusCode === 401) {
// HTTP 401未授权
console.log('🔒 HTTP 401未授权,自动跳转到登录页');
handleUnauthorized('登录已失效,请重新登录');
reject(res);
} else if (res.statusCode === 404) {
// 404错误静默处理
console.log('404错误被静默处理,不显示用户提示');
reject(res);
} else {
// 其他HTTP状态码错误
const errorMsg = `请求失败: ${res.statusCode}`;
uni.showToast({
title: errorMsg,
icon: 'none',
});
reject(res);
}
},
fail: (error) => {
console.error('❌ 网络请求失败:', error);
let message = '网络请求失败';
if (error.errMsg) {
if (error.errMsg.includes('timeout')) {
message = '请求超时,请检查网络连接';
} else if (error.errMsg.includes('fail')) {
message = '网络连接失败,请检查网络';
}
}
uni.showToast({
title: message,
icon: 'none',
});
reject(error);
},
});
});
};
// API方法封装
export const api = {
// 用户登录 - 使用统一的request方法和加密拦截器
login: (data) => {
console.log('🔐 开始登录流程...');
return request({
url: '/auth/login',
method: 'POST',
data: {
username: data.username,
password: data.password,
tenantId: data.tenantId || '000000',
clientId: data.clientId || REQUEST_CONFIG.clientId,
grantType: data.grantType || 'password',
},
header: {
isToken: false, // 登录接口不需要token
isEncrypt: 'true', // 启用加密
},
});
},
// 获取用户信息
getUserInfo: () => {
const token = getToken();
console.log(
'🎫 获取用户信息,当前token:',
token ? token.substring(0, 20) + '...' : '无token',
);
return request({
url: '/system/user/getInfo',
method: 'GET',
});
},
// 获取租户列表
getTenantList: (isToken = false) =>
request({
url: '/auth/tenant/list',
method: 'GET',
header: {
isToken: isToken,
},
}),
// 注销登录
logout: () =>
request({
url: '/auth/logout',
method: 'POST',
}),
};
// 通用下载方法
export const download = (url, params, fileName) => {
// 显示加载提示
uni.showLoading({
title: '正在下载数据,请稍候',
mask: true,
});
return request({
url: url,
method: 'POST',
data: params,
header: {
'Content-Type': 'application/x-www-form-urlencoded',
},
timeout: 1200000, // 20分钟超时
responseType: 'arraybuffer',
})
.then((resp) => {
// 处理下载响应
// 注意:uni-app中文件下载需要特殊处理
console.log('文件下载完成:', fileName);
uni.hideLoading();
// 这里需要根据平台进行不同的文件保存处理
// 具体实现可能需要平台特定的代码
})
.catch((error) => {
console.error('下载失败:', error);
uni.hideLoading();
uni.showToast({
title: '下载文件出现错误,请联系管理员!',
icon: 'none',
});
});
};