141 lines
4.6 KiB
TypeScript
Executable File
141 lines
4.6 KiB
TypeScript
Executable File
import { Badge } from '@nice/ui/components/badge';
|
||
import { Button } from '@nice/ui/components/button';
|
||
import { Input } from '@nice/ui/components/input';
|
||
import { Label } from '@nice/ui/components/label';
|
||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@nice/ui/components/select';
|
||
import { TabsList, TabsTrigger } from '@nice/ui/components/tabs';
|
||
import { IconSearch } from '@tabler/icons-react';
|
||
import { SORT_OPTIONS, BATCH_ACTIONS } from '@/lib/articles/constants';
|
||
import { TermSelect } from '../selector/term-select';
|
||
import { useArticlesContext } from './context';
|
||
|
||
export function ArticleToolbar() {
|
||
const {
|
||
filters,
|
||
selectedArticles,
|
||
batchAction,
|
||
stats,
|
||
updateFilters,
|
||
setBatchAction,
|
||
handleBatchAction,
|
||
resetSelection,
|
||
} = useArticlesContext();
|
||
|
||
return (
|
||
<div className="bg-background px-6 py-4 ">
|
||
<div className="flex items-center justify-between gap-4">
|
||
{/* 左侧:状态标签页 */}
|
||
<div className="flex items-center">
|
||
<TabsList className="grid w-auto grid-cols-5 h-9">
|
||
<TabsTrigger value="all" className="px-3 text-sm">
|
||
全部{' '}
|
||
<Badge variant="secondary" className="ml-1 text-xs">
|
||
{stats.all}
|
||
</Badge>
|
||
</TabsTrigger>
|
||
<TabsTrigger value="published" className="px-3 text-sm">
|
||
已发布{' '}
|
||
<Badge variant="secondary" className="ml-1 text-xs">
|
||
{stats.published}
|
||
</Badge>
|
||
</TabsTrigger>
|
||
<TabsTrigger value="draft" className="px-3 text-sm">
|
||
草稿{' '}
|
||
<Badge variant="secondary" className="ml-1 text-xs">
|
||
{stats.draft}
|
||
</Badge>
|
||
</TabsTrigger>
|
||
<TabsTrigger value="archived" className="px-3 text-sm">
|
||
已归档{' '}
|
||
<Badge variant="secondary" className="ml-1 text-xs">
|
||
{stats.archived}
|
||
</Badge>
|
||
</TabsTrigger>
|
||
<TabsTrigger value="trash" className="px-3 text-sm">
|
||
已删除{' '}
|
||
<Badge variant="secondary" className="ml-1 text-xs">
|
||
{stats.deleted}
|
||
</Badge>
|
||
</TabsTrigger>
|
||
</TabsList>
|
||
</div>
|
||
|
||
{/* 中间:批量操作 */}
|
||
<div className="flex items-center justify-center">
|
||
{selectedArticles.length > 0 && (
|
||
<div className="flex items-center gap-2">
|
||
<Badge variant="secondary" className="text-sm font-medium">
|
||
{selectedArticles.length} 篇已选
|
||
</Badge>
|
||
<Select value={batchAction} onValueChange={setBatchAction}>
|
||
<SelectTrigger className="w-32 h-9">
|
||
<SelectValue placeholder="批量操作" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
{BATCH_ACTIONS.map((action) => (
|
||
<SelectItem key={action.value} value={action.value}>
|
||
<div className="flex items-center gap-2">
|
||
<action.icon className="h-3 w-3" />
|
||
{action.label}
|
||
</div>
|
||
</SelectItem>
|
||
))}
|
||
</SelectContent>
|
||
</Select>
|
||
<Button size="sm" onClick={handleBatchAction} disabled={!batchAction} className="h-9">
|
||
执行
|
||
</Button>
|
||
<Button size="sm" variant="outline" onClick={resetSelection} className="h-9">
|
||
取消
|
||
</Button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* 右侧:搜索、分类和排序 */}
|
||
<div className="flex items-center gap-3">
|
||
{/* 搜索和分类 */}
|
||
<div className="flex items-center gap-3">
|
||
<div className="relative w-64">
|
||
<IconSearch className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
||
<Input
|
||
placeholder="搜索文章..."
|
||
value={filters.searchTerm}
|
||
onChange={(e) => updateFilters({ searchTerm: e.target.value })}
|
||
className="pl-10 h-9"
|
||
/>
|
||
</div>
|
||
<TermSelect
|
||
taxonomySlug="category"
|
||
value={filters.categoryFilter}
|
||
onValueChange={(value) => updateFilters({ categoryFilter: value as string })}
|
||
placeholder="全部分类"
|
||
className="h-9 w-32"
|
||
allowClear
|
||
/>
|
||
</div>
|
||
|
||
{/* 排序 */}
|
||
<div className="flex items-center gap-2">
|
||
<Label htmlFor="sort-select" className="text-sm font-medium text-muted-foreground whitespace-nowrap">
|
||
排序:
|
||
</Label>
|
||
<Select value={filters.sortBy} onValueChange={(value) => updateFilters({ sortBy: value })}>
|
||
<SelectTrigger id="sort-select" className="w-48 h-9">
|
||
<SelectValue />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
{SORT_OPTIONS.map((option) => (
|
||
<SelectItem key={option.value} value={option.value}>
|
||
{option.label}
|
||
</SelectItem>
|
||
))}
|
||
</SelectContent>
|
||
</Select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|