diff --git a/frontend/src/component/bucket/list_bucket.tsx b/frontend/src/component/bucket/list_bucket.tsx index d2616a6..e3e56cd 100644 --- a/frontend/src/component/bucket/list_bucket.tsx +++ b/frontend/src/component/bucket/list_bucket.tsx @@ -4,7 +4,7 @@ import {VirtualizerScrollView} from "@fluentui/react-components/unstable"; import React from "react"; import {useStoreBucket} from "../../store/bucket"; import {Bucket} from "../../interfaces/connection"; -import {useStoreFile} from "../../store/file"; +import {useStoreFile, useStoreFileFilter} from "../../store/file"; import {useStoreConnection} from "../../store/connection"; const useStyles = makeStyles({ @@ -35,13 +35,13 @@ const useStyles = makeStyles({ export function ListBucketComponent() { const styles = useStyles(); - const {conn_active} = useStoreConnection() const {bucket_set, bucket_list} = useStoreBucket() - const {files_get} = useStoreFile() + const {filter_set, prefix_set} = useStoreFileFilter() async function handleClick(item: Bucket) { - bucket_set(item) - files_get(conn_active!, item, "") + await bucket_set(item) + await filter_set('') + await prefix_set('') } function handleRightClick(e: React.MouseEvent, item: Bucket) { @@ -50,7 +50,7 @@ export function ListBucketComponent() { return diff --git a/frontend/src/component/connection/list.tsx b/frontend/src/component/connection/list.tsx index b7b6d76..ae1ac82 100644 --- a/frontend/src/component/connection/list.tsx +++ b/frontend/src/component/connection/list.tsx @@ -90,7 +90,7 @@ const useStyles = makeStyles({ export function ConnectionList() { const styles = useStyles() const {dispatchMessage} = useToast(); - const {conn_list, conn_update} = useStoreConnection(); + const {conn_get, conn_list, conn_set} = useStoreConnection(); const [conn_filter, set_conn_filter] = useState(''); const {bucket_get, bucket_set} = useStoreBucket() const [ctx_menu, set_ctx_menu] = useState<{ @@ -104,6 +104,9 @@ export function ConnectionList() { document.addEventListener("click", (e) => { set_ctx_menu({x: 0, y: 0, display: 'none'}); }) + setTimeout(() => { + conn_get().then() + }, 1000) return () => { document.removeEventListener("click", (e) => { }) @@ -113,7 +116,7 @@ export function ConnectionList() { async function handleSelect(item: Connection) { conn_list.map((one: Connection) => { if (item.id === one.id && one.active) { - conn_update(one) + conn_set(one) bucket_get(one, false) bucket_set(null) } @@ -128,9 +131,9 @@ export function ConnectionList() { return } - conn_update({...item, active: true}) + await conn_set({...item, active: true}) bucket_get(item, true) - bucket_set(null) + await bucket_set(null) } async function handleDisconnect(item: Connection | null) { @@ -140,7 +143,7 @@ export function ConnectionList() { dispatchMessage(res.msg, "error") return } - conn_update({...item, active: false}) + await conn_set({...item, active: false}) } async function handleRightClick(e: React.MouseEvent, item: Connection) { @@ -148,8 +151,8 @@ export function ConnectionList() { set_menu_conn(item) const ele = document.querySelector('#list-connection-container') - const eleX = ele ? ele.clientWidth : 0 - const eleY = ele ? ele.clientHeight : 0 + const eleX = ele ? ele.clientWidth : 0; + const eleY = ele ? ele.clientHeight : 0; const positionX = (e.pageX + eleX > window.innerWidth) ? e.pageX - eleX : e.pageX const positionY = (e.pageY + eleY > window.innerHeight) ? e.pageY - eleY : e.pageY diff --git a/frontend/src/component/connection/new.tsx b/frontend/src/component/connection/new.tsx index 5e2e65f..2bc540b 100644 --- a/frontend/src/component/connection/new.tsx +++ b/frontend/src/component/connection/new.tsx @@ -69,7 +69,7 @@ export function ConnectionCreate(props: ConnectionCreateProps) { dispatchMessage(res.msg, res.status === 200 ? "success" : "error"); if (res.status === 200) { dispatchMessage("新建连接成功", "success"); - conn_get() + await conn_get() props.openFn(false) } } diff --git a/frontend/src/component/file/content.tsx b/frontend/src/component/file/content.tsx index d4982df..b30058c 100644 --- a/frontend/src/component/file/content.tsx +++ b/frontend/src/component/file/content.tsx @@ -3,6 +3,7 @@ import {ListBucketComponent} from "../bucket/list_bucket"; import {makeStyles} from "@fluentui/react-components"; import {useStoreBucket} from "../../store/bucket"; import {ListFileComponent} from "./list_file"; +import {useState} from "react"; const useStyles = makeStyles({ content: { @@ -18,8 +19,9 @@ export function Content() { const styles = useStyles() const {bucket_active } = useStoreBucket() + return
- + { bucket_active ? : diff --git a/frontend/src/component/file/list_file.tsx b/frontend/src/component/file/list_file.tsx index 92cd964..5dce090 100644 --- a/frontend/src/component/file/list_file.tsx +++ b/frontend/src/component/file/list_file.tsx @@ -1,4 +1,4 @@ -import {makeStyles, MenuItem, MenuList, Text, tokens} from "@fluentui/react-components"; +import {makeStyles, MenuItem, MenuList, Spinner, Text, tokens} from "@fluentui/react-components"; import { ArrowDownloadFilled, DeleteRegular, @@ -18,18 +18,44 @@ import {VirtualizerScrollView} from "@fluentui/react-components/unstable"; import React, {useEffect, useState} from "react"; import {useStoreBucket} from "../../store/bucket"; import {S3File} from "../../interfaces/connection"; -import {useStoreFile} from "../../store/file"; +import {useStoreFile, useStoreFileFilter} from "../../store/file"; import {useStoreConnection} from "../../store/connection"; import {TrimSuffix} from "../../hook/strings"; import {Dial} from "../../api"; import {useToast} from "../../message"; +import {CanPreview} from "../../hook/preview"; const useStyles = makeStyles({ container: { marginTop: '0.5rem', - maxWidth: 'calc(100vw - 25rem - 1px)', - width: 'calc(100vw - 25rem - 1px)', - height: 'calc(100vh - 9rem)', + maxWidth: 'calc(100vw - 25.2rem)', + width: 'calc(100vw - 25.2rem)', + maxHeight: 'calc(100vh - 10rem)', + height: 'calc(100vh - 10rem)', + }, + loading: { + flex: "1", + width: '100%', + height: '100%', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + flexDirection: 'column', + }, + no_data: { + flex: "1", + height: '100%', + width: '100%', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + fontSize: '8rem', + flexDirection: 'column', + }, + list: { + flex: "1", + height: '100%', + width: '100%', }, row: { height: '32px', @@ -49,16 +75,6 @@ const useStyles = makeStyles({ width: 'calc(100vw - 32rem)', display: "block", }, - no_data: { - flex: "1", - height: '100%', - width: '100%', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - fontSize: '8rem', - flexDirection: 'column', - }, ctx_menu: { position: "absolute", zIndex: "1000", @@ -70,6 +86,7 @@ const useStyles = makeStyles({ }, }) + export function ListFileComponent() { const styles = useStyles(); @@ -77,11 +94,13 @@ export function ListFileComponent() { const {conn_active} = useStoreConnection(); const {bucket_active} = useStoreBucket() const {file_active, files_get, file_set, files_list} = useStoreFile() + const {prefix, filter, prefix_set} = useStoreFileFilter() const [ctx_menu, set_ctx_menu] = useState<{ x: number, y: number, display: 'none' | 'block' }>({x: 0, y: 0, display: 'none'}); + const [loading, set_loading] = useState(true) useEffect(() => { document.addEventListener("click", (e) => { @@ -93,6 +112,13 @@ export function ListFileComponent() { } }, []) + useEffect(() => { + set_loading(true) + files_get(conn_active!, bucket_active!, prefix, filter).then(() => { + // set_loading(false) + }) + }, [conn_active, bucket_active, prefix, filter]); + const filename = (key: string) => { let strs = TrimSuffix(key, "/").split("/") return strs[strs.length - 1] @@ -100,7 +126,7 @@ export function ListFileComponent() { async function handleClick(item: S3File) { if (item.type === 1) { - files_get(conn_active!, bucket_active!, item.key) + await prefix_set(item.key) return } } @@ -109,8 +135,8 @@ export function ListFileComponent() { e.preventDefault() await file_set(item.key) const ele = document.querySelector('#list-file-container') - const eleX = ele ? ele.clientWidth : 0 - const eleY = ele ? ele.clientHeight : 0 + let eleX = ele ? ele.clientWidth : 0 + let eleY = ele ? ele.clientHeight : 0 const positionX = (e.pageX + eleX > window.innerWidth) ? e.pageX - eleX : e.pageX const positionY = (e.pageY + eleY > window.innerHeight) ? e.pageY - eleY : e.pageY set_ctx_menu({ @@ -118,12 +144,11 @@ export function ListFileComponent() { y: positionY, display: 'block', }) - // const res = await Dial('/api/file/info', {conn_id: conn_active?.id, bucket: bucket_active?.name, key: item.key}) } async function handleDownload(file: string | null) { if (!file) return - const res1 = await Dial<{result:string}>("/runtime/dialog/save", { + const res1 = await Dial<{ result: string }>("/runtime/dialog/save", { default_filename: file, }) if (res1.status !== 200) { @@ -141,7 +166,11 @@ export function ListFileComponent() { } } - return <> + async function handlePreview() { + dispatchMessage('todo', 'warning') + } + + return
}>下载 { - // await handleDisconnect(menu_conn) + await handlePreview() }} icon={}>预览 }>删除
- - {files_list.length ? - - {(idx) => { - return
{ - await handleClick(files_list[idx]) - }} - onContextMenu={async (e) => { - await handleRightClick(e, files_list[idx]) - }}> - : - }> - - {filename(files_list[idx].key)} - - -
- }} -
:
-
- -
- - 没有文件 - -
- } +
+ +
+ { + // (!loading) && (files_list.length === 0) ? + //
+ //
+ // + //
+ // + // 没有文件 + // + //
+ // : <> + } + { + // (!loading && files_list) ? + // + // {(idx) => { + // return
{ + // await handleClick(files_list[idx]) + // }} + // onContextMenu={async (e) => { + // await handleRightClick(e, files_list[idx]) + // }}> + // : + // }> + // + // {filename(files_list[idx].key)} + // + // + //
+ // }} + //
: <> + } + - +
} type FileIconProps = { diff --git a/frontend/src/component/file/path.tsx b/frontend/src/component/file/path.tsx index 6c93abb..80fb832 100644 --- a/frontend/src/component/file/path.tsx +++ b/frontend/src/component/file/path.tsx @@ -1,10 +1,12 @@ import {Button, Input, makeStyles, Text, tokens, Tooltip} from "@fluentui/react-components"; import {useStoreBucket} from "../../store/bucket"; import {ArchiveRegular, ArrowCurveUpLeftFilled} from "@fluentui/react-icons"; -import {useStoreFile} from "../../store/file"; -import React from "react"; +import {useStoreFile, useStoreFileFilter} from "../../store/file"; +import React, {useState} from "react"; import {debounce} from 'lodash' import {useStoreConnection} from "../../store/connection"; +import {ListFileComponent} from "./list_file"; +import {ListBucketComponent} from "../bucket/list_bucket"; const useStyles = makeStyles({ container: { @@ -51,17 +53,18 @@ const useStyles = makeStyles({ }, }) + export function Path() { const styles = useStyles() const {conn_active} = useStoreConnection() const {bucket_active, bucket_get, bucket_set} = useStoreBucket() - const {prefix, files_get} = useStoreFile() + const {prefix, filter, prefix_set, filter_set} = useStoreFileFilter() async function handleClickUp() { const dirs = prefix.split('/').filter((item => item)) if (dirs.length > 0) { dirs.pop() - files_get(conn_active!, bucket_active!, dirs.join("/")) + await prefix_set(dirs.join('/')) return } @@ -70,8 +73,8 @@ export function Path() { } - const handleFilterChange = debounce((e) => { - files_get(conn_active!, bucket_active!, prefix, e.target.value) + const handleFilterChange = debounce(async (e) => { + await filter_set(e.target.value) }, 500) return
diff --git a/frontend/src/component/home/body.tsx b/frontend/src/component/home/body.tsx index c253692..52736ac 100644 --- a/frontend/src/component/home/body.tsx +++ b/frontend/src/component/home/body.tsx @@ -1,11 +1,4 @@ -import {Button, Input, makeStyles, MenuItem, MenuList, mergeClasses, tokens, Tooltip} from "@fluentui/react-components"; -import {DismissRegular} from "@fluentui/react-icons"; -import {useEffect, useState} from "react"; -import {Connection} from "../../interfaces/connection"; -import {useStoreBucket} from "../../store/bucket"; -import {useStoreConnection} from "../../store/connection"; -import {Dial} from "../../api"; -import {useToast} from "../../message"; +import {makeStyles} from "@fluentui/react-components"; import {ConnectionList} from "../connection/list"; import {Content} from "../file/content"; @@ -20,15 +13,9 @@ const useStyles = makeStyles({ export function Body() { const styles = useStyles(); - const {conn_get} = useStoreConnection(); - - useEffect(() => { - conn_get() - }, []); - return
- +
} \ No newline at end of file diff --git a/frontend/src/component/home/header.tsx b/frontend/src/component/home/header.tsx index 91fcbdf..89a52c1 100644 --- a/frontend/src/component/home/header.tsx +++ b/frontend/src/component/home/header.tsx @@ -1,6 +1,6 @@ -import {Button, Dialog, DialogTrigger, makeStyles} from "@fluentui/react-components"; +import {Button, Dialog, DialogTrigger, makeStyles} from "@fluentui/react-components"; import {ConnectionCreate} from "../connection/new"; -import {AppsAddInRegular, DocumentArrowUpRegular, PlugConnectedAddRegular} from "@fluentui/react-icons"; +import {AppsAddInRegular, DocumentArrowUpRegular, PlugConnectedAddRegular} from "@fluentui/react-icons"; import {useState} from "react"; import {useStoreConnection} from "../../store/connection"; import {BucketCreate} from "../bucket/new"; @@ -10,6 +10,7 @@ import {UploadFiles} from "../file/upload_files"; const useStyles = makeStyles({ header: { height: "5rem", + minHeight: '5rem', width: "100%", display: 'flex', alignItems: "center", @@ -64,7 +65,7 @@ export function Header() { open={open_upload} onOpenChange={(event, data) => set_open_upload(data.open)}> - diff --git a/frontend/src/hook/preview.ts b/frontend/src/hook/preview.ts new file mode 100644 index 0000000..f04631f --- /dev/null +++ b/frontend/src/hook/preview.ts @@ -0,0 +1,13 @@ +export function CanPreview(filename: string) { + const fs = filename.split(".") + switch (fs[fs.length - 1]) { + case "jpg": + return "image/jpg" + case "jpeg": + return "image/jpg" + case "png": + return "image/png" + default: + return "" + } +} \ No newline at end of file diff --git a/frontend/src/store/connection.tsx b/frontend/src/store/connection.tsx index 3d5508f..1986e76 100644 --- a/frontend/src/store/connection.tsx +++ b/frontend/src/store/connection.tsx @@ -5,8 +5,8 @@ import {Dial} from "../api"; interface StoreConnection { conn_active: Connection | null; conn_list: Connection[]; - conn_get: () => void; - conn_update: (connection: Connection) => Promise; + conn_get: () => Promise; + conn_set: (connection: Connection) => Promise; } export const useStoreConnection = create()((set) => ({ @@ -21,7 +21,7 @@ export const useStoreConnection = create()((set) => ({ set({conn_list: res.data.list}) }, - conn_update: async (connection: Connection) => { + conn_set: async (connection: Connection) => { set((state) => { return { conn_active: connection.active? connection: null, diff --git a/frontend/src/store/file.tsx b/frontend/src/store/file.tsx index 89682b0..026eeae 100644 --- a/frontend/src/store/file.tsx +++ b/frontend/src/store/file.tsx @@ -5,10 +5,8 @@ import {Dial} from "../api"; interface StoreFile { file_active: string | null, file_set: (key: string) => Promise, - prefix: string; - filter: string; files_list: S3File[]; - files_get: (conn: Connection, bucket: Bucket, prefix?: string, filter?: string) => void; + files_get: (conn: Connection, bucket: Bucket, prefix?: string, filter?: string) => Promise; } export const useStoreFile = create()((set) => ({ @@ -18,8 +16,6 @@ export const useStoreFile = create()((set) => ({ return {file_active: key} }) }, - prefix: "", - filter: "", files_list: [], files_get: async (conn: Connection, bucket: Bucket, prefix = '', filter = '') => { const res = await Dial<{ list: S3File[] }>('/api/bucket/files', { @@ -33,7 +29,25 @@ export const useStoreFile = create()((set) => ({ } set((state) => { - return {files_list: res.data.list, prefix: prefix, filter: filter} + return {files_list: res.data.list} }) - } + }, })) + +interface StoreFileFilter { + prefix: string; + filter: string; + prefix_set: (prefix: string) => Promise; + filter_set: (filter: string) => Promise; +} + +export const useStoreFileFilter = create()((set) => ({ + prefix: '', + filter: '', + prefix_set: async (keyword: string) => { + set(state => {return {prefix: keyword}}) + }, + filter_set: async (keyword: string) => { + set(state => {return {filter: keyword}}) + }, +})) \ No newline at end of file diff --git a/internal/handler/connection.go b/internal/handler/connection.go index e829219..d85403a 100644 --- a/internal/handler/connection.go +++ b/internal/handler/connection.go @@ -8,7 +8,6 @@ import ( "github.com/loveuer/nf-disk/internal/s3" "github.com/loveuer/nf-disk/ndh" "github.com/samber/lo" - "time" ) func ConnectionTest(c *ndh.Ctx) error { @@ -210,11 +209,5 @@ func ConnectionBuckets(c *ndh.Ctx) error { return c.Send500(err.Error()) } - // todo: for frontend test - buckets = append(buckets, &s3.ListBucketRes{ - Name: "这是一个非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长的名字", - CreatedAt: time.Now().UnixMilli(), - }) - return c.Send200(map[string]any{"list": buckets}) }