diff --git a/CHANGELOG.md b/CHANGELOG.md index e3467a97..8c444e46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,24 @@ # 1.3.0 +注意: 如果你使用老版本的`文件上传`/`图片上传` 可暂时使用 + +- `component: 'ImageUploadOld'` +- `component: 'FileUploadOld'` + +代替 **建议替换为新版本** + +大致变动: + +- `accept string[] -> string` +- `resultField 已经移除 统一使用ossId` +- `maxNumber -> maxCount` + +具体参数查看: `apps/web-antd/src/components/upload/src/props.d.ts` + **REFACTOR** -- 文件上传/图片上传重构(破坏性更新 不兼容之前的api) -- 文件上传/图片上传**不再支持**url用法 强制使用ossId +- **文件上传/图片上传重构(破坏性更新 不兼容之前的api)** +- **文件上传/图片上传**不再支持**url用法 强制使用ossId** - TableSwitch组件重构 - 管理员租户切换不再返回首页 直接刷新当前页(除特殊页面外会回到首页) - modalLoading/drawerLoading改为调用内部的lock/unlock方法 diff --git a/apps/web-antd/src/adapter/component/index.ts b/apps/web-antd/src/adapter/component/index.ts index 4ef5e99a..628f1a62 100644 --- a/apps/web-antd/src/adapter/component/index.ts +++ b/apps/web-antd/src/adapter/component/index.ts @@ -40,6 +40,7 @@ import { import { Tinymce as RichTextarea } from '#/components/tinymce'; import { FileUpload, ImageUpload } from '#/components/upload'; +import { FileUploadOld, ImageUploadOld } from '#/components/upload-old'; const withDefaultPlaceholder = ( component: T, @@ -99,8 +100,10 @@ export type ComponentType = | 'DefaultButton' | 'Divider' | 'FileUpload' + | 'FileUploadOld' | 'IconPicker' | 'ImageUpload' + | 'ImageUploadOld' | 'Input' | 'InputNumber' | 'InputPassword' @@ -175,6 +178,8 @@ async function initComponentAdapter() { ImageUpload, FileUpload, RichTextarea, + ImageUploadOld, + FileUploadOld, }; // 将组件注册到全局共享状态中 diff --git a/apps/web-antd/src/api/core/upload.ts b/apps/web-antd/src/api/core/upload.ts index 33db90ca..f057dab5 100644 --- a/apps/web-antd/src/api/core/upload.ts +++ b/apps/web-antd/src/api/core/upload.ts @@ -19,18 +19,21 @@ export interface UploadResult { /** * 通过单文件上传接口 * @param file 上传的文件 - * @param otherData 其他请求参数 后端拓展可能会用到 * @param options 一些配置项 * @param options.onUploadProgress 上传进度事件 * @param options.signal 上传取消信号 + * @param options.otherData 其他请求参数 后端拓展可能会用到 * @returns 上传结果 */ export function uploadApi( file: Blob | File, - otherData?: Record, - options?: { onUploadProgress?: AxiosProgressEvent; signal?: AbortSignal }, + options?: { + onUploadProgress?: AxiosProgressEvent; + otherData?: Record; + signal?: AbortSignal; + }, ) { - const { onUploadProgress, signal } = options ?? {}; + const { onUploadProgress, signal, otherData = {} } = options ?? {}; return requestClient.upload( '/resource/oss/upload', { file, ...otherData }, diff --git a/apps/web-antd/src/components/upload-old/index.ts b/apps/web-antd/src/components/upload-old/index.ts new file mode 100644 index 00000000..6a352f5e --- /dev/null +++ b/apps/web-antd/src/components/upload-old/index.ts @@ -0,0 +1,8 @@ +/** + * @description: 旧版文件上传组件 使用FileUpload代替 + */ +export { default as FileUploadOld } from './src/file-upload.vue'; +/** + * @description: 旧版图片上传组件 使用ImageUpload代替 + */ +export { default as ImageUploadOld } from './src/image-upload.vue'; diff --git a/apps/web-antd/src/components/upload-old/src/file-upload.vue b/apps/web-antd/src/components/upload-old/src/file-upload.vue new file mode 100644 index 00000000..b45ec43d --- /dev/null +++ b/apps/web-antd/src/components/upload-old/src/file-upload.vue @@ -0,0 +1,240 @@ + + + + + diff --git a/apps/web-antd/src/components/upload-old/src/helper.ts b/apps/web-antd/src/components/upload-old/src/helper.ts new file mode 100644 index 00000000..ff70b413 --- /dev/null +++ b/apps/web-antd/src/components/upload-old/src/helper.ts @@ -0,0 +1,51 @@ +import { fileTypeFromBlob } from '@vben/utils'; + +/** + * 不支持txt文件 @see https://github.com/sindresorhus/file-type/issues/55 + * 需要自行修改 + * @param file file对象 + * @param accepts 文件类型数组 包括拓展名(不带点) 文件头(image/png等 不包括泛写法即image/*) + * @returns 是否通过文件类型校验 + */ +export async function checkFileType(file: File, accepts: string[]) { + if (!accepts || accepts?.length === 0) { + return true; + } + console.log(file); + const fileType = await fileTypeFromBlob(file); + if (!fileType) { + console.error('无法获取文件类型'); + return false; + } + console.log('文件类型', fileType); + // 是否文件拓展名/文件头任意有一个匹配 + return accepts.includes(fileType.ext) || accepts.includes(fileType.mime); +} + +/** + * 默认图片类型 + */ +export const defaultImageAccept = ['jpg', 'jpeg', 'png', 'gif', 'webp']; +/** + * 判断文件类型是否符合要求 + * @param file file对象 + * @param accepts 文件类型数组 包括拓展名(不带点) 文件头(image/png等 不包括泛写法即image/*) + * @returns 是否通过文件类型校验 + */ +export async function checkImageFileType(file: File, accepts: string[]) { + // 空的accepts 使用默认规则 + if (!accepts || accepts.length === 0) { + accepts = defaultImageAccept; + } + const fileType = await fileTypeFromBlob(file); + if (!fileType) { + console.error('无法获取文件类型'); + return false; + } + console.log('文件类型', fileType); + // 是否文件拓展名/文件头任意有一个匹配 + if (accepts.includes(fileType.ext) || accepts.includes(fileType.mime)) { + return true; + } + return false; +} diff --git a/apps/web-antd/src/components/upload-old/src/image-upload.vue b/apps/web-antd/src/components/upload-old/src/image-upload.vue new file mode 100644 index 00000000..dc56430c --- /dev/null +++ b/apps/web-antd/src/components/upload-old/src/image-upload.vue @@ -0,0 +1,323 @@ + + + + + diff --git a/apps/web-antd/src/components/upload-old/src/typing.ts b/apps/web-antd/src/components/upload-old/src/typing.ts new file mode 100644 index 00000000..8d728b6c --- /dev/null +++ b/apps/web-antd/src/components/upload-old/src/typing.ts @@ -0,0 +1,37 @@ +import type { Recordable } from '@vben/types'; + +export enum UploadResultStatus { + DONE = 'done', + ERROR = 'error', + SUCCESS = 'success', + UPLOADING = 'uploading', +} + +export interface FileItem { + thumbUrl?: string; + name: string; + size: number | string; + type?: string; + percent: number; + file: File; + status?: UploadResultStatus; + response?: Recordable | { fileName: string; ossId: string; url: string }; + uuid: string; +} + +export interface Wrapper { + record: FileItem; + uidKey: string; + valueKey: string; +} + +export interface BaseFileItem { + uid: number | string; + url: string; + name?: string; +} +export interface PreviewFileItem { + url: string; + name: string; + type: string; +} diff --git a/apps/web-antd/src/components/upload-old/src/use-upload.ts b/apps/web-antd/src/components/upload-old/src/use-upload.ts new file mode 100644 index 00000000..4ae552f3 --- /dev/null +++ b/apps/web-antd/src/components/upload-old/src/use-upload.ts @@ -0,0 +1,61 @@ +import type { Ref } from 'vue'; + +import { computed, unref } from 'vue'; + +import { $t } from '@vben/locales'; + +export function useUploadType({ + acceptRef, + helpTextRef, + maxNumberRef, + maxSizeRef, +}: { + acceptRef: Ref; + helpTextRef: Ref; + maxNumberRef: Ref; + maxSizeRef: Ref; +}) { + // 文件类型限制 + const getAccept = computed(() => { + const accept = unref(acceptRef); + if (accept && accept.length > 0) { + return accept; + } + return []; + }); + const getStringAccept = computed(() => { + return unref(getAccept) + .map((item) => { + return item.indexOf('/') > 0 || item.startsWith('.') + ? item + : `.${item}`; + }) + .join(','); + }); + + // 支持jpg、jpeg、png格式,不超过2M,最多可选择10张图片,。 + const getHelpText = computed(() => { + const helpText = unref(helpTextRef); + if (helpText) { + return helpText; + } + const helpTexts: string[] = []; + + const accept = unref(acceptRef); + if (accept.length > 0) { + helpTexts.push($t('component.upload.accept', [accept.join(',')])); + } + + const maxSize = unref(maxSizeRef); + if (maxSize) { + helpTexts.push($t('component.upload.maxSize', [maxSize])); + } + + const maxNumber = unref(maxNumberRef); + if (maxNumber && maxNumber !== Infinity) { + helpTexts.push($t('component.upload.maxNumber', [maxNumber])); + } + return helpTexts.join(','); + }); + return { getAccept, getStringAccept, getHelpText }; +} diff --git a/apps/web-antd/src/components/upload/src/hook.ts b/apps/web-antd/src/components/upload/src/hook.ts index b874a741..1b5785a9 100644 --- a/apps/web-antd/src/components/upload/src/hook.ts +++ b/apps/web-antd/src/components/upload/src/hook.ts @@ -286,9 +286,10 @@ export function useUpload( const percent = Math.trunc((e.loaded / e.total!) * 100); info.onProgress!({ percent }); }; - const res = await api(info.file as File, props?.data ?? {}, { + const res = await api(info.file as File, { onUploadProgress: progressEvent, signal: uploadAbort.signal, + otherData: props?.data, }); info.onSuccess!(res); if (props.showSuccessMsg) {