update upload way

This commit is contained in:
xion 2025-03-31 21:18:39 +08:00
parent 0f03491a84
commit 122fbec7da
17 changed files with 188 additions and 18 deletions

3
.gitmodules vendored
View File

@ -22,3 +22,6 @@
[submodule "submodules/store"] [submodule "submodules/store"]
path = submodules/store path = submodules/store
url = git@git.xiongxiao.me:kevisual/store.git 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

@ -1 +1 @@
Subproject commit 62c5c2a82a2d346e27e064301a7c36c114f8512a Subproject commit 0a8d7abf9a78bce528e31741d4643e017812616d

@ -1 +1 @@
Subproject commit 508ec960292ad5b009e594082a2e8945ab783d28 Subproject commit a99d9c2322df537d7ffaac09545e9b2e121511fa

View File

@ -20,6 +20,7 @@
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0", "@emotion/styled": "^11.14.0",
"@kevisual/components": "workspace:*", "@kevisual/components": "workspace:*",
"@kevisual/query-upload": "workspace:*",
"@kevisual/router": "^0.0.9", "@kevisual/router": "^0.0.9",
"@kevisual/store": "^0.0.2", "@kevisual/store": "^0.0.2",
"@mui/material": "^6.4.8", "@mui/material": "^6.4.8",

View File

@ -5,3 +5,5 @@ export { iText } from './i-text/index.ts';
export { uploadFiles, uploadFileChunked, getDirectoryAndName, toFile, createDirectory } from './pages/upload/app'; export { uploadFiles, uploadFileChunked, getDirectoryAndName, toFile, createDirectory } from './pages/upload/app';
export { DialogDirectory, DialogDeleteDirectory } from './pages/upload/DialogDirectory'; export { DialogDirectory, DialogDeleteDirectory } from './pages/upload/DialogDirectory';
export { uploadChunkV2 } from './pages/upload/v2/upload-chunk';

View File

