initHeader

This commit is contained in:
2024-08-22 11:27:06 +08:00
parent fe895bd37c
commit e0aaa7a30d
178 changed files with 5726 additions and 4999 deletions

View File

@@ -0,0 +1,59 @@
import { ElMessage } from "element-plus";
/**
* @description hex颜色转rgb颜色
* @param {String} str 颜色值字符串
* @returns {String} 返回处理后的颜色值
*/
export function hexToRgb(str: any) {
let hexs: any = "";
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(str)) return ElMessage.warning("输入错误的hex");
str = str.replace("#", "");
hexs = str.match(/../g);
for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16);
return hexs;
}
/**
* @description rgb颜色转Hex颜色
* @param {*} r 代表红色
* @param {*} g 代表绿色
* @param {*} b 代表蓝色
* @returns {String} 返回处理后的颜色值
*/
export function rgbToHex(r: any, g: any, b: any) {
let reg = /^\d{1,3}$/;
if (!reg.test(r) || !reg.test(g) || !reg.test(b)) return ElMessage.warning("输入错误的rgb颜色值");
let hexs = [r.toString(16), g.toString(16), b.toString(16)];
for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`;
return `#${hexs.join("")}`;
}
/**
* @description 加深颜色值
* @param {String} color 颜色值字符串
* @param {Number} level 加深的程度限0-1之间
* @returns {String} 返回处理后的颜色值
*/
export function getDarkColor(color: string, level: number) {
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值");
let rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.round(20.5 * level + rgb[i] * (1 - level));
return rgbToHex(rgb[0], rgb[1], rgb[2]);
}
/**
* @description 变浅颜色值
* @param {String} color 颜色值字符串
* @param {Number} level 加深的程度限0-1之间
* @returns {String} 返回处理后的颜色值
*/
export function getLightColor(color: string, level: number) {
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值");
let rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.round(255 * level + rgb[i] * (1 - level));
return rgbToHex(rgb[0], rgb[1], rgb[2]);
}

View File

@@ -0,0 +1,17 @@
// ? 系统全局字典
/**
* @description用户性别
*/
export const genderType = [
{ label: "男", value: 1 },
{ label: "女", value: 2 }
];
/**
* @description用户状态
*/
export const userStatus = [
{ label: "启用", value: 1, tagType: "success" },
{ label: "禁用", value: 0, tagType: "danger" }
];

View File

@@ -0,0 +1,14 @@
// ? Element 常用表单校验规则
/**
* @rule 手机号
*/
export function checkPhoneNumber(rule: any, value: any, callback: any) {
const regexp = /^(((13[0-9]{1})|(15[0-9]{1})|(16[0-9]{1})|(17[3-8]{1})|(18[0-9]{1})|(19[0-9]{1})|(14[5-7]{1}))+\d{8})$/;
if (value === "") callback("请输入手机号码");
if (!regexp.test(value)) {
callback(new Error("请输入正确的手机号码"));
} else {
return callback();
}
}

View File

@@ -0,0 +1,27 @@
import { ElNotification } from "element-plus";
/**
* @description 全局代码错误捕捉
* */
const errorHandler = (error: any) => {
// 过滤 HTTP 请求错误
if (error.status || error.status == 0) return false;
let errorMap: { [key: string]: string } = {
InternalError: "Javascript引擎内部错误",
ReferenceError: "未找到对象",
TypeError: "使用了错误的类型或对象",
RangeError: "使用内置对象时,参数超范围",
SyntaxError: "语法错误",
EvalError: "错误的使用了Eval",
URIError: "URI错误"
};
let errorName = errorMap[error.name] || "未知错误";
ElNotification({
title: errorName,
message: error,
type: "error",
duration: 3000
});
};
export default errorHandler;

View File

