diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 4ffdfe2..aaaeec7 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -13,6 +13,7 @@ const Icon = ({ name, size = 16 }) => { edit: , trash: , x: , + settings: , } return icons[name] || icons.globe } @@ -24,7 +25,11 @@ function App() { const [searchQuery, setSearchQuery] = useState('') const [modal, setModal] = useState({ show: false, type: 'website', data: null }) const [loading, setLoading] = useState(true) - const [form, setForm] = useState({ name: '', url: '', icon: '', description: '', category_id: '', is_favorite: false, color: '#667eea', icon_name: 'folder' }) + const [showCategoryManager, setShowCategoryManager] = useState(false) + const [form, setForm] = useState({ + name: '', url: '', icon: '', description: '', category_id: '', is_favorite: false, + color: '#667eea', icon_name: 'folder', sort_order: 0 + }) useEffect(() => { loadData() }, []) @@ -32,8 +37,8 @@ function App() { setLoading(true) try { const [c, w] = await Promise.all([categoryApi.getAll(), websiteApi.getAll()]) - setCategories(c.data) - setWebsites(w.data) + setCategories(c.data || []) + setWebsites(w.data || []) } catch (e) { console.error(e) } setLoading(false) } @@ -41,7 +46,10 @@ function App() { // 过滤网站 const filteredWebsites = websites.filter(site => { const matchCat = selectedCategory === null || site.category_id === selectedCategory - const matchSearch = !searchQuery || site.name.includes(searchQuery) || (site.description && site.description.includes(searchQuery)) + const query = searchQuery.toLowerCase().trim() + const matchSearch = !query || + (site.name && site.name.toLowerCase().includes(query)) || + (site.description && site.description.toLowerCase().includes(query)) return matchCat && matchSearch }) @@ -55,9 +63,14 @@ function App() { // 打开弹窗 const openModal = (type, data = null) => { if (type === 'website') { - setForm(data ? { ...data, category_id: data.category_id || '', is_favorite: !!data.is_favorite } : { name: '', url: '', icon: '', description: '', category_id: '', is_favorite: false }) + setForm(data ? { ...data, category_id: data.category_id || '', is_favorite: !!data.is_favorite } : { name: '', url: '', icon: '', description: '', category_id: '', is_favorite: false, color: '#667eea', icon_name: 'folder', sort_order: 0 }) } else { - setForm(data ? { name: data.name, icon_name: data.icon || 'folder', color: data.color || '#667eea' } : { name: '', icon_name: 'folder', color: '#667eea' }) + setForm(data ? { + name: data.name, + icon_name: data.icon || 'folder', + color: data.color || '#667eea', + sort_order: data.sort_order || 0 + } : { name: '', icon_name: 'folder', color: '#667eea', sort_order: 0 }) } setModal({ show: true, type, data }) } @@ -71,7 +84,12 @@ function App() { const payload = { ...form, category_id: form.category_id || null } modal.data ? await websiteApi.update(modal.data.id, payload) : await websiteApi.create(payload) } else { - const payload = { name: form.name, icon: form.icon_name, color: form.color } + const payload = { + name: form.name, + icon: form.icon_name, + color: form.color, + sort_order: parseInt(form.sort_order) || 0 + } modal.data ? await categoryApi.update(modal.data.id, payload) : await categoryApi.create(payload) } closeModal() @@ -135,7 +153,12 @@ function App() {
- + +
@@ -146,7 +169,7 @@ function App() { setSearchQuery(e.target.value)} /> @@ -187,9 +210,9 @@ function App() { {filteredWebsites.length === 0 && (
-
暂无网站
-
点击左侧「添加网站」开始收藏你的第一个链接
- +
{searchQuery ? '没有找到匹配的网站' : '暂无网站'}
+
{searchQuery ? '尝试其他关键词' : '点击左侧「添加网站」开始收藏你的第一个链接'}
+ {!searchQuery && }
)} @@ -215,7 +238,7 @@ function App() {
- setForm({...form, description: e.target.value})} placeholder="简短描述"/> + setForm({...form, description: e.target.value})} placeholder="简短描述"/>
@@ -237,16 +260,32 @@ function App() { setForm({...form, name: e.target.value})} placeholder="分类名称"/>
+
+ + setForm({...form, sort_order: e.target.value})} placeholder="0"/> +
setForm({...form, color: e.target.value})}/>
+ {modal.data && ( +
+ +
+ )} )}
- +
@@ -259,26 +298,19 @@ function App() { function SiteCard({ site, onClick, onToggleFav, onEdit, onDelete }) { const handleDelete = (e) => { e.stopPropagation() - e.preventDefault() onDelete() } - const handleEdit = (e) => { e.stopPropagation() onEdit() } - const handleFav = (e) => { e.stopPropagation() onToggleFav() } - const handleCardClick = () => { - onClick() - } - return ( -
+
{site.category && {site.category.name}}
{site.icon && site.icon.startsWith('http') ? : }