272 lines
6.9 KiB
TypeScript
272 lines
6.9 KiB
TypeScript
import { $t } from '@/locales';
|
|
|
|
/**
|
|
* Transform record to option
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* const record = {
|
|
* key1: 'label1',
|
|
* key2: 'label2'
|
|
* };
|
|
* const options = transformRecordToOption(record);
|
|
* // [
|
|
* // { value: 'key1', label: 'label1' },
|
|
* // { value: 'key2', label: 'label2' }
|
|
* // ]
|
|
* ```;
|
|
*
|
|
* @param record
|
|
*/
|
|
export function transformRecordToOption<T extends Record<string, string>>(record: T) {
|
|
return Object.entries(record).map(([value, label]) => ({
|
|
value,
|
|
label
|
|
})) as CommonType.Option<keyof T, T[keyof T]>[];
|
|
}
|
|
|
|
/**
|
|
* Translate options
|
|
*
|
|
* @param options
|
|
*/
|
|
export function translateOptions(options: CommonType.Option<string, App.I18n.I18nKey>[]) {
|
|
return options.map(option => ({
|
|
...option,
|
|
label: $t(option.label)
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Toggle html class
|
|
*
|
|
* @param className
|
|
*/
|
|
export function toggleHtmlClass(className: string) {
|
|
function add() {
|
|
document.documentElement.classList.add(className);
|
|
}
|
|
|
|
function remove() {
|
|
document.documentElement.classList.remove(className);
|
|
}
|
|
|
|
return {
|
|
add,
|
|
remove
|
|
};
|
|
}
|
|
|
|
// 辅助函数:将 hex 转为 rgba
|
|
export function hexToRgba(hex: string, alpha: number) {
|
|
// 去掉 # 号
|
|
hex = hex.replace('#', '');
|
|
// 处理缩写形式 #RGB
|
|
if (hex.length === 3) {
|
|
hex = hex.split('').map(c => c + c).join('');
|
|
}
|
|
const r = parseInt(hex.substring(0, 2), 16);
|
|
const g = parseInt(hex.substring(2, 4), 16);
|
|
const b = parseInt(hex.substring(4, 6), 16);
|
|
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
}
|
|
export const getColorWithOpacity = (color: string, opacity: number) => {
|
|
// 如果 color 本身已经是 rgba 或 hsl 等,可自行扩展判断
|
|
// 这里仅处理 hex
|
|
return hexToRgba(color, opacity);
|
|
};
|
|
|
|
// 时间戳转换成年月日时分秒
|
|
export function formatTimestamp(timestamp: number | string | Date | null,format: string = 'yyyy-MM-dd HH:mm:ss') {
|
|
if(!timestamp) {
|
|
return '';
|
|
}
|
|
const date = new Date(Number(timestamp));
|
|
const year = date.getFullYear();
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
const hours = String(date.getHours()).padStart(2, '0');
|
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
if(format === 'yyyy-MM-dd') {
|
|
return `${year}-${month}-${day}`;
|
|
}
|
|
if(format === 'yyyy-MM-dd HH:mm') {
|
|
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
|
}
|
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
}
|
|
|
|
/**
|
|
* 任意时间格式 → 时间戳(支持 只有小时)
|
|
* @param {string|number|Date} input
|
|
* @param {'ms'|'s'} unit 默认毫秒
|
|
*/
|
|
export function anyToTimestamp(input: string | number | Date | null | undefined, unit = 'ms') {
|
|
if (input == null || input === '') return null;
|
|
|
|
const toResult = (ms: number) =>
|
|
Number.isNaN(ms) ? null : unit === 's' ? Math.floor(ms / 1000) : ms;
|
|
|
|
// 1. 数字
|
|
if (typeof input === 'number') return toResult(input);
|
|
|
|
// 2. Date
|
|
if (input instanceof Date) return toResult(input.getTime());
|
|
|
|
let str = String(input).trim();
|
|
|
|
// 3. 纯数字字符串
|
|
if (/^\d+$/.test(str)) return toResult(Number(str));
|
|
|
|
// 4. 统一分隔符
|
|
str = str
|
|
.replace(/[年月]/g, '-')
|
|
.replace(/[日号]/g, '')
|
|
.replace(/[时分]/g, ':')
|
|
.replace(/秒/g, '')
|
|
.replace(/[./]/g, '-')
|
|
.replace(/\s+/g, ' ')
|
|
.trim();
|
|
|
|
// 5. 手动解析:小时、分钟、秒、毫秒全部可选
|
|
const m = str.match(
|
|
/^(\d{4})-(\d{1,2})-(\d{1,2})(?:[ T](\d{1,2}))?(?::(\d{1,2}))?(?::(\d{1,2}))?(?:\.(\d{1,3}))?/
|
|
);
|
|
if (m) {
|
|
const [, y, mo, d, h = '0', mi = '0', s = '0', ms = '0'] = m;
|
|
const t = new Date(
|
|
+y,
|
|
+mo - 1,
|
|
+d,
|
|
+h,
|
|
+mi,
|
|
+s,
|
|
+ms.padEnd(3, '0')
|
|
).getTime();
|
|
if (!Number.isNaN(t)) return toResult(t);
|
|
}
|
|
|
|
// 6. 兜底
|
|
let t = new Date(str.replace(' ', 'T')).getTime();
|
|
if (!Number.isNaN(t)) return toResult(t);
|
|
|
|
t = Date.parse(str);
|
|
if (!Number.isNaN(t)) return toResult(t);
|
|
|
|
return null;
|
|
};
|
|
|
|
// 辅助函数:Fisher-Yates 洗牌算法
|
|
function shuffle(arr:any) {
|
|
for (let i = arr.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
}
|
|
return arr;
|
|
}
|
|
// 辅助函数:从字符串中随机获取字符
|
|
function getRandomChar(str:any) {
|
|
return str[Math.floor(Math.random() * str.length)];
|
|
}
|
|
// 生成至少12位,包含四类字符中的至少三种的随机密码
|
|
export function generateSecurePassword(passwordLength:number) {
|
|
// 定义字符池
|
|
const charSets:any = {
|
|
uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
|
lowercase: 'abcdefghijklmnopqrstuvwxyz',
|
|
numbers: '0123456789',
|
|
symbols: '!@#$%^&*()_+-=[]{}|;:,.<>?'
|
|
};
|
|
|
|
// 随机选择三种字符类型(保证至少三类)
|
|
const requiredTypes = shuffle(Object.keys(charSets)).slice(0, 3);
|
|
|
|
// 生成必选字符(每类至少1个)
|
|
let password = requiredTypes
|
|
.map((type:any) => getRandomChar(charSets[type]))
|
|
.join('');
|
|
|
|
// 生成剩余字符(允许包含所有四类)
|
|
const remainingLength = passwordLength - password.length;
|
|
const allChars = Object.values(charSets).join('');
|
|
for (let i = 0; i < remainingLength; i++) {
|
|
password += getRandomChar(allChars);
|
|
}
|
|
|
|
// 打乱字符顺序
|
|
return shuffle(password.split('')).join('');
|
|
}
|
|
|
|
// 根据当前时间,获取指定的时间范围(返回时间戳)
|
|
export function getTimeRangeStamp(timeRange: string){
|
|
const endTime = anyToTimestamp(new Date());
|
|
if(!endTime) {
|
|
return null;
|
|
}
|
|
// 近7天
|
|
if(timeRange === 'week'){
|
|
return {
|
|
from: endTime - (7 * 24 * 60 * 60 * 1000),
|
|
to: endTime
|
|
}
|
|
}
|
|
// 近一个月
|
|
if(timeRange === 'month'){
|
|
return {
|
|
from: endTime - (30 * 24 * 60 * 60 * 1000),
|
|
to: endTime
|
|
}
|
|
}
|
|
// 近三个月
|
|
if(timeRange === 'quarter'){
|
|
return {
|
|
from: endTime - (90 * 24 * 60 * 60 * 1000),
|
|
to: endTime
|
|
}
|
|
}
|
|
// 近半年
|
|
if(timeRange === 'half_year'){
|
|
return {
|
|
from: endTime - (180 * 24 * 60 * 60 * 1000),
|
|
to: endTime
|
|
}
|
|
}
|
|
// 近一年
|
|
if(timeRange === 'year'){
|
|
return {
|
|
from: endTime - (365 * 24 * 60 * 60 * 1000),
|
|
to: endTime
|
|
}
|
|
}
|
|
}
|
|
|
|
// 下载文件
|
|
export function downloadFileFun(url: string, name: string) {
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = name;
|
|
a.style.display = 'none';
|
|
document.body.appendChild(a)
|
|
a.click()
|
|
document.body.removeChild(a);
|
|
}
|
|
|
|
// 获取 token
|
|
export const getToken = () => {
|
|
return localStorage.getItem('token');
|
|
};
|
|
|
|
// tag自定义样式颜色
|
|
// 文字颜色
|
|
export const tagTextColor = (color: string) => {
|
|
return color || '#333';
|
|
}
|
|
// 背景颜色
|
|
export const tagBgColor = (color: string) => {
|
|
return getColorWithOpacity((color || '#333'), 0.08);
|
|
}
|
|
// 边框颜色
|
|
export const tagBorderColor = (color: string) => {
|
|
return getColorWithOpacity((color || '#333'), 0.3);
|
|
} |