@@ -1,229 +0,0 @@
import type { AxiosRequestConfig, Method } from 'axios'
import axios from 'axios'
import { ElLoading, ElNotification, type LoadingOptions } from 'element-plus'
import { refreshToken } from '@/api/user'
import router from '@/router/index'
import { useUserInfoStore } from '@/stores/modules/user'
window.requests = []
window.tokenRefreshing = false
const pendingMap = new Map()
const loadingInstance: LoadingInstance = {
target: null,
count: 0,
}
/**
* 创建`Axios`
* 默认开启`reductDataFormat(简洁响应)`,返回类型为`ApiPromise`
* 关闭`reductDataFormat`,返回类型则为`AxiosPromise`
*/
function createAxios<Data = any, T = ApiPromise<Data>>(
axiosConfig: AxiosRequestConfig,
options: Options = {},
loading: LoadingOptions = {},
): T {
const userInfo = useUserInfoStore()
const Axios = axios.create({
baseURL: '/api',
timeout: 1000 * 60 * 5,
headers: {},
responseType: 'json',
})
options = Object.assign(
{
CancelDuplicateRequest: true, // 是否开启取消重复请求, 默认为 true
loading: false, // 是否开启loading层效果, 默认为false
reductDataFormat: true, // 是否开启简洁的数据结构响应, 默认为true
showErrorMessage: true, // 是否开启接口错误信息展示,默认为true
showCodeMessage: true, // 是否开启code不为1时的信息提示, 默认为true
showSuccessMessage: false, // 是否开启code为1时的信息提示, 默认为false
anotherToken: '', // 当前请求使用另外的用户token
},
options,
)
// 请求拦截
Axios.interceptors.request.use(
config => {
removePending(config)
options.CancelDuplicateRequest && addPending(config)
// 创建loading实例
if (options.loading) {
loadingInstance.count++
if (loadingInstance.count === 1) {
loadingInstance.target = ElLoading.service(loading)
}
}
// 自动携带token
if (config.headers) {
const token = userInfo.getToken()
if (token) {
;(config.headers as any).Authorization = token
} else {
config.headers.Authorization = 'Basic bmpjbnRlc3Q6bmpjbnBxcw=='
}
}
return config
},
error => {
return Promise.reject(error)
},
)
// 响应拦截
Axios.interceptors.response.use(
response => {
removePending(response.config)
options.loading && closeLoading(options) // 关闭loading
if (
response.data.code === 'A0000' ||
response.data.type === 'application/json'
) {
return options.reductDataFormat ? response.data : response
} else if (response.data.code == 'A0202') {
if (!window.tokenRefreshing) {
window.tokenRefreshing = true
return refreshToken()
.then(res => {
userInfo.setToken(res.data.token, 'auth')
response.headers.Authorization = `${res.data.token}`
window.requests.forEach(cb => cb(res.data.token))
window.requests = []
return Axios(response.config)
})
.catch(err => {
userInfo.removeToken()
router.push({ name: 'login' })
return Promise.reject(err)
})
.finally(() => {
window.tokenRefreshing = false
})
} else {
return new Promise(resolve => {
// 用函数形式将 resolve 存入,等待刷新后再执行
window.requests.push((token: string) => {
response.headers.Authorization = `${token}`
resolve(Axios(response.config))
})
})
}
} else if (response.data.code == 'A0024') {
userInfo.removeToken()
router.push({ name: 'login' })
return Promise.reject(response.data)
} else {
if (options.showCodeMessage) {
ElNotification({
type: 'error',
message: response.data.message || '未知错误',
})
}
return Promise.reject(response.data)
}
},
error => {
error.config && removePending(error.config)
options.loading && closeLoading(options) // 关闭loading
return Promise.reject(error) // 错误继续返回给到具体页面
},
)
return Axios(axiosConfig) as T
}
export default createAxios
/**
* 关闭Loading层实例
*/
function closeLoading(options: Options) {
if (options.loading && loadingInstance.count > 0) loadingInstance.count--
if (loadingInstance.count === 0) {
loadingInstance.target.close()
loadingInstance.target = null
}
}
/**
* 储存每个请求的唯一cancel回调, 以此为标识
*/
function addPending(config: AxiosRequestConfig) {
const pendingKey = getPendingKey(config)
config.cancelToken =
config.cancelToken ||
new axios.CancelToken(cancel => {
if (!pendingMap.has(pendingKey)) {
pendingMap.set(pendingKey, cancel)
}
})
}
/**
* 删除重复的请求
*/
function removePending(config: AxiosRequestConfig) {
const pendingKey = getPendingKey(config)
if (pendingMap.has(pendingKey)) {
const cancelToken = pendingMap.get(pendingKey)
cancelToken(pendingKey)
pendingMap.delete(pendingKey)
}
}
/**
* 生成每个请求的唯一key
*/
function getPendingKey(config: AxiosRequestConfig) {
let { data } = config
const { url, method, params, headers } = config
if (typeof data === 'string') data = JSON.parse(data) // response里面返回的config.data是个字符串对象
return [
url,
method,
headers && (headers as anyObj).Authorization ? (headers as anyObj).Authorization : '',
headers && (headers as anyObj)['ba-user-token'] ? (headers as anyObj)['ba-user-token'] : '',
JSON.stringify(params),
JSON.stringify(data),
].join('&')
}
/**
* 根据请求方法组装请求数据/参数
*/
export function requestPayload(method: Method, data: anyObj) {
if (method == 'GET') {
return {
params: data,
}
} else if (method == 'POST') {
return {
data: data,
}
}
}
interface LoadingInstance {
target: any
count: number
}
interface Options {
// 是否开启取消重复请求, 默认为 true
CancelDuplicateRequest?: boolean
// 是否开启loading层效果, 默认为false
loading?: boolean
// 是否开启简洁的数据结构响应, 默认为true
reductDataFormat?: boolean
// 是否开启code不为A0000时的信息提示, 默认为true
showCodeMessage?: boolean
// 是否开启code为0时的信息提示, 默认为false
showSuccessMessage?: boolean
// 当前请求使用另外的用户token
anotherToken?: string
}

View File

@@ -1,4 +1,5 @@
import { isArray } from "@/utils/is";
import { FieldNamesProps } from "@/components/ProTable/interface";
const mode = import.meta.env.VITE_ROUTER_MODE;
@@ -275,6 +276,28 @@ export function handleProp(prop: string) {
return propArr[propArr.length - 1];
}
/**
* @description 根据枚举列表查询当需要的数据(如果指定了 label 和 value 的 key值会自动识别格式化
* @param {String} callValue 当前单元格值
* @param {Array} enumData 字典列表
* @param {Array} fieldNames label && value && children 的 key 值
* @param {String} type 过滤类型(目前只有 tag
* @returns {String}
* */
export function filterEnum(callValue: any, enumData?: any, fieldNames?: FieldNamesProps, type?: "tag") {
const value = fieldNames?.value ?? "value";
const label = fieldNames?.label ?? "label";
const children = fieldNames?.children ?? "children";
let filterData: { [key: string]: any } = {};
// 判断 enumData 是否为数组
if (Array.isArray(enumData)) filterData = findItemNested(enumData, callValue, value, children);
// 判断是否输出的结果为 tag 类型
if (type == "tag") {
return filterData?.tagType ? filterData.tagType : "";
} else {
return filterData ? filterData[label] : "--";
}
}
/**
* @description 递归查找 callValue 对应的 enum 值

View File

@@ -1,9 +0,0 @@
import { useUserInfoStore } from '@/stores/modules/user'
import { USER_INFO } from '@/constants/storeKey'
export function clearUserInfo() {
const userInfo = useUserInfoStore()
userInfo.$reset()
// 清除用户信息缓存
localStorage.removeItem(USER_INFO)
}

View File

@@ -0,0 +1,5 @@
import mitt from "mitt";
const mittBus = mitt();
export default mittBus;

13
frontend/src/utils/svg.ts Normal file
View File

@@ -0,0 +1,13 @@
/**
* @description Loading Svg
*/
export const loadingSvg = `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`;