@ -5,6 +5,9 @@ import { FileText, CloudUpload as UploadIcon } from 'lucide-react';
import { uploadFileChunked } from './utils/upload-chunk'; import { uploadFileChunked } from './utils/upload-chunk';
import { filterFiles } from './utils/filter-files'; import { filterFiles } from './utils/filter-files';
import { IconButton } from '@kevisual/components/button/index.tsx'; import { IconButton } from '@kevisual/components/button/index.tsx';
import { uploadChunkV2 } from './v2/upload-chunk';
import { uploadFilesV2 } from './v2/upload';
type UploadButtonProps = { type UploadButtonProps = {
/** /**
* *
@ -51,12 +54,10 @@ export const UploadButton = (props: UploadButtonProps) => {
console.log(acceptedFiles); console.log(acceptedFiles);
acceptedFiles = filterFiles(acceptedFiles); acceptedFiles = filterFiles(acceptedFiles);
if (acceptedFiles.length > 1) { if (acceptedFiles.length > 1) {
const res = await uploadFiles(acceptedFiles, { directory, appKey, version, username }); const res = await uploadFilesV2(acceptedFiles, { directory, appKey, version, username });
console.log('uploadFiles res', res);
props.onUpload?.(res); props.onUpload?.(res);
} else if (acceptedFiles.length === 1) { } else if (acceptedFiles.length === 1) {
const res = await uploadFileChunked(acceptedFiles[0], { directory, appKey, version, username }); const res = await uploadChunkV2(acceptedFiles[0], { directory, appKey, version, username });
console.log('uploadFiles res', res);
props.onUpload?.(res); props.onUpload?.(res);
} }
}; };
@ -83,11 +84,9 @@ export const Upload = ({ uploadDirectory = false }: { uploadDirectory?: boolean
const onDrop = async (acceptedFiles) => { const onDrop = async (acceptedFiles) => {
acceptedFiles = filterFiles(acceptedFiles); acceptedFiles = filterFiles(acceptedFiles);
if (acceptedFiles.length > 1) { if (acceptedFiles.length > 1) {
const res = await uploadFiles(acceptedFiles, {}); const res = await uploadFilesV2(acceptedFiles, {});
console.log('uploadFiles res', res);
} else if (acceptedFiles.length === 1) { } else if (acceptedFiles.length === 1) {
const res = await uploadFileChunked(acceptedFiles[0], {}); const res = await uploadChunkV2(acceptedFiles[0], {});
console.log('uploadFiles res', res);
} }
}; };

View File

@ -1,4 +1,4 @@
import { toTextFile } from '../app'; import { toTextFile } from '../tools/to-file';
import { ConvertOpts, uploadFileChunked } from './upload-chunk'; import { ConvertOpts, uploadFileChunked } from './upload-chunk';
/** /**
* directory的路径进行解析 * directory的路径进行解析

View File

@ -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;
};

View File

@ -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;
};

31
pnpm-lock.yaml generated
View File

@ -481,6 +481,9 @@ importers:
'@kevisual/components': '@kevisual/components':
specifier: workspace:* specifier: workspace:*
version: link:../components version: link:../components
'@kevisual/query-upload':
specifier: workspace:*
version: link:../../submodules/query-upload
'@kevisual/router': '@kevisual/router':
specifier: ^0.0.9 specifier: ^0.0.9
version: 0.0.9 version: 0.0.9
@ -600,6 +603,18 @@ importers:
specifier: ^8.4.0 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) 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: submodules/store:
dependencies: dependencies:
eventemitter3: eventemitter3:
@ -3181,6 +3196,14 @@ packages:
eventemitter3@5.0.1: eventemitter3@5.0.1:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} 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: exsolve@1.0.4:
resolution: {integrity: sha512-xsZH6PXaER4XoV+NiT7JHp1bJodJVT+cxeSH1G0f0tlT0lJqYuHUP3bUx2HtfTDvOagMINYp8rsqusxud3RXhw==} resolution: {integrity: sha512-xsZH6PXaER4XoV+NiT7JHp1bJodJVT+cxeSH1G0f0tlT0lJqYuHUP3bUx2HtfTDvOagMINYp8rsqusxud3RXhw==}
@ -6931,7 +6954,7 @@ snapshots:
'@types/qrcode@1.5.5': '@types/qrcode@1.5.5':
dependencies: dependencies:
'@types/node': 22.13.13 '@types/node': 22.13.14
'@types/react-dom@19.0.4(@types/react@19.0.12)': '@types/react-dom@19.0.4(@types/react@19.0.12)':
dependencies: dependencies:
@ -7812,6 +7835,12 @@ snapshots:
eventemitter3@5.0.1: {} eventemitter3@5.0.1: {}
eventsource-parser@3.0.1: {}
eventsource@3.0.6:
dependencies:
eventsource-parser: 3.0.1
exsolve@1.0.4: {} exsolve@1.0.4: {}
extend@3.0.2: {} extend@3.0.2: {}

View File

@ -75,7 +75,7 @@ export const App = () => {
<Route path='/container/*' element={<ContainerApp />} /> <Route path='/container/*' element={<ContainerApp />} />
<Route path='/map/*' element={<MapApp />} /> <Route path='/map/*' element={<MapApp />} />
<Route path='/user/*' element={<UserApp />} /> <Route path='/user/*' element={<UserApp />} />
<Route path='/user1/*' element={<UserApp />} /> <Route path='/user-center/*' element={<UserApp />} />
<Route path='/org/*' element={<OrgApp />} /> <Route path='/org/*' element={<OrgApp />} />
<Route path='/config/*' element={<ConfigApp />} /> <Route path='/config/*' element={<ConfigApp />} />
<Route path='/app/*' element={<UserAppApp />} /> <Route path='/app/*' element={<UserAppApp />} />

View File

@ -1,6 +1,7 @@
import { QueryClient } from '@kevisual/query'; import { QueryClient } from '@kevisual/query';
import { QueryLoginBrowser } from '@kevisual/query-login'; import { QueryLoginBrowser } from '@kevisual/query-login';
import { toastLogin } from '@kevisual/resources/pages/message/ToastLogin.tsx'; import { toastLogin } from '@kevisual/resources/pages/message/ToastLogin.tsx';
import { toast } from 'react-toastify';
export const query = new QueryClient({ export const query = new QueryClient({
io: true, io: true,
}); });
@ -25,6 +26,15 @@ query.afterResponse = async (res, ctx) => {
afterAlso401: () => { afterAlso401: () => {
toastLogin(); toastLogin();
}, },
afterCheck: (res) => {
if (res.code === 200) {
// console.log('afterCheck');
toast.success('刷新登陆信息');
setTimeout(() => {
window.location.reload();
}, 1000);
}
},
}); });
return newRes as any; return newRes as any;

@ -1 +1 @@
Subproject commit 05f037383436cd0e30a6a12e7a4e08bf5b884ea8 Subproject commit 98c8a2ad86331566058ca7cc12df5ee2b406fa2f

@ -1 +1 @@
Subproject commit 39f0d7c566584811b73c027fb3eb43075d74ee1f Subproject commit 757be9fc2fbfa2ac0af92a3002cccc9edba995ba

@ -0,0 +1 @@
Subproject commit a7faaca4188cae055deb1af1d1b758b6d5439f8d

@ -1 +1 @@
Subproject commit 415f008209c9579cccc5692bdc8a5f9f97b78d8c Subproject commit 69784e8ed4f4546c91b2653adc75034e5946123c

View File

@ -7,8 +7,8 @@ const isDev = process.env.NODE_ENV === 'development';
const plugins: any[] = [basicSsl()]; const plugins: any[] = [basicSsl()];
plugins.push(tailwindcss()); plugins.push(tailwindcss());
const devBackend = 'https://kevisual.silkyai.cn'; const devBackend = 'https://kevisual.silkyai.cn';
// const meBackend = 'https://kevisual.xiongxiao.me'; const meBackend = 'https://kevisual.xiongxiao.me';
const meBackend = 'https://kevisual.cn'; // const meBackend = 'https://kevisual.cn';
// const backend = isDev ? devBackend : meBackend; // const backend = isDev ? devBackend : meBackend;
const backendWss = devBackend.replace(/^https:/, 'wss:'); const backendWss = devBackend.replace(/^https:/, 'wss:');
const backend = meBackend; const backend = meBackend;