Skip to content

Commit

Permalink
feat(web): impl storage page
Browse files Browse the repository at this point in the history
* feat(web): impl storage page
  • Loading branch information
walle233 authored Dec 26, 2022
1 parent 90b177f commit 5c42bc6
Show file tree
Hide file tree
Showing 11 changed files with 189 additions and 51 deletions.
2 changes: 1 addition & 1 deletion web/src/components/FileUplaod/index.module.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.formFileUpload {
height: 16rem;
width: 28rem;
width: 29rem;
max-width: 100%;
text-align: center;
position: relative;
Expand Down
21 changes: 15 additions & 6 deletions web/src/components/FileUplaod/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import React from "react";
import React, { useEffect } from "react";
import clsx from "clsx";

import styles from "./index.module.scss";

// drag drop file component
function FileUpload(props: { onUpload: (files: any) => void }) {
const { onUpload = () => {} } = props;
function FileUpload(props: { onUpload: (files: any) => void, uploadType: "file" | "folder" }) {
const { onUpload = () => {}, uploadType } = props;
// drag state
const [dragActive, setDragActive] = React.useState(false);
// ref
const inputRef = React.useRef<any>(null);

useEffect(() => {
if (uploadType === "folder") {
inputRef.current.setAttribute("webkitdirectory", "");
inputRef.current.setAttribute("directory", "");
} else {
inputRef.current.removeAttribute("webkitdirectory");
inputRef.current.removeAttribute("directory");
}
}, [uploadType]);

// handle drag events
const handleDrag = function (e: any) {
e.preventDefault();
Expand Down Expand Up @@ -57,7 +67,6 @@ function FileUpload(props: { onUpload: (files: any) => void }) {
type="file"
className={styles.inputFileUpload}
multiple={true}
// @ts-ignore
onChange={handleChange}
/>
<label
Expand All @@ -68,9 +77,9 @@ function FileUpload(props: { onUpload: (files: any) => void }) {
htmlFor="input-file-upload"
>
<div>
<p>Drag and drop your file here or</p>
<p>Drag and drop your {uploadType} here or</p>
<button className={styles.uploadButton} onClick={onButtonClick}>
Upload a file
{uploadType === "file" ? "Upload File" : "Upload Folder"}
</button>
</div>
</label>
Expand Down
18 changes: 16 additions & 2 deletions web/src/hooks/useAwsS3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,31 @@ function useAwsS3() {
Delimiter: "/",
})
.promise();
return res.Contents || [];

const files = res.Contents || []
const dirs = res.CommonPrefixes || []
return [...files, ...dirs];
};


const getFileUrl = (bucket: string, key: string) => {
const res = s3.getSignedUrl('getObject', { Bucket: bucket, Key: key })
return res
}

const uploadFile = async (bucketName: string, key: string, body: any, { contentType }: any) => {
const res = await s3
.putObject({ Bucket: bucketName, Key: key, ContentType: contentType, Body: body })
.promise();
return res;
};

return { s3, getList, uploadFile };
const deleteFile = async (bucket: string, key: string) => {
const res = await s3.deleteObject({ Bucket: bucket, Key: key }).promise()
return res
}

return { s3, getList, uploadFile, getFileUrl, deleteFile };
}

export default useAwsS3;
8 changes: 6 additions & 2 deletions web/src/pages/app/storages/mods/CreateBucketModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ import {
import IconWrap from "@/components/IconWrap";

import { useBucketCreateMutation, useBucketUpdateMutation } from "../../service";
import useStorageStore from "../../store";

import { TBucket } from "@/apis/typing";
import useGlobalStore from "@/pages/globalStore";

function CreateBucketModal(props: { storage?: TBucket }) {
const { isOpen, onOpen, onClose } = useDisclosure();
const { t } = useTranslation();
const store = useStorageStore((store) => store);

const { storage } = props;

Expand Down Expand Up @@ -65,13 +67,15 @@ function CreateBucketModal(props: { storage?: TBucket }) {
});

if (!res.error) {
store.setCurrentStorage(res.data);
showSuccess("update success.");
onClose();
}
} else {
res = await bucketCreateMutation.mutateAsync(values);

if (!res.error) {
store.setCurrentStorage(res.data);
showSuccess("create success.");
onClose();
}
Expand All @@ -89,15 +93,15 @@ function CreateBucketModal(props: { storage?: TBucket }) {
setFocus("shortName");
}, 0);
}}
tooltip="创建 Bucket"
tooltip={isEdit ? "编辑 Bucket" : "创建 Bucket" }
>
{isEdit ? <EditIcon fontSize={13} /> : <AddIcon fontSize={10} />}
</IconWrap>

<Modal isOpen={isOpen} onClose={onClose} size="lg">
<ModalOverlay />
<ModalContent>
<ModalHeader>Create Bucket</ModalHeader>
<ModalHeader>{isEdit ? "编辑 Bucket" : "创建 Bucket" }</ModalHeader>
<ModalCloseButton />

<ModalBody pb={6}>
Expand Down
17 changes: 11 additions & 6 deletions web/src/pages/app/storages/mods/CreateFolderModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@ import {
} from "@chakra-ui/react";
import { t } from "i18next";

import useStorageStore, { TFile } from "../../store";

function CreateModal() {
const { isOpen, onOpen, onClose } = useDisclosure();
const { prefix, setPrefix } = useStorageStore();


const { register, setFocus, handleSubmit } = useForm<{ name: string }>();
const { register, setFocus, handleSubmit } = useForm<{ prefix: string }>();

return (
<>
Expand All @@ -28,7 +32,7 @@ function CreateModal() {
onClick={() => {
onOpen();
setTimeout(() => {
setFocus("name");
setFocus("prefix");
}, 0);
}}
>
Expand All @@ -44,9 +48,9 @@ function CreateModal() {
<ModalBody pb={6}>
<VStack spacing={6} align="flex-start">
<FormControl>
<FormLabel htmlFor="name">文件夹名称</FormLabel>
<FormLabel htmlFor="prefix">文件夹名称</FormLabel>
<Input
{...register("name", {
{...register("prefix", {
required: true,
})}
variant="filled"
Expand All @@ -62,8 +66,9 @@ function CreateModal() {
<Button
colorScheme="primary"
type="submit"
onClick={handleSubmit(() => {
console.log("submit");
onClick={handleSubmit((value) => {
setPrefix(prefix + value.prefix + "/");
onClose();
})}
>
{t("Common.Dialog.Confirm")}
Expand Down
79 changes: 58 additions & 21 deletions web/src/pages/app/storages/mods/FileList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,76 @@
import { DeleteIcon, DownloadIcon } from "@chakra-ui/icons";
import { useEffect, useState } from "react";
import { DeleteIcon, DownloadIcon, ViewIcon } from "@chakra-ui/icons";
import { HStack, Table, TableContainer, Tbody, Td, Th, Thead, Tr } from "@chakra-ui/react";
import { useQuery } from "@tanstack/react-query";
import { t } from "i18next";

import ConfirmButton from "@/components/ConfirmButton";
import IconWrap from "@/components/IconWrap";
import PanelHeader from "@/components/Panel/Header";
import { formatDate } from "@/utils/format";
import { formatDate, formatSize } from "@/utils/format";

import useStorageStore, { TFile } from "../../store";
import CreateFolderModal from "../CreateFolderModal";
import HostingDrawer from "../HostingDrawer";
import PathLink from "../PathLink";
import UploadButton from "../UploadButton";

import useAwsS3 from "@/hooks/useAwsS3";
import RightPanel from "@/pages/app/mods/RightPanel";

export default function FileList() {
const { getList } = useAwsS3();
const { getList, getFileUrl, deleteFile } = useAwsS3();

const { currentStorage } = useStorageStore();
const { currentStorage, prefix, setPrefix } = useStorageStore();
const bucketName = currentStorage?.metadata.name;

const query = useQuery(
["fileList", bucketName, "/"],
() => getList(bucketName, { marker: "", prefix: "/" }),
["fileList", bucketName],
() => getList(bucketName, { marker: "", prefix }),
{
enabled: !!bucketName,
},
);

useEffect(() => {
query.refetch();
}, [bucketName, prefix]);

const viewAppFile = (file: TFile) => {
if (file.Prefix) {
changeDirectory(file);
return
}

window.open(getFileUrl(bucketName!, file.Key), "_blank");
}

const changeDirectory = (file: TFile) => {
setPrefix(file.Prefix!)
}

return (
<RightPanel>
<PanelHeader>
<HStack spacing={2}>
<UploadButton />
<UploadButton onUploadSuccess={() => query.refetch()} />
<CreateFolderModal />
<HostingDrawer />
<PathLink />
{/* <HostingDrawer /> */}
</HStack>
<HStack spacing={12}>
<span>容量: {currentStorage?.spec.storage} </span>
<span>已用: {currentStorage?.status.capacity.storage} </span>
<span>文件数: {currentStorage?.status.capacity.objectCount} </span>
<span>已用: {currentStorage?.status?.capacity?.storage} </span>
<span>文件数: {currentStorage?.status?.capacity?.objectCount} </span>
</HStack>
</PanelHeader>
<div className="m-2 ">
<TableContainer>
<Table variant="simple" size="sm">
<Thead>
<Tr>
<Th>..</Th>
<Th>文件路径</Th>
<Th>文件类型</Th>
<Th isNumeric>大小</Th>
<Th isNumeric>更新时间</Th>
<Th isNumeric>
Expand All @@ -59,18 +81,33 @@ export default function FileList() {
<Tbody>
{(query?.data || []).map((file: TFile) => {
return (
<Tr _hover={{ bgColor: "#efefef" }} key={file.Key}>
<Td>jpg</Td>
<Td style={{ maxWidth: 200 }}>{file.Key}</Td>
<Td isNumeric>{file.Size}</Td>
<Td isNumeric>{formatDate(file.LastModified)}</Td>
<Tr _hover={{ bgColor: "#efefef" }} key={file.Key || file.Prefix}>
<Td style={{ maxWidth: 200 }}>
{file.Prefix ?
<a className="cursor-pointer text-blue-700 underline" onClick={() => changeDirectory(file)}>{bucketName + '/' + file.Prefix}</a>
: bucketName + '/' + file.Key}
</Td>
<Td>--</Td>
<Td isNumeric>{file.Size ? formatSize(file.Size) : '--'}</Td>
<Td isNumeric>{file.LastModified ? formatDate(file.LastModified) : '--'}</Td>
<Td isNumeric className="flex justify-end">
<IconWrap onClick={() => {}}>
<DownloadIcon fontSize={12} />
</IconWrap>
<IconWrap onClick={() => {}}>
<DeleteIcon fontSize={12} />
<IconWrap onClick={() => viewAppFile(file)}>
<ViewIcon fontSize={12} />
</IconWrap>
<ConfirmButton
onSuccessAction={async () => {
await deleteFile(bucketName!, file.Key);
query.refetch();
}}
headerText={String(t("Delete"))}
bodyText={'确认要删除文件吗?'}
>
<IconWrap tooltip={String(t("Delete"))}>
<DeleteIcon
fontSize={14}
/>
</IconWrap>
</ConfirmButton>
</Td>
</Tr>
);
Expand Down
38 changes: 38 additions & 0 deletions web/src/pages/app/storages/mods/PathLink/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import useStorageStore from "../../store";

function PathLink() {
const { currentStorage, prefix, setPrefix } = useStorageStore();
const bucketName = currentStorage?.metadata.name || "";

const strs = prefix?.split("/").filter((s) => s !== "");

const paths = strs?.map((s, i) => {
return {
name: s,
path: strs[i - 1] ? `${strs[i - 1]}/${s}/` : `/${s}/`,
};
});

paths?.unshift({
name: bucketName,
path: "/",
});


const changeDirectory = (path: string) => {
setPrefix(path);
};

return (
<div>
{paths?.map((p) => (
<span key={p.path}>
<span className="text-blue-700 underline cursor-pointer" onClick={() => changeDirectory(p.path)}>{p.name}</span>
<span className="text-gray-500">/</span>
</span>
))}
</div>
);
}

export default PathLink;
5 changes: 3 additions & 2 deletions web/src/pages/app/storages/mods/StorageListPanel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export default function StorageListPanel() {

const bucketListQuery = useBucketListQuery({
onSuccess(data) {
if (data?.data?.items?.length) {
store.setCurrentStorage(data?.data?.items[0]);
if (data?.data?.items?.length) {
if (!store.currentStorage) store.setCurrentStorage(data?.data?.items[0]);
}
},
});
Expand All @@ -43,6 +43,7 @@ export default function StorageListPanel() {
className="group"
onClick={() => {
store.setCurrentStorage(storage);
store.setPrefix("/")
}}
>
<div className="relative flex-1">
Expand Down
Loading

0 comments on commit 5c42bc6

Please sign in to comment.