diff --git a/packages/components/src/theme/index.tsx b/packages/components/src/theme/index.tsx
index 810d48a..b4c6dda 100644
--- a/packages/components/src/theme/index.tsx
+++ b/packages/components/src/theme/index.tsx
@@ -1,7 +1,42 @@
-import { createTheme, ThemeOptions } from '@mui/material/styles';
+import { createTheme, Shadows, ThemeOptions } from '@mui/material/styles';
import { useTheme as useMuiTheme, Theme } from '@mui/material/styles';
import { amber } from '@mui/material/colors';
+const generateShadows = (color: string): Shadows => {
+ return [
+ 'none',
+ `0px 2px 1px -1px ${color}`,
+ `0px 1px 1px 0px ${color}`,
+ `0px 1px 3px 0px ${color}`,
+ `0px 2px 4px -1px ${color}`,
+
+ `0px 3px 5px -1px ${color}`,
+ `0px 3px 5px -1px ${color}`,
+ `0px 4px 5px -2px ${color}`,
+ `0px 5px 5px -3px ${color}`,
+ `0px 5px 6px -3px ${color}`,
+
+ `0px 6px 6px -3px ${color}`,
+ `0px 6px 7px -4px ${color}`,
+ `0px 7px 8px -4px ${color}`,
+ `0px 7px 8px -4px ${color}`,
+ `0px 8px 9px -5px ${color}`,
+
+ `0px 8px 9px -5px ${color}`,
+ `0px 9px 10px -5px ${color}`,
+ `0px 9px 11px -6px ${color}`,
+ `0px 10px 12px -6px ${color}`,
+ `0px 10px 13px -6px ${color}`,
+
+ `0px 11px 13px -7px ${color}`,
+ `0px 11px 14px -7px ${color}`,
+ `0px 12px 15px -7px ${color}`,
+ `0px 12px 16px -8px ${color}`,
+ `0px 13px 17px -8px ${color}`,
+ ];
+};
export const themeOptions: ThemeOptions = {
+ // @ts-ignore
+ // cssVariables: true,
palette: {
primary: {
main: '#ffc107', // amber[300]
@@ -22,6 +57,7 @@ export const themeOptions: ThemeOptions = {
// paper: '#f5f5f5', // 设置纸张背景颜色
},
},
+ shadows: generateShadows('rgba(255, 193, 7, 0.2)'),
typography: {
// fontFamily: 'Roboto, sans-serif',
},
@@ -30,6 +66,22 @@ export const themeOptions: ThemeOptions = {
defaultProps: {
disableRipple: true,
},
+ styleOverrides: {
+ root: {
+ '&:hover': {
+ backgroundColor: amber[100],
+ },
+ },
+ },
+ },
+ MuiButtonGroup: {
+ styleOverrides: {
+ root: {
+ '& .MuiButton-root': {
+ borderColor: amber[600],
+ },
+ },
+ },
},
MuiTextField: {
styleOverrides: {
@@ -51,6 +103,13 @@ export const themeOptions: ThemeOptions = {
},
},
},
+ MuiCard: {
+ styleOverrides: {
+ root: {
+ // border: `1px solid ${amber[300]}`,
+ },
+ },
+ },
},
};
diff --git a/packages/resources/src/assets/index.css b/packages/resources/src/assets/index.css
index 237fe8c..f7676de 100644
--- a/packages/resources/src/assets/index.css
+++ b/packages/resources/src/assets/index.css
@@ -5,6 +5,9 @@
@apply w-20 h-20 bg-gray-300 rounded-full animate-spin;
}
}
+:root {
+ --scrollbar-color: #ffbf00;
+}
#root {
width: 100%;
height: 100%;
@@ -18,3 +21,8 @@
z-index: 9999;
pointer-events: none;
}
+
+.scrollbar {
+ scrollbar-width: thin;
+ scrollbar-color: var(--scrollbar-color) #fff;
+}
diff --git a/packages/resources/src/pages/file/draw/FileDrawer.tsx b/packages/resources/src/pages/file/draw/FileDrawer.tsx
new file mode 100644
index 0000000..53365f1
--- /dev/null
+++ b/packages/resources/src/pages/file/draw/FileDrawer.tsx
@@ -0,0 +1,16 @@
+import { useResourceStore } from '@/pages/store/resource';
+import { useResourceFileStore } from '@/pages/store/resource-file';
+import { Drawer } from '@mui/material';
+
+export const FileDrawer = () => {
+ const { prefix } = useResourceStore();
+ const { resource, openDrawer, setOpenDrawer } = useResourceFileStore();
+ return (
+ setOpenDrawer(false)} anchor='right' {...(!openDrawer && { inert: true })}>
+
+
{resource?.name ? resource.name.replace(prefix, '') : resource?.prefix?.replace(prefix, '')}
+
{JSON.stringify(resource, null, 2)}
+
+
+ );
+};
diff --git a/packages/resources/src/pages/file/index.tsx b/packages/resources/src/pages/file/index.tsx
index 10a324e..efbf0a1 100644
--- a/packages/resources/src/pages/file/index.tsx
+++ b/packages/resources/src/pages/file/index.tsx
@@ -1,50 +1,93 @@
-import { useEffect } from 'react';
+import { useEffect, useMemo } from 'react';
import { useResourceStore } from '../store/resource';
-import { useSettingsStore } from '../store/settings';
-import { Box, Button, Card, CardContent, Typography, ButtonGroup, useTheme } from '@mui/material';
-import { FileText, Image, File, Table, Grid } from 'lucide-react';
-import { getIcon } from './FileIcon';
+import { Box, Button, Typography, ButtonGroup } from '@mui/material';
+import { FileText, Table, Grid } from 'lucide-react';
import { FileTable } from './list/FileTable';
import { FileCard } from './list/FileCard';
+import { PrefixRedirect } from './modules/PrefixRedirect';
+import { UploadButton } from '../upload';
+import { FileDrawer } from './draw/FileDrawer';
+import { useResourceFileStore } from '../store/resource-file';
export const FileApp = () => {
- const { list, getList, prefix, setListType, listType } = useResourceStore();
- const { settings } = useSettingsStore();
-
+ const { getList, prefix, setListType, listType } = useResourceStore();
+ const { getStatFile, prefix: statPrefix, openDrawer } = useResourceFileStore();
useEffect(() => {
getList();
}, []);
- const theme = useTheme();
+ const directory = useMemo(() => {
+ const _prefix = prefix.split('/');
+ let dir = _prefix.slice(2).join('/');
+ if (dir.endsWith('/')) {
+ dir = dir.slice(0, -1);
+ }
+ return dir;
+ }, [prefix]);
+ useEffect(() => {
+ if (statPrefix && openDrawer) {
+ getStatFile();
+ }
+ }, [statPrefix, openDrawer]);
+ const handleUpload = (res: any) => {
+ getList();
+ };
return (
-
-
-
-
+
+
+
+
+ Resources
+
+
+
+
+
+
+
+
+
+
+
- Resources
-
-
-
-
-
- Prefix:
-
- {prefix}
-
-
-
-
-
+ {listType === 'card' ? : }
+
-
{listType === 'card' ? : }
-
+
+
);
};
diff --git a/packages/resources/src/pages/file/list/FileCard.tsx b/packages/resources/src/pages/file/list/FileCard.tsx
index 2491a1e..e2029f5 100644
--- a/packages/resources/src/pages/file/list/FileCard.tsx
+++ b/packages/resources/src/pages/file/list/FileCard.tsx
@@ -1,21 +1,40 @@
import { useResourceStore } from '@/pages/store/resource';
import { Card, CardContent, Typography } from '@mui/material';
import { getIcon } from '../FileIcon';
-
+import { amber } from '@mui/material/colors';
+import clsx from 'clsx';
+import { useResourceFileStore } from '@/pages/store/resource-file';
export const FileCard = () => {
- const { list, prefix } = useResourceStore();
+ const { list, prefix, onOpenPrefix } = useResourceStore();
+ const { setPrefix, setOpenDrawer } = useResourceFileStore();
return (
<>
{list.map((resource) => (
-
+
- {getIcon(resource.name)}
- {resource.name ? resource.name.replace(prefix, '') : resource.prefix?.replace(prefix, '')}
+ className={clsx('flex items-center gap-2', {
+ 'cursor-pointer': true,
+ })}
+ onClick={(e) => {
+ if (!resource.name) {
+ onOpenPrefix(resource.prefix || '');
+ } else {
+ setPrefix(resource.name || '');
+ setOpenDrawer(true);
+ }
+ e.stopPropagation();
+ }}>
+ {getIcon(resource.name)}
+ {resource.name ? resource.name.replace(prefix, '') : resource.prefix?.replace(prefix, '')}
{resource.lastModified && Last Modified: {resource.lastModified}}
{resource.size > 0 && Size: {resource.size} bytes}
diff --git a/packages/resources/src/pages/file/list/FileTable.tsx b/packages/resources/src/pages/file/list/FileTable.tsx
index 84acbbd..e3808dc 100644
--- a/packages/resources/src/pages/file/list/FileTable.tsx
+++ b/packages/resources/src/pages/file/list/FileTable.tsx
@@ -3,48 +3,87 @@ import { Button, Paper, Table, TableBody, TableCell, TableContainer, TableHead,
import prettyBytes from 'pretty-bytes';
import dayjs from 'dayjs';
import { getIcon } from '../FileIcon';
+import { Download, Trash } from 'lucide-react';
+import clsx from 'clsx';
+import { useResourceFileStore } from '@/pages/store/resource-file';
export const FileTable = () => {
- const { list, prefix, download } = useResourceStore();
+ const { list, prefix, download, onOpenPrefix, deleteFile } = useResourceStore();
+ const { setOpenDrawer, setPrefix } = useResourceFileStore();
return (
Name
- Size
- Last Modified
- Actions
+ Size
+ Last Modified
+ Actions
- {list.map((row) => (
-
-
-
- {getIcon(row.name)}
- {row.name ? row.name.replace(prefix, '') : row.prefix?.replace?.(prefix, '')}
-
-
- {row.size ? prettyBytes(row.size) : ''}
- {row.lastModified ? dayjs(row.lastModified).format('YYYY-MM-DD HH:mm:ss') : ''}
-
- {!row.prefix ? (
-
diff --git a/packages/resources/src/pages/file/modules/PrefixRedirect.tsx b/packages/resources/src/pages/file/modules/PrefixRedirect.tsx
new file mode 100644
index 0000000..11b4517
--- /dev/null
+++ b/packages/resources/src/pages/file/modules/PrefixRedirect.tsx
@@ -0,0 +1,35 @@
+import { useResourceStore } from '@/pages/store/resource';
+import { Breadcrumbs, Typography } from '@mui/material';
+import clsx from 'clsx';
+import { useMemo } from 'react';
+
+export const PrefixRedirect = () => {
+ const { prefix, onOpenPrefix } = useResourceStore();
+ const prefixCom = useMemo(() => {
+ const _prefix = prefix.split('/').filter(Boolean);
+ return _prefix.map((item, index) => {
+ const path = _prefix.slice(0, index + 1).join('/');
+ const onClick = () => {
+ console.log('path', path);
+ const openPath = path + '/';
+ if (openPath !== prefix) {
+ onOpenPrefix(openPath);
+ }
+ };
+ return {
+ name: item,
+ path,
+ onClick: index > 0 ? onClick : undefined,
+ };
+ });
+ }, [prefix]);
+ return (
+
+ {prefixCom.map((item) => (
+
+ {item.name}
+
+ ))}
+
+ );
+};
diff --git a/packages/resources/src/pages/layout/Left.tsx b/packages/resources/src/pages/layout/Left.tsx
index ae4a9e5..31a419f 100644
--- a/packages/resources/src/pages/layout/Left.tsx
+++ b/packages/resources/src/pages/layout/Left.tsx
@@ -106,7 +106,9 @@ export const Left = ({ children }: LeftProps) => {
- {children}
+
+ {children}
+
);
};
diff --git a/packages/resources/src/pages/main/index.tsx b/packages/resources/src/pages/main/index.tsx
index 2ae81e6..fc53afc 100644
--- a/packages/resources/src/pages/main/index.tsx
+++ b/packages/resources/src/pages/main/index.tsx
@@ -18,5 +18,5 @@ export const Main = () => {
if (activeMenu === ActiveMenu.Statistic) {
return ;
}
- return {activeMenu}
;
+ return {activeMenu}
;
};
diff --git a/packages/resources/src/pages/settings/index.tsx b/packages/resources/src/pages/settings/index.tsx
index 0256b6d..2f0c8ed 100644
--- a/packages/resources/src/pages/settings/index.tsx
+++ b/packages/resources/src/pages/settings/index.tsx
@@ -80,11 +80,11 @@ export const Settings = () => {
-
+
-
+ {/*
-
+ */}
diff --git a/packages/resources/src/pages/store/resource-file.ts b/packages/resources/src/pages/store/resource-file.ts
new file mode 100644
index 0000000..057d977
--- /dev/null
+++ b/packages/resources/src/pages/store/resource-file.ts
@@ -0,0 +1,35 @@
+import { create } from 'zustand';
+import { Resource } from './resource';
+import { query } from '@/modules/query';
+
+interface ResourceFileStore {
+ resource: Resource | null;
+ setResource: (resource: Resource) => void;
+ openDrawer: boolean;
+ setOpenDrawer: (openDrawer: boolean) => void;
+ prefix: string;
+ setPrefix: (prefix: string, replace?: string) => void;
+ getStatFile: () => Promise;
+}
+
+export const useResourceFileStore = create((set, get) => ({
+ resource: null,
+ setResource: (resource) => set({ resource }),
+ openDrawer: false,
+ setOpenDrawer: (openDrawer) => set({ openDrawer }),
+ prefix: '',
+ setPrefix: (prefix, replace) => set({ prefix: replace ? prefix.replace(replace, '') : prefix }),
+ getStatFile: async () => {
+ const { prefix } = get();
+ const res = await query.post({
+ path: 'file',
+ key: 'stat',
+ data: {
+ prefix,
+ },
+ });
+ if (res.code === 200) {
+ set({ resource: { ...res.data, name: prefix } });
+ }
+ },
+}));
diff --git a/packages/resources/src/pages/store/resource.ts b/packages/resources/src/pages/store/resource.ts
index d524e9c..d3c5887 100644
--- a/packages/resources/src/pages/store/resource.ts
+++ b/packages/resources/src/pages/store/resource.ts
@@ -19,11 +19,17 @@ interface ResourceStore {
setList: (list: Resource[]) => void;
prefix: string;
setPrefix: (prefix: string) => void;
- getList: () => Promise;
download: (resource: Resource) => void;
listType: 'table' | 'card';
setListType: (listType: 'table' | 'card') => void;
init: () => void;
+ /**
+ * 打开前缀
+ * @param prefix 前缀
+ */
+ onOpenPrefix: (prefix: string) => void;
+ getList: () => Promise;
+ deleteFile: (resource: Resource) => Promise;
}
export const useResourceStore = create((set, get) => ({
@@ -76,4 +82,29 @@ export const useResourceStore = create((set, get) => ({
set({ listType: listType as 'table' | 'card' });
}
},
+ onOpenPrefix: (prefix: string) => {
+ set({ prefix });
+ get().getList();
+ },
+ deleteFile: async (resource: Resource) => {
+ console.log('deleteFile', resource);
+ const name = resource.name;
+ if (!name) {
+ toast.error('Resource is not a file');
+ return;
+ }
+ const res = await query.post({
+ path: 'file',
+ key: 'delete',
+ data: {
+ prefix: name,
+ },
+ });
+ if (res.code === 200) {
+ get().getList();
+ toast.success('Delete file success');
+ } else {
+ toast.error(res.message || 'Request failed');
+ }
+ },
}));
diff --git a/packages/resources/src/pages/upload/index.tsx b/packages/resources/src/pages/upload/index.tsx
index 7ebcafb..8f5b8d4 100644
--- a/packages/resources/src/pages/upload/index.tsx
+++ b/packages/resources/src/pages/upload/index.tsx
@@ -1,14 +1,39 @@
-import { Box, useTheme, Container, Typography } from '@mui/material';
+import { Box, useTheme, Container, Typography, Button } from '@mui/material';
import { useDropzone } from 'react-dropzone';
import { uploadFiles } from './utils/upload';
import { FileText, CloudUpload as UploadIcon } from 'lucide-react';
import { uploadFileChunked } from './utils/upload-chunk';
-
+export const UploadButton = (props: { prefix?: string; onUpload?: (res: any) => void }) => {
+ const onDrop = async (acceptedFiles) => {
+ console.log(acceptedFiles);
+ if (acceptedFiles.length > 1) {
+ const res = await uploadFiles(acceptedFiles, { directory: props.prefix });
+ console.log('uploadFiles res', res);
+ props.onUpload?.(res);
+ } else if (acceptedFiles.length === 1) {
+ const res = await uploadFileChunked(acceptedFiles[0], { directory: props.prefix });
+ console.log('uploadFiles res', res);
+ props.onUpload?.(res);
+ }
+ };
+ const { getRootProps, getInputProps } = useDropzone({ onDrop });
+ return (
+
+
+
+
+
+
+ );
+};
export const Upload = () => {
const onDrop = async (acceptedFiles) => {
console.log(acceptedFiles);
- // Handle the files here
- // const res = await uploadFiles(acceptedFiles, {});
if (acceptedFiles.length > 1) {
const res = await uploadFiles(acceptedFiles, {});
console.log('uploadFiles res', res);
diff --git a/packages/resources/src/pages/upload/utils/upload-chunk.ts b/packages/resources/src/pages/upload/utils/upload-chunk.ts
index 2ea182e..78da689 100644
--- a/packages/resources/src/pages/upload/utils/upload-chunk.ts
+++ b/packages/resources/src/pages/upload/utils/upload-chunk.ts
@@ -8,9 +8,11 @@ type ConvertOpts = {
appKey?: string;
version?: string;
username?: string;
+ directory?: string;
};
export const uploadFileChunked = async (file: File, opts: ConvertOpts) => {
+ const { directory } = opts;
return new Promise(async (resolve, reject) => {
const token = localStorage.getItem('token');
if (!token) {
@@ -22,7 +24,8 @@ export const uploadFileChunked = async (file: File, opts: ConvertOpts) => {
const filename = file.name;
const load = toast.loading(`${filename} 上传中...`);
NProgress.start();
- const eventSource = new EventSource('http://49.232.155.236:11015/api/s1/events?taskId=' + taskId);
+ // const eventSource = new EventSource('http://49.232.155.236:11015/api/s1/events?taskId=' + taskId);
+ const eventSource = new EventSource('/api/s1/events?taskId=' + taskId);
// 监听服务器推送的进度更新
eventSource.onmessage = function (event) {
console.log('Progress update:', event.data);
@@ -60,7 +63,9 @@ export const uploadFileChunked = async (file: File, opts: ConvertOpts) => {
formData.append('file', chunk, file.name);
formData.append('chunkIndex', currentChunk.toString());
formData.append('totalChunks', totalChunks.toString());
-
+ if (directory) {
+ formData.append('directory', directory);
+ }
try {
const res = await fetch('/api/s1/resources/upload/chunk?taskId=' + taskId, {
method: 'POST',
@@ -70,19 +75,17 @@ export const uploadFileChunked = async (file: File, opts: ConvertOpts) => {
Authorization: `Bearer ${token}`,
},
}).then((response) => response.json());
-
- console.log(`Chunk ${currentChunk + 1}/${totalChunks} uploaded`, res);
+ fetch('/api/s1/events/close?taskId=' + taskId);
+ eventSource.close();
+ NProgress.done();
+ toast.dismiss(load);
+ resolve(res);
+ // console.log(`Chunk ${currentChunk + 1}/${totalChunks} uploaded`, res);
} catch (error) {
console.log('Error uploading chunk', error);
reject(error);
return;
}
}
-
- fetch('/api/s1/events/close?taskId=' + taskId);
- eventSource.close();
- NProgress.done();
- toast.dismiss(load);
- resolve({ message: 'All chunks uploaded successfully' });
});
};
diff --git a/packages/resources/src/pages/upload/utils/upload.ts b/packages/resources/src/pages/upload/utils/upload.ts
index 89595ae..595d7fb 100644
--- a/packages/resources/src/pages/upload/utils/upload.ts
+++ b/packages/resources/src/pages/upload/utils/upload.ts
@@ -8,13 +8,18 @@ type ConvertOpts = {
appKey?: string;
version?: string;
username?: string;
+ directory?: string;
};
export const uploadFiles = async (files: File[], opts: ConvertOpts) => {
+ const { directory } = opts;
return new Promise((resolve, reject) => {
const formData = new FormData();
for (let i = 0; i < files.length; i++) {
formData.append('file', files[i], files[i].name);
}
+ if (directory) {
+ formData.append('directory', directory);
+ }
const token = localStorage.getItem('token');
if (!token) {
toastLogin();
@@ -23,8 +28,8 @@ export const uploadFiles = async (files: File[], opts: ConvertOpts) => {
const taskId = nanoid();
// 49.232.155.236:11015
// const eventSource = new EventSource('https://kevisual.silkyai.cn/api/s1/events?taskId=' + taskId);
- // const eventSource = new EventSource('/api/s1/events?taskId=' + taskId);
- const eventSource = new EventSource('http://49.232.155.236:11015/api/s1/events?taskId=' + taskId);
+ const eventSource = new EventSource('/api/s1/events?taskId=' + taskId);
+ // const eventSource = new EventSource('http://49.232.155.236:11015/api/s1/events?taskId=' + taskId);
const load = toast.loading('上传中...');
NProgress.start();
eventSource.onopen = async function (event) {
@@ -63,7 +68,6 @@ export const uploadFiles = async (files: File[], opts: ConvertOpts) => {
if (progress) {
NProgress.set(progress);
}
-
};
eventSource.onerror = function (event) {
console.log('eventSource.onerror', event);