refactor: 升级框架
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/shared",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
||||
@@ -40,6 +40,44 @@ export async function downloadFileFromUrl({
|
||||
openWindow(source, { target });
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载图片(允许跨域)
|
||||
* @param url - 图片 URL
|
||||
* @param canvasWidth - 画布宽度
|
||||
* @param canvasHeight - 画布高度
|
||||
* @param drawWithImageSize - 将图片绘制在画布上时带上图片的宽高值, 默认是要带上的
|
||||
* @returns
|
||||
*/
|
||||
export function downloadImageByCanvas({
|
||||
url,
|
||||
canvasWidth,
|
||||
canvasHeight,
|
||||
drawWithImageSize = true,
|
||||
}: {
|
||||
canvasHeight?: number;
|
||||
canvasWidth?: number;
|
||||
drawWithImageSize?: boolean;
|
||||
url: string;
|
||||
}) {
|
||||
const image = new Image();
|
||||
// image.setAttribute('crossOrigin', 'anonymous')
|
||||
image.src = url;
|
||||
image.addEventListener('load', () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = canvasWidth || image.width;
|
||||
canvas.height = canvasHeight || image.height;
|
||||
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
ctx?.clearRect(0, 0, canvas.width, canvas.height);
|
||||
if (drawWithImageSize) {
|
||||
ctx.drawImage(image, 0, 0, image.width, image.height);
|
||||
} else {
|
||||
ctx.drawImage(image, 0, 0);
|
||||
}
|
||||
const url = canvas.toDataURL('image/png');
|
||||
downloadFileFromImageUrl({ source: url, fileName: 'image.png' });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 Base64 下载文件
|
||||
*/
|
||||
@@ -140,6 +178,63 @@ export function urlToBase64(url: string, mineType?: string): Promise<string> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Base64 字符串转换为文件对象
|
||||
* @param base64 - Base64 字符串
|
||||
* @param fileName - 文件名
|
||||
* @returns File 对象
|
||||
*/
|
||||
export function base64ToFile(base64: string, fileName: string): File {
|
||||
// 输入验证
|
||||
if (!base64 || typeof base64 !== 'string') {
|
||||
throw new Error('base64 参数必须是非空字符串');
|
||||
}
|
||||
|
||||
// 将 base64 按照逗号进行分割,将前缀与后续内容分隔开
|
||||
const data = base64.split(',');
|
||||
if (data.length !== 2 || !data[0] || !data[1]) {
|
||||
throw new Error('无效的 base64 格式');
|
||||
}
|
||||
|
||||
// 利用正则表达式从前缀中获取类型信息(image/png、image/jpeg、image/webp等)
|
||||
const typeMatch = data[0].match(/:(.*?);/);
|
||||
if (!typeMatch || !typeMatch[1]) {
|
||||
throw new Error('无法解析 base64 类型信息');
|
||||
}
|
||||
const type = typeMatch[1];
|
||||
|
||||
// 从类型信息中获取具体的文件格式后缀(png、jpeg、webp)
|
||||
const typeParts = type.split('/');
|
||||
if (typeParts.length !== 2 || !typeParts[1]) {
|
||||
throw new Error('无效的 MIME 类型格式');
|
||||
}
|
||||
const suffix = typeParts[1];
|
||||
|
||||
try {
|
||||
// 使用 atob() 对 base64 数据进行解码,结果是一个文件数据流以字符串的格式输出
|
||||
const bstr = window.atob(data[1]);
|
||||
|
||||
// 获取解码结果字符串的长度
|
||||
const n = bstr.length;
|
||||
// 根据解码结果字符串的长度创建一个等长的整型数字数组
|
||||
const u8arr = new Uint8Array(n);
|
||||
|
||||
// 优化的 Uint8Array 填充逻辑
|
||||
for (let i = 0; i < n; i++) {
|
||||
// 使用 charCodeAt() 获取字符对应的字节值(Base64 解码后的字符串是字节级别的)
|
||||
// eslint-disable-next-line unicorn/prefer-code-point
|
||||
u8arr[i] = bstr.charCodeAt(i);
|
||||
}
|
||||
|
||||
// 返回 File 文件对象
|
||||
return new File([u8arr], `${fileName}.${suffix}`, { type });
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Base64 解码失败: ${error instanceof Error ? error.message : '未知错误'}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用下载触发函数
|
||||
* @param href - 文件下载的 URL
|
||||
|
||||
@@ -3,6 +3,7 @@ export * from './date';
|
||||
export * from './diff';
|
||||
export * from './dom';
|
||||
export * from './download';
|
||||
export * from './formatNumber';
|
||||
export * from './inference';
|
||||
export * from './letter';
|
||||
export * from './merge';
|
||||
@@ -13,6 +14,7 @@ export * from './to';
|
||||
export * from './tree';
|
||||
export * from './unique';
|
||||
export * from './update-css-variables';
|
||||
export * from './upload';
|
||||
export * from './util';
|
||||
export * from './uuid'; // add by 芋艿:从 vben2.0 复制
|
||||
export * from './window';
|
||||
|
||||
@@ -1,43 +1,299 @@
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
/** 时间段选择器拓展 */
|
||||
export function rangePickerExtend() {
|
||||
return {
|
||||
// 显示格式
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: ['开始时间', '结束时间'],
|
||||
ranges: {
|
||||
今天: [dayjs().startOf('day'), dayjs().endOf('day')],
|
||||
最近7天: [
|
||||
dayjs().subtract(7, 'day').startOf('day'),
|
||||
dayjs().endOf('day'),
|
||||
],
|
||||
最近30天: [
|
||||
dayjs().subtract(30, 'day').startOf('day'),
|
||||
dayjs().endOf('day'),
|
||||
],
|
||||
昨天: [
|
||||
dayjs().subtract(1, 'day').startOf('day'),
|
||||
dayjs().subtract(1, 'day').endOf('day'),
|
||||
],
|
||||
本周: [dayjs().startOf('week'), dayjs().endOf('day')],
|
||||
本月: [dayjs().startOf('month'), dayjs().endOf('day')],
|
||||
},
|
||||
showTime: {
|
||||
defaultValue: [
|
||||
dayjs('00:00:00', 'HH:mm:ss'),
|
||||
dayjs('23:59:59', 'HH:mm:ss'),
|
||||
],
|
||||
format: 'HH:mm:ss',
|
||||
},
|
||||
transformDateFunc: (dates: any) => {
|
||||
if (dates && dates.length === 2) {
|
||||
// 格式化为后台支持的时间格式
|
||||
return [dates.createTime[0], dates.createTime[1]].join(',');
|
||||
import { formatDate } from './date';
|
||||
|
||||
/**
|
||||
* @param {Date | number | string} time 需要转换的时间
|
||||
* @param {string} fmt 需要转换的格式 如 yyyy-MM-dd、yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
export function formatTime(time: Date | number | string, fmt: string) {
|
||||
if (time) {
|
||||
const date = new Date(time);
|
||||
const o = {
|
||||
'M+': date.getMonth() + 1,
|
||||
'd+': date.getDate(),
|
||||
'H+': date.getHours(),
|
||||
'm+': date.getMinutes(),
|
||||
's+': date.getSeconds(),
|
||||
'q+': Math.floor((date.getMonth() + 3) / 3),
|
||||
S: date.getMilliseconds(),
|
||||
};
|
||||
const yearMatch = fmt.match(/y+/);
|
||||
if (yearMatch) {
|
||||
fmt = fmt.replace(
|
||||
yearMatch[0],
|
||||
`${date.getFullYear()}`.slice(4 - yearMatch[0].length),
|
||||
);
|
||||
}
|
||||
for (const k in o) {
|
||||
const match = fmt.match(new RegExp(`(${k})`));
|
||||
if (match) {
|
||||
fmt = fmt.replace(
|
||||
match[0],
|
||||
match[0].length === 1
|
||||
? (o[k as keyof typeof o] as any)
|
||||
: `00${o[k as keyof typeof o]}`.slice(
|
||||
`${o[k as keyof typeof o]}`.length,
|
||||
),
|
||||
);
|
||||
}
|
||||
return {};
|
||||
},
|
||||
// 如果需要10位时间戳(秒级)可以使用 valueFormat: 'X'
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
};
|
||||
}
|
||||
return fmt;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前日期是第几周
|
||||
* @param dateTime 当前传入的日期值
|
||||
* @returns 返回第几周数字值
|
||||
*/
|
||||
export function getWeek(dateTime: Date): number {
|
||||
const temptTime = new Date(dateTime);
|
||||
// 周几
|
||||
const weekday = temptTime.getDay() || 7;
|
||||
// 周1+5天=周六
|
||||
temptTime.setDate(temptTime.getDate() - weekday + 1 + 5);
|
||||
let firstDay = new Date(temptTime.getFullYear(), 0, 1);
|
||||
const dayOfWeek = firstDay.getDay();
|
||||
let spendDay = 1;
|
||||
if (dayOfWeek !== 0) spendDay = 7 - dayOfWeek + 1;
|
||||
firstDay = new Date(temptTime.getFullYear(), 0, 1 + spendDay);
|
||||
const d = Math.ceil((temptTime.valueOf() - firstDay.valueOf()) / 86_400_000);
|
||||
return Math.ceil(d / 7);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将时间转换为 `几秒前`、`几分钟前`、`几小时前`、`几天前`
|
||||
* @param param 当前时间,new Date() 格式或者字符串时间格式
|
||||
* @param format 需要转换的时间格式字符串
|
||||
* @description param 10秒: 10 * 1000
|
||||
* @description param 1分: 60 * 1000
|
||||
* @description param 1小时: 60 * 60 * 1000
|
||||
* @description param 24小时:60 * 60 * 24 * 1000
|
||||
* @description param 3天: 60 * 60* 24 * 1000 * 3
|
||||
* @returns 返回拼接后的时间字符串
|
||||
*/
|
||||
export function formatPast(
|
||||
param: Date | string,
|
||||
format = 'YYYY-MM-DD HH:mm:ss',
|
||||
): string {
|
||||
// 传入格式处理、存储转换值
|
||||
let s: number, t: any;
|
||||
// 获取js 时间戳
|
||||
let time: number = Date.now();
|
||||
// 是否是对象
|
||||
typeof param === 'string' || typeof param === 'object'
|
||||
? (t = new Date(param).getTime())
|
||||
: (t = param);
|
||||
// 当前时间戳 - 传入时间戳
|
||||
time = Number.parseInt(`${time - t}`);
|
||||
if (time < 10_000) {
|
||||
// 10秒内
|
||||
return '刚刚';
|
||||
} else if (time < 60_000 && time >= 10_000) {
|
||||
// 超过10秒少于1分钟内
|
||||
s = Math.floor(time / 1000);
|
||||
return `${s}秒前`;
|
||||
} else if (time < 3_600_000 && time >= 60_000) {
|
||||
// 超过1分钟少于1小时
|
||||
s = Math.floor(time / 60_000);
|
||||
return `${s}分钟前`;
|
||||
} else if (time < 86_400_000 && time >= 3_600_000) {
|
||||
// 超过1小时少于24小时
|
||||
s = Math.floor(time / 3_600_000);
|
||||
return `${s}小时前`;
|
||||
} else if (time < 259_200_000 && time >= 86_400_000) {
|
||||
// 超过1天少于3天内
|
||||
s = Math.floor(time / 86_400_000);
|
||||
return `${s}天前`;
|
||||
} else {
|
||||
// 超过3天
|
||||
const date =
|
||||
typeof param === 'string' || typeof param === 'object'
|
||||
? new Date(param)
|
||||
: param;
|
||||
return formatDate(date, format) as string;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间问候语
|
||||
* @param param 当前时间,new Date() 格式
|
||||
* @description param 调用 `formatAxis(new Date())` 输出 `上午好`
|
||||
* @returns 返回拼接后的时间字符串
|
||||
*/
|
||||
export function formatAxis(param: Date): string {
|
||||
const hour: number = new Date(param).getHours();
|
||||
if (hour < 6) return '凌晨好';
|
||||
else if (hour < 9) return '早上好';
|
||||
else if (hour < 12) return '上午好';
|
||||
else if (hour < 14) return '中午好';
|
||||
else if (hour < 17) return '下午好';
|
||||
else if (hour < 19) return '傍晚好';
|
||||
else if (hour < 22) return '晚上好';
|
||||
else return '夜里好';
|
||||
}
|
||||
|
||||
/**
|
||||
* 将毫秒,转换成时间字符串。例如说,xx 分钟
|
||||
*
|
||||
* @param ms 毫秒
|
||||
* @returns {string} 字符串
|
||||
*/
|
||||
export function formatPast2(ms: number): string {
|
||||
const day = Math.floor(ms / (24 * 60 * 60 * 1000));
|
||||
const hour = Math.floor(ms / (60 * 60 * 1000) - day * 24);
|
||||
const minute = Math.floor(ms / (60 * 1000) - day * 24 * 60 - hour * 60);
|
||||
const second = Math.floor(
|
||||
ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60,
|
||||
);
|
||||
if (day > 0) {
|
||||
return `${day} 天${hour} 小时 ${minute} 分钟`;
|
||||
}
|
||||
if (hour > 0) {
|
||||
return `${hour} 小时 ${minute} 分钟`;
|
||||
}
|
||||
if (minute > 0) {
|
||||
return `${minute} 分钟`;
|
||||
}
|
||||
return second > 0 ? `${second} 秒` : `${0} 秒`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置起始日期,时间为00:00:00
|
||||
* @param param 传入日期
|
||||
* @returns 带时间00:00:00的日期
|
||||
*/
|
||||
export function beginOfDay(param: Date): Date {
|
||||
return new Date(
|
||||
param.getFullYear(),
|
||||
param.getMonth(),
|
||||
param.getDate(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置结束日期,时间为23:59:59
|
||||
* @param param 传入日期
|
||||
* @returns 带时间23:59:59的日期
|
||||
*/
|
||||
export function endOfDay(param: Date): Date {
|
||||
return new Date(
|
||||
param.getFullYear(),
|
||||
param.getMonth(),
|
||||
param.getDate(),
|
||||
23,
|
||||
59,
|
||||
59,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个日期间隔天数
|
||||
* @param param1 日期1
|
||||
* @param param2 日期2
|
||||
*/
|
||||
export function betweenDay(param1: Date, param2: Date): number {
|
||||
param1 = convertDate(param1);
|
||||
param2 = convertDate(param2);
|
||||
// 计算差值
|
||||
return Math.floor((param2.getTime() - param1.getTime()) / (24 * 3600 * 1000));
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期计算
|
||||
* @param param1 日期
|
||||
* @param param2 添加的时间
|
||||
*/
|
||||
export function addTime(param1: Date, param2: number): Date {
|
||||
param1 = convertDate(param1);
|
||||
return new Date(param1.getTime() + param2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期转换
|
||||
* @param param 日期
|
||||
*/
|
||||
export function convertDate(param: Date | string): Date {
|
||||
if (typeof param === 'string') {
|
||||
return new Date(param);
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定的两个日期, 是否为同一天
|
||||
* @param a 日期 A
|
||||
* @param b 日期 B
|
||||
*/
|
||||
export function isSameDay(a: dayjs.ConfigType, b: dayjs.ConfigType): boolean {
|
||||
if (!a || !b) return false;
|
||||
|
||||
const aa = dayjs(a);
|
||||
const bb = dayjs(b);
|
||||
return (
|
||||
aa.year() === bb.year() &&
|
||||
aa.month() === bb.month() &&
|
||||
aa.day() === bb.day()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一天的开始时间、截止时间
|
||||
* @param date 日期
|
||||
* @param days 天数
|
||||
*/
|
||||
export function getDayRange(
|
||||
date: dayjs.ConfigType,
|
||||
days: number,
|
||||
): [dayjs.ConfigType, dayjs.ConfigType] {
|
||||
const day = dayjs(date).add(days, 'd');
|
||||
return getDateRange(day, day);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近7天的开始时间、截止时间
|
||||
*/
|
||||
export function getLast7Days(): [dayjs.ConfigType, dayjs.ConfigType] {
|
||||
const lastWeekDay = dayjs().subtract(7, 'd');
|
||||
const yesterday = dayjs().subtract(1, 'd');
|
||||
return getDateRange(lastWeekDay, yesterday);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近30天的开始时间、截止时间
|
||||
*/
|
||||
export function getLast30Days(): [dayjs.ConfigType, dayjs.ConfigType] {
|
||||
const lastMonthDay = dayjs().subtract(30, 'd');
|
||||
const yesterday = dayjs().subtract(1, 'd');
|
||||
return getDateRange(lastMonthDay, yesterday);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近1年的开始时间、截止时间
|
||||
*/
|
||||
export function getLast1Year(): [dayjs.ConfigType, dayjs.ConfigType] {
|
||||
const lastYearDay = dayjs().subtract(1, 'y');
|
||||
const yesterday = dayjs().subtract(1, 'd');
|
||||
return getDateRange(lastYearDay, yesterday);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定日期的开始时间、截止时间
|
||||
* @param beginDate 开始日期
|
||||
* @param endDate 截止日期
|
||||
*/
|
||||
export function getDateRange(
|
||||
beginDate: dayjs.ConfigType,
|
||||
endDate: dayjs.ConfigType,
|
||||
): [string, string] {
|
||||
return [
|
||||
dayjs(beginDate).startOf('d').format('YYYY-MM-DD HH:mm:ss'),
|
||||
dayjs(endDate).endOf('d').format('YYYY-MM-DD HH:mm:ss'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -164,4 +164,48 @@ function handleTree(
|
||||
return tree;
|
||||
}
|
||||
|
||||
export { filterTree, handleTree, mapTree, traverseTreeValues };
|
||||
/**
|
||||
* 获取节点的完整结构
|
||||
* @param tree 树数据
|
||||
* @param nodeId 节点 id
|
||||
*/
|
||||
function treeToString(tree: any[], nodeId: number | string) {
|
||||
if (tree === undefined || !Array.isArray(tree) || tree.length === 0) {
|
||||
console.warn('tree must be an array');
|
||||
return '';
|
||||
}
|
||||
// 校验是否是一级节点
|
||||
const node = tree.find((item) => item.id === nodeId);
|
||||
if (node !== undefined) {
|
||||
return node.name;
|
||||
}
|
||||
let str = '';
|
||||
|
||||
function performAThoroughValidation(arr: any[]) {
|
||||
if (arr === undefined || !Array.isArray(arr) || arr.length === 0) {
|
||||
return false;
|
||||
}
|
||||
for (const item of arr) {
|
||||
if (item.id === nodeId) {
|
||||
str += ` / ${item.name}`;
|
||||
return true;
|
||||
} else if (item.children !== undefined && item.children.length > 0) {
|
||||
str += ` / ${item.name}`;
|
||||
if (performAThoroughValidation(item.children)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const item of tree) {
|
||||
str = `${item.name}`;
|
||||
if (performAThoroughValidation(item.children)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
export { filterTree, handleTree, mapTree, traverseTreeValues, treeToString };
|
||||
|
||||
Reference in New Issue
Block a user