Compare commits

...

2 Commits

Author SHA1 Message Date
loveuer
3ff83f12b4 wip: file preview 2024-10-14 22:31:59 +08:00
zhaoyupeng
6f15f82122 wip: file list 2024-10-14 18:08:49 +08:00
16 changed files with 237 additions and 135 deletions

View File

@ -4,7 +4,7 @@ import {VirtualizerScrollView} from "@fluentui/react-components/unstable";
import React from "react"; import React from "react";
import {useStoreBucket} from "../../store/bucket"; import {useStoreBucket} from "../../store/bucket";
import {Bucket} from "../../interfaces/connection"; import {Bucket} from "../../interfaces/connection";
import {useStoreFile} from "../../store/file"; import {useStoreFile, useStoreFileFilter} from "../../store/file";
import {useStoreConnection} from "../../store/connection"; import {useStoreConnection} from "../../store/connection";
const useStyles = makeStyles({ const useStyles = makeStyles({
@ -35,13 +35,13 @@ const useStyles = makeStyles({
export function ListBucketComponent() { export function ListBucketComponent() {
const styles = useStyles(); const styles = useStyles();
const {conn_active} = useStoreConnection()
const {bucket_set, bucket_list} = useStoreBucket() const {bucket_set, bucket_list} = useStoreBucket()
const {files_get} = useStoreFile() const {filter_set, prefix_set} = useStoreFileFilter()
async function handleClick(item: Bucket) { async function handleClick(item: Bucket) {
bucket_set(item) await bucket_set(item)
files_get(conn_active!, item, "") await filter_set('')
await prefix_set('')
} }
function handleRightClick(e: React.MouseEvent<HTMLDivElement>, item: Bucket) { function handleRightClick(e: React.MouseEvent<HTMLDivElement>, item: Bucket) {
@ -50,7 +50,7 @@ export function ListBucketComponent() {
return <MenuList className={styles.container}> return <MenuList className={styles.container}>
<VirtualizerScrollView <VirtualizerScrollView
numItems={bucket_list?bucket_list.length:0} numItems={bucket_list ? bucket_list.length : 0}
itemSize={32} itemSize={32}
container={{role: 'list', style: {maxHeight: 'calc(100vh - 9rem)'}}} container={{role: 'list', style: {maxHeight: 'calc(100vh - 9rem)'}}}
> >

View File

@ -90,7 +90,7 @@ const useStyles = makeStyles({
export function ConnectionList() { export function ConnectionList() {
const styles = useStyles() const styles = useStyles()
const {dispatchMessage} = useToast(); const {dispatchMessage} = useToast();
const {conn_list, conn_update} = useStoreConnection(); const {conn_get, conn_list, conn_set} = useStoreConnection();
const [conn_filter, set_conn_filter] = useState<string>(''); const [conn_filter, set_conn_filter] = useState<string>('');
const {bucket_get, bucket_set} = useStoreBucket() const {bucket_get, bucket_set} = useStoreBucket()
const [ctx_menu, set_ctx_menu] = useState<{ const [ctx_menu, set_ctx_menu] = useState<{
@ -104,6 +104,9 @@ export function ConnectionList() {
document.addEventListener("click", (e) => { document.addEventListener("click", (e) => {
set_ctx_menu({x: 0, y: 0, display: 'none'}); set_ctx_menu({x: 0, y: 0, display: 'none'});
}) })
setTimeout(() => {
conn_get().then()
}, 1000)
return () => { return () => {
document.removeEventListener("click", (e) => { document.removeEventListener("click", (e) => {
}) })
@ -113,7 +116,7 @@ export function ConnectionList() {
async function handleSelect(item: Connection) { async function handleSelect(item: Connection) {
conn_list.map((one: Connection) => { conn_list.map((one: Connection) => {
if (item.id === one.id && one.active) { if (item.id === one.id && one.active) {
conn_update(one) conn_set(one)
bucket_get(one, false) bucket_get(one, false)
bucket_set(null) bucket_set(null)
} }
@ -128,9 +131,9 @@ export function ConnectionList() {
return return
} }
conn_update({...item, active: true}) await conn_set({...item, active: true})
bucket_get(item, true) bucket_get(item, true)
bucket_set(null) await bucket_set(null)
} }
async function handleDisconnect(item: Connection | null) { async function handleDisconnect(item: Connection | null) {
@ -140,7 +143,7 @@ export function ConnectionList() {
dispatchMessage(res.msg, "error") dispatchMessage(res.msg, "error")
return return
} }
conn_update({...item, active: false}) await conn_set({...item, active: false})
} }
async function handleRightClick(e: React.MouseEvent<HTMLDivElement>, item: Connection) { async function handleRightClick(e: React.MouseEvent<HTMLDivElement>, item: Connection) {
@ -148,8 +151,8 @@ export function ConnectionList() {
set_menu_conn(item) set_menu_conn(item)
const ele = document.querySelector('#list-connection-container') const ele = document.querySelector('#list-connection-container')
const eleX = ele ? ele.clientWidth : 0 const eleX = ele ? ele.clientWidth : 0;
const eleY = ele ? ele.clientHeight : 0 const eleY = ele ? ele.clientHeight : 0;
const positionX = (e.pageX + eleX > window.innerWidth) ? e.pageX - eleX : e.pageX const positionX = (e.pageX + eleX > window.innerWidth) ? e.pageX - eleX : e.pageX
const positionY = (e.pageY + eleY > window.innerHeight) ? e.pageY - eleY : e.pageY const positionY = (e.pageY + eleY > window.innerHeight) ? e.pageY - eleY : e.pageY

View File

@ -69,7 +69,7 @@ export function ConnectionCreate(props: ConnectionCreateProps) {
dispatchMessage(res.msg, res.status === 200 ? "success" : "error"); dispatchMessage(res.msg, res.status === 200 ? "success" : "error");
if (res.status === 200) { if (res.status === 200) {
dispatchMessage("新建连接成功", "success"); dispatchMessage("新建连接成功", "success");
conn_get() await conn_get()
props.openFn(false) props.openFn(false)
} }
} }

View File

@ -3,6 +3,8 @@ import {ListBucketComponent} from "../bucket/list_bucket";
import {makeStyles} from "@fluentui/react-components"; import {makeStyles} from "@fluentui/react-components";
import {useStoreBucket} from "../../store/bucket"; import {useStoreBucket} from "../../store/bucket";
import {ListFileComponent} from "./list_file"; import {ListFileComponent} from "./list_file";
import {useState} from "react";
import {PreviewFile} from "../preview/preview";
const useStyles = makeStyles({ const useStyles = makeStyles({
content: { content: {
@ -17,13 +19,17 @@ const useStyles = makeStyles({
export function Content() { export function Content() {
const styles = useStyles() const styles = useStyles()
const {bucket_active } = useStoreBucket() const [preview, set_preview] = useState<{ url: string, content_type: string }>({url: '', content_type: ''})
const {bucket_active} = useStoreBucket()
return <div className={styles.content}> return <div className={styles.content}>
<Path/> <Path/>
{ {
preview.url ? <PreviewFile url={preview.url} content_type={preview.content_type}/> :
(
bucket_active ? bucket_active ?
<ListFileComponent/> : <ListFileComponent set_preview_fn={set_preview}/> : <ListBucketComponent/>
<ListBucketComponent/> )
} }
</div> </div>
} }

View File

@ -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 { import {
ArrowDownloadFilled, ArrowDownloadFilled,
DeleteRegular, DeleteRegular,
@ -18,18 +18,45 @@ import {VirtualizerScrollView} from "@fluentui/react-components/unstable";
import React, {useEffect, useState} from "react"; import React, {useEffect, useState} from "react";
import {useStoreBucket} from "../../store/bucket"; import {useStoreBucket} from "../../store/bucket";
import {S3File} from "../../interfaces/connection"; import {S3File} from "../../interfaces/connection";
import {useStoreFile} from "../../store/file"; import {useStoreFile, useStoreFileFilter} from "../../store/file";
import {useStoreConnection} from "../../store/connection"; import {useStoreConnection} from "../../store/connection";
import {TrimSuffix} from "../../hook/strings"; import {TrimSuffix} from "../../hook/strings";
import {Dial} from "../../api"; import {Dial} from "../../api";
import {useToast} from "../../message"; import {useToast} from "../../message";
import {CanPreview} from "../../hook/preview";
import {useStorePreview} from "../../store/preview";
const useStyles = makeStyles({ const useStyles = makeStyles({
container: { container: {
marginTop: '0.5rem', marginTop: '0.5rem',
maxWidth: 'calc(100vw - 25rem - 1px)', maxWidth: 'calc(100vw - 25.2rem)',
width: 'calc(100vw - 25rem - 1px)', width: 'calc(100vw - 25.2rem)',
height: 'calc(100vh - 9rem)', 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: { row: {
height: '32px', height: '32px',
@ -49,16 +76,6 @@ const useStyles = makeStyles({
width: 'calc(100vw - 32rem)', width: 'calc(100vw - 32rem)',
display: "block", display: "block",
}, },
no_data: {
flex: "1",
height: '100%',
width: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
fontSize: '8rem',
flexDirection: 'column',
},
ctx_menu: { ctx_menu: {
position: "absolute", position: "absolute",
zIndex: "1000", zIndex: "1000",
@ -70,18 +87,27 @@ const useStyles = makeStyles({
}, },
}) })
export function ListFileComponent() {
export interface ListFileComponentProps {
set_preview_fn: React.Dispatch<React.SetStateAction<{url:string, content_type: string}>>
}
export function ListFileComponent(props: ListFileComponentProps) {
const styles = useStyles(); const styles = useStyles();
const {dispatchMessage} = useToast(); const {dispatchMessage} = useToast();
const {conn_active} = useStoreConnection(); const {conn_active} = useStoreConnection();
const {bucket_active} = useStoreBucket() const {bucket_active} = useStoreBucket()
const {file_active, files_get, file_set, files_list} = useStoreFile() const {file_active, files_get, file_set, files_list} = useStoreFile()
const {prefix, filter, prefix_set} = useStoreFileFilter()
const [preview_content_type, set_preview_content_type] = useState('')
const {preview_get} = useStorePreview()
const [ctx_menu, set_ctx_menu] = useState<{ const [ctx_menu, set_ctx_menu] = useState<{
x: number, x: number,
y: number, y: number,
display: 'none' | 'block' display: 'none' | 'block'
}>({x: 0, y: 0, display: 'none'}); }>({x: 0, y: 0, display: 'none'});
const [loading, set_loading] = useState(true)
useEffect(() => { useEffect(() => {
document.addEventListener("click", (e) => { document.addEventListener("click", (e) => {
@ -93,6 +119,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) => { const filename = (key: string) => {
let strs = TrimSuffix(key, "/").split("/") let strs = TrimSuffix(key, "/").split("/")
return strs[strs.length - 1] return strs[strs.length - 1]
@ -100,7 +133,7 @@ export function ListFileComponent() {
async function handleClick(item: S3File) { async function handleClick(item: S3File) {
if (item.type === 1) { if (item.type === 1) {
files_get(conn_active!, bucket_active!, item.key) await prefix_set(item.key)
return return
} }
} }
@ -108,9 +141,10 @@ export function ListFileComponent() {
async function handleRightClick(e: React.MouseEvent<HTMLDivElement>, item: S3File) { async function handleRightClick(e: React.MouseEvent<HTMLDivElement>, item: S3File) {
e.preventDefault() e.preventDefault()
await file_set(item.key) await file_set(item.key)
set_preview_content_type(CanPreview(item.name))
const ele = document.querySelector('#list-file-container') const ele = document.querySelector('#list-file-container')
const eleX = ele ? ele.clientWidth : 0 let eleX = ele ? ele.clientWidth : 0
const eleY = ele ? ele.clientHeight : 0 let eleY = ele ? ele.clientHeight : 0
const positionX = (e.pageX + eleX > window.innerWidth) ? e.pageX - eleX : e.pageX const positionX = (e.pageX + eleX > window.innerWidth) ? e.pageX - eleX : e.pageX
const positionY = (e.pageY + eleY > window.innerHeight) ? e.pageY - eleY : e.pageY const positionY = (e.pageY + eleY > window.innerHeight) ? e.pageY - eleY : e.pageY
set_ctx_menu({ set_ctx_menu({
@ -118,12 +152,11 @@ export function ListFileComponent() {
y: positionY, y: positionY,
display: 'block', 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) { async function handleDownload(file: string | null) {
if (!file) return if (!file) return
const res1 = await Dial<{result:string}>("/runtime/dialog/save", { const res1 = await Dial<{ result: string }>("/runtime/dialog/save", {
default_filename: file, default_filename: file,
}) })
if (res1.status !== 200) { if (res1.status !== 200) {
@ -141,7 +174,20 @@ export function ListFileComponent() {
} }
} }
return <> async function handlePreview() {
const res = await Dial<{url:string, method: string}>('/api/file/get', {
conn_id: conn_active?.id,
bucket: bucket_active?.name,
key: file_active ?? "",
})
if (res.status !== 200) {
dispatchMessage('预览失败', 'warning')
return
}
props.set_preview_fn({url: res.data.url, content_type: preview_content_type})
}
return <div className={styles.container}>
<div <div
id={'list-file-container'} id={'list-file-container'}
className={styles.ctx_menu} className={styles.ctx_menu}
@ -154,16 +200,26 @@ export function ListFileComponent() {
}} }}
icon={<ArrowDownloadFilled/>}></MenuItem> icon={<ArrowDownloadFilled/>}></MenuItem>
<MenuItem <MenuItem
disabled disabled={!preview_content_type}
onClick={async () => { onClick={async () => {
// await handleDisconnect(menu_conn) await handlePreview()
}} }}
icon={<PreviewLinkRegular/>}></MenuItem> icon={<PreviewLinkRegular/>}></MenuItem>
<MenuItem icon={<DeleteRegular/>}></MenuItem> <MenuItem icon={<DeleteRegular/>}></MenuItem>
</MenuList> </MenuList>
</div> </div>
<MenuList className={styles.container}> <div className={styles.loading} style={{display: loading ? 'flex' : 'none'}}>
{files_list.length ? <Spinner appearance="primary" label="加载中..."/>
</div>
<div className={styles.no_data} style={{display: (!loading && !files_list.length) ? 'flex' : 'none'}}>
<div>
<DocumentDismissRegular/>
</div>
<Text size={900}>
</Text>
</div>
<div style={{display: (!loading && files_list.length) ? 'block' : 'none'}}>
<VirtualizerScrollView <VirtualizerScrollView
numItems={files_list.length} numItems={files_list.length}
itemSize={32} itemSize={32}
@ -187,17 +243,9 @@ export function ListFileComponent() {
</MenuItem> </MenuItem>
</div> </div>
}} }}
</VirtualizerScrollView> : <div className={styles.no_data}> </VirtualizerScrollView>
<div>
<DocumentDismissRegular/>
</div> </div>
<Text size={900}>
</Text>
</div> </div>
}
</MenuList>
</>
} }
type FileIconProps = { type FileIconProps = {

View File

@ -1,10 +1,12 @@
import {Button, Input, makeStyles, Text, tokens, Tooltip} from "@fluentui/react-components"; import {Button, Input, makeStyles, Text, tokens, Tooltip} from "@fluentui/react-components";
import {useStoreBucket} from "../../store/bucket"; import {useStoreBucket} from "../../store/bucket";
import {ArchiveRegular, ArrowCurveUpLeftFilled} from "@fluentui/react-icons"; import {ArchiveRegular, ArrowCurveUpLeftFilled} from "@fluentui/react-icons";
import {useStoreFile} from "../../store/file"; import {useStoreFile, useStoreFileFilter} from "../../store/file";
import React from "react"; import React, {useState} from "react";
import {debounce} from 'lodash' import {debounce} from 'lodash'
import {useStoreConnection} from "../../store/connection"; import {useStoreConnection} from "../../store/connection";
import {ListFileComponent} from "./list_file";
import {ListBucketComponent} from "../bucket/list_bucket";
const useStyles = makeStyles({ const useStyles = makeStyles({
container: { container: {
@ -51,17 +53,18 @@ const useStyles = makeStyles({
}, },
}) })
export function Path() { export function Path() {
const styles = useStyles() const styles = useStyles()
const {conn_active} = useStoreConnection() const {conn_active} = useStoreConnection()
const {bucket_active, bucket_get, bucket_set} = useStoreBucket() const {bucket_active, bucket_get, bucket_set} = useStoreBucket()
const {prefix, files_get} = useStoreFile() const {prefix, filter, prefix_set, filter_set} = useStoreFileFilter()
async function handleClickUp() { async function handleClickUp() {
const dirs = prefix.split('/').filter((item => item)) const dirs = prefix.split('/').filter((item => item))
if (dirs.length > 0) { if (dirs.length > 0) {
dirs.pop() dirs.pop()
files_get(conn_active!, bucket_active!, dirs.join("/")) await prefix_set(dirs.join('/'))
return return
} }
@ -70,8 +73,8 @@ export function Path() {
} }
const handleFilterChange = debounce((e) => { const handleFilterChange = debounce(async (e) => {
files_get(conn_active!, bucket_active!, prefix, e.target.value) await filter_set(e.target.value)
}, 500) }, 500)
return <div className={styles.container}> return <div className={styles.container}>

View File

@ -12,7 +12,7 @@ import {useToast} from "../../message";
import {Dial} from "../../api"; import {Dial} from "../../api";
import {useStoreConnection} from "../../store/connection"; import {useStoreConnection} from "../../store/connection";
import {useStoreBucket} from "../../store/bucket"; import {useStoreBucket} from "../../store/bucket";
import {useStoreFile} from "../../store/file"; import {useStoreFile, useStoreFileFilter} from "../../store/file";
import {MoreHorizontalRegular} from "@fluentui/react-icons"; import {MoreHorizontalRegular} from "@fluentui/react-icons";
const useStyle = makeStyles({ const useStyle = makeStyles({
@ -43,7 +43,8 @@ export function UploadFiles(props: UploadFilesProps) {
const { conn_active} = useStoreConnection(); const { conn_active} = useStoreConnection();
const {bucket_active} = useStoreBucket(); const {bucket_active} = useStoreBucket();
const {prefix, files_get} = useStoreFile() const {files_get} = useStoreFile()
const {prefix } = useStoreFileFilter()
const [selected, set_selected] = useState<string[]>([]); const [selected, set_selected] = useState<string[]>([]);

View File

@ -1,11 +1,4 @@
import {Button, Input, makeStyles, MenuItem, MenuList, mergeClasses, tokens, Tooltip} from "@fluentui/react-components"; import {makeStyles} 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 {ConnectionList} from "../connection/list"; import {ConnectionList} from "../connection/list";
import {Content} from "../file/content"; import {Content} from "../file/content";
@ -20,15 +13,9 @@ const useStyles = makeStyles({
export function Body() { export function Body() {
const styles = useStyles(); const styles = useStyles();
const {conn_get} = useStoreConnection();
useEffect(() => {
conn_get()
}, []);
return <div className={styles.body}> return <div className={styles.body}>
<ConnectionList/> <ConnectionList/>
<Content /> <Content/>
</div> </div>
} }

View File

@ -10,6 +10,7 @@ import {UploadFiles} from "../file/upload_files";
const useStyles = makeStyles({ const useStyles = makeStyles({
header: { header: {
height: "5rem", height: "5rem",
minHeight: '5rem',
width: "100%", width: "100%",
display: 'flex', display: 'flex',
alignItems: "center", alignItems: "center",
@ -64,7 +65,7 @@ export function Header() {
open={open_upload} open={open_upload}
onOpenChange={(event, data) => set_open_upload(data.open)}> onOpenChange={(event, data) => set_open_upload(data.open)}>
<DialogTrigger disableButtonEnhancement> <DialogTrigger disableButtonEnhancement>
<Button appearance="primary" icon={<DocumentArrowUpRegular />}> <Button appearance="primary" icon={<DocumentArrowUpRegular/>}>
</Button> </Button>
</DialogTrigger> </DialogTrigger>

View File

@ -1,3 +1,21 @@
export function PreviewFile() { import {CardPreview, makeStyles} from "@fluentui/react-components";
const useStyle = makeStyles({
container: {
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}
})
export function PreviewFile(props: {url:string, content_type:string}) {
const styles = useStyle()
return <div className={styles.container}>
<CardPreview
>
<img src={props.url} />
</CardPreview>
</div>
} }

View File

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

View File

@ -5,8 +5,8 @@ import {Dial} from "../api";
interface StoreConnection { interface StoreConnection {
conn_active: Connection | null; conn_active: Connection | null;
conn_list: Connection[]; conn_list: Connection[];
conn_get: () => void; conn_get: () => Promise<void>;
conn_update: (connection: Connection) => Promise<void>; conn_set: (connection: Connection) => Promise<void>;
} }
export const useStoreConnection = create<StoreConnection>()((set) => ({ export const useStoreConnection = create<StoreConnection>()((set) => ({
@ -21,7 +21,7 @@ export const useStoreConnection = create<StoreConnection>()((set) => ({
set({conn_list: res.data.list}) set({conn_list: res.data.list})
}, },
conn_update: async (connection: Connection) => { conn_set: async (connection: Connection) => {
set((state) => { set((state) => {
return { return {
conn_active: connection.active? connection: null, conn_active: connection.active? connection: null,

View File

@ -5,10 +5,8 @@ import {Dial} from "../api";
interface StoreFile { interface StoreFile {
file_active: string | null, file_active: string | null,
file_set: (key: string) => Promise<void>, file_set: (key: string) => Promise<void>,
prefix: string;
filter: string;
files_list: S3File[]; 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<void>;
} }
export const useStoreFile = create<StoreFile>()((set) => ({ export const useStoreFile = create<StoreFile>()((set) => ({
@ -18,8 +16,6 @@ export const useStoreFile = create<StoreFile>()((set) => ({
return {file_active: key} return {file_active: key}
}) })
}, },
prefix: "",
filter: "",
files_list: [], files_list: [],
files_get: async (conn: Connection, bucket: Bucket, prefix = '', filter = '') => { files_get: async (conn: Connection, bucket: Bucket, prefix = '', filter = '') => {
const res = await Dial<{ list: S3File[] }>('/api/bucket/files', { const res = await Dial<{ list: S3File[] }>('/api/bucket/files', {
@ -33,7 +29,25 @@ export const useStoreFile = create<StoreFile>()((set) => ({
} }
set((state) => { 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<void>;
filter_set: (filter: string) => Promise<void>;
}
export const useStoreFileFilter = create<StoreFileFilter>()((set) => ({
prefix: '',
filter: '',
prefix_set: async (keyword: string) => {
set(state => {return {prefix: keyword}})
},
filter_set: async (keyword: string) => {
set(state => {return {filter: keyword}})
},
})) }))

View File

@ -1,18 +1,33 @@
import {create} from 'zustand' import {create} from 'zustand'
import {Dial} from "../api";
import {Bucket, Connection} from "../interfaces/connection"; import {Bucket, Connection} from "../interfaces/connection";
interface StorePreview { interface StorePreview {
preview_key: string;
preview_url: string; preview_url: string;
preview_content_type: string; preview_content_type: string;
preview_set: (key: string) => void; preview_get: (conn:Connection,bucket: Bucket,key: string) => Promise<void>;
} }
export const useStorePreview = create<StorePreview>()((set) => ({ export const useStorePreview = create<StorePreview>()((set) => ({
preview_key: '',
preview_url: '', preview_url: '',
preview_content_type: '', preview_content_type: '',
preview_set: async (key: string) => set(state => { preview_get: async (conn: Connection, bucket: Bucket,key: string) => {
return {preview_key: key} if (key === '') {
}), return set(()=>{return {preview_url: ''}})
}
let res = await Dial<{url:string,method:string}>('/api/file/get', {
conn_id: conn.id,
bucket: bucket.name,
key: key
})
if(res.status!=200) {
return set(()=>{return {preview_url: ''}})
}
set(()=>{
return {preview_url:res.data.url}
})
},
})) }))

View File

@ -8,7 +8,6 @@ import (
"github.com/loveuer/nf-disk/internal/s3" "github.com/loveuer/nf-disk/internal/s3"
"github.com/loveuer/nf-disk/ndh" "github.com/loveuer/nf-disk/ndh"
"github.com/samber/lo" "github.com/samber/lo"
"time"
) )
func ConnectionTest(c *ndh.Ctx) error { func ConnectionTest(c *ndh.Ctx) error {
@ -210,11 +209,5 @@ func ConnectionBuckets(c *ndh.Ctx) error {
return c.Send500(err.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}) return c.Send200(map[string]any{"list": buckets})
} }

View File

@ -12,10 +12,10 @@ import (
) )
type ObjectInfo struct { type ObjectInfo struct {
Bucket string Bucket string `json:"bucket"`
Key string Key string `json:"key"`
ContentType string ContentType string `json:"content_type"`
Expire int64 Expire int64 `json:"expire"`
} }
func (c *Client) GetObjectInfo(ctx context.Context, bucket string, key string) (*ObjectInfo, error) { func (c *Client) GetObjectInfo(ctx context.Context, bucket string, key string) (*ObjectInfo, error) {
@ -62,9 +62,9 @@ func (presigner *Presigner) GetObject(ctx context.Context, bucketName string, ob
} }
type ObjectEntry struct { type ObjectEntry struct {
URL string URL string `json:"url"`
Method string Method string `json:"method"`
Header http.Header Header http.Header `json:"header"`
} }
func (c *Client) GetObjectEntry(ctx context.Context, bucket string, key string, lifetimes ...int64) (*ObjectEntry, error) { func (c *Client) GetObjectEntry(ctx context.Context, bucket string, key string, lifetimes ...int64) (*ObjectEntry, error) {
@ -92,7 +92,7 @@ func (c *Client) GetObjectEntry(ctx context.Context, bucket string, key string,
type ObjectEntity struct { type ObjectEntity struct {
ObjectInfo ObjectInfo
Body io.ReadCloser Body io.ReadCloser `json:"body"`
} }
func (c *Client) GetObject(ctx context.Context, bucket string, key string) (*ObjectEntity, error) { func (c *Client) GetObject(ctx context.Context, bucket string, key string) (*ObjectEntity, error) {