feat: 将一些utils放到package/core/base/shared/src/utils
parent
3a46fa2c26
commit
d09b993bc8
|
@ -12,6 +12,7 @@ import {
|
|||
} from '@vben/plugins/vxe-table';
|
||||
import {
|
||||
erpNumberFormatter,
|
||||
formatPast2,
|
||||
formatToFractionDigit,
|
||||
isFunction,
|
||||
isString,
|
||||
|
@ -296,32 +297,7 @@ setupVbenVxeTable({
|
|||
|
||||
vxeUI.formats.add('formatPast2', {
|
||||
tableCellFormatMethod({ cellValue }) {
|
||||
if (cellValue === null || cellValue === undefined) {
|
||||
return '';
|
||||
}
|
||||
// 定义时间单位常量,便于维护
|
||||
const SECOND = 1000;
|
||||
const MINUTE = 60 * SECOND;
|
||||
const HOUR = 60 * MINUTE;
|
||||
const DAY = 24 * HOUR;
|
||||
|
||||
// 计算各时间单位
|
||||
const day = Math.floor(cellValue / DAY);
|
||||
const hour = Math.floor((cellValue % DAY) / HOUR);
|
||||
const minute = Math.floor((cellValue % HOUR) / MINUTE);
|
||||
const second = Math.floor((cellValue % MINUTE) / SECOND);
|
||||
|
||||
// 根据时间长短返回不同格式
|
||||
if (day > 0) {
|
||||
return `${day} 天${hour} 小时 ${minute} 分钟`;
|
||||
}
|
||||
if (hour > 0) {
|
||||
return `${hour} 小时 ${minute} 分钟`;
|
||||
}
|
||||
if (minute > 0) {
|
||||
return `${minute} 分钟`;
|
||||
}
|
||||
return second > 0 ? `${second} 秒` : `${0} 秒`;
|
||||
return formatPast2(cellValue);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { PageParam, PageResult } from '@vben/request';
|
||||
|
||||
import type { AiWriteTypeEnum } from '#/utils/constants';
|
||||
import type { AiWriteTypeEnum } from '#/utils';
|
||||
|
||||
import { useAppConfig } from '@vben/hooks';
|
||||
import { fetchEventSource } from '@vben/request';
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
const download0 = (data: Blob, fileName: string, mineType: string) => {
|
||||
// 创建 blob
|
||||
const blob = new Blob([data], { type: mineType });
|
||||
// 创建 href 超链接,点击进行下载
|
||||
window.URL = window.URL || window.webkitURL;
|
||||
const href = URL.createObjectURL(blob);
|
||||
const downA = document.createElement('a');
|
||||
downA.href = href;
|
||||
downA.download = fileName;
|
||||
downA.click();
|
||||
// 销毁超连接
|
||||
window.URL.revokeObjectURL(href);
|
||||
};
|
||||
|
||||
export const download = {
|
||||
// 下载 Excel 方法
|
||||
excel: (data: Blob, fileName: string) => {
|
||||
download0(data, fileName, 'application/vnd.ms-excel');
|
||||
},
|
||||
// 下载 Word 方法
|
||||
word: (data: Blob, fileName: string) => {
|
||||
download0(data, fileName, 'application/msword');
|
||||
},
|
||||
// 下载 Zip 方法
|
||||
zip: (data: Blob, fileName: string) => {
|
||||
download0(data, fileName, 'application/zip');
|
||||
},
|
||||
// 下载 Html 方法
|
||||
html: (data: Blob, fileName: string) => {
|
||||
download0(data, fileName, 'text/html');
|
||||
},
|
||||
// 下载 Markdown 方法
|
||||
markdown: (data: Blob, fileName: string) => {
|
||||
download0(data, fileName, 'text/markdown');
|
||||
},
|
||||
// 下载 Json 方法
|
||||
json: (data: Blob, fileName: string) => {
|
||||
download0(data, fileName, 'application/json');
|
||||
},
|
||||
// 下载图片(允许跨域)
|
||||
image: ({
|
||||
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');
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'image.png';
|
||||
a.click();
|
||||
});
|
||||
},
|
||||
base64ToFile: (base64: any, fileName: string) => {
|
||||
// 将base64按照 , 进行分割 将前缀 与后续内容分隔开
|
||||
const data = base64.split(',');
|
||||
// 利用正则表达式 从前缀中获取图片的类型信息(image/png、image/jpeg、image/webp等)
|
||||
const type = data[0].match(/:(.*?);/)[1];
|
||||
// 从图片的类型信息中 获取具体的文件格式后缀(png、jpeg、webp)
|
||||
const suffix = type.split('/')[1];
|
||||
// 使用atob()对base64数据进行解码 结果是一个文件数据流 以字符串的格式输出
|
||||
const bstr = window.atob(data[1]);
|
||||
// 获取解码结果字符串的长度
|
||||
let n = bstr.length;
|
||||
// 根据解码结果字符串的长度创建一个等长的整形数字数组
|
||||
// 但在创建时 所有元素初始值都为 0
|
||||
const u8arr = new Uint8Array(n);
|
||||
// 将整形数组的每个元素填充为解码结果字符串对应位置字符的UTF-16 编码单元
|
||||
while (n--) {
|
||||
// charCodeAt():获取给定索引处字符对应的 UTF-16 代码单元
|
||||
u8arr[n] = bstr.charCodeAt(n);
|
||||
}
|
||||
|
||||
// 将File文件对象返回给方法的调用者
|
||||
return new File([u8arr], `${fileName}.${suffix}`, {
|
||||
type,
|
||||
});
|
||||
},
|
||||
};
|
|
@ -1,124 +0,0 @@
|
|||
import { formatDate } from '@vben/utils';
|
||||
|
||||
/**
|
||||
* 将时间转换为 `几秒前`、`几分钟前`、`几小时前`、`几天前`
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将毫秒,转换成时间字符串。例如说,xx 分钟
|
||||
*
|
||||
* @param ms 毫秒
|
||||
* @returns {string} 字符串
|
||||
*/
|
||||
// TODO @xingyu:这个要融合到哪里去 date 么?
|
||||
export function formatPast2(ms: number): string {
|
||||
// 定义时间单位常量,便于维护
|
||||
const SECOND = 1000;
|
||||
const MINUTE = 60 * SECOND;
|
||||
const HOUR = 60 * MINUTE;
|
||||
const DAY = 24 * HOUR;
|
||||
|
||||
// 计算各时间单位
|
||||
const day = Math.floor(ms / DAY);
|
||||
const hour = Math.floor((ms % DAY) / HOUR);
|
||||
const minute = Math.floor((ms % HOUR) / MINUTE);
|
||||
const second = Math.floor((ms % MINUTE) / SECOND);
|
||||
|
||||
// 根据时间长短返回不同格式
|
||||
if (day > 0) {
|
||||
return `${day} 天${hour} 小时 ${minute} 分钟`;
|
||||
}
|
||||
if (hour > 0) {
|
||||
return `${hour} 小时 ${minute} 分钟`;
|
||||
}
|
||||
if (minute > 0) {
|
||||
return `${minute} 分钟`;
|
||||
}
|
||||
return second > 0 ? `${second} 秒` : `${0} 秒`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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(),
|
||||
};
|
||||
if (/(y+)/.test(fmt)) {
|
||||
fmt = fmt.replace(
|
||||
RegExp.$1,
|
||||
`${date.getFullYear()}`.slice(4 - RegExp.$1.length),
|
||||
);
|
||||
}
|
||||
for (const k in o) {
|
||||
if (new RegExp(`(${k})`).test(fmt)) {
|
||||
fmt = fmt.replace(
|
||||
RegExp.$1,
|
||||
RegExp.$1.length === 1 ? o[k] : `00${o[k]}`.slice(`${o[k]}`.length),
|
||||
);
|
||||
}
|
||||
}
|
||||
return fmt;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
|
@ -3,5 +3,3 @@ export * from './dict';
|
|||
export * from './formCreate';
|
||||
export * from './rangePickerProps';
|
||||
export * from './routerHelper';
|
||||
export * from './upload';
|
||||
export * from './utils';
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
/**
|
||||
* Created by 芋道源码
|
||||
*
|
||||
* AI 枚举类
|
||||
*
|
||||
* 问题:为什么不放在 src/utils/common-utils.ts 呢?
|
||||
* 回答:主要 AI 是可选模块,考虑到独立、解耦,所以放在了 /views/ai/utils/common-utils.ts
|
||||
*/
|
||||
|
||||
/** 判断字符串是否包含中文 */
|
||||
export const hasChinese = (str: string) => {
|
||||
return /[\u4E00-\u9FA5]/.test(str);
|
||||
};
|
|
@ -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 下载文件
|
||||
*/
|
||||
|
|
|
@ -14,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,5 +1,7 @@
|
|||
import dayjs from 'dayjs';
|
||||
|
||||
import { isEmpty } from '.';
|
||||
|
||||
/** 时间段选择器拓展 */
|
||||
export function rangePickerExtend() {
|
||||
return {
|
||||
|
@ -41,3 +43,134 @@ export function rangePickerExtend() {
|
|||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 将时间转换为 `几秒前`、`几分钟前`、`几小时前`、`几天前`
|
||||
* @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 dayjs(date).format(format);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将毫秒,转换成时间字符串。例如说,xx 分钟
|
||||
*
|
||||
* @param ms 毫秒
|
||||
* @returns {string} 字符串
|
||||
*/
|
||||
export function formatPast2(ms: number): string {
|
||||
if (isEmpty(ms)) {
|
||||
return '';
|
||||
}
|
||||
// 定义时间单位常量,便于维护
|
||||
const SECOND = 1000;
|
||||
const MINUTE = 60 * SECOND;
|
||||
const HOUR = 60 * MINUTE;
|
||||
const DAY = 24 * HOUR;
|
||||
|
||||
// 计算各时间单位
|
||||
const day = Math.floor(ms / DAY);
|
||||
const hour = Math.floor((ms % DAY) / HOUR);
|
||||
const minute = Math.floor((ms % HOUR) / MINUTE);
|
||||
const second = Math.floor((ms % MINUTE) / SECOND);
|
||||
|
||||
// 根据时间长短返回不同格式
|
||||
if (day > 0) {
|
||||
return `${day} 天${hour} 小时 ${minute} 分钟`;
|
||||
}
|
||||
if (hour > 0) {
|
||||
return `${hour} 小时 ${minute} 分钟`;
|
||||
}
|
||||
if (minute > 0) {
|
||||
return `${minute} 分钟`;
|
||||
}
|
||||
return second > 0 ? `${second} 秒` : `${0} 秒`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 fmt;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* @param supportedFileTypes 支持的文件类型数组,如 ['PDF', 'DOC', 'DOCX']
|
||||
* @returns 用于文件上传组件 accept 属性的字符串
|
||||
*/
|
||||
export const generateAcceptedFileTypes = (
|
||||
export function generateAcceptedFileTypes(
|
||||
supportedFileTypes: string[],
|
||||
): string => {
|
||||
): string {
|
||||
const allowedExtensions = supportedFileTypes.map((ext) => ext.toLowerCase());
|
||||
const mimeTypes: string[] = [];
|
||||
|
||||
|
@ -64,4 +64,4 @@ export const generateAcceptedFileTypes = (
|
|||
const extensions = allowedExtensions.map((ext) => `.${ext}`);
|
||||
|
||||
return [...mimeTypes, ...extensions].join(',');
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue