feat: 完成了 新建桶; 上传文件(基本功能)
todo: 上传 rename, 上传 public 权限选择 bug: 首次加载 conns list; 上传的时候前缀过滤失败
This commit is contained in:
@ -1 +1 @@
|
||||
b20ef5a27687e07e09878451f9a2e1aa
|
||||
674272f31b23a798d77a17321c6a8785
|
@ -1,5 +1,5 @@
|
||||
import {makeStyles, MenuItem, MenuList, Text, tokens} from "@fluentui/react-components";
|
||||
import {ArchiveRegular, DocumentBulletListRegular} from "@fluentui/react-icons";
|
||||
import {ArchiveRegular} from "@fluentui/react-icons";
|
||||
import {VirtualizerScrollView} from "@fluentui/react-components/unstable";
|
||||
import React from "react";
|
||||
import {useStoreBucket} from "../../store/bucket";
|
||||
@ -50,7 +50,7 @@ export function ListBucketComponent() {
|
||||
|
||||
return <MenuList className={styles.container}>
|
||||
<VirtualizerScrollView
|
||||
numItems={bucket_list.length}
|
||||
numItems={bucket_list?bucket_list.length:0}
|
||||
itemSize={32}
|
||||
container={{role: 'list', style: {maxHeight: 'calc(100vh - 9rem)'}}}
|
||||
>
|
111
frontend/src/component/bucket/new.tsx
Normal file
111
frontend/src/component/bucket/new.tsx
Normal file
@ -0,0 +1,111 @@
|
||||
import {
|
||||
DialogTrigger,
|
||||
DialogSurface,
|
||||
DialogTitle,
|
||||
DialogBody,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
Button, Field, Input, makeStyles, tokens, Checkbox,
|
||||
} from "@fluentui/react-components";
|
||||
import {useState} from "react";
|
||||
import {useToast} from "../../message";
|
||||
import {useStoreConnection} from "../../store/connection";
|
||||
import {useStoreBucket} from "../../store/bucket";
|
||||
|
||||
const useStyle = makeStyles({
|
||||
container: {
|
||||
backgroundColor: tokens.colorNeutralBackground1,
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
gridColumnStart: 0,
|
||||
},
|
||||
content: {},
|
||||
input: {
|
||||
margin: '1rem',
|
||||
},
|
||||
checks: {
|
||||
margin: '1rem',
|
||||
display: "flex",
|
||||
},
|
||||
});
|
||||
|
||||
export interface BucketCreateProps {
|
||||
openFn: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export function BucketCreate(props: BucketCreateProps) {
|
||||
const styles = useStyle();
|
||||
const {dispatchMessage} = useToast();
|
||||
|
||||
const [name, set_name] = useState<string>();
|
||||
const [public_read, set_public_read] = useState(false);
|
||||
const [public_read_write, set_public_read_write] = useState(false);
|
||||
|
||||
const {conn_active} = useStoreConnection();
|
||||
const {bucket_create, bucket_get} = useStoreBucket()
|
||||
|
||||
async function create() {
|
||||
if (!name) {
|
||||
dispatchMessage('桶名不能为空', "warning")
|
||||
return
|
||||
}
|
||||
|
||||
bucket_create(conn_active!, name, public_read, public_read_write);
|
||||
bucket_get(conn_active!, true)
|
||||
props.openFn(false)
|
||||
}
|
||||
|
||||
return <>
|
||||
<DialogSurface>
|
||||
<DialogBody>
|
||||
<DialogTitle>在 {conn_active?.name} 新建桶</DialogTitle>
|
||||
<DialogContent>
|
||||
<div className={styles.content}>
|
||||
<Field className={styles.input} label="桶名">
|
||||
<Input
|
||||
value={name}
|
||||
onChange={(e) => {
|
||||
set_name(e.target.value)
|
||||
}}></Input>
|
||||
</Field>
|
||||
<div className={styles.checks}>
|
||||
<Field>
|
||||
<Checkbox
|
||||
checked={public_read}
|
||||
onChange={(e) => {
|
||||
if (public_read_write) {
|
||||
return
|
||||
}
|
||||
|
||||
set_public_read(e.target.checked);
|
||||
}}
|
||||
label={"公共读"}></Checkbox>
|
||||
</Field>
|
||||
<Field>
|
||||
<Checkbox
|
||||
checked={public_read_write}
|
||||
onChange={(e) => {
|
||||
set_public_read_write(e.target.checked)
|
||||
if (e.target.checked) {
|
||||
set_public_read(e.target.checked)
|
||||
}
|
||||
}}
|
||||
label={"公共读/写"}></Checkbox>
|
||||
</Field>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogActions className={styles.container}>
|
||||
<DialogTrigger disableButtonEnhancement>
|
||||
<Button appearance="secondary">取消</Button>
|
||||
</DialogTrigger>
|
||||
<Button onClick={async () => {
|
||||
await create()
|
||||
}} appearance="primary">新建</Button>
|
||||
</DialogActions>
|
||||
</DialogBody>
|
||||
</DialogSurface>
|
||||
</>
|
||||
}
|
@ -2,16 +2,16 @@ import {
|
||||
Button,
|
||||
Input,
|
||||
makeStyles,
|
||||
Menu,
|
||||
|
||||
MenuItem,
|
||||
MenuList, MenuPopover, MenuProps,
|
||||
mergeClasses, PositioningImperativeRef,
|
||||
MenuList,
|
||||
mergeClasses,
|
||||
tokens,
|
||||
Tooltip
|
||||
} from "@fluentui/react-components"
|
||||
import {DatabaseLinkRegular, DismissRegular} from "@fluentui/react-icons";
|
||||
import React, {useState} from "react";
|
||||
import {Bucket, Connection} from "../../interfaces/connection";
|
||||
import React, { useEffect, useState} from "react";
|
||||
import { Connection} from "../../interfaces/connection";
|
||||
import {useToast} from "../../message";
|
||||
import {Dial} from "../../api";
|
||||
import {useStoreConnection} from "../../store/connection";
|
||||
@ -40,6 +40,15 @@ const useStyles = makeStyles({
|
||||
marginLeft: "0.5rem",
|
||||
marginRight: "0.5rem",
|
||||
},
|
||||
ctx_menu: {
|
||||
position: "absolute",
|
||||
zIndex: "1000",
|
||||
width: "15rem",
|
||||
backgroundColor: tokens.colorNeutralBackground1,
|
||||
boxShadow: `${tokens.shadow16}`,
|
||||
paddingTop: "4px",
|
||||
paddingBottom: "4px",
|
||||
},
|
||||
items: {
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
@ -78,6 +87,20 @@ export function ConnectionList() {
|
||||
const {conn_list, conn_update} = useStoreConnection();
|
||||
const [conn_filter, set_conn_filter] = useState<string>('');
|
||||
const {bucket_get, bucket_set} = useStoreBucket()
|
||||
const [ctx_menu, set_ctx_menu] = useState<{
|
||||
x: number,
|
||||
y: number,
|
||||
display: 'none' | 'block'
|
||||
}>({x: 0, y: 0, display: 'none'});
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("click", (e) => {
|
||||
set_ctx_menu({x: 0, y: 0, display: 'none'});
|
||||
})
|
||||
return () => {
|
||||
document.removeEventListener("click", (e) => {})
|
||||
}
|
||||
}, [])
|
||||
|
||||
async function handleSelect(item: Connection) {
|
||||
conn_list.map((one: Connection) => {
|
||||
@ -114,6 +137,11 @@ export function ConnectionList() {
|
||||
e.preventDefault()
|
||||
console.log('[DEBUG] right click connection =', item, 'event =', e)
|
||||
console.log(`[DEBUG] click position: [${e.pageX}, ${e.pageY}]`)
|
||||
set_ctx_menu({
|
||||
x: e.pageX,
|
||||
y: e.pageY,
|
||||
display: 'block',
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
@ -132,6 +160,15 @@ export function ConnectionList() {
|
||||
onChange={(e) => set_conn_filter(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.ctx_menu}
|
||||
style={{left: ctx_menu.x, top: ctx_menu.y, display: ctx_menu.display}}
|
||||
>
|
||||
<MenuList>
|
||||
<MenuItem>连接</MenuItem>
|
||||
<MenuItem>设置</MenuItem>
|
||||
<MenuItem>删除</MenuItem>
|
||||
</MenuList>
|
||||
</div>
|
||||
<div className={styles.items}>
|
||||
<MenuList>
|
||||
{conn_list.filter(item => item.name.includes(conn_filter)).map(item => {
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
DialogBody,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
Button, Spinner, Field, Input, FieldProps, makeStyles, tokens,
|
||||
Button, Spinner, Field, Input, makeStyles, tokens,
|
||||
} from "@fluentui/react-components";
|
||||
import {useState} from "react";
|
||||
import {CheckmarkFilled, DismissRegular} from "@fluentui/react-icons";
|
||||
@ -84,8 +84,6 @@ export function ConnectionCreate(props: ConnectionCreateProps) {
|
||||
<div className='connection-form-field'>
|
||||
<Field
|
||||
label="name"
|
||||
validationState="success"
|
||||
validationMessage="This is a success message."
|
||||
>
|
||||
<Input placeholder='名称 (example: 测试S3-minio)' value={value.name}
|
||||
onChange={(e) => {
|
||||
@ -96,11 +94,11 @@ export function ConnectionCreate(props: ConnectionCreateProps) {
|
||||
<div className='connection-form-field'>
|
||||
<Field
|
||||
label="endpoint"
|
||||
validationState="success"
|
||||
validationMessage="This is a success message."
|
||||
required
|
||||
>
|
||||
<Input placeholder='地址 (example: https://ip_or_server-name:port)'
|
||||
value={value.endpoint}
|
||||
required
|
||||
onChange={(e) => {
|
||||
setValue({...value, endpoint: e.target.value});
|
||||
}}/>
|
||||
@ -109,10 +107,11 @@ export function ConnectionCreate(props: ConnectionCreateProps) {
|
||||
<div className='connection-form-field'>
|
||||
<Field
|
||||
label="secret access"
|
||||
validationState="success"
|
||||
validationMessage="This is a success message."
|
||||
required
|
||||
>
|
||||
<Input placeholder='' value={value.access} onChange={(e) => {
|
||||
<Input placeholder=''
|
||||
required
|
||||
value={value.access} onChange={(e) => {
|
||||
setValue({...value, access: e.target.value});
|
||||
}}/>
|
||||
</Field>
|
||||
@ -120,10 +119,11 @@ export function ConnectionCreate(props: ConnectionCreateProps) {
|
||||
<div className='connection-form-field'>
|
||||
<Field
|
||||
label="secret key"
|
||||
validationState="success"
|
||||
validationMessage="This is a success message."
|
||||
required
|
||||
>
|
||||
<Input placeholder='' value={value.key} onChange={(e) => {
|
||||
<Input placeholder=''
|
||||
required
|
||||
value={value.key} onChange={(e) => {
|
||||
setValue({...value, key: e.target.value});
|
||||
}}/>
|
||||
</Field>
|
||||
|
@ -1,8 +1,7 @@
|
||||
import {Path} from "./path";
|
||||
import {ListBucketComponent} from "./list_bucket";
|
||||
import {ListBucketComponent} from "../bucket/list_bucket";
|
||||
import {makeStyles} from "@fluentui/react-components";
|
||||
import {useStoreBucket} from "../../store/bucket";
|
||||
import {useStoreFile} from "../../store/file";
|
||||
import {ListFileComponent} from "./list_file";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
@ -18,9 +17,7 @@ const useStyles = makeStyles({
|
||||
export function Content() {
|
||||
|
||||
const styles = useStyles()
|
||||
const {bucket_active, bucket_list} = useStoreBucket()
|
||||
const {file_list} = useStoreFile()
|
||||
|
||||
const {bucket_active } = useStoreBucket()
|
||||
return <div className={styles.content}>
|
||||
<Path/>
|
||||
{
|
||||
|
@ -1,9 +1,15 @@
|
||||
import {makeStyles, MenuItem, MenuList, Text, tokens} from "@fluentui/react-components";
|
||||
import {ArchiveRegular, DocumentBulletListRegular, DocumentDismissRegular, FolderRegular} from "@fluentui/react-icons";
|
||||
import {
|
||||
|
||||
DocumentBulletListRegular, DocumentChevronDoubleRegular, DocumentCssRegular, DocumentDatabaseRegular,
|
||||
DocumentDismissRegular,
|
||||
DocumentImageRegular, DocumentJavascriptRegular, DocumentPdfRegular, DocumentYmlRegular,
|
||||
FolderRegular
|
||||
} from "@fluentui/react-icons";
|
||||
import {VirtualizerScrollView} from "@fluentui/react-components/unstable";
|
||||
import React, {useEffect} from "react";
|
||||
import React from "react";
|
||||
import {useStoreBucket} from "../../store/bucket";
|
||||
import {Bucket, S3File} from "../../interfaces/connection";
|
||||
import { S3File} from "../../interfaces/connection";
|
||||
import {useStoreFile} from "../../store/file";
|
||||
import {useStoreConnection} from "../../store/connection";
|
||||
import {TrimSuffix} from "../../hook/strings";
|
||||
@ -85,7 +91,8 @@ export function ListFileComponent() {
|
||||
handleRightClick(e, files_list[idx])
|
||||
}}>
|
||||
<MenuItem className={styles.item}
|
||||
icon={files_list[idx].type ? <FolderRegular/> : <DocumentBulletListRegular/>}>
|
||||
icon={files_list[idx].type ? <FolderRegular/> :
|
||||
<FileIcon name={files_list[idx].name}/>}>
|
||||
<Text truncate wrap={false} className={styles.text}>
|
||||
{filename(files_list[idx].key)}
|
||||
</Text>
|
||||
@ -94,7 +101,7 @@ export function ListFileComponent() {
|
||||
}}
|
||||
</VirtualizerScrollView> : <div className={styles.no_data}>
|
||||
<div>
|
||||
<DocumentDismissRegular />
|
||||
<DocumentDismissRegular/>
|
||||
</div>
|
||||
<Text size={900}>
|
||||
没有文件
|
||||
@ -102,4 +109,47 @@ export function ListFileComponent() {
|
||||
</div>
|
||||
}
|
||||
</MenuList>
|
||||
}
|
||||
|
||||
type FileIconProps = {
|
||||
name: string
|
||||
}
|
||||
|
||||
function FileIcon(props: FileIconProps) {
|
||||
const strings = props.name.split(".")
|
||||
const suffix = strings[strings.length - 1]
|
||||
switch (suffix) {
|
||||
case "png":
|
||||
return <DocumentImageRegular/>
|
||||
case "jpg":
|
||||
return <DocumentImageRegular/>
|
||||
case "jpeg":
|
||||
return <DocumentImageRegular/>
|
||||
case "gif":
|
||||
return <DocumentImageRegular/>
|
||||
case "db":
|
||||
return <DocumentDatabaseRegular/>
|
||||
case "sqlite":
|
||||
return <DocumentDatabaseRegular/>
|
||||
case "sqlite3":
|
||||
return <DocumentDatabaseRegular/>
|
||||
case "pdf":
|
||||
return <DocumentPdfRegular/>
|
||||
case "css":
|
||||
return <DocumentCssRegular />
|
||||
case "js":
|
||||
return <DocumentJavascriptRegular/>
|
||||
case "yaml":
|
||||
return <DocumentYmlRegular/>
|
||||
case "yml":
|
||||
return <DocumentYmlRegular/>
|
||||
case "html":
|
||||
return <DocumentChevronDoubleRegular/>
|
||||
case "json":
|
||||
return <DocumentChevronDoubleRegular/>
|
||||
case "go":
|
||||
return <DocumentChevronDoubleRegular/>
|
||||
default:
|
||||
return <DocumentBulletListRegular/>
|
||||
}
|
||||
}
|
@ -54,11 +54,19 @@ const useStyles = makeStyles({
|
||||
export function Path() {
|
||||
const styles = useStyles()
|
||||
const {conn_active} = useStoreConnection()
|
||||
const {bucket_active} = useStoreBucket()
|
||||
const {bucket_active, bucket_get, bucket_set} = useStoreBucket()
|
||||
const {prefix, files_get} = useStoreFile()
|
||||
|
||||
async function handleClickUp() {
|
||||
const dirs = prefix.split('/').filter((item => item))
|
||||
if (dirs.length > 0) {
|
||||
dirs.pop()
|
||||
files_get(conn_active!, bucket_active!, dirs.join("/"))
|
||||
return
|
||||
}
|
||||
|
||||
bucket_get(conn_active!, false)
|
||||
bucket_set(null)
|
||||
}
|
||||
|
||||
|
||||
|
123
frontend/src/component/file/upload_files.tsx
Normal file
123
frontend/src/component/file/upload_files.tsx
Normal file
@ -0,0 +1,123 @@
|
||||
import {
|
||||
DialogTrigger,
|
||||
DialogSurface,
|
||||
DialogTitle,
|
||||
DialogBody,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
Button, Field, Input, makeStyles, tokens, Tooltip,
|
||||
} from "@fluentui/react-components";
|
||||
import {useState} from "react";
|
||||
import {useToast} from "../../message";
|
||||
import {Dial} from "../../api";
|
||||
import {useStoreConnection} from "../../store/connection";
|
||||
import {useStoreBucket} from "../../store/bucket";
|
||||
import {useStoreFile} from "../../store/file";
|
||||
import {MoreHorizontalRegular} from "@fluentui/react-icons";
|
||||
|
||||
const useStyle = makeStyles({
|
||||
container: {
|
||||
backgroundColor: tokens.colorNeutralBackground1,
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
gridColumnStart: 0,
|
||||
},
|
||||
input: {
|
||||
cursor: "pointer",
|
||||
display: 'flex',
|
||||
},
|
||||
select: {
|
||||
minWidth: 'unset',
|
||||
},
|
||||
});
|
||||
|
||||
export interface UploadFilesProps {
|
||||
openFn: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export function UploadFiles(props: UploadFilesProps) {
|
||||
const styles = useStyle();
|
||||
const {dispatchMessage} = useToast();
|
||||
|
||||
const { conn_active} = useStoreConnection();
|
||||
const {bucket_active} = useStoreBucket();
|
||||
const {prefix, files_get} = useStoreFile()
|
||||
|
||||
const [selected, set_selected] = useState<string[]>([]);
|
||||
|
||||
async function handleSelect() {
|
||||
const res = await Dial<{ result: string[] }>('/runtime/dialog/open', {
|
||||
title: '选择文件',
|
||||
type: 'multi'
|
||||
})
|
||||
|
||||
if (res.status !== 200) {
|
||||
return
|
||||
}
|
||||
|
||||
set_selected(res.data.result)
|
||||
}
|
||||
|
||||
async function create() {
|
||||
let ok = true
|
||||
for (const item of selected) {
|
||||
const res = await Dial('/api/file/upload', {
|
||||
conn_id: conn_active?.id,
|
||||
bucket: bucket_active?.name,
|
||||
location: item,
|
||||
detect_content_type: true,
|
||||
})
|
||||
|
||||
if (res.status !== 200) {
|
||||
dispatchMessage(`上传文件: ${item} 失败`, "error")
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if(ok) {
|
||||
files_get(conn_active!, bucket_active!, prefix)
|
||||
dispatchMessage('上传成功!', 'success')
|
||||
props.openFn(false)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return <>
|
||||
<DialogSurface>
|
||||
<DialogBody>
|
||||
<DialogTitle>上传文件到 {`${bucket_active?.name} / ${prefix}`}</DialogTitle>
|
||||
<DialogContent>
|
||||
<Field label="选择文件" required>
|
||||
<Input
|
||||
className={styles.input}
|
||||
value={selected?.join('; ')}
|
||||
contentAfter={
|
||||
<Tooltip content={'点击选择文件'} relationship={'description'}>
|
||||
<Button
|
||||
className={styles.select}
|
||||
onClick={async () => {
|
||||
await handleSelect()
|
||||
}}
|
||||
size={'small'}
|
||||
appearance={'transparent'}>
|
||||
<MoreHorizontalRegular/>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
}/>
|
||||
</Field>
|
||||
</DialogContent>
|
||||
<DialogActions className={styles.container}>
|
||||
<DialogTrigger disableButtonEnhancement>
|
||||
<Button appearance="secondary">取消</Button>
|
||||
</DialogTrigger>
|
||||
<Button onClick={async () => {
|
||||
await create()
|
||||
}} appearance="primary">上传</Button>
|
||||
</DialogActions>
|
||||
</DialogBody>
|
||||
</DialogSurface>
|
||||
</>
|
||||
}
|
@ -1,37 +1,76 @@
|
||||
import {Button, Dialog, DialogTrigger, makeStyles} from "@fluentui/react-components";
|
||||
import {Button, Dialog, DialogTrigger, makeStyles} from "@fluentui/react-components";
|
||||
import {ConnectionCreate} from "../connection/new";
|
||||
import {CloudAddFilled} 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";
|
||||
import {useStoreBucket} from "../../store/bucket";
|
||||
import {UploadFiles} from "../file/upload_files";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
header: {
|
||||
height: "5rem",
|
||||
height: "5rem",
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
alignItems: "center",
|
||||
borderBottom: "1px solid lightgray",
|
||||
},
|
||||
button_new_connection: {
|
||||
margin: '0.5rem',
|
||||
button_new: {
|
||||
margin: '0.5rem',
|
||||
},
|
||||
})
|
||||
|
||||
export function Header() {
|
||||
const styles = useStyles();
|
||||
const [openCreate, setOpenCreate] = useState(false);
|
||||
const {conn_active} = useStoreConnection()
|
||||
const {bucket_active} = useStoreBucket()
|
||||
const [open_create_conn, set_open_create_conn] = useState(false);
|
||||
const [open_create_bucket, set_open_create_bucket] = useState(false);
|
||||
const [open_upload, set_open_upload] = useState(false);
|
||||
|
||||
return <div className={styles.header}>
|
||||
<div className={styles.button_new_connection}>
|
||||
<div className={styles.button_new}>
|
||||
<Dialog
|
||||
open={openCreate}
|
||||
onOpenChange={(event, data) => setOpenCreate(data.open)}>
|
||||
open={open_create_conn}
|
||||
onOpenChange={(event, data) => set_open_create_conn(data.open)}>
|
||||
<DialogTrigger disableButtonEnhancement>
|
||||
<Button appearance="primary" icon={<CloudAddFilled/>}>
|
||||
<Button appearance="primary" icon={<PlugConnectedAddRegular/>}>
|
||||
新建连接
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<ConnectionCreate openFn={setOpenCreate}/>
|
||||
<ConnectionCreate openFn={set_open_create_conn}/>
|
||||
</Dialog>
|
||||
</div>
|
||||
|
||||
{conn_active &&
|
||||
<div className={styles.button_new}>
|
||||
<Dialog
|
||||
open={open_create_bucket}
|
||||
onOpenChange={(event, data) => set_open_create_bucket(data.open)}>
|
||||
<DialogTrigger disableButtonEnhancement>
|
||||
<Button appearance="primary" icon={<AppsAddInRegular/>}>
|
||||
新建桶
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<BucketCreate openFn={set_open_create_bucket}/>
|
||||
</Dialog>
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
bucket_active &&
|
||||
<div className={styles.button_new}>
|
||||
<Dialog
|
||||
open={open_upload}
|
||||
onOpenChange={(event, data) => set_open_upload(data.open)}>
|
||||
<DialogTrigger disableButtonEnhancement>
|
||||
<Button appearance="primary" icon={<DocumentArrowUpRegular />}>
|
||||
上传
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<UploadFiles openFn={set_open_upload}/>
|
||||
</Dialog>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
@ -3,4 +3,8 @@ export function TrimSuffix(str: string, suffix: string) {
|
||||
return str.substring(0, str.length - suffix.length);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
export function GetBaseFileName(fullPath: string) {
|
||||
return fullPath.replace(/.*[\/\\]/, '');
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ interface StoreBucket {
|
||||
bucket_set: (Bucket: Bucket | null) => void;
|
||||
bucket_list: Bucket[];
|
||||
bucket_get: (conn: Connection, refresh: boolean) => void;
|
||||
bucket_create: (conn: Connection, name: string, public_read: boolean, public_read_write: boolean) => void;
|
||||
}
|
||||
|
||||
let bucket_map: { [id: number]: Bucket[] };
|
||||
@ -34,5 +35,21 @@ export const useStoreBucket = create<StoreBucket>()((set) => ({
|
||||
|
||||
return {bucket_list: bucket_map[conn.id]};
|
||||
})
|
||||
},
|
||||
bucket_create: async (conn: Connection, name: string, public_read: boolean) => {
|
||||
const res = await Dial<{ bucket: string }>('/api/bucket/create', {
|
||||
conn_id: conn.id,
|
||||
name: name,
|
||||
public_read: public_read,
|
||||
public_read_write: public_read,
|
||||
})
|
||||
|
||||
if (res.status !== 200) {
|
||||
return
|
||||
}
|
||||
|
||||
set((state) => {
|
||||
return {bucket_list: [...state.bucket_list, {name: res.data.bucket, created_at: 0}]}
|
||||
})
|
||||
}
|
||||
}))
|
||||
|
3
frontend/wailsjs/go/controller/App.d.ts
vendored
3
frontend/wailsjs/go/controller/App.d.ts
vendored
@ -1,7 +1,4 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
import {context} from '../models';
|
||||
|
||||
export function Init(arg1:context.Context):Promise<void>;
|
||||
|
||||
export function Invoke(arg1:string,arg2:string):Promise<string>;
|
||||
|
@ -2,10 +2,6 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export function Init(arg1) {
|
||||
return window['go']['controller']['App']['Init'](arg1);
|
||||
}
|
||||
|
||||
export function Invoke(arg1, arg2) {
|
||||
return window['go']['controller']['App']['Invoke'](arg1, arg2);
|
||||
}
|
||||
|
Reference in New Issue
Block a user