refactor(web): drop per-model checkbox selection from batch actions (#18233)

This commit is contained in:
chanx
2026-08-14 09:49:36 +08:00
committed by GitHub
parent 58abab50cc
commit 5e871986e2
5 changed files with 24 additions and 115 deletions

View File

@@ -2372,11 +2372,9 @@ Example: Virtual Hosted Style`,
'Please select at least one model before verification.',
addCustomModel: 'Add custom model',
addCustomModelTitle: 'Add custom model',
batchAddModels: 'Add all visible models',
batchRemoveModels: 'Remove all visible models',
batchAddModels: 'Add all models',
batchRemoveModels: 'Remove all models',
batchVerifyModels: 'Batch verify',
selectAllFiltered: 'Select all',
batchVerifySelected: 'Verify {{count}} model(s)',
editCustomModelTitle: 'Edit model',
modelMaxTokens: 'Max tokens',
modelFeatures: 'Model features',

View File

@@ -2007,11 +2007,9 @@ NER使用 spaCy NER 和基于规则的关键词提取来抽取 Entities 和 R
selectModelBeforeVerify: '请至少选择一个模型后再验证。',
addCustomModel: '添加自定义模型',
addCustomModelTitle: '添加自定义模型',
batchAddModels: '批量添加当前模型',
batchRemoveModels: '批量移除当前模型',
batchAddModels: '批量添加模型',
batchRemoveModels: '批量移除模型',
batchVerifyModels: '批量验证',
selectAllFiltered: '全选',
batchVerifySelected: '验证 {{count}} 个模型',
editCustomModelTitle: '编辑模型',
modelMaxTokens: '最大 Token 数',
modelTypes: {

View File

@@ -14,7 +14,6 @@
* limitations under the License.
*/
import { Checkbox } from '@/components/ui/checkbox';
import { Minus, Plus } from 'lucide-react';
import { ModelRowProps } from '../interface';
import { ModelTypeBadges } from './model-type-badges';
@@ -26,8 +25,6 @@ export function ModelRow({
isAdded,
verifyStatus,
hideActions,
isSelected,
onToggleSelect,
onVerify,
onAdd,
onRemove,
@@ -42,13 +39,6 @@ export function ModelRow({
>
<div className="flex gap-1 min-w-0">
<div className="flex items-center gap-2 min-w-0">
{onToggleSelect && (
<Checkbox
checked={isSelected ?? false}
onCheckedChange={onToggleSelect}
aria-label={`Select ${model.name}`}
/>
)}
<span className="font-medium text-sm text-text-primary truncate">
{model.name}
</span>

View File

@@ -15,20 +15,12 @@
*/
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { SearchInput } from '@/components/ui/input';
import { useCommonTranslation, useTranslate } from '@/hooks/common-hooks';
import { useFetchInstanceModels } from '@/hooks/use-llm-request';
import { IProviderModelItem } from '@/interfaces/request/llm';
import {
ListMinus,
ListPlus,
Loader2,
Plus,
Search,
ShieldCheck,
} from 'lucide-react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Loader2, Plus, Search, ShieldCheck } from 'lucide-react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { AddCustomModelDialog } from '../add-custom-model-dialog';
import { mapModelKey } from '../available-models';
@@ -178,50 +170,9 @@ export function ModelsSection(props: ModelsSectionProps) {
verifyTransform,
});
// 6a. Model selection for batch verify.
const [selectedModels, setSelectedModels] = useState<Set<string>>(new Set());
const toggleModel = useCallback((name: string) => {
setSelectedModels((prev) => {
const next = new Set(prev);
if (next.has(name)) {
next.delete(name);
} else {
next.add(name);
}
return next;
});
}, []);
const toggleAllFiltered = useCallback(() => {
setSelectedModels((prev) => {
const allSelected = filteredModels.every((m) => prev.has(m.name));
const next = new Set(prev);
if (allSelected) {
filteredModels.forEach((m) => next.delete(m.name));
} else {
filteredModels.forEach((m) => next.add(m.name));
}
return next;
});
}, [filteredModels]);
const filteredSelectedCount = useMemo(
() => filteredModels.filter((m) => selectedModels.has(m.name)).length,
[filteredModels, selectedModels],
);
const selectAllChecked: boolean | 'indeterminate' =
filteredSelectedCount === 0
? false
: filteredSelectedCount === filteredModels.length
? true
: 'indeterminate';
const handleBatchVerifyClick = useCallback(() => {
const selected = filteredModels.filter((m) => selectedModels.has(m.name));
handleBatchVerify(selected);
}, [filteredModels, selectedModels, handleBatchVerify]);
handleBatchVerify(filteredModels);
}, [filteredModels, handleBatchVerify]);
// 7. Add / remove / batch toggle / custom add.
const {
@@ -327,33 +278,6 @@ export function ModelsSection(props: ModelsSectionProps) {
placeholder={t('setting.search')}
rootClassName="flex-1"
/>
{!hideActions && (
<Button
variant="outline"
size="icon-sm"
onClick={handleBatchToggleModels}
disabled={batchLoading || filteredModels.length === 0}
data-testid="models-batch-toggle"
aria-label={
allFilteredAdded
? tSetting('batchRemoveModels')
: tSetting('batchAddModels')
}
title={
allFilteredAdded
? tSetting('batchRemoveModels')
: tSetting('batchAddModels')
}
>
{batchLoading ? (
<Loader2 className="size-4 animate-spin" />
) : allFilteredAdded ? (
<ListMinus className="size-4" />
) : (
<ListPlus className="size-4" />
)}
</Button>
)}
</div>
<div className="flex flex-wrap gap-1.5">
<TagFilterButton
@@ -377,20 +301,11 @@ export function ModelsSection(props: ModelsSectionProps) {
</div>
<div className="flex items-center gap-2">
<Checkbox
checked={selectAllChecked}
onCheckedChange={toggleAllFiltered}
disabled={batchVerifying || filteredModels.length === 0}
aria-label={tSetting('selectAllFiltered')}
/>
<span className="text-sm text-text-secondary">
{tSetting('selectAllFiltered')}
</span>
<Button
variant="outline"
size="sm"
onClick={handleBatchVerifyClick}
disabled={selectedModels.size === 0 || batchVerifying}
disabled={batchVerifying || filteredModels.length === 0}
data-testid="models-batch-verify"
className="ml-auto"
>
@@ -399,8 +314,22 @@ export function ModelsSection(props: ModelsSectionProps) {
) : (
<ShieldCheck className="size-3" />
)}
{tSetting('batchVerifySelected', { count: selectedModels.size })}
{tSetting('batchVerifyModels')}
</Button>
{!hideActions && (
<Button
variant="outline"
size="sm"
onClick={handleBatchToggleModels}
disabled={batchLoading || filteredModels.length === 0}
data-testid="models-batch-toggle"
>
{batchLoading && <Loader2 className="size-3 animate-spin" />}
{allFilteredAdded
? tSetting('batchRemoveModels')
: tSetting('batchAddModels')}
</Button>
)}
</div>
<div className="bg-bg-card rounded-lg max-h-80 overflow-auto scrollbar-auto border border-border-button">
@@ -418,8 +347,6 @@ export function ModelsSection(props: ModelsSectionProps) {
isAdded={addedSet.has(model.name)}
verifyStatus={verify[model.name] ?? 'idle'}
hideActions={hideActions}
isSelected={selectedModels.has(model.name)}
onToggleSelect={() => toggleModel(model.name)}
onVerify={() => handleVerify(model)}
onAdd={() => handleAddModel(model)}
onRemove={() => handleRemoveModel(model)}

View File

@@ -138,10 +138,6 @@ export interface ModelRowProps {
onRemove: () => void;
onEdit: () => void;
editLabel: string;
/** Whether this row is currently selected for batch operations. */
isSelected?: boolean;
/** Toggle this row's selection state. */
onToggleSelect?: () => void;
}
export interface TagFilterButtonProps {