init: 初始系统模板

This commit is contained in:
TsMask
2023-09-05 14:38:23 +08:00
parent a5bc16ae4f
commit 1075c8ae4f
130 changed files with 22531 additions and 1 deletions

17
src/plugins/auth-token.ts Normal file
View File

@@ -0,0 +1,17 @@
import Cookies from 'js-cookie';
import { TOKEN_COOKIE } from '@/constants/token-constants';
/**获取cookis中Token字符串 */
export function getToken(): string {
return Cookies.get(TOKEN_COOKIE) || '';
}
/**设置cookis中Token字符串 */
export function setToken(token: string): void {
Cookies.set(TOKEN_COOKIE, token);
}
/**移除cookis中Token字符串 */
export function removeToken(): void {
Cookies.remove(TOKEN_COOKIE);
}

54
src/plugins/auth-user.ts Normal file
View File

@@ -0,0 +1,54 @@
import { ADMIN_PERMISSION, ADMIN_ROLE_KEY } from '@/constants/admin-constants';
import useUserStore from '@/store/modules/user';
/**
* 只需含有其中权限
* @param role 权限字符数组
* @returns true | false
*/
export function hasPermissions(permissions: string[]): boolean {
if (!permissions || permissions.length === 0) return false;
const userPermissions = useUserStore().permissions;
if (!userPermissions || userPermissions.length === 0) return false;
if (userPermissions.includes(ADMIN_PERMISSION)) return true;
return permissions.some(p => userPermissions.some(up => up === p));
}
/**
* 同时匹配其中权限
* @param role 权限字符数组
* @returns true | false
*/
export function matchPermissions(permissions: string[]): boolean {
if (!permissions || permissions.length === 0) return false;
const userPermissions = useUserStore().permissions;
if (!userPermissions || userPermissions.length === 0) return false;
if (userPermissions.includes(ADMIN_PERMISSION)) return true;
return permissions.every(p => userPermissions.some(up => up === p));
}
/**
* 只需含有其中角色
* @param role 角色字符数组
* @returns true | false
*/
export function hasRoles(roles: string[]): boolean {
if (!roles || roles.length === 0) return false;
const userRoles = useUserStore().roles;
if (!userRoles || userRoles.length === 0) return false;
if (userRoles.includes(ADMIN_ROLE_KEY)) return true;
return roles.some(r => userRoles.some(ur => ur === r));
}
/**
* 同时匹配其中角色
* @param role 角色字符数组
* @returns true | false
*/
export function matchRoles(roles: string[]): boolean {
if (!roles || roles.length === 0) return false;
const userRoles = useUserStore().roles;
if (!userRoles || userRoles.length === 0) return false;
if (userRoles.includes(ADMIN_ROLE_KEY)) return true;
return roles.every(r => userRoles.some(ur => ur === r));
}

270
src/plugins/http-fetch.ts Normal file
View File

