前端: 完成完整登录和文件管理界面

This commit is contained in:
Frontend Developer 2026-03-10 09:53:55 +00:00
parent 4b395fe2c3
commit 4e448794f7

View File

@ -6,19 +6,31 @@
<title>CloudDisk</title> <title>CloudDisk</title>
<link rel="stylesheet" href="https://unpkg.com/antd@5.12.0/dist/reset.css"> <link rel="stylesheet" href="https://unpkg.com/antd@5.12.0/dist/reset.css">
<style> <style>
body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } * { margin: 0; padding: 0; box-sizing: border-box; }
#root { min-height: 100vh; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.login-container { display: flex; justify-content: center; align-items: center; height: 100vh; background: #f0f2f5; } .login-wrap { display: flex; justify-content: center; align-items: center; height: 100vh; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
.login-box { width: 400px; padding: 40px; background: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); } .login-box { width: 400px; padding: 40px; background: white; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); }
.login-title { text-align: center; margin-bottom: 30px; font-size: 24px; } .logo { text-align: center; margin-bottom: 30px; font-size: 28px; font-weight: bold; color: #1890ff; }
.main-layout { min-height: 100vh; } .main-layout { min-height: 100vh; }
.header { background: #1890ff; padding: 0 20px; display: flex; align-items: center; justify-content: space-between; color: white; font-size: 20px; font-weight: bold; } .header { background: linear-gradient(90deg, #1890ff, #40a9ff); padding: 0 24px; height: 56px; display: flex; align-items: center; justify-content: space-between; color: white; }
.toolbar { padding: 16px; background: #fff; border-bottom: 1px solid #f0f0f0; } .header-title { font-size: 20px; font-weight: bold; }
.file-list { padding: 16px; background: #fff; } .toolbar { padding: 16px 24px; background: #fff; border-bottom: 1px solid #f0f0f0; display: flex; gap: 12px; }
.content { padding: 24px; background: #fff; }
.file-item { display: flex; align-items: center; padding: 12px; border-bottom: 1px solid #f0f0f0; cursor: pointer; transition: background 0.2s; }
.file-item:hover { background: #f5f5f5; }
.file-icon { font-size: 24px; margin-right: 12px; }
.file-info { flex: 1; }
.file-name { font-size: 14px; }
.file-meta { font-size: 12px; color: #999; }
.empty-wrap { text-align: center; padding: 60px 0; color: #999; }
.folder { color: #faad14; }
.pdf { color: #ff4d4f; }
.img { color: #1890ff; }
.doc { color: #1890ff; }
</style> </style>
</head> </head>
<body> <body>
<div id="root">Loading...</div> <div id="root"></div>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script> <script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script> <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
@ -26,76 +38,164 @@
<script src="https://unpkg.com/@ant-design/icons@5.2.6/dist/index-umd.min.js"></script> <script src="https://unpkg.com/@ant-design/icons@5.2.6/dist/index-umd.min.js"></script>
<script> <script>
const { Button, Layout, Menu, Input, Table, Avatar, Spin } = antd; const { Button, Input, Modal, Progress, Message, Breadcrumb, Dropdown, Spin } = antd;
const { FolderOutlined, FileOutlined, UploadOutlined, DeleteOutlined, DownloadOutlined, ShareAltOutlined, UserOutlined, ClockCircleOutlined, HomeOutlined } = icons; const { FolderOutlined, FileOutlined, FilePdfOutlined, FileImageOutlined, FileWordOutlined, FileExcelOutlined, UploadOutlined, FolderAddOutlined, DeleteOutlined, DownloadOutlined, ShareAltOutlined, UserOutlined, SearchOutlined, HomeOutlined, ClockCircleOutlined, CloudOutlined, SettingOutlined, LogoutOutlined } = icons;
const { Header, Sider, Content } = Layout;
// Mock Data
const mockFiles = [
{ id: 1, name: '项目文档', type: 'folder', size: 0, updated_at: '2026-03-10 10:30' },
{ id: 2, name: '产品需求.pdf', type: 'pdf', size: 2500000, updated_at: '2026-03-09 15:20' },
{ id: 3, name: '首页设计.png', type: 'img', size: 3200000, updated_at: '2026-03-08 09:15' },
{ id: 4, name: '会议记录.docx', type: 'doc', size: 156000, updated_at: '2026-03-07 14:30' },
{ id: 5, name: '数据报表.xlsx', type: 'xls', size: 450000, updated_at: '2026-03-06 11:00' },
];
// Get Icon by type
const getIcon = (type, name) => {
if (type === 'folder') return React.createElement(FolderOutlined, { className: 'file-icon folder' });
const ext = name.split('.').pop();
const iconMap = {
pdf: FilePdfOutlined,
img: FileImageOutlined,
doc: FileWordOutlined,
docx: FileWordOutlined,
xls: FileExcelOutlined,
xlsx: FileExcelOutlined,
};
const Icon = iconMap[ext] || FileOutlined;
return React.createElement(Icon, { className: 'file-icon ' + (ext === 'pdf' ? 'pdf' : ext === 'img' ? 'img' : 'doc') });
};
// Format size
const formatSize = (bytes) => {
if (!bytes) return '-';
return (bytes / 1024 / 1024).toFixed(2) + ' MB';
};
// App Component // App Component
function App() { function App() {
const [page, setPage] = React.useState('files');
const [isLoggedIn, setIsLoggedIn] = React.useState(false); const [isLoggedIn, setIsLoggedIn] = React.useState(false);
const [loading, setLoading] = React.useState(false);
const [username, setUsername] = React.useState(''); const [username, setUsername] = React.useState('');
const [password, setPassword] = React.useState(''); const [password, setPassword] = React.useState('');
const [files, setFiles] = React.useState(mockFiles);
const [selectedFiles, setSelectedFiles] = React.useState([]);
const [uploadModal, setUploadModal] = React.useState(false);
const [searchKeyword, setSearchKeyword] = React.useState('');
const handleLogin = () => { const handleLogin = () => { if (username && password) setIsLoggedIn(true); };
if (username && password) { const handleLogout = () => { setIsLoggedIn(false); };
setIsLoggedIn(true);
} const filteredFiles = searchKeyword
}; ? files.filter(f => f.name.includes(searchKeyword))
: files;
const menuItems = [ const menuItems = [
{ key: 'files', icon: React.createElement(FolderOutlined), label: '我的文件' }, { key: 'files', icon: React.createElement(FolderOutlined), label: '我的文件', onClick: () => setPage('files') },
{ key: 'recent', icon: React.createElement(ClockCircleOutlined), label: '最近访问' }, { key: 'recent', icon: React.createElement(ClockCircleOutlined), label: '最近访问', onClick: () => setPage('recent') },
{ key: 'shared', icon: React.createElement(ShareAltOutlined), label: '共享文件' }, { key: 'shared', icon: React.createElement(ShareAltOutlined), label: '共享文件', onClick: () => setPage('shared') },
{ key: 'trash', icon: React.createElement(DeleteOutlined), label: '回收站' }, { key: 'trash', icon: React.createElement(DeleteOutlined), label: '回收站', onClick: () => setPage('trash') },
]; ];
// Login Page
if (!isLoggedIn) { if (!isLoggedIn) {
return React.createElement('div', { className: 'login-container' }, return React.createElement('div', { className: 'login-wrap' },
React.createElement('div', { className: 'login-box' }, React.createElement('div', { className: 'login-box' },
React.createElement('h1', { className: 'login-title' }, 'CloudDisk'), React.createElement('div', { className: 'logo' }, '☁️ CloudDisk'),
React.createElement(Input, { React.createElement(Input, { placeholder: '用户名', value: username, onChange: e => setUsername(e.target.value), style: { marginBottom: 16 }, prefix: React.createElement(UserOutlined) }),
placeholder: '用户名', React.createElement(Input.Password, { placeholder: '密码', value: password, onChange: e => setPassword(e.target.value), style: { marginBottom: 16 } }),
value: username, React.createElement(Button, { type: 'primary', block: true, onClick: handleLogin, style: { height: 40, fontSize: 16 } }, '登 录'),
onChange: (e) => setUsername(e.target.value), React.createElement('p', { style: { textAlign: 'center', marginTop: 16, color: '#999', fontSize: 12 } }, '还没有账号?立即注册')
style: { marginBottom: '16px' }
}),
React.createElement(Input.Password, {
placeholder: '密码',
value: password,
onChange: (e) => setPassword(e.target.value),
style: { marginBottom: '16px' }
}),
React.createElement(Button, {
type: 'primary',
block: true,
onClick: handleLogin
}, '登录')
) )
); );
} }
return React.createElement(Layout, { className: 'main-layout' }, // Main App
React.createElement(Header, { className: 'header' }, return React.createElement('div', { className: 'main-layout' },
React.createElement('span', null, 'CloudDisk'), // Header
React.createElement(Avatar, { icon: React.createElement(UserOutlined), style: { cursor: 'pointer' } }) React.createElement('div', { className: 'header' },
React.createElement('span', { className: 'header-title' }, 'CloudDisk'),
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 16 } },
React.createElement(Input, { placeholder: '搜索文件...', prefix: React.createElement(SearchOutlined), style: { width: 200, background: 'rgba(255,255,255,0.2)', border: 'none', color: 'white' } }),
React.createElement(Button, { type: 'text', icon: React.createElement(SettingOutlined), style: { color: 'white' } }),
React.createElement(Button, { type: 'text', icon: React.createElement(LogoutOutlined), onClick: handleLogout, style: { color: 'white' } })
)
), ),
React.createElement(Layout, {},
React.createElement(Sider, { width: 200, style: { background: '#fff' } }, // Content
React.createElement(Menu, { mode: 'inline', defaultSelectedKeys: ['files'], items: menuItems, style: { height: '100%' } }) React.createElement('div', { style: { display: 'flex' } },
), // Sidebar
React.createElement(Content, { style: { padding: '20px', background: '#fff' } }, React.createElement('div', { style: { width: 200, background: '#fff', borderRight: '1px solid #f0f0f0', padding: '16px 0' } },
React.createElement('div', { className: 'toolbar' }, ...menuItems.map(item =>
React.createElement(Button, { icon: React.createElement(UploadOutlined), style: { marginRight: '8px' } }, '上传文件'), React.createElement('div', {
React.createElement(Button, { icon: React.createElement(FolderOutlined) }, '新建文件夹') key: item.key,
onClick: item.onClick,
style: {
padding: '12px 24px',
cursor: 'pointer',
background: page === item.key ? '#e6f7ff' : 'transparent',
color: page === item.key ? '#1890ff' : '#666',
display: 'flex', alignItems: 'center', gap: 8
}
},
React.createElement(item.icon),
React.createElement('span', null, item.label)
)
), ),
React.createElement('p', { style: { color: '#999', textAlign: 'center', marginTop: '50px' } }, '暂无文件,请上传文件开始使用') React.createElement('div', { style: { padding: '16px 24px', borderTop: '1px solid #f0f0f0', marginTop: 'auto' } },
React.createElement(CloudOutlined, { style: { marginRight: 8 } }),
React.createElement('span', { fontSize: 12, color: '#999' }, '2.1GB / 10GB')
)
),
// File List
React.createElement('div', { style: { flex: 1, background: '#f5f5f5', minHeight: 'calc(100vh - 56px)' } },
// Toolbar
React.createElement('div', { className: 'toolbar', style: { background: '#fff', marginBottom: 2 } },
React.createElement(Button, { icon: React.createElement(UploadOutlined), type: 'primary', onClick: () => setUploadModal(true) }, '上传文件'),
React.createElement(Button, { icon: React.createElement(FolderAddOutlined) }, '新建文件夹'),
React.createElement(Button, { icon: React.createElement(DeleteOutlined), disabled: selectedFiles.length === 0 }, '删除'),
React.createElement(Button, { icon: React.createElement(DownloadOutlined), disabled: selectedFiles.length === 0 }, '下载'),
React.createElement(Button, { icon: React.createElement(ShareAltOutlined), disabled: selectedFiles.length === 0 }, '分享')
),
// Breadcrumb
React.createElement(Breadcrumb, { style: { padding: '12px 24px', background: '#fff' },
React.createElement(Breadcrumb.Item, null, React.createElement(HomeOutlined), ' 全部文件')
),
// Files
React.createElement('div', { style: { background: '#fff', margin: '0 24px 24px', borderRadius: 8 } },
filteredFiles.length === 0
? React.createElement('div', { className: 'empty-wrap' }, React.createElement(FolderOutlined, { style: { fontSize: 48, marginBottom: 16 } }), React.createElement('p', null, '暂无文件'))
: filteredFiles.map(file =>
React.createElement('div', { key: file.id, className: 'file-item' },
getIcon(file.type, file.name),
React.createElement('div', { className: 'file-info' },
React.createElement('div', { className: 'file-name' }, file.name),
React.createElement('div', { className: 'file-meta' }, file.updated_at + ' · ' + formatSize(file.size))
)
)
)
)
)
),
// Upload Modal
React.createElement(Modal, {
title: '上传文件',
open: uploadModal,
onCancel: () => setUploadModal(false),
footer: null
},
React.createElement('div', { padding: '40px 0', textAlign: 'center', border: '2px dashed #d9d9d9', borderRadius: 8 },
React.createElement(UploadOutlined, { style: { fontSize: 48, color: '#1890ff' } }),
React.createElement('p', { marginTop: 16 }, '点击或拖拽文件到此处上传')
) )
) )
); );
} }
// Render
ReactDOM.createRoot(document.getElementById('root')).render(React.createElement(App)); ReactDOM.createRoot(document.getElementById('root')).render(React.createElement(App));
</script> </script>
</body> </body>