feat: download 统一使用@vben/utils

pull/144/MERGE
xingyu4j 2025-06-15 19:46:09 +08:00
parent c9aac08ef9
commit 2885dc43bf
4 changed files with 59 additions and 221 deletions

View File

@ -1,215 +0,0 @@
/**
*
*
*/
// TODO @ziye请使用 @vben/utils/download 代替 packages/@core/base/shared/src/utils/download.ts
/**
*
*/
interface ImageDownloadOptions {
/** 图片 URL */
url: string;
/** 指定画布宽度 */
canvasWidth?: number;
/** 指定画布高度 */
canvasHeight?: number;
/** 将图片绘制在画布上时带上图片的宽高值,默认为 true */
drawWithImageSize?: boolean;
}
/**
*
* @param data - Blob
* @param fileName -
* @param mimeType - MIME
*/
export const download0 = (data: Blob, fileName: string, mimeType: string) => {
try {
// 创建 blob
const blob = new Blob([data], { type: mimeType });
// 创建 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);
} catch (error) {
console.error('文件下载失败:', error);
throw new Error(
`文件下载失败: ${error instanceof Error ? error.message : '未知错误'}`,
);
}
};
/**
*
* @param url -
* @param fileName -
*/
const triggerDownload = (url: string, fileName: string) => {
const a = document.createElement('a');
a.href = url;
a.download = fileName;
a.click();
};
export const download = {
/**
* Excel
* @param data - Blob
* @param fileName -
*/
excel: (data: Blob, fileName: string) => {
download0(data, fileName, 'application/vnd.ms-excel');
},
/**
* Word
* @param data - Blob
* @param fileName -
*/
word: (data: Blob, fileName: string) => {
download0(data, fileName, 'application/msword');
},
/**
* Zip
* @param data - Blob
* @param fileName -
*/
zip: (data: Blob, fileName: string) => {
download0(data, fileName, 'application/zip');
},
/**
* HTML
* @param data - Blob
* @param fileName -
*/
html: (data: Blob, fileName: string) => {
download0(data, fileName, 'text/html');
},
/**
* Markdown
* @param data - Blob
* @param fileName -
*/
markdown: (data: Blob, fileName: string) => {
download0(data, fileName, 'text/markdown');
},
/**
* JSON
* @param data - Blob
* @param fileName -
*/
json: (data: Blob, fileName: string) => {
download0(data, fileName, 'application/json');
},
/**
*
* @param options -
*/
image: (options: ImageDownloadOptions) => {
const {
url,
canvasWidth,
canvasHeight,
drawWithImageSize = true,
} = options;
const image = new Image();
// image.setAttribute('crossOrigin', 'anonymous')
image.src = url;
image.addEventListener('load', () => {
try {
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 dataUrl = canvas.toDataURL('image/png');
triggerDownload(dataUrl, 'image.png');
} catch (error) {
console.error('图片下载失败:', error);
throw new Error(
`图片下载失败: ${error instanceof Error ? error.message : '未知错误'}`,
);
}
});
image.addEventListener('error', () => {
throw new Error('图片加载失败');
});
},
/**
* Base64
* @param base64 - Base64
* @param fileName -
* @returns File
*/
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 : '未知错误'}`,
);
}
},
};

View File

@ -1,6 +1,5 @@
export * from './constants';
export * from './dict';
export * from './download';
export * from './formCreate';
export * from './rangePickerProps';
export * from './routerHelper';

View File

@ -3,13 +3,13 @@ import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import { base64ToFile } from '@vben/utils';
import { Button, message, Space, Tooltip } from 'ant-design-vue';
// TODO @ziye element-plus
import Vue3Signature from 'vue3-signature';
import { uploadFile } from '#/api/infra/file';
import { download } from '#/utils';
defineOptions({
name: 'BpmProcessInstanceSignature',
@ -31,10 +31,7 @@ const [Modal, modalApi] = useVbenModal({
content: '签名上传中请稍等。。。',
});
const signFileUrl = await uploadFile({
file: download.base64ToFile(
signature?.value?.save('image/jpeg') || '',
'签名',
),
file: base64ToFile(signature?.value?.save('image/jpeg') || '', '签名'),
});
emits('success', signFileUrl);
// TODO @ziyeps ide

View File

@ -140,6 +140,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