Подготовить HY2XS к production-сборке

This commit is contained in:
2026-04-25 23:13:12 +05:00
commit 84a4e94567
277 changed files with 26513 additions and 0 deletions
+95
View File
@@ -0,0 +1,95 @@
/**
* 格式化字节大小
* @param bytes 字节数
* @param decimals 小数位数,默认为 2
* @returns 格式化后的字节大小字符串
*/
export const formatBytes = (bytes: number, decimals = 2): string => {
// 检查是否为特殊值
if (bytes === -1) {
return "Unlimited";
}
if (bytes === 0) {
return "0 Bytes";
}
// 计算单位和大小
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
// 返回格式化后的字符串
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
};
export const calculateBytes = (value = 0, unit = "Bytes"): number => {
// 将单位转换为大写,并去除空格
const formattedUnit = unit.toUpperCase().trim();
// 定义存储单位和对应的字节数的映射关系
const unitToBytes: Record<string, number> = {
BYTES: 1,
KB: 1024 ** 1,
MB: 1024 ** 2,
GB: 1024 ** 3,
TB: 1024 ** 4,
PB: 1024 ** 5,
EB: 1024 ** 6,
ZB: 1024 ** 7,
YB: 1024 ** 8,
};
// 检查传入的单位是否存在于映射关系中
if (!Object.prototype.hasOwnProperty.call(unitToBytes, formattedUnit)) {
throw new Error("Invalid unit");
}
if (value == -1) {
return -1;
}
// 计算并返回字节数
return value * unitToBytes[formattedUnit];
};
/**
* 格式化存储容量单位
* @param bytes 存储容量(字节数)
* @param decimals 小数位数,默认为 2
* @returns 格式化后的存储容量值
*/
export const formatStorageCapacity = (bytes: number, decimals = 2): number => {
// 检查输入是否有效
if (!bytes || bytes <= 0) {
return bytes;
}
// 计算存储单位
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const i = Math.floor(Math.log(bytes) / Math.log(k));
// 格式化存储容量值并返回
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
};
/**
* 格式化存储容量单位
* @param bytes 存储容量(字节数)
* @returns 格式化后的存储单位
*/
export const formatStorageUnit = (bytes: number): string => {
// 检查输入是否有效
if (!bytes || bytes <= 0) {
return "Bytes";
}
// 计算存储单位
const k = 1024;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
// 返回格式化后的存储单位
return sizes[i];
};
+51
View File
@@ -0,0 +1,51 @@
/**
* 浅拷贝,忽略 null,支持嵌套对象
* @param target
* @param source
*/
export const assignWith = <T>(target: T, source: Partial<T>): void => {
if (source === null || typeof source !== "object") {
return;
}
for (const key in source) {
if (source[key] !== null) {
if (typeof source[key] === "object") {
if (!target[key]) {
target[key] = (Array.isArray(source[key]) ? [] : {}) as T[Extract<
keyof T,
string
>];
}
assignWith(target[key] as any, source[key] as any);
} else {
target[key] = source[key] as T[Extract<keyof T, string>];
}
}
}
};
/**
* 深拷贝,忽略 null,支持嵌套对象
* @param source
*/
export const deepCopy = <T>(source: Partial<T>): T => {
if (source === null || typeof source !== "object") {
return source;
}
if (Array.isArray(source)) {
const arrCopy = [] as any[];
source.forEach((item, index) => {
arrCopy[index] = deepCopy(item);
});
return arrCopy as any;
}
const objCopy = {} as { [key: string]: any };
Object.keys(source).forEach((key) => {
objCopy[key] = deepCopy((source as { [key: string]: any })[key]);
});
return objCopy as T;
};
+12
View File
@@ -0,0 +1,12 @@
// translate router.meta.title, be used in breadcrumb sidebar tagsview
import i18n from "@/lang/index";
export function translateRouteTitleI18n(title: any) {
// 判断是否存在国际化配置,如果没有原生返回
const hasKey = i18n.global.te("route." + title);
if (hasKey) {
const translatedTitle = i18n.global.t("route." + title);
return translatedTitle;
}
return title;
}
+39
View File
@@ -0,0 +1,39 @@
/**
* Check if an element has a class
* @param {HTMLElement} ele
* @param {string} cls
* @returns {boolean}
*/
export function hasClass(ele: HTMLElement, cls: string) {
return !!ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)"));
}
/**
* Add class to element
* @param {HTMLElement} ele
* @param {string} cls
*/
export function addClass(ele: HTMLElement, cls: string) {
if (!hasClass(ele, cls)) ele.className += " " + cls;
}
/**
* Remove class from element
* @param {HTMLElement} ele
* @param {string} cls
*/
export function removeClass(ele: HTMLElement, cls: string) {
if (hasClass(ele, cls)) {
const reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
ele.className = ele.className.replace(reg, " ");
}
}
/**
* @param {string} path
* @returns {Boolean}
*/
export function isExternal(path: string) {
const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path);
return isExternal;
}
+62
View File
@@ -0,0 +1,62 @@
import axios, { InternalAxiosRequestConfig, AxiosResponse } from "axios";
import { useAccountStoreHook } from "@/store/modules/account";
const dynamicBase = (window as any).__dynamic_base__ || "";
// 创建 axios 实例
const service = axios.create({
baseURL: `${dynamicBase}${import.meta.env.VITE_APP_BASE_API}`,
timeout: 50000,
headers: { "Content-Type": "application/json;charset=utf-8" },
});
// 请求拦截器
service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const accountStore = useAccountStoreHook();
if (accountStore.token) {
config.headers.Authorization = accountStore.token;
}
return config;
},
(error: any) => {
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
(response: AxiosResponse) => {
const { code, message } = response.data;
if (code === 20000) {
return response.data;
}
// 响应数据为二进制流处理(文件导出)
if (response.data instanceof ArrayBuffer || response.data instanceof Blob) {
return response;
}
ElMessage.error(message || "系统出错");
return Promise.reject(new Error(message || "Error"));
},
(error: any) => {
if (error.response.data) {
const { code, msg } = error.response.data;
// token 过期,重新登录
if (code === "A0230") {
ElMessageBox.confirm("当前页面已失效,请重新登录", "提示", {
confirmButtonText: "确定",
type: "warning",
}).then(() => {
localStorage.clear();
window.location.href = "/";
});
} else {
ElMessage.error(msg || "系统出错");
}
}
return Promise.reject(error.message);
}
);
// 导出 axios 实例
export default service;
+69
View File
@@ -0,0 +1,69 @@
const easeInOutQuad = (t: number, b: number, c: number, d: number) => {
t /= d / 2;
if (t < 1) {
return (c / 2) * t * t + b;
}
t--;
return (-c / 2) * (t * (t - 2) - 1) + b;
};
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
const requestAnimFrame = (function () {
return (
window.requestAnimationFrame ||
(window as any).webkitRequestAnimationFrame ||
(window as any).mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
}
);
})();
/**
* Because it's so fucking difficult to detect the scrolling element, just move them all
* @param {number} amount
*/
const move = (amount: number) => {
document.documentElement.scrollTop = amount;
(document.body.parentNode as HTMLElement).scrollTop = amount;
document.body.scrollTop = amount;
};
const position = () => {
return (
document.documentElement.scrollTop ||
(document.body.parentNode as HTMLElement).scrollTop ||
document.body.scrollTop
);
};
/**
* @param {number} to
* @param {number} duration
* @param {Function} callback
*/
export const scrollTo = (to: number, duration: number, callback?: any) => {
const start = position();
const change = to - start;
const increment = 20;
let currentTime = 0;
duration = typeof duration === "undefined" ? 500 : duration;
const animateScroll = function () {
// increment the time
currentTime += increment;
// find the value with the quadratic in-out easing function
const val = easeInOutQuad(currentTime, start, change, duration);
// move the document.body
move(val);
// do the animation unless its over
if (currentTime < duration) {
requestAnimFrame(animateScroll);
} else {
if (callback && typeof callback === "function") {
// the animation is done so lets callback
callback();
}
}
};
animateScroll();
};
+101
View File
@@ -0,0 +1,101 @@
/**
* 将时间戳转换为格式化日期时间字符串(YYYY-MM-DD HH:mm:ss
* @param timestamp 时间戳
* @returns 格式化日期时间字符串
*/
export const timestampToDateTime = (timestamp: number): string => {
const date = new Date(timestamp);
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const day = date.getDate().toString().padStart(2, "0");
const hours = date.getHours().toString().padStart(2, "0");
const minutes = date.getMinutes().toString().padStart(2, "0");
const seconds = date.getSeconds().toString().padStart(2, "0");
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};
export const calculateTimeDifference = (timestamp: number): string => {
const now = Date.now();
const diff = timestamp - now;
if (diff <= 0) {
return "-";
}
const seconds = Math.floor(diff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
const remainingHours = hours % 24;
const remainingMinutes = minutes % 60;
const remainingSeconds = seconds % 60;
const parts: string[] = [];
if (days > 0) {
parts.push(`${days}天`);
}
if (remainingHours > 0) {
parts.push(`${remainingHours}小时`);
}
if (remainingMinutes > 0) {
parts.push(`${remainingMinutes}分钟`);
}
if (remainingSeconds > 0) {
parts.push(`${remainingSeconds}秒`);
}
return parts.join(" ");
};
/**
* 获取一小时后的时间戳
* @returns 一周后的时间戳
*/
export const getHourLater = (): number => {
const date = new Date();
date.setHours(date.getHours() + 1);
return date.getTime();
};
/**
* 获取一天后的时间戳
* @returns 一周后的时间戳
*/
export const getDayLater = (): number => {
const date = new Date();
date.setDate(date.getDate() + 1);
return date.getTime();
};
/**
* 获取一周后的时间戳
* @returns 一周后的时间戳
*/
export const getWeekLater = (): number => {
const date = new Date();
date.setDate(date.getDate() + 7);
return date.getTime();
};
/**
* 获取一个月后的时间戳
* @returns 一个月后的时间戳
*/
export const getMonthLater = (): number => {
const date = new Date();
date.setMonth(date.getMonth() + 1);
return date.getTime();
};
/**
* 获取一年后的时间戳
* @returns 一年后的时间戳
*/
export const getYearLater = (): number => {
const date = new Date();
date.setFullYear(date.getFullYear() + 1);
return date.getTime();
};