feat: files ui improvements

This commit is contained in:
kangfenmao 2024-10-30 20:45:48 +08:00
parent 2313f66ad9
commit b148c5adf5
4 changed files with 173 additions and 32 deletions

View File

@ -162,7 +162,12 @@
"name": "Name", "name": "Name",
"size": "Size", "size": "Size",
"count": "Count", "count": "Count",
"created_at": "Created At" "created_at": "Created At",
"image": "Image",
"text": "Text",
"document": "Document",
"actions": "Actions",
"open": "Open"
}, },
"agents": { "agents": {
"title": "Agents", "title": "Agents",

View File

@ -162,7 +162,12 @@
"name": "文件名", "name": "文件名",
"size": "大小", "size": "大小",
"count": "文件数", "count": "文件数",
"created_at": "创建时间" "created_at": "创建时间",
"image": "图片",
"text": "文本",
"document": "文档",
"actions": "操作",
"open": "打开"
}, },
"agents": { "agents": {
"title": "智能体", "title": "智能体",
@ -336,7 +341,7 @@
"new_topic": "新建话题", "new_topic": "新建话题",
"zoom_in": "放大界面", "zoom_in": "放大界面",
"zoom_out": "缩小界面", "zoom_out": "缩小界面",
"zoom_reset": "<EFBFBD><EFBFBD><EFBFBD>置缩放" "zoom_reset": "置缩放"
} }
}, },
"translate": { "translate": {

View File

@ -162,7 +162,12 @@
"name": "名稱", "name": "名稱",
"size": "大小", "size": "大小",
"count": "數量", "count": "數量",
"created_at": "建立時間" "created_at": "建立時間",
"image": "圖片",
"text": "文本",
"document": "文檔",
"actions": "操作",
"open": "打開"
}, },
"agents": { "agents": {
"title": "智能體", "title": "智能體",

View File

@ -1,45 +1,39 @@
import { FileImageOutlined, FilePdfOutlined, FileTextOutlined } from '@ant-design/icons'
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar' import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
import Scrollbar from '@renderer/components/Scrollbar' import Scrollbar from '@renderer/components/Scrollbar'
import db from '@renderer/databases' import db from '@renderer/databases'
import FileManager from '@renderer/services/FileManager' import FileManager from '@renderer/services/FileManager'
import { FileType, FileTypes } from '@renderer/types' import { FileType, FileTypes } from '@renderer/types'
import { formatFileSize } from '@renderer/utils' import { formatFileSize } from '@renderer/utils'
import { Image, Table } from 'antd' import { Col, Image, Menu, Row, Spin, Table } from 'antd'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useLiveQuery } from 'dexie-react-hooks' import { useLiveQuery } from 'dexie-react-hooks'
import { FC } from 'react' import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
const FilesPage: FC = () => { const FilesPage: FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
const files = useLiveQuery<FileType[]>(() => db.files.orderBy('count').reverse().toArray()) const [fileType, setFileType] = useState<FileTypes>(FileTypes.IMAGE)
const files = useLiveQuery<FileType[]>(() => db.files.where('type').equals(fileType).sortBy('count'), [fileType])
const dataSource = files?.map((file) => { const dataSource = files?.map((file) => {
const isImage = file.type === FileTypes.IMAGE
const ImageView = <Image src={FileManager.getFileUrl(file)} preview={false} style={{ maxHeight: '40px' }} />
console.log(FileManager.getFileUrl(file))
return { return {
key: file.id, key: file.id,
file: isImage ? ImageView : <FileNameText className="text-nowrap">{file.origin_name}</FileNameText>, file: <FileNameText className="text-nowrap">{file.origin_name}</FileNameText>,
name: <a href={'file://' + FileManager.getSafePath(file)}>{file.origin_name}</a>,
size: formatFileSize(file), size: formatFileSize(file),
count: file.count, count: file.count,
created_at: dayjs(file.created_at).format('MM-DD HH:mm') created_at: dayjs(file.created_at).format('MM-DD HH:mm'),
actions: <a href={'file://' + FileManager.getSafePath(file)}>{t('files.open')}</a>
} }
}) })
const columns = [ const columns = [
{
title: t('files.file'),
dataIndex: 'file',
key: 'file',
width: '300px'
},
{ {
title: t('files.name'), title: t('files.name'),
dataIndex: 'name', dataIndex: 'file',
key: 'name' key: 'file'
}, },
{ {
title: t('files.size'), title: t('files.size'),
@ -58,23 +52,66 @@ const FilesPage: FC = () => {
dataIndex: 'created_at', dataIndex: 'created_at',
key: 'created_at', key: 'created_at',
width: '120px' width: '120px'
},
{
title: t('files.actions'),
dataIndex: 'actions',
key: 'actions',
width: '50px'
} }
] ]
const menuItems = [
{ key: FileTypes.IMAGE, label: t('files.image'), icon: <FileImageOutlined /> },
{ key: FileTypes.TEXT, label: t('files.text'), icon: <FileTextOutlined /> },
{ key: FileTypes.DOCUMENT, label: t('files.document'), icon: <FilePdfOutlined /> }
]
return ( return (
<Container> <Container>
<Navbar> <Navbar>
<NavbarCenter style={{ borderRight: 'none' }}>{t('files.title')}</NavbarCenter> <NavbarCenter style={{ borderRight: 'none' }}>{t('files.title')}</NavbarCenter>
</Navbar> </Navbar>
<ContentContainer id="content-container"> <ContentContainer id="content-container">
<SideNav>
<Menu selectedKeys={[fileType]} items={menuItems} onSelect={({ key }) => setFileType(key as FileTypes)} />
</SideNav>
<TableContainer right> <TableContainer right>
<Table {fileType === FileTypes.IMAGE ? (
dataSource={dataSource} <Image.PreviewGroup>
columns={columns} <Row gutter={[16, 16]}>
style={{ width: '100%' }} {files?.map((file) => (
size="small" <Col key={file.id} xs={24} sm={12} md={8} lg={4} xl={3}>
pagination={{ pageSize: 100 }} <ImageWrapper>
/> <LoadingWrapper>
<Spin />
</LoadingWrapper>
<Image
src={FileManager.getFileUrl(file)}
style={{ height: '100%', objectFit: 'cover', cursor: 'pointer' }}
preview={{ mask: false }}
onLoad={(e) => {
const img = e.target as HTMLImageElement
img.parentElement?.classList.add('loaded')
}}
/>
<ImageInfo>
<div>{formatFileSize(file)}</div>
</ImageInfo>
</ImageWrapper>
</Col>
))}
</Row>
</Image.PreviewGroup>
) : (
<Table
dataSource={dataSource}
columns={columns}
style={{ width: '100%' }}
size="small"
pagination={{ pageSize: 100 }}
/>
)}
</TableContainer> </TableContainer>
</ContentContainer> </ContentContainer>
</Container> </Container>
@ -92,9 +129,7 @@ const ContentContainer = styled.div`
display: flex; display: flex;
flex: 1; flex: 1;
flex-direction: row; flex-direction: row;
justify-content: center; min-height: 100%;
height: 100%;
padding: 0 2px;
` `
const TableContainer = styled(Scrollbar)` const TableContainer = styled(Scrollbar)`
@ -107,7 +142,98 @@ const TableContainer = styled(Scrollbar)`
const FileNameText = styled.div` const FileNameText = styled.div`
font-size: 14px; font-size: 14px;
color: var(--color-text); color: var(--color-text);
max-width: 300px; `
const ImageWrapper = styled.div`
position: relative;
aspect-ratio: 1;
overflow: hidden;
border-radius: 8px;
background-color: var(--color-background-soft);
display: flex;
align-items: center;
justify-content: center;
.ant-image {
height: 100%;
width: 100%;
opacity: 0;
transition:
opacity 0.3s ease,
transform 0.3s ease;
&.loaded {
opacity: 1;
}
}
&:hover {
.ant-image.loaded {
transform: scale(1.05);
}
div:last-child {
opacity: 1;
}
}
`
const LoadingWrapper = styled.div`
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--color-background-soft);
`
const ImageInfo = styled.div`
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.6);
color: white;
padding: 5px 8px;
opacity: 0;
transition: opacity 0.3s ease;
font-size: 12px;
> div:first-child {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`
const SideNav = styled.div`
width: var(--assistants-width);
border-right: 0.5px solid var(--color-border);
padding: 15px;
.ant-menu {
border-inline-end: none !important;
background: transparent;
}
.ant-menu-item {
height: 40px;
line-height: 40px;
margin: 4px 0;
width: 100%;
&:hover {
background-color: var(--color-background-soft);
}
&.ant-menu-item-selected {
background-color: var(--color-background-soft);
color: var(--color-primary);
}
}
` `
export default FilesPage export default FilesPage