diff --git a/.gitmodules b/.gitmodules
index 15a607d..ca798f6 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -22,3 +22,6 @@
[submodule "submodules/store"]
path = submodules/store
url = git@git.xiongxiao.me:kevisual/store.git
+[submodule "submodules/query-upload"]
+ path = submodules/query-upload
+ url = git@git.xiongxiao.me:kevisual/kevisual-query-upload.git
diff --git a/packages/components b/packages/components
index 62c5c2a..0a8d7ab 160000
--- a/packages/components
+++ b/packages/components
@@ -1 +1 @@
-Subproject commit 62c5c2a82a2d346e27e064301a7c36c114f8512a
+Subproject commit 0a8d7abf9a78bce528e31741d4643e017812616d
diff --git a/packages/mark b/packages/mark
index 508ec96..a99d9c2 160000
--- a/packages/mark
+++ b/packages/mark
@@ -1 +1 @@
-Subproject commit 508ec960292ad5b009e594082a2e8945ab783d28
+Subproject commit a99d9c2322df537d7ffaac09545e9b2e121511fa
diff --git a/packages/resources/package.json b/packages/resources/package.json
index 1cd7675..3dead02 100644
--- a/packages/resources/package.json
+++ b/packages/resources/package.json
@@ -20,6 +20,7 @@
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@kevisual/components": "workspace:*",
+ "@kevisual/query-upload": "workspace:*",
"@kevisual/router": "^0.0.9",
"@kevisual/store": "^0.0.2",
"@mui/material": "^6.4.8",
diff --git a/packages/resources/src/index.ts b/packages/resources/src/index.ts
index c2310a6..fb079bd 100644
--- a/packages/resources/src/index.ts
+++ b/packages/resources/src/index.ts
@@ -5,3 +5,5 @@ export { iText } from './i-text/index.ts';
export { uploadFiles, uploadFileChunked, getDirectoryAndName, toFile, createDirectory } from './pages/upload/app';
export { DialogDirectory, DialogDeleteDirectory } from './pages/upload/DialogDirectory';
+
+export { uploadChunkV2 } from './pages/upload/v2/upload-chunk';
diff --git a/packages/resources/src/pages/upload/index.tsx b/packages/resources/src/pages/upload/index.tsx
index 502bf81..ac96a90 100644
--- a/packages/resources/src/pages/upload/index.tsx
+++ b/packages/resources/src/pages/upload/index.tsx
@@ -5,6 +5,9 @@ import { FileText, CloudUpload as UploadIcon } from 'lucide-react';
import { uploadFileChunked } from './utils/upload-chunk';
import { filterFiles } from './utils/filter-files';
import { IconButton } from '@kevisual/components/button/index.tsx';
+import { uploadChunkV2 } from './v2/upload-chunk';
+import { uploadFilesV2 } from './v2/upload';
+
type UploadButtonProps = {
/**
* 前缀
@@ -51,12 +54,10 @@ export const UploadButton = (props: UploadButtonProps) => {
console.log(acceptedFiles);
acceptedFiles = filterFiles(acceptedFiles);
if (acceptedFiles.length > 1) {
- const res = await uploadFiles(acceptedFiles, { directory, appKey, version, username });
- console.log('uploadFiles res', res);
+ const res = await uploadFilesV2(acceptedFiles, { directory, appKey, version, username });
props.onUpload?.(res);
} else if (acceptedFiles.length === 1) {
- const res = await uploadFileChunked(acceptedFiles[0], { directory, appKey, version, username });
- console.log('uploadFiles res', res);
+ const res = await uploadChunkV2(acceptedFiles[0], { directory, appKey, version, username });
props.onUpload?.(res);
}
};
@@ -83,11 +84,9 @@ export const Upload = ({ uploadDirectory = false }: { uploadDirectory?: boolean
const onDrop = async (acceptedFiles) => {
acceptedFiles = filterFiles(acceptedFiles);
if (acceptedFiles.length > 1) {
- const res = await uploadFiles(acceptedFiles, {});
- console.log('uploadFiles res', res);
+ const res = await uploadFilesV2(acceptedFiles, {});
} else if (acceptedFiles.length === 1) {
- const res = await uploadFileChunked(acceptedFiles[0], {});
- console.log('uploadFiles res', res);
+ const res = await uploadChunkV2(acceptedFiles[0], {});
}
};
diff --git a/packages/resources/src/pages/upload/utils/create-directory.ts b/packages/resources/src/pages/upload/utils/create-directory.ts
index 488e5f1..4290cc2 100644
--- a/packages/resources/src/pages/upload/utils/create-directory.ts
+++ b/packages/resources/src/pages/upload/utils/create-directory.ts
@@ -1,4 +1,4 @@
-import { toTextFile } from '../app';
+import { toTextFile } from '../tools/to-file';
import { ConvertOpts, uploadFileChunked } from './upload-chunk';
/**
* 对创建的directory的路径进行解析,
diff --git a/packages/resources/src/pages/upload/v2/upload-chunk.ts b/packages/resources/src/pages/upload/v2/upload-chunk.ts
new file mode 100644
index 0000000..196a2e6
--- /dev/null
+++ b/packages/resources/src/pages/upload/v2/upload-chunk.ts
@@ -0,0 +1,55 @@
+import NProgress from 'nprogress';
+import 'nprogress/nprogress.css';
+import { Id, toast } from 'react-toastify';
+import { nanoid } from 'nanoid';
+import { toastLogin } from '@kevisual/resources/pages/message/ToastLogin';
+import { uploadFileChunked, UploadProgress } from '@kevisual/query-upload/query-upload';
+
+export type ConvertOpts = {
+ appKey?: string;
+ version?: string;
+ username?: string;
+ directory?: string;
+ isPublic?: boolean;
+ filename?: string;
+};
+
+export const uploadChunkV2 = async (file: File, opts: ConvertOpts) => {
+ const filename = opts.filename || file.name;
+ const token = localStorage.getItem('token');
+ if (!token) {
+ console.log('uploadChunk token', token);
+ toastLogin();
+ return;
+ }
+ let loaded: Id;
+ const uploadProgress = new UploadProgress({
+ onStart: function () {
+ NProgress.start();
+ loaded = toast.loading(`${filename} 上传中...`);
+ },
+ onDone: () => {
+ NProgress.done();
+ toast.dismiss(loaded);
+ },
+ onProgress: (progress, data) => {
+ NProgress.set(progress);
+ console.log('uploadChunk progress', progress, data);
+ toast.update(loaded, {
+ render: `${filename} 上传中... ${progress.toFixed(2)}%`,
+ isLoading: true,
+ autoClose: false,
+ });
+ },
+ });
+
+ const result = await uploadFileChunked(file, opts, {
+ uploadProgress,
+ token,
+ createEventSource: (url: string, searchParams: URLSearchParams) => {
+ return new EventSource(url + '?' + searchParams.toString());
+ },
+ FormDataFn: FormData,
+ });
+ return result;
+};
diff --git a/packages/resources/src/pages/upload/v2/upload.ts b/packages/resources/src/pages/upload/v2/upload.ts
new file mode 100644
index 0000000..3954256
--- /dev/null
+++ b/packages/resources/src/pages/upload/v2/upload.ts
@@ -0,0 +1,70 @@
+import NProgress from 'nprogress';
+import 'nprogress/nprogress.css';
+import { Id, toast } from 'react-toastify';
+import { nanoid } from 'nanoid';
+import { toastLogin } from '@kevisual/resources/pages/message/ToastLogin';
+import { uploadFiles, UploadProgress } from '@kevisual/query-upload/query-upload';
+
+export type ConvertOpts = {
+ appKey?: string;
+ version?: string;
+ username?: string;
+ directory?: string;
+ isPublic?: boolean;
+
+ maxSize?: number;
+ maxCount?: number;
+};
+
+export const uploadFilesV2 = async (files: File[], opts: ConvertOpts) => {
+ const token = localStorage.getItem('token');
+ if (!token) {
+ console.log('uploadChunk token', token);
+ toastLogin();
+ return;
+ }
+ const length = files.length;
+ const maxSize = opts.maxSize || 20 * 1024 * 1024; // 10MB
+ const totalSize = files.reduce((acc, file) => acc + file.size, 0);
+ if (totalSize > maxSize) {
+ toast.error('有文件大小不能超过20MB');
+ return;
+ }
+ const maxCount = opts.maxCount || 10;
+ if (length > maxCount) {
+ toast.error(`最多只能上传${maxCount}个文件`);
+ return;
+ }
+ toast.info(`上传中,共${length}个文件`);
+
+ let loaded: Id;
+ const uploadProgress = new UploadProgress({
+ onStart: function () {
+ NProgress.start();
+ loaded = toast.loading(`上传中...`);
+ },
+ onDone: () => {
+ NProgress.done();
+ toast.dismiss(loaded);
+ },
+ onProgress: (progress, data) => {
+ NProgress.set(progress);
+ console.log('uploadChunk progress', progress, data);
+ toast.update(loaded, {
+ render: `上传中... ${progress.toFixed(2)}%`,
+ isLoading: true,
+ autoClose: false,
+ });
+ },
+ });
+
+ const result = await uploadFiles(files, opts, {
+ uploadProgress,
+ token,
+ createEventSource: (url: string, searchParams: URLSearchParams) => {
+ return new EventSource(url + '?' + searchParams.toString());
+ },
+ FormDataFn: FormData,
+ });
+ return result;
+};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index bcd8d5f..6358d79 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -481,6 +481,9 @@ importers:
'@kevisual/components':
specifier: workspace:*
version: link:../components
+ '@kevisual/query-upload':
+ specifier: workspace:*
+ version: link:../../submodules/query-upload
'@kevisual/router':
specifier: ^0.0.9
version: 0.0.9
@@ -600,6 +603,18 @@ importers:
specifier: ^8.4.0
version: 8.4.0(@microsoft/api-extractor@7.52.2(@types/node@22.13.13))(jiti@2.4.2)(postcss@8.5.3)(typescript@5.8.2)(yaml@2.5.1)
+ submodules/query-upload:
+ devDependencies:
+ '@types/node':
+ specifier: ^22.13.14
+ version: 22.13.14
+ eventsource:
+ specifier: ^3.0.6
+ version: 3.0.6
+ tsup:
+ specifier: ^8.4.0
+ version: 8.4.0(@microsoft/api-extractor@7.52.2(@types/node@22.13.14))(jiti@2.4.2)(postcss@8.5.3)(typescript@5.8.2)(yaml@2.5.1)
+
submodules/store:
dependencies:
eventemitter3:
@@ -3181,6 +3196,14 @@ packages:
eventemitter3@5.0.1:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
+ eventsource-parser@3.0.1:
+ resolution: {integrity: sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==}
+ engines: {node: '>=18.0.0'}
+
+ eventsource@3.0.6:
+ resolution: {integrity: sha512-l19WpE2m9hSuyP06+FbuUUf1G+R0SFLrtQfbRb9PRr+oimOfxQhgGCbVaXg5IvZyyTThJsxh6L/srkMiCeBPDA==}
+ engines: {node: '>=18.0.0'}
+
exsolve@1.0.4:
resolution: {integrity: sha512-xsZH6PXaER4XoV+NiT7JHp1bJodJVT+cxeSH1G0f0tlT0lJqYuHUP3bUx2HtfTDvOagMINYp8rsqusxud3RXhw==}
@@ -6931,7 +6954,7 @@ snapshots:
'@types/qrcode@1.5.5':
dependencies:
- '@types/node': 22.13.13
+ '@types/node': 22.13.14
'@types/react-dom@19.0.4(@types/react@19.0.12)':
dependencies:
@@ -7812,6 +7835,12 @@ snapshots:
eventemitter3@5.0.1: {}
+ eventsource-parser@3.0.1: {}
+
+ eventsource@3.0.6:
+ dependencies:
+ eventsource-parser: 3.0.1
+
exsolve@1.0.4: {}
extend@3.0.2: {}
diff --git a/src/App.tsx b/src/App.tsx
index 069893d..0b797b1 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -75,7 +75,7 @@ export const App = () => {
} />
} />
} />
- } />
+ } />
} />
} />
} />
diff --git a/src/modules/query.ts b/src/modules/query.ts
index 92f3afb..a4dd823 100644
--- a/src/modules/query.ts
+++ b/src/modules/query.ts
@@ -1,6 +1,7 @@
import { QueryClient } from '@kevisual/query';
import { QueryLoginBrowser } from '@kevisual/query-login';
import { toastLogin } from '@kevisual/resources/pages/message/ToastLogin.tsx';
+import { toast } from 'react-toastify';
export const query = new QueryClient({
io: true,
});
@@ -25,6 +26,15 @@ query.afterResponse = async (res, ctx) => {
afterAlso401: () => {
toastLogin();
},
+ afterCheck: (res) => {
+ if (res.code === 200) {
+ // console.log('afterCheck');
+ toast.success('刷新登陆信息');
+ setTimeout(() => {
+ window.location.reload();
+ }, 1000);
+ }
+ },
});
return newRes as any;
diff --git a/submodules/query-login b/submodules/query-login
index 05f0373..98c8a2a 160000
--- a/submodules/query-login
+++ b/submodules/query-login
@@ -1 +1 @@
-Subproject commit 05f037383436cd0e30a6a12e7a4e08bf5b884ea8
+Subproject commit 98c8a2ad86331566058ca7cc12df5ee2b406fa2f
diff --git a/submodules/query-mark b/submodules/query-mark
index 39f0d7c..757be9f 160000
--- a/submodules/query-mark
+++ b/submodules/query-mark
@@ -1 +1 @@
-Subproject commit 39f0d7c566584811b73c027fb3eb43075d74ee1f
+Subproject commit 757be9fc2fbfa2ac0af92a3002cccc9edba995ba
diff --git a/submodules/query-upload b/submodules/query-upload
new file mode 160000
index 0000000..a7faaca
--- /dev/null
+++ b/submodules/query-upload
@@ -0,0 +1 @@
+Subproject commit a7faaca4188cae055deb1af1d1b758b6d5439f8d
diff --git a/submodules/store b/submodules/store
index 415f008..69784e8 160000
--- a/submodules/store
+++ b/submodules/store
@@ -1 +1 @@
-Subproject commit 415f008209c9579cccc5692bdc8a5f9f97b78d8c
+Subproject commit 69784e8ed4f4546c91b2653adc75034e5946123c
diff --git a/vite.config.ts b/vite.config.ts
index 092b342..0e32b46 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -7,8 +7,8 @@ const isDev = process.env.NODE_ENV === 'development';
const plugins: any[] = [basicSsl()];
plugins.push(tailwindcss());
const devBackend = 'https://kevisual.silkyai.cn';
-// const meBackend = 'https://kevisual.xiongxiao.me';
-const meBackend = 'https://kevisual.cn';
+const meBackend = 'https://kevisual.xiongxiao.me';
+// const meBackend = 'https://kevisual.cn';
// const backend = isDev ? devBackend : meBackend;
const backendWss = devBackend.replace(/^https:/, 'wss:');
const backend = meBackend;