diff --git a/web/src/components/table/channels/modals/EditChannelModal.jsx b/web/src/components/table/channels/modals/EditChannelModal.jsx
index c13aca13..259553d0 100644
--- a/web/src/components/table/channels/modals/EditChannelModal.jsx
+++ b/web/src/components/table/channels/modals/EditChannelModal.jsx
@@ -1174,27 +1174,27 @@ const EditChannelModal = (props) => {
>
)}
- {isEdit && isMultiKeyChannel && (
-
setKeyMode(value)}
- extraText={
-
- {keyMode === 'replace'
- ? t('覆盖模式:将完全替换现有的所有密钥')
- : t('追加模式:将新密钥添加到现有密钥列表末尾')
- }
-
+ {isEdit && isMultiKeyChannel && (
+ setKeyMode(value)}
+ extraText={
+
+ {keyMode === 'replace'
+ ? t('覆盖模式:将完全替换现有的所有密钥')
+ : t('追加模式:将新密钥添加到现有密钥列表末尾')
}
- />
+
+ }
+ />
)}
{batch && multiToSingle && (
<>
diff --git a/web/src/components/table/channels/modals/ModelTestModal.jsx b/web/src/components/table/channels/modals/ModelTestModal.jsx
index 1d159473..7e845a04 100644
--- a/web/src/components/table/channels/modals/ModelTestModal.jsx
+++ b/web/src/components/table/channels/modals/ModelTestModal.jsx
@@ -175,7 +175,7 @@ const ModelTestModal = ({
{currentTestChannel.name} {t('渠道的模型测试')}
-
+
{t('共')} {currentTestChannel.models.split(',').length} {t('个模型')}
diff --git a/web/src/components/table/models/ModelsActions.jsx b/web/src/components/table/models/ModelsActions.jsx
index 9eacab69..b10d0500 100644
--- a/web/src/components/table/models/ModelsActions.jsx
+++ b/web/src/components/table/models/ModelsActions.jsx
@@ -20,13 +20,15 @@ For commercial licensing, please contact support@quantumnous.com
import React, { useState } from 'react';
import MissingModelsModal from './modals/MissingModelsModal.jsx';
import PrefillGroupManagement from './modals/PrefillGroupManagement.jsx';
-import { Button, Space, Modal } from '@douyinfe/semi-ui';
+import EditPrefillGroupModal from './modals/EditPrefillGroupModal.jsx';
+import { Button, Modal } from '@douyinfe/semi-ui';
+import { showSuccess, showError, copy } from '../../../helpers';
import CompactModeToggle from '../../common/ui/CompactModeToggle';
-import { showError } from '../../../helpers';
import SelectionNotification from './components/SelectionNotification.jsx';
const ModelsActions = ({
selectedKeys,
+ setSelectedKeys,
setEditingModel,
setShowEdit,
batchDeleteModels,
@@ -38,13 +40,11 @@ const ModelsActions = ({
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [showMissingModal, setShowMissingModal] = useState(false);
const [showGroupManagement, setShowGroupManagement] = useState(false);
+ const [showAddPrefill, setShowAddPrefill] = useState(false);
+ const [prefillInit, setPrefillInit] = useState({ id: undefined });
// Handle delete selected models with confirmation
const handleDeleteSelectedModels = () => {
- if (selectedKeys.length === 0) {
- showError(t('请至少选择一个模型!'));
- return;
- }
setShowDeleteModal(true);
};
@@ -54,6 +54,30 @@ const ModelsActions = ({
setShowDeleteModal(false);
};
+ // Handle clear selection
+ const handleClearSelected = () => {
+ setSelectedKeys([]);
+ };
+
+ // Handle add selected models to prefill group
+ const handleCopyNames = async () => {
+ const text = selectedKeys.map(m => m.model_name).join(',');
+ if (!text) return;
+ const ok = await copy(text);
+ if (ok) {
+ showSuccess(t('已复制模型名称'));
+ } else {
+ showError(t('复制失败'));
+ }
+ };
+
+ const handleAddToPrefill = () => {
+ // Prepare initial data
+ const items = selectedKeys.map((m) => m.model_name);
+ setPrefillInit({ id: undefined, type: 'model', items });
+ setShowAddPrefill(true);
+ };
+
return (
<>
@@ -71,7 +95,6 @@ const ModelsActions = ({
{t('添加模型')}
-
setShowGroupManagement(false)}
/>
+
+ setShowAddPrefill(false)}
+ editingGroup={prefillInit}
+ onSuccess={() => setShowAddPrefill(false)}
+ />
>
);
};
diff --git a/web/src/components/table/models/components/SelectionNotification.jsx b/web/src/components/table/models/components/SelectionNotification.jsx
index 6546a257..d886a7c0 100644
--- a/web/src/components/table/models/components/SelectionNotification.jsx
+++ b/web/src/components/table/models/components/SelectionNotification.jsx
@@ -18,7 +18,7 @@ For commercial licensing, please contact support@quantumnous.com
*/
import React, { useEffect } from 'react';
-import { Notification, Button, Space } from '@douyinfe/semi-ui';
+import { Notification, Button, Space, Typography } from '@douyinfe/semi-ui';
// 固定通知 ID,保持同一个实例即可避免闪烁
const NOTICE_ID = 'models-batch-actions';
@@ -28,22 +28,52 @@ const NOTICE_ID = 'models-batch-actions';
* 1. 当 selectedKeys.length > 0 时,使用固定 id 创建/更新通知
* 2. 当 selectedKeys 清空时关闭通知
*/
-const SelectionNotification = ({ selectedKeys = [], t, onDelete }) => {
+const SelectionNotification = ({ selectedKeys = [], t, onDelete, onAddPrefill, onClear, onCopy }) => {
// 根据选中数量决定显示/隐藏或更新通知
useEffect(() => {
const selectedCount = selectedKeys.length;
if (selectedCount > 0) {
+ const titleNode = (
+
+ {t('批量操作')}
+ {t('已选择 {{count}} 个模型', { count: selectedCount })}
+
+ );
+
const content = (
-
- {t('已选择 {{count}} 个模型', { count: selectedCount })}
+
+
+
+
);
@@ -51,7 +81,7 @@ const SelectionNotification = ({ selectedKeys = [], t, onDelete }) => {
// 使用相同 id 更新通知(若已存在则就地更新,不存在则创建)
Notification.info({
id: NOTICE_ID,
- title: t('批量操作'),
+ title: titleNode,
content,
duration: 0, // 不自动关闭
position: 'bottom',
@@ -61,7 +91,7 @@ const SelectionNotification = ({ selectedKeys = [], t, onDelete }) => {
// 取消全部勾选时关闭通知
Notification.close(NOTICE_ID);
}
- }, [selectedKeys, t, onDelete]);
+ }, [selectedKeys, t, onDelete, onAddPrefill, onClear, onCopy]);
// 卸载时确保关闭通知
useEffect(() => {
diff --git a/web/src/components/table/models/index.jsx b/web/src/components/table/models/index.jsx
index 4732e83d..93d63470 100644
--- a/web/src/components/table/models/index.jsx
+++ b/web/src/components/table/models/index.jsx
@@ -42,6 +42,7 @@ const ModelsPage = () => {
// Actions state
selectedKeys,
+ setSelectedKeys,
setEditingModel,
setShowEdit,
batchDeleteModels,
@@ -100,6 +101,7 @@ const ModelsPage = () => {
{
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
@@ -332,23 +322,6 @@ const EditModelModal = (props) => {
showClear
/>
-
- ({ label: g.name, value: g.id }))}
- showClear
- onChange={(value) => {
- const g = tagGroups.find(item => item.id === value);
- if (g && formApiRef.current) {
- formApiRef.current.setValue('tags', g.items || []);
- }
- }}
- style={{ width: '100%' }}
- />
-
-
{
formApiRef.current.setValue('tags', normalized);
}}
style={{ width: '100%' }}
+ extraText={(
+
+ {tagGroups.map(group => (
+
+ ))}
+
+
+ )}
/>
-
-
-
- {/* 供应商信息 */}
-
-
-
-
-
-
-
{t('供应商信息')}
-
{t('设置模型的供应商相关信息')}
-
-
-
{
style={{ width: '100%' }}
/>
-
-
-
- {/* 功能配置 */}
-
-
-
-
-
-
-
{t('功能配置')}
-
{t('设置模型的功能和状态')}
-
-
-
- ({ label: g.name, value: g.id }))}
- showClear
- style={{ width: '100%' }}
- onChange={(value) => {
- const g = endpointGroups.find(item => item.id === value);
- if (g && formApiRef.current) {
- formApiRef.current.setValue('endpoints', g.items || []);
- }
- }}
- />
-
-
-
-
+ {endpointGroups.map(group => (
+
+ ))}
+
+
+ )}
/>
diff --git a/web/src/components/table/models/modals/MissingModelsModal.jsx b/web/src/components/table/models/modals/MissingModelsModal.jsx
index 41ff9d13..f181b112 100644
--- a/web/src/components/table/models/modals/MissingModelsModal.jsx
+++ b/web/src/components/table/models/modals/MissingModelsModal.jsx
@@ -111,7 +111,7 @@ const MissingModelsModal = ({
{t('未配置的模型列表')}
-
+
{t('共')} {missingModels.length} {t('个未配置模型')}
diff --git a/web/src/hooks/models/useModelsData.js b/web/src/hooks/models/useModelsData.js
index 0195858d..b41bdfc2 100644
--- a/web/src/hooks/models/useModelsData.js
+++ b/web/src/hooks/models/useModelsData.js
@@ -328,6 +328,7 @@ export const useModelsData = () => {
selectedKeys,
rowSelection,
handleRow,
+ setSelectedKeys,
// Modal state
showEdit,