feat(phase7): add classification source filters
This commit is contained in:
@@ -47,6 +47,13 @@ const classificationOptions = [
|
||||
{ label: 'other', value: 'other' },
|
||||
];
|
||||
|
||||
const sourceOptions = [
|
||||
{ label: '全部来源', value: 'all' },
|
||||
{ label: '自动分类', value: 'auto' },
|
||||
{ label: '手动分类', value: 'manual' },
|
||||
{ label: '未分类', value: 'unclassified' },
|
||||
];
|
||||
|
||||
const batchModeOptions = [
|
||||
{ label: '仅未分类', value: 'unclassified_only' },
|
||||
{ label: '包含已分类重跑', value: 'include_classified' },
|
||||
@@ -83,6 +90,7 @@ const ProjectAssets: React.FC = () => {
|
||||
const [classificationJobId, setClassificationJobId] = useState<string>('');
|
||||
const [classifyingAssetId, setClassifyingAssetId] = useState<string>('');
|
||||
const [classificationFilter, setClassificationFilter] = useState<string>('all');
|
||||
const [classificationSourceFilter, setClassificationSourceFilter] = useState<string>('all');
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||
const [batchMode, setBatchMode] = useState<'unclassified_only' | 'include_classified'>('unclassified_only');
|
||||
const [batchProgress, setBatchProgress] = useState<BatchProgressState>(defaultBatchProgress);
|
||||
@@ -166,9 +174,7 @@ const ProjectAssets: React.FC = () => {
|
||||
const pollBatchJobs = async () => {
|
||||
try {
|
||||
const results = await Promise.all(batchJobIds.map((jobId) => getClassificationJob(jobId)));
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
if (cancelled) return;
|
||||
|
||||
const completedIds = results.filter((job) => job.status === 'completed').map((job) => job.id);
|
||||
const failedIds = results.filter((job) => job.status === 'failed').map((job) => job.id);
|
||||
@@ -230,11 +236,28 @@ const ProjectAssets: React.FC = () => {
|
||||
);
|
||||
|
||||
const filteredAssets = useMemo(() => {
|
||||
const items = assetsQuery.data || [];
|
||||
if (classificationFilter === 'all') return items;
|
||||
if (classificationFilter === 'unclassified') return items.filter((item) => !item.metadata?.classification);
|
||||
return items.filter((item) => item.metadata?.classification === classificationFilter);
|
||||
}, [assetsQuery.data, classificationFilter]);
|
||||
let items = assetsQuery.data || [];
|
||||
|
||||
if (classificationFilter !== 'all') {
|
||||
if (classificationFilter === 'unclassified') {
|
||||
items = items.filter((item) => !item.metadata?.classification);
|
||||
} else {
|
||||
items = items.filter((item) => item.metadata?.classification === classificationFilter);
|
||||
}
|
||||
}
|
||||
|
||||
if (classificationSourceFilter !== 'all') {
|
||||
if (classificationSourceFilter === 'unclassified') {
|
||||
items = items.filter((item) => !item.metadata?.classification);
|
||||
} else if (classificationSourceFilter === 'auto') {
|
||||
items = items.filter((item) => item.metadata?.auto_classification === 'completed');
|
||||
} else if (classificationSourceFilter === 'manual') {
|
||||
items = items.filter((item) => !!item.metadata?.classification && item.metadata?.auto_classification !== 'completed');
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}, [assetsQuery.data, classificationFilter, classificationSourceFilter]);
|
||||
|
||||
const selectedAssets = useMemo(
|
||||
() => filteredAssets.filter((item) => selectedRowKeys.includes(item.id)),
|
||||
@@ -268,15 +291,7 @@ const ProjectAssets: React.FC = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
setBatchProgress({
|
||||
total: selectedAssets.length,
|
||||
submitted: 0,
|
||||
failed: 0,
|
||||
skipped: skippedAssets.length,
|
||||
completed: 0,
|
||||
running: true,
|
||||
tracking: false,
|
||||
});
|
||||
setBatchProgress({ total: selectedAssets.length, submitted: 0, failed: 0, skipped: skippedAssets.length, completed: 0, running: true, tracking: false });
|
||||
setBatchJobIds([]);
|
||||
setBatchCompletedIds([]);
|
||||
setBatchFailedIds([]);
|
||||
@@ -297,27 +312,11 @@ const ProjectAssets: React.FC = () => {
|
||||
} catch {
|
||||
failed += 1;
|
||||
}
|
||||
setBatchProgress({
|
||||
total: selectedAssets.length,
|
||||
submitted,
|
||||
failed,
|
||||
skipped: skippedAssets.length,
|
||||
completed: 0,
|
||||
running: true,
|
||||
tracking: false,
|
||||
});
|
||||
setBatchProgress({ total: selectedAssets.length, submitted, failed, skipped: skippedAssets.length, completed: 0, running: true, tracking: false });
|
||||
}
|
||||
|
||||
setBatchJobIds(createdJobIds);
|
||||
setBatchProgress({
|
||||
total: selectedAssets.length,
|
||||
submitted,
|
||||
failed,
|
||||
skipped: skippedAssets.length,
|
||||
completed: 0,
|
||||
running: createdJobIds.length > 0,
|
||||
tracking: createdJobIds.length > 0,
|
||||
});
|
||||
setBatchProgress({ total: selectedAssets.length, submitted, failed, skipped: skippedAssets.length, completed: 0, running: createdJobIds.length > 0, tracking: createdJobIds.length > 0 });
|
||||
|
||||
if (failed === 0) {
|
||||
message.success(`批量分类已提交:成功 ${submitted},跳过 ${skippedAssets.length}`);
|
||||
@@ -343,7 +342,7 @@ const ProjectAssets: React.FC = () => {
|
||||
<Space>
|
||||
<Tag color="blue">{classification}</Tag>
|
||||
<Tag>{Math.round(confidence * 100)}%</Tag>
|
||||
{autoClassification === 'completed' ? <Tag color="green">自动</Tag> : null}
|
||||
{autoClassification === 'completed' ? <Tag color="green">自动</Tag> : <Tag>手动</Tag>}
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
@@ -418,6 +417,7 @@ const ProjectAssets: React.FC = () => {
|
||||
extra={
|
||||
<Space>
|
||||
<Select style={{ width: 180 }} value={classificationFilter} options={classificationOptions} onChange={setClassificationFilter} />
|
||||
<Select style={{ width: 180 }} value={classificationSourceFilter} options={sourceOptions} onChange={setClassificationSourceFilter} />
|
||||
<Select style={{ width: 180 }} value={batchMode} options={batchModeOptions} onChange={(value) => setBatchMode(value)} />
|
||||
<Select
|
||||
style={{ width: 280 }}
|
||||
|
||||
Reference in New Issue
Block a user