wip: file preview

This commit is contained in:
loveuer 2024-10-14 22:31:59 +08:00
parent 6f15f82122
commit 3ff83f12b4
6 changed files with 115 additions and 67 deletions

View File

@ -4,6 +4,7 @@ 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 {useState} from "react";
import {PreviewFile} from "../preview/preview";
const useStyles = makeStyles({ const useStyles = makeStyles({
content: { content: {
@ -18,14 +19,17 @@ const useStyles = makeStyles({
export function Content() { export function Content() {
const styles = useStyles() const styles = useStyles()
const [preview, set_preview] = useState<{ url: string, content_type: string }>({url: '', content_type: ''})
const {bucket_active} = useStoreBucket() 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

@ -24,6 +24,7 @@ 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 {CanPreview} from "../../hook/preview";
import {useStorePreview} from "../../store/preview";
const useStyles = makeStyles({ const useStyles = makeStyles({
container: { container: {
@ -87,7 +88,11 @@ 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();
@ -95,6 +100,8 @@ export function ListFileComponent() {
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 {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,
@ -115,7 +122,7 @@ export function ListFileComponent() {
useEffect(() => { useEffect(() => {
set_loading(true) set_loading(true)
files_get(conn_active!, bucket_active!, prefix, filter).then(() => { files_get(conn_active!, bucket_active!, prefix, filter).then(() => {
// set_loading(false) set_loading(false)
}) })
}, [conn_active, bucket_active, prefix, filter]); }, [conn_active, bucket_active, prefix, filter]);
@ -134,6 +141,7 @@ 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')
let eleX = ele ? ele.clientWidth : 0 let eleX = ele ? ele.clientWidth : 0
let eleY = ele ? ele.clientHeight : 0 let eleY = ele ? ele.clientHeight : 0
@ -167,7 +175,16 @@ export function ListFileComponent() {
} }
async function handlePreview() { async function handlePreview() {
dispatchMessage('todo', 'warning') 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}> return <div className={styles.container}>
@ -183,7 +200,7 @@ export function ListFileComponent() {
}} }}
icon={<ArrowDownloadFilled/>}></MenuItem> icon={<ArrowDownloadFilled/>}></MenuItem>
<MenuItem <MenuItem
disabled={!CanPreview(file_active ?? '')} disabled={!preview_content_type}
onClick={async () => { onClick={async () => {
await handlePreview() await handlePreview()
}} }}
@ -194,47 +211,40 @@ export function ListFileComponent() {
<div className={styles.loading} style={{display: loading ? 'flex' : 'none'}}> <div className={styles.loading} style={{display: loading ? 'flex' : 'none'}}>
<Spinner appearance="primary" label="加载中..."/> <Spinner appearance="primary" label="加载中..."/>
</div> </div>
{ <div className={styles.no_data} style={{display: (!loading && !files_list.length) ? 'flex' : 'none'}}>
// (!loading) && (files_list.length === 0) ? <div>
// <div className={styles.no_data}> <DocumentDismissRegular/>
// <div> </div>
// <DocumentDismissRegular/> <Text size={900}>
// </div>
// <Text size={900}> </Text>
// 没有文件 </div>
// </Text> <div style={{display: (!loading && files_list.length) ? 'block' : 'none'}}>
// </div> <VirtualizerScrollView
// : <></> numItems={files_list.length}
} itemSize={32}
{ container={{role: 'list', style: {maxHeight: 'calc(100vh - 9rem)'}}}
// (!loading && files_list) ? >
// <VirtualizerScrollView {(idx) => {
// numItems={files_list.length} return <div
// itemSize={32} className={styles.row} key={idx}
// container={{role: 'list', style: {maxHeight: 'calc(100vh - 9rem)'}}} onClick={async () => {
// > await handleClick(files_list[idx])
// {(idx) => { }}
// return <div onContextMenu={async (e) => {
// className={styles.row} key={idx} await handleRightClick(e, files_list[idx])
// onClick={async () => { }}>
// await handleClick(files_list[idx]) <MenuItem className={styles.item}
// }} icon={files_list[idx].type ? <FolderRegular/> :
// onContextMenu={async (e) => { <FileIcon name={files_list[idx].name}/>}>
// await handleRightClick(e, files_list[idx]) <Text truncate wrap={false} className={styles.text}>
// }}> {filename(files_list[idx].key)}
// <MenuItem className={styles.item} </Text>
// icon={files_list[idx].type ? <FolderRegular/> : </MenuItem>
// <FileIcon name={files_list[idx].name}/>}> </div>
// <Text truncate wrap={false} className={styles.text}> }}
// {filename(files_list[idx].key)} </VirtualizerScrollView>
// </Text> </div>
// </MenuItem>
// </div>
// }}
// </VirtualizerScrollView> : <></>
}
<MenuList className={styles.list}>
</MenuList>
</div> </div>
} }

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,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

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

@ -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) {