2e14873f2d
Track application source and tests; exclude local env, user workspaces, and runtime data via .gitignore. Co-authored-by: Cursor <cursoragent@cursor.com>
64 lines
2.1 KiB
TypeScript
64 lines
2.1 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { fetchFeatured, removeFeatured, setFeatured } from '../api/client';
|
|
|
|
export function FeaturedPage() {
|
|
const [items, setItems] = useState<Awaited<ReturnType<typeof fetchFeatured>>['items']>([]);
|
|
const [postId, setPostId] = useState('');
|
|
const [position, setPosition] = useState('trending');
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const load = async () => {
|
|
try {
|
|
const data = await fetchFeatured();
|
|
setItems(data.items);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : '加载失败');
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
void load();
|
|
}, []);
|
|
|
|
return (
|
|
<div className="grid">
|
|
<div className="card grid">
|
|
<h3>添加精选</h3>
|
|
<input value={postId} onChange={(e) => setPostId(e.target.value)} placeholder="帖子 ID" />
|
|
<select value={position} onChange={(e) => setPosition(e.target.value)}>
|
|
<option value="homepage_banner">首页轮播</option>
|
|
<option value="trending">热门趋势</option>
|
|
<option value="category_top">分类置顶</option>
|
|
</select>
|
|
<button
|
|
type="button"
|
|
className="btn"
|
|
onClick={() =>
|
|
void setFeatured({ post_id: postId.trim(), position })
|
|
.then(() => {
|
|
setPostId('');
|
|
return load();
|
|
})
|
|
.catch((err) => setError(err instanceof Error ? err.message : '添加失败'))
|
|
}
|
|
>
|
|
保存
|
|
</button>
|
|
</div>
|
|
{error ? <p className="alert">{error}</p> : null}
|
|
{items.map((item) => (
|
|
<div key={item.id} className="card" style={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
<div>
|
|
<strong>{item.position}</strong>
|
|
<p>{item.title}</p>
|
|
<p style={{ color: '#68716c', fontSize: 12 }}>{item.author}</p>
|
|
</div>
|
|
<button type="button" className="btn secondary" onClick={() => void removeFeatured(item.id).then(load)}>
|
|
移除
|
|
</button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|