diff --git a/apps/vben5/apps/app-antd/package.json b/apps/vben5/apps/app-antd/package.json index f259b3b0b..8fe700c52 100644 --- a/apps/vben5/apps/app-antd/package.json +++ b/apps/vben5/apps/app-antd/package.json @@ -29,6 +29,7 @@ "@abp/account": "workspace:*", "@abp/ai-management": "workspace:*", "@abp/auditing": "workspace:*", + "@abp/blob-management": "workspace:*", "@abp/cache-management": "workspace:*", "@abp/components": "workspace:*", "@abp/core": "workspace:*", @@ -39,7 +40,6 @@ "@abp/localization": "workspace:*", "@abp/notifications": "workspace:*", "@abp/openiddict": "workspace:*", - "@abp/oss": "workspace:*", "@abp/permissions": "workspace:*", "@abp/platform": "workspace:*", "@abp/request": "workspace:*", diff --git a/apps/vben5/apps/app-antd/src/views/oss/containers/index.vue b/apps/vben5/apps/app-antd/src/views/blob-management/blobs/index.vue similarity index 58% rename from apps/vben5/apps/app-antd/src/views/oss/containers/index.vue rename to apps/vben5/apps/app-antd/src/views/blob-management/blobs/index.vue index f9dbfea0d..d3171b49b 100644 --- a/apps/vben5/apps/app-antd/src/views/oss/containers/index.vue +++ b/apps/vben5/apps/app-antd/src/views/blob-management/blobs/index.vue @@ -1,15 +1,15 @@ - + diff --git a/apps/vben5/apps/app-antd/src/views/oss/objects/index.vue b/apps/vben5/apps/app-antd/src/views/blob-management/containers/index.vue similarity index 50% rename from apps/vben5/apps/app-antd/src/views/oss/objects/index.vue rename to apps/vben5/apps/app-antd/src/views/blob-management/containers/index.vue index bc87ba072..ff62610ca 100644 --- a/apps/vben5/apps/app-antd/src/views/oss/objects/index.vue +++ b/apps/vben5/apps/app-antd/src/views/blob-management/containers/index.vue @@ -1,15 +1,15 @@ - - + + diff --git a/apps/vben5/packages/@abp/blob-management/package.json b/apps/vben5/packages/@abp/blob-management/package.json new file mode 100644 index 000000000..de1e2f0ec --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/package.json @@ -0,0 +1,50 @@ +{ + "name": "@abp/blob-management", + "version": "10.0.2", + "homepage": "https://github.com/colinin/abp-next-admin", + "bugs": "https://github.com/colinin/abp-next-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/colinin/abp-next-admin.git", + "directory": "packages/@abp/blob-management" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./global": { + "types": "./global.d.ts" + } + }, + "dependencies": { + "@abp/components": "workspace:*", + "@abp/core": "workspace:*", + "@abp/features": "workspace:*", + "@abp/request": "workspace:*", + "@abp/ui": "workspace:*", + "@ant-design/icons-vue": "catalog:", + "@vben/access": "workspace:*", + "@vben/common-ui": "workspace:*", + "@vben/hooks": "workspace:*", + "@vben/icons": "workspace:*", + "@vben/layouts": "workspace:*", + "@vben/locales": "workspace:*", + "@vben/stores": "workspace:*", + "@vben/utils": "workspace:*", + "@vue-office/docx": "catalog:", + "@vue-office/excel": "catalog:", + "@vue-office/pdf": "catalog:", + "@vue-office/pptx": "catalog:", + "ant-design-vue": "catalog:", + "vue": "catalog:", + "vue-demi": "catalog:", + "vue-simple-uploader": "catalog:", + "vxe-table": "catalog:" + } +} diff --git a/apps/vben5/packages/@abp/blob-management/src/api/index.ts b/apps/vben5/packages/@abp/blob-management/src/api/index.ts new file mode 100644 index 000000000..3bc09d6b2 --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/api/index.ts @@ -0,0 +1,2 @@ +export { useBlobContainersApi } from './useBlobContainersApi'; +export { useBlobsApi } from './useBlobsApi'; diff --git a/apps/vben5/packages/@abp/blob-management/src/api/useBlobContainersApi.ts b/apps/vben5/packages/@abp/blob-management/src/api/useBlobContainersApi.ts new file mode 100644 index 000000000..f8d8064d2 --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/api/useBlobContainersApi.ts @@ -0,0 +1,52 @@ +import type { PagedResultDto } from '@abp/core'; + +import type { + BlobContainerCreateDto, + BlobContainerDto, + BlobContainerGetPagedListInput, +} from '../types/containers'; + +import { useRequest } from '@abp/request'; + +export function useBlobContainersApi() { + const { cancel, request } = useRequest(); + + function deleteApi(id: string): Promise { + return request(`/api/blob-management/containers/${id}`, { + method: 'DELETE', + }); + } + + function getApi(id: string): Promise { + return request(`/api/blob-management/containers/${id}`, { + method: 'GET', + }); + } + + function getPagedListApi( + input?: BlobContainerGetPagedListInput, + ): Promise> { + return request>( + `/api/blob-management/containers`, + { + method: 'GET', + params: input, + }, + ); + } + + function createApi(input: BlobContainerCreateDto): Promise { + return request(`/api/blob-management/containers`, { + method: 'POST', + data: input, + }); + } + + return { + cancel, + createApi, + deleteApi, + getApi, + getPagedListApi, + }; +} diff --git a/apps/vben5/packages/@abp/blob-management/src/api/useBlobsApi.ts b/apps/vben5/packages/@abp/blob-management/src/api/useBlobsApi.ts new file mode 100644 index 000000000..f1c109147 --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/api/useBlobsApi.ts @@ -0,0 +1,79 @@ +import type { PagedResultDto } from '@abp/core'; + +import type { + BlobDto, + BlobFolderCreateDto, + BlobGetPagedListInput, +} from '../types/blobs'; + +import { useRequest } from '@abp/request'; + +import { BlobType } from '../types/blobs'; + +export function useBlobsApi() { + const { cancel, request } = useRequest(); + + function createFolderApi(input: BlobFolderCreateDto): Promise { + return request(`/api/blob-management/blobs/folder`, { + method: 'POST', + data: input, + }); + } + + function deleteApi(id: string): Promise { + return request(`/api/blob-management/blobs/${id}`, { + method: 'DELETE', + }); + } + + function getApi(id: string): Promise { + return request(`/api/blob-management/blobs/${id}`, { + method: 'GET', + }); + } + + function getContentApi(id: string): Promise { + return request(`/api/blob-management/blobs/uid/${id}/content`, { + method: 'GET', + responseType: 'blob', + }); + } + + function getFolderPagedListApi( + input: BlobGetPagedListInput, + ): Promise> { + return getPagedListApi({ + ...input, + type: BlobType.Folder, + }); + } + + function getFilePagedListApi( + input: BlobGetPagedListInput, + ): Promise> { + return getPagedListApi({ + ...input, + type: BlobType.File, + }); + } + + function getPagedListApi( + input: BlobGetPagedListInput, + ): Promise> { + return request>(`/api/blob-management/blobs`, { + method: 'GET', + params: input, + }); + } + + return { + cancel, + createFolderApi, + deleteApi, + getApi, + getContentApi, + getFolderPagedListApi, + getFilePagedListApi, + getPagedListApi, + }; +} diff --git a/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobFilePreviewModal.vue b/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobFilePreviewModal.vue new file mode 100644 index 000000000..55caa5ddf --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobFilePreviewModal.vue @@ -0,0 +1,411 @@ + + + + + + + + + + + + + + + 📄 + + + + + + + + + + 🔍 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ blobText }} + + + + + + + + {{ $t('BlobManagement.Blobs:Download') }} + + + + + + + + + diff --git a/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobFileTable.vue b/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobFileTable.vue new file mode 100644 index 000000000..36dab6532 --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobFileTable.vue @@ -0,0 +1,301 @@ + + + + + + + + {{ $t('BlobManagement.Blobs:UploadFile') }} + + + + + + {{ $t('BlobManagement.Blobs:Preview') }} + + + {{ $t('BlobManagement.Blobs:Download') }} + + + {{ $t('AbpUi.Delete') }} + + + + + + + gridApi.query()" /> + + + diff --git a/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobFileUploadModal.vue b/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobFileUploadModal.vue new file mode 100644 index 000000000..f884b24ca --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobFileUploadModal.vue @@ -0,0 +1,339 @@ + + + + + + + + + + + {{ $t('BlobManagement.Blobs:SelectFile') }} + + + + + + + + + + + {{ formatSize(row.size) }} + + + + + + + {{ $t('BlobManagement.UploadStatus:Error') }} + + + + {{ $t('BlobManagement.UploadStatus:Pause') }} + + + {{ $t('BlobManagement.UploadStatus:Completed') }} + + {{ + `${row.progressText} ${formatSize(row.averageSpeed)}/s` + }} + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobFolderModal.vue b/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobFolderModal.vue new file mode 100644 index 000000000..58d4f78e3 --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobFolderModal.vue @@ -0,0 +1,65 @@ + + + + + + + + + diff --git a/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobFolderTree.vue b/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobFolderTree.vue new file mode 100644 index 000000000..a9e50f87f --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobFolderTree.vue @@ -0,0 +1,283 @@ + + + + + + onContainerChange(e!.toString())" + /> + + {{ $t('BlobManagement.Blobs:CreateFolder') }} + + + + onFolderChange(info.node?.dataRef?.id)" + @expand="onFolderExpand" + /> + + + + + + + + diff --git a/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobViewPage.vue b/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobViewPage.vue new file mode 100644 index 000000000..1e38e435b --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/components/blobs/BlobViewPage.vue @@ -0,0 +1,45 @@ + + + + + + + + + + + + diff --git a/apps/vben5/packages/@abp/blob-management/src/components/containers/BlobContainerModal.vue b/apps/vben5/packages/@abp/blob-management/src/components/containers/BlobContainerModal.vue new file mode 100644 index 000000000..bd8734e9a --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/components/containers/BlobContainerModal.vue @@ -0,0 +1,59 @@ + + + + + + + + + diff --git a/apps/vben5/packages/@abp/blob-management/src/components/containers/BlobContainerTable.vue b/apps/vben5/packages/@abp/blob-management/src/components/containers/BlobContainerTable.vue new file mode 100644 index 000000000..2f544fe6f --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/components/containers/BlobContainerTable.vue @@ -0,0 +1,189 @@ + + + + + + + {{ $t('BlobManagement.BlobContainers:Create') }} + + + + + + {{ $t('AbpUi.Delete') }} + + + + + gridApi.query()" /> + + + diff --git a/apps/vben5/packages/@abp/blob-management/src/components/index.ts b/apps/vben5/packages/@abp/blob-management/src/components/index.ts new file mode 100644 index 000000000..e00d3cd64 --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/components/index.ts @@ -0,0 +1,2 @@ +export { default as BlobViewPage } from './blobs/BlobViewPage.vue'; +export { default as BlobContainerTable } from './containers/BlobContainerTable.vue'; diff --git a/apps/vben5/packages/@abp/blob-management/src/global.d.ts b/apps/vben5/packages/@abp/blob-management/src/global.d.ts new file mode 100644 index 000000000..2c4e1b1bb --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/global.d.ts @@ -0,0 +1,57 @@ +declare module 'vue-simple-uploader'; + +declare module '@vue-office/docx' { + import { DefineComponent } from 'vue'; + + const component: DefineComponent; + export default component; +} + +declare module '@vue-office/docx/lib/v3/index.js' { + import { DefineComponent } from 'vue'; + + const component: DefineComponent; + export default component; +} + +declare module '@vue-office/excel' { + import { DefineComponent } from 'vue'; + + const component: DefineComponent; + export default component; +} + +declare module '@vue-office/excel/lib/v3/index.js' { + import { DefineComponent } from 'vue'; + + const component: DefineComponent; + export default component; +} + +declare module '@vue-office/pdf' { + import { DefineComponent } from 'vue'; + + const component: DefineComponent; + export default component; +} + +declare module '@vue-office/pdf/lib/v3/index.js' { + import { DefineComponent } from 'vue'; + + const component: DefineComponent; + export default component; +} + +declare module '@vue-office/pptx' { + import { DefineComponent } from 'vue'; + + const component: DefineComponent; + export default component; +} + +declare module '@vue-office/pptx/lib/v3/index.js' { + import { DefineComponent } from 'vue'; + + const component: DefineComponent; + export default component; +} diff --git a/apps/vben5/packages/@abp/blob-management/src/index.ts b/apps/vben5/packages/@abp/blob-management/src/index.ts new file mode 100644 index 000000000..314dad0cd --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/index.ts @@ -0,0 +1,3 @@ +export * from './api'; +export * from './components'; +export * from './types'; diff --git a/apps/vben5/packages/@abp/blob-management/src/types/blobs.ts b/apps/vben5/packages/@abp/blob-management/src/types/blobs.ts new file mode 100644 index 000000000..e2ad008b2 --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/types/blobs.ts @@ -0,0 +1,78 @@ +import type { + ExtensibleAuditedEntityDto, + PagedAndSortedResultRequestDto, +} from '@abp/core'; + +export enum BlobType { + File = 1, + Folder = 0, +} + +interface BlobDto extends ExtensibleAuditedEntityDto { + contentType?: string; + downloadCount: number; + expirationTime?: Date; + name: string; + path: string; + size: number; + type: BlobType; +} + +interface BlobFolderCreateBaseDto { + name: string; + parentId?: string; +} + +interface BlobFileCreateBaseDto { + compareMd5?: string; + file: Blob | File; + name: string; + parentId?: string; +} + +interface BlobFileChunkCreateBaseDto extends BlobFileCreateBaseDto { + chunkNumber: number; + chunkSize: number; + currentChunkSize: number; + totalChunks: number; +} + +interface BlobFileCreateDto extends BlobFileCreateBaseDto { + containerId: string; +} + +interface BlobFileChunkCreateDto extends BlobFileChunkCreateBaseDto { + containerId: string; +} + +interface BlobFolderCreateDto extends BlobFolderCreateBaseDto { + containerId: string; +} + +interface BlobDownloadByNameInput { + blobName: string; + containerName: string; +} + +interface BlobGetPagedListInputBase extends PagedAndSortedResultRequestDto { + contentType?: string; + parentId?: string; + type?: BlobType; +} + +interface BlobGetPagedListInput extends BlobGetPagedListInputBase { + containerId: string; +} + +export type { + BlobDownloadByNameInput, + BlobDto, + BlobFileChunkCreateBaseDto, + BlobFileChunkCreateDto, + BlobFileCreateBaseDto, + BlobFileCreateDto, + BlobFolderCreateBaseDto, + BlobFolderCreateDto, + BlobGetPagedListInput, + BlobGetPagedListInputBase, +}; diff --git a/apps/vben5/packages/@abp/blob-management/src/types/containers.ts b/apps/vben5/packages/@abp/blob-management/src/types/containers.ts new file mode 100644 index 000000000..9158bc2b7 --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/types/containers.ts @@ -0,0 +1,24 @@ +import type { + ExtensibleAuditedEntityDto, + IHasConcurrencyStamp, + PagedAndSortedResultRequestDto, +} from '@abp/core'; + +interface BlobContainerDto + extends ExtensibleAuditedEntityDto, IHasConcurrencyStamp { + name: string; +} + +interface BlobContainerCreateDto { + name: string; +} + +interface BlobContainerGetPagedListInput extends PagedAndSortedResultRequestDto { + filter?: string; +} + +export type { + BlobContainerCreateDto, + BlobContainerDto, + BlobContainerGetPagedListInput, +}; diff --git a/apps/vben5/packages/@abp/blob-management/src/types/index.ts b/apps/vben5/packages/@abp/blob-management/src/types/index.ts new file mode 100644 index 000000000..738f79aa4 --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/src/types/index.ts @@ -0,0 +1,2 @@ +export * from './blobs'; +export * from './containers'; diff --git a/apps/vben5/packages/@abp/blob-management/tsconfig.json b/apps/vben5/packages/@abp/blob-management/tsconfig.json new file mode 100644 index 000000000..ce1a891fb --- /dev/null +++ b/apps/vben5/packages/@abp/blob-management/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/apps/vben5/packages/@abp/request/src/hooks/useErrorFormat.ts b/apps/vben5/packages/@abp/request/src/hooks/useErrorFormat.ts index 6bd1eb8ad..0d19534c3 100644 --- a/apps/vben5/packages/@abp/request/src/hooks/useErrorFormat.ts +++ b/apps/vben5/packages/@abp/request/src/hooks/useErrorFormat.ts @@ -17,6 +17,12 @@ export function useErrorFormat(response: AxiosResponse) { /** 如果请求错误,抛出异常 */ function throwIfError(): void { if (!hasError()) return; + throw Object.assign({}, response, { message: getErrorMessage(), response }); + } + + /** 获取错误消息 */ + function getErrorMessage(): string | undefined { + if (!hasError()) return undefined; const errorJson = data.error as RemoteServiceErrorInfo; let errorMessage = errorJson.message; if (errorJson.validationErrors) { @@ -24,11 +30,12 @@ export function useErrorFormat(response: AxiosResponse) { .map((error) => error.message) .join('\n'); } - throw Object.assign({}, response, { message: errorMessage, response }); + return errorMessage; } return { hasError, + getErrorMessage, throwIfError, }; } diff --git a/apps/vben5/packages/@abp/request/src/hooks/useWrapperResult.ts b/apps/vben5/packages/@abp/request/src/hooks/useWrapperResult.ts index f76ef6e2b..152de7f76 100644 --- a/apps/vben5/packages/@abp/request/src/hooks/useWrapperResult.ts +++ b/apps/vben5/packages/@abp/request/src/hooks/useWrapperResult.ts @@ -3,9 +3,20 @@ import type { AxiosResponse } from 'axios'; import { useErrorFormat } from './useErrorFormat'; export function useWrapperResult(response: AxiosResponse) { - const { hasError, throwIfError: throwIfAbpError } = useErrorFormat(response); + const { + hasError: hasAbpError, + getErrorMessage: getAbpErrorMessage, + throwIfError: throwIfAbpError, + } = useErrorFormat(response); const _defaultWrapperHeaderKey: string = '_abpwrapresult'; const { data, headers } = response; + + /** 是否请求错误 */ + function hasError(): boolean { + if (hasAbpError()) return true; + return Reflect.has(data, 'code') && data.code !== '0'; + } + /** 是否已包装结果 */ function hasWrapResult(): boolean { if ( @@ -14,7 +25,7 @@ export function useWrapperResult(response: AxiosResponse) { ) { return String(headers[_defaultWrapperHeaderKey]).includes('true'); } - return hasError(); + return hasAbpError(); } /** 获取包装结果 */ @@ -26,8 +37,7 @@ export function useWrapperResult(response: AxiosResponse) { /** 如果请求错误,抛出异常 */ function throwIfError(): void { throwIfAbpError(); - const hasSuccess = data && Reflect.has(data, 'code') && data.code === '0'; - if (!hasSuccess) { + if (hasError()) { const content = data.details || data.message; throw Object.assign({}, response, { response: { @@ -41,8 +51,19 @@ export function useWrapperResult(response: AxiosResponse) { } } + /** 获取错误消息 */ + function getErrorMessage(): string | undefined { + if (!hasWrapResult()) return undefined; + const errorMessage = getAbpErrorMessage(); + if (errorMessage) return errorMessage; + if (!hasError()) return undefined; + return data.message; + } + return { getData, + getErrorMessage, + hasError, hasWrapResult, }; } diff --git a/apps/vben5/pnpm-workspace.yaml b/apps/vben5/pnpm-workspace.yaml index ccab9922d..e267880d2 100644 --- a/apps/vben5/pnpm-workspace.yaml +++ b/apps/vben5/pnpm-workspace.yaml @@ -83,6 +83,10 @@ catalog: '@vite-pwa/vitepress': ^1.1.0 '@vitejs/plugin-vue': ^6.0.4 '@vitejs/plugin-vue-jsx': ^5.1.4 + '@vue-office/docx': ^1.6.3 + '@vue-office/excel': ^1.7.14 + '@vue-office/pdf': ^2.0.10 + '@vue-office/pptx': ^1.0.1 '@vue/shared': ^3.5.27 '@vue/test-utils': ^2.4.6 '@vueuse/core': ^14.1.0 @@ -229,6 +233,7 @@ catalog: vitepress-plugin-group-icons: ^1.7.1 vitest: ^3.2.4 vue: ^3.5.27 + vue-demi: ^0.14.6 vue-eslint-parser: ^10.2.0 vue-i18n: ^11.2.8 vue-json-viewer: ^3.0.4