@@ -0,0 +1,270 @@
import { getToken, removeToken } from '@/plugins/auth-token';
import { sessionGetJSON, sessionSetJSON } from '@/utils/cache-session-utils';
import { TOKEN_KEY, TOKEN_KEY_PREFIX } from '@/constants/token-constants';
import { CACHE_SESSION_FATCH } from '@/constants/cache-keys-constants';
import {
APP_REQUEST_HEADER_CODE,
APP_REQUEST_HEADER_VERSION,
} from '@/constants/app-constants';
/**响应结果类型 */
export type ResultType = {
/**响应码 */
code: number | 200 | 500;
/**信息 */
msg: string;
/**数据 */
data?: any;
/**未知属性 */
[key: string]: any;
};
/**防止重复提交类型 */
type RepeatSubmitType = {
/**请求地址 */
url: string;
/**请求数据 */
data: string;
/**请求时间 */
time: number;
};
/**请求参数类型 */
type OptionsType = {
/**请求地址 */
url: string;
/**请求方法 */
method: 'get' | 'post' | 'put' | 'delete';
/**请求头 */
headers?: HeadersInit;
/**地址栏参数 */
params?: Record<string, string | number | boolean | undefined>;
/**发送数据 */
data?: Record<string, any> | FormData | object;
/**请求数据类型 */
dataType?: 'form-data' | 'json';
/**响应数据类型 */
responseType?: 'text' | 'json' | 'blob' | 'arrayBuffer';
/**请求缓存策略 */
cache?: RequestCache;
/**请求的凭证,如 omit、same-origin、include */
credentials?: RequestCredentials;
/**请求体 */
body?: BodyInit;
/**防止数据重复提交 */
repeatSubmit?: boolean;
/**携带授权Token请求头 */
whithToken?: boolean;
};
/**全局配置类型 */
type ConfigType = {
/**请求的根域名地址-不带/后缀 */
baseUrl: string;
/**超时时间,毫秒 */
timeout: number;
};
/**默认配置 */
const FATCH_CONFIG: ConfigType = {
baseUrl: import.meta.env.VITE_API_BASE_URL,
timeout: 10 * 1000,
};
/**默认请求参数 */
const FATCH_OPTIONS: OptionsType = {
url: '',
method: 'get',
headers: {
[APP_REQUEST_HEADER_CODE]: import.meta.env.VITE_APP_CODE,
[APP_REQUEST_HEADER_VERSION]: import.meta.env.VITE_APP_VERSION,
// 使用mock.apifox.cn时开启
// apifoxToken: '8zCzh3vipdEwd1ukv9lQEuTekdWIH7xN',
},
dataType: 'json',
responseType: 'json',
cache: 'no-cache',
credentials: undefined,
repeatSubmit: true,
whithToken: true,
};
/**请求前的拦截 */
function beforeRequest(options: OptionsType): OptionsType | Promise<any> {
options.headers = Object.assign({}, options.headers);
//console.log('请求前的拦截', options);
// 给发送数据类型设置请求头
if (options.dataType === 'json') {
Reflect.set(
options.headers,
'content-type',
'application/json;charset=utf-8'
);
}
// 是否需要设置 token
const token = getToken();
if (options.whithToken && token) {
Reflect.set(options.headers, TOKEN_KEY, TOKEN_KEY_PREFIX + token);
}
// 是否需要防止数据重复提交
if (
options.repeatSubmit &&
options.dataType === 'json' &&
['post', 'put'].includes(options.method)
) {
const requestObj: RepeatSubmitType = {
url: options.url,
data: JSON.stringify(options.data),
time: Date.now(),
};
const sessionObj: RepeatSubmitType = sessionGetJSON(CACHE_SESSION_FATCH);
if (sessionObj) {
const { url, data, time } = sessionObj;
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
if (
requestObj.url === url &&
requestObj.data === data &&
requestObj.time - time < interval
) {
const message = '数据正在处理,请勿重复提交';
console.warn(`[${url}]: ${message}`);
return Promise.reject(message);
} else {
sessionSetJSON(CACHE_SESSION_FATCH, requestObj);
}
} else {
sessionSetJSON(CACHE_SESSION_FATCH, requestObj);
}
}
// get请求拼接地址栏参数
if (options.method === 'get' && options.params) {
let paramStr = '';
const params = options.params;
for (const key in params) {
const value = params[key];
// 空字符或未定义的值不作为参数发送
if (value === '' || value === undefined) continue;
paramStr += `&${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
}
if (paramStr && paramStr.startsWith('&')) {
options.url = `${options.url}?${paramStr.substring(1)}`;
}
}
// 非get参数提交
if (options.data instanceof FormData) {
options.body = options.data;
} else {
options.body = JSON.stringify(options.data);
}
return options;
}
/**请求后的拦截 */
function interceptorResponse(res: ResultType): ResultType | Promise<any> {
//console.log('请求后的拦截', res);
// 登录失效时,移除授权令牌并重新刷新页面
if (res.code === 401) {
removeToken();
window.location.reload();
}
return res;
}
/**
* 请求http
*
* @param options 请求参数
*
* responseType改变响应结果类型
* @returns 返回 Promise<ResultType>
*/
export async function request(options: OptionsType): Promise<ResultType> {
// 请求超时控制请求终止
const controller = new AbortController();
const { signal } = controller;
const timeoutId = setTimeout(() => {
controller.abort(); // 终止请求
}, FATCH_CONFIG.timeout);
options = Object.assign({ signal }, FATCH_OPTIONS, options);
// 检查请求拦截
const beforeReq = beforeRequest(options);
if (beforeReq instanceof Promise) {
return await beforeReq;
}
options = beforeReq;
// 判断用户传递的URL是否http或/开头
if (!options.url.startsWith('http')) {
const uri = options.url.startsWith('/') ? options.url : `/${options.url}`;
options.url = FATCH_CONFIG.baseUrl + uri;
}
try {
const res = await fetch(options.url, options);
// console.log('请求结果:', res);
if (res.status === 500) {
return {
code: 500,
msg: '服务器连接出错!',
};
}
// 根据响应数据类型返回
switch (options.responseType) {
case 'text': // 文本数据
const str = await res.text();
return {
code: 200,
msg: str,
};
case 'json': // json格式数据
const result = await res.json();
// 请求后的拦截
const beforeRes = interceptorResponse(result);
if (beforeRes instanceof Promise) {
return await beforeRes;
}
return result;
case 'blob': // 二进制数据则直接返回
case 'arrayBuffer':
const contentType = res.headers.get('content-type') || '';
if (contentType.startsWith('application/json')) {
const result = await res.json();
return result as ResultType;
}
const data =
options.responseType === 'blob'
? await res.blob()
: await res.arrayBuffer();
return {
code: 200,
msg: '成功',
data: data,
status: res.status,
headers: res.headers,
};
default:
return {
code: 500,
msg: '未知响应数据类型',
};
}
} catch (error: any) {
// 请求被终止时
if (error.name === 'AbortError') {
return {
code: 500,
msg: '网络连接超时!',
};
}
throw error;
} finally {
clearTimeout(timeoutId); // 请求成功,清除超时计时器
}
}