Spaces:
Sleeping
Sleeping
Update components/ModelConfigManager.tsx
Browse files- components/ModelConfigManager.tsx +111 -74
components/ModelConfigManager.tsx
CHANGED
|
@@ -23,7 +23,8 @@ import {
|
|
| 23 |
EyeOff,
|
| 24 |
Star,
|
| 25 |
StarOff,
|
| 26 |
-
Shield
|
|
|
|
| 27 |
} from 'lucide-react';
|
| 28 |
|
| 29 |
interface ModelConfigManagerProps {
|
|
@@ -101,6 +102,19 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 101 |
}));
|
| 102 |
};
|
| 103 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
// ============ 渠道管理 ============
|
| 105 |
|
| 106 |
const handleSaveChannel = () => {
|
|
@@ -133,13 +147,6 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 133 |
};
|
| 134 |
|
| 135 |
const handleDeleteChannel = (id: string) => {
|
| 136 |
-
// 检查是否是预置渠道
|
| 137 |
-
const channel = channels.find(ch => ch.id === id);
|
| 138 |
-
if (channel?.isPreset) {
|
| 139 |
-
showMessage('error', '预置渠道不能删除');
|
| 140 |
-
return;
|
| 141 |
-
}
|
| 142 |
-
|
| 143 |
// 检查是否有模型使用此渠道
|
| 144 |
const modelsUsingChannel = models.filter(model => model.channelId === id);
|
| 145 |
if (modelsUsingChannel.length > 0) {
|
|
@@ -170,6 +177,18 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 170 |
}
|
| 171 |
};
|
| 172 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
// ============ 模型管理 ============
|
| 174 |
|
| 175 |
const handleSaveModel = () => {
|
|
@@ -310,7 +329,7 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 310 |
};
|
| 311 |
|
| 312 |
const handleReset = () => {
|
| 313 |
-
if (window.confirm('
|
| 314 |
try {
|
| 315 |
ModelConfigManager.resetToDefaults();
|
| 316 |
loadConfig();
|
|
@@ -489,6 +508,7 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 489 |
onClick={() => setEditingChannel({
|
| 490 |
isNew: true,
|
| 491 |
isDefault: false,
|
|
|
|
| 492 |
timeout: 30000,
|
| 493 |
baseUrl: 'https://api.openai.com/v1'
|
| 494 |
})}
|
|
@@ -509,32 +529,39 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 509 |
{channel.isDefault && (
|
| 510 |
<Star size={16} className="text-yellow-400" title="默认渠道" />
|
| 511 |
)}
|
| 512 |
-
{channel.
|
| 513 |
-
<Shield size={16} className="text-green-400" title="
|
| 514 |
)}
|
| 515 |
</div>
|
| 516 |
<p className="text-gray-400 text-sm">{channel.baseUrl}</p>
|
| 517 |
<div className="flex items-center space-x-2 mt-2">
|
| 518 |
<Key size={12} className="text-gray-500" />
|
| 519 |
<span className="text-gray-500 text-xs">
|
| 520 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 521 |
? channel.apiKey || '未设置'
|
| 522 |
: '••••••••••••••••••••••••••••••••••••••••'
|
| 523 |
}
|
| 524 |
</span>
|
| 525 |
-
|
| 526 |
-
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
|
|
|
|
|
|
| 531 |
</div>
|
| 532 |
{channel.description && (
|
| 533 |
<p className="text-gray-500 text-xs mt-1">{channel.description}</p>
|
| 534 |
)}
|
| 535 |
</div>
|
| 536 |
<div className="flex space-x-1">
|
| 537 |
-
{!channel.isDefault && !channel.
|
| 538 |
<button
|
| 539 |
onClick={() => handleSetDefaultChannel(channel.id)}
|
| 540 |
className="p-1 text-gray-400 hover:text-yellow-400"
|
|
@@ -543,14 +570,16 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 543 |
<StarOff size={16} />
|
| 544 |
</button>
|
| 545 |
)}
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
|
| 549 |
-
|
| 550 |
-
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
|
|
|
|
|
|
| 554 |
<button
|
| 555 |
onClick={() => handleDeleteChannel(channel.id)}
|
| 556 |
className="p-1 text-gray-400 hover:text-red-400"
|
|
@@ -573,7 +602,6 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 573 |
<div className="p-4">
|
| 574 |
<h3 className="text-lg font-medium text-white mb-4">
|
| 575 |
{editingChannel.isNew ? '添加新渠道' : '编辑渠道'}
|
| 576 |
-
{editingChannel.isPreset && <span className="ml-2 text-sm text-green-400">(预置渠道)</span>}
|
| 577 |
</h3>
|
| 578 |
|
| 579 |
{validationErrors.length > 0 && (
|
|
@@ -588,6 +616,19 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 588 |
</div>
|
| 589 |
)}
|
| 590 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 591 |
<div className="space-y-4">
|
| 592 |
<div>
|
| 593 |
<label className="block text-gray-300 text-sm mb-1">渠道名称</label>
|
|
@@ -608,28 +649,32 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 608 |
onChange={(e) => setEditingChannel({ ...editingChannel, baseUrl: e.target.value })}
|
| 609 |
className="w-full bg-gray-700 text-white p-2 rounded text-sm"
|
| 610 |
placeholder="https://api.openai.com/v1"
|
| 611 |
-
disabled={editingChannel.isPreset}
|
| 612 |
/>
|
| 613 |
-
{editingChannel.isPreset && (
|
| 614 |
-
<p className="text-gray-500 text-xs mt-1">预置渠道的URL不能修改</p>
|
| 615 |
-
)}
|
| 616 |
</div>
|
| 617 |
|
| 618 |
<div>
|
| 619 |
-
<label className="block text-gray-300 text-sm mb-1">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 620 |
<div className="relative">
|
| 621 |
-
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
|
| 625 |
-
|
| 626 |
-
|
| 627 |
-
|
| 628 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 629 |
</div>
|
| 630 |
-
{editingChannel.isPreset && (
|
| 631 |
-
<p className="text-gray-500 text-xs mt-1">预置渠道的密钥不能修改</p>
|
| 632 |
-
)}
|
| 633 |
</div>
|
| 634 |
|
| 635 |
<div>
|
|
@@ -654,19 +699,17 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 654 |
/>
|
| 655 |
</div>
|
| 656 |
|
| 657 |
-
|
| 658 |
-
<
|
| 659 |
-
<
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
|
| 664 |
-
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
|
| 668 |
-
</div>
|
| 669 |
-
)}
|
| 670 |
|
| 671 |
<div className="flex justify-end space-x-2 pt-4">
|
| 672 |
<button
|
|
@@ -710,7 +753,7 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 710 |
category: 'GPT-4系列',
|
| 711 |
maxTokens: 4096,
|
| 712 |
temperature: 0.7,
|
| 713 |
-
channelId: channels.
|
| 714 |
})}
|
| 715 |
className="px-3 py-1 bg-sky-600 hover:bg-sky-700 text-white rounded text-sm flex items-center space-x-1"
|
| 716 |
>
|
|
@@ -731,7 +774,6 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 731 |
<p className="text-gray-500 text-xs">{model.category}</p>
|
| 732 |
<p className="text-gray-500 text-xs">
|
| 733 |
渠道: {channel?.name || '未找到渠道'}
|
| 734 |
-
{channel?.isPreset && <span className="ml-1 text-green-400">(预置)</span>}
|
| 735 |
</p>
|
| 736 |
</div>
|
| 737 |
<div className="flex space-x-1">
|
|
@@ -827,7 +869,7 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 827 |
<option value="">选择渠道</option>
|
| 828 |
{channels.map((channel) => (
|
| 829 |
<option key={channel.id} value={channel.id}>
|
| 830 |
-
{channel.name}{channel.
|
| 831 |
</option>
|
| 832 |
))}
|
| 833 |
</select>
|
|
@@ -959,7 +1001,9 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 959 |
</p>
|
| 960 |
<p className="text-gray-500 text-xs">
|
| 961 |
渠道: {associatedChannel?.name || '未找到渠道'}
|
| 962 |
-
{associatedChannel?.
|
|
|
|
|
|
|
| 963 |
</p>
|
| 964 |
</div>
|
| 965 |
<div className="flex space-x-1">
|
|
@@ -1033,7 +1077,8 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 1033 |
const channel = channels.find(ch => ch.id === model.channelId);
|
| 1034 |
return (
|
| 1035 |
<option key={model.id} value={model.id}>
|
| 1036 |
-
{model.name} ({channel?.name || '未知渠道'}
|
|
|
|
| 1037 |
</option>
|
| 1038 |
);
|
| 1039 |
})}
|
|
@@ -1120,7 +1165,7 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 1120 |
<div className="space-y-2">
|
| 1121 |
<div className="flex justify-between text-sm">
|
| 1122 |
<span className="text-gray-300">API渠道:</span>
|
| 1123 |
-
<span className="text-white">{storageInfo.channels}
|
| 1124 |
</div>
|
| 1125 |
<div className="flex justify-between text-sm">
|
| 1126 |
<span className="text-gray-300">模型数量:</span>
|
|
@@ -1140,17 +1185,9 @@ const ModelConfigManagerComponent: React.FC<ModelConfigManagerProps> = ({
|
|
| 1140 |
<p className="text-gray-300 text-sm">
|
| 1141 |
所有配置数据存储在浏览器的localStorage中。清除浏览器数据可能会导致配置丢失。建议定期导出配置进行备份。
|
| 1142 |
</p>
|
| 1143 |
-
|
| 1144 |
-
|
| 1145 |
-
|
| 1146 |
-
<Shield size={16} className="text-green-400" />
|
| 1147 |
-
<span className="text-green-300 font-medium">预置渠道说明</span>
|
| 1148 |
-
</div>
|
| 1149 |
-
<p className="text-green-200 text-sm mt-2">
|
| 1150 |
-
系统包含预置的默认服务渠道,提供开箱即用的体验。预置渠道不会被导出,重置时会自动恢复。
|
| 1151 |
-
</p>
|
| 1152 |
-
</div>
|
| 1153 |
-
)}
|
| 1154 |
<div className="flex space-x-3">
|
| 1155 |
<button
|
| 1156 |
onClick={handleReset}
|
|
|
|
| 23 |
EyeOff,
|
| 24 |
Star,
|
| 25 |
StarOff,
|
| 26 |
+
Shield,
|
| 27 |
+
Lock
|
| 28 |
} from 'lucide-react';
|
| 29 |
|
| 30 |
interface ModelConfigManagerProps {
|
|
|
|
| 102 |
}));
|
| 103 |
};
|
| 104 |
|
| 105 |
+
// 获取渠道显示的API密钥内容
|
| 106 |
+
const getDisplayApiKey = (channel: ApiChannel): string => {
|
| 107 |
+
if (channel.isProtected) {
|
| 108 |
+
return '●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● (受保护)';
|
| 109 |
+
}
|
| 110 |
+
return channel.apiKey || '未设置';
|
| 111 |
+
};
|
| 112 |
+
|
| 113 |
+
// 检查用户是否可以修改此渠道
|
| 114 |
+
const canEditChannel = (channel: ApiChannel): boolean => {
|
| 115 |
+
return channel.isCustom || !channel.isProtected;
|
| 116 |
+
};
|
| 117 |
+
|
| 118 |
// ============ 渠道管理 ============
|
| 119 |
|
| 120 |
const handleSaveChannel = () => {
|
|
|
|
| 147 |
};
|
| 148 |
|
| 149 |
const handleDeleteChannel = (id: string) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
// 检查是否有模型使用此渠道
|
| 151 |
const modelsUsingChannel = models.filter(model => model.channelId === id);
|
| 152 |
if (modelsUsingChannel.length > 0) {
|
|
|
|
| 177 |
}
|
| 178 |
};
|
| 179 |
|
| 180 |
+
const handleEditChannel = (channel: ApiChannel) => {
|
| 181 |
+
if (channel.isProtected && !channel.isCustom) {
|
| 182 |
+
// 对于受保护的预置渠道,创建一个编辑副本,但不显示真实的API密钥
|
| 183 |
+
setEditingChannel({
|
| 184 |
+
...channel,
|
| 185 |
+
apiKey: '' // 不显示受保护的密钥
|
| 186 |
+
});
|
| 187 |
+
} else {
|
| 188 |
+
setEditingChannel(channel);
|
| 189 |
+
}
|
| 190 |
+
};
|
| 191 |
+
|
| 192 |
// ============ 模型管理 ============
|
| 193 |
|
| 194 |
const handleSaveModel = () => {
|
|
|
|
| 329 |
};
|
| 330 |
|
| 331 |
const handleReset = () => {
|
| 332 |
+
if (window.confirm('确定要重置为默认配置吗?这将删除所有自定义配置。')) {
|
| 333 |
try {
|
| 334 |
ModelConfigManager.resetToDefaults();
|
| 335 |
loadConfig();
|
|
|
|
| 508 |
onClick={() => setEditingChannel({
|
| 509 |
isNew: true,
|
| 510 |
isDefault: false,
|
| 511 |
+
isProtected: false,
|
| 512 |
timeout: 30000,
|
| 513 |
baseUrl: 'https://api.openai.com/v1'
|
| 514 |
})}
|
|
|
|
| 529 |
{channel.isDefault && (
|
| 530 |
<Star size={16} className="text-yellow-400" title="默认渠道" />
|
| 531 |
)}
|
| 532 |
+
{channel.isProtected && (
|
| 533 |
+
<Shield size={16} className="text-green-400" title="受保护配置" />
|
| 534 |
)}
|
| 535 |
</div>
|
| 536 |
<p className="text-gray-400 text-sm">{channel.baseUrl}</p>
|
| 537 |
<div className="flex items-center space-x-2 mt-2">
|
| 538 |
<Key size={12} className="text-gray-500" />
|
| 539 |
<span className="text-gray-500 text-xs">
|
| 540 |
+
{channel.isProtected ? (
|
| 541 |
+
<span className="flex items-center space-x-1">
|
| 542 |
+
<Lock size={12} />
|
| 543 |
+
<span>受保护密钥 (已预置)</span>
|
| 544 |
+
</span>
|
| 545 |
+
) : showApiKeys[channel.id]
|
| 546 |
? channel.apiKey || '未设置'
|
| 547 |
: '••••••••••••••••••••••••••••••••••••••••'
|
| 548 |
}
|
| 549 |
</span>
|
| 550 |
+
{!channel.isProtected && (
|
| 551 |
+
<button
|
| 552 |
+
onClick={() => toggleApiKeyVisibility(channel.id)}
|
| 553 |
+
className="text-gray-500 hover:text-gray-300"
|
| 554 |
+
>
|
| 555 |
+
{showApiKeys[channel.id] ? <EyeOff size={12} /> : <Eye size={12} />}
|
| 556 |
+
</button>
|
| 557 |
+
)}
|
| 558 |
</div>
|
| 559 |
{channel.description && (
|
| 560 |
<p className="text-gray-500 text-xs mt-1">{channel.description}</p>
|
| 561 |
)}
|
| 562 |
</div>
|
| 563 |
<div className="flex space-x-1">
|
| 564 |
+
{!channel.isDefault && !channel.isProtected && (
|
| 565 |
<button
|
| 566 |
onClick={() => handleSetDefaultChannel(channel.id)}
|
| 567 |
className="p-1 text-gray-400 hover:text-yellow-400"
|
|
|
|
| 570 |
<StarOff size={16} />
|
| 571 |
</button>
|
| 572 |
)}
|
| 573 |
+
{canEditChannel(channel) && (
|
| 574 |
+
<button
|
| 575 |
+
onClick={() => handleEditChannel(channel)}
|
| 576 |
+
className="p-1 text-gray-400 hover:text-sky-400"
|
| 577 |
+
title="编辑"
|
| 578 |
+
>
|
| 579 |
+
<Edit3 size={16} />
|
| 580 |
+
</button>
|
| 581 |
+
)}
|
| 582 |
+
{channel.isCustom && (
|
| 583 |
<button
|
| 584 |
onClick={() => handleDeleteChannel(channel.id)}
|
| 585 |
className="p-1 text-gray-400 hover:text-red-400"
|
|
|
|
| 602 |
<div className="p-4">
|
| 603 |
<h3 className="text-lg font-medium text-white mb-4">
|
| 604 |
{editingChannel.isNew ? '添加新渠道' : '编辑渠道'}
|
|
|
|
| 605 |
</h3>
|
| 606 |
|
| 607 |
{validationErrors.length > 0 && (
|
|
|
|
| 616 |
</div>
|
| 617 |
)}
|
| 618 |
|
| 619 |
+
{/* 受保护渠道的提示 */}
|
| 620 |
+
{editingChannel.isProtected && !editingChannel.isNew && (
|
| 621 |
+
<div className="mb-4 p-3 bg-blue-600 rounded">
|
| 622 |
+
<div className="flex items-center space-x-2 mb-2">
|
| 623 |
+
<Shield size={16} className="text-white" />
|
| 624 |
+
<span className="text-white font-medium">受保护配置</span>
|
| 625 |
+
</div>
|
| 626 |
+
<p className="text-white text-sm">
|
| 627 |
+
此渠道的API密钥已预置且受到保护,您无法查看或修改密钥内容。如需使用自定义密钥,请创建新的渠道。
|
| 628 |
+
</p>
|
| 629 |
+
</div>
|
| 630 |
+
)}
|
| 631 |
+
|
| 632 |
<div className="space-y-4">
|
| 633 |
<div>
|
| 634 |
<label className="block text-gray-300 text-sm mb-1">渠道名称</label>
|
|
|
|
| 649 |
onChange={(e) => setEditingChannel({ ...editingChannel, baseUrl: e.target.value })}
|
| 650 |
className="w-full bg-gray-700 text-white p-2 rounded text-sm"
|
| 651 |
placeholder="https://api.openai.com/v1"
|
|
|
|
| 652 |
/>
|
|
|
|
|
|
|
|
|
|
| 653 |
</div>
|
| 654 |
|
| 655 |
<div>
|
| 656 |
+
<label className="block text-gray-300 text-sm mb-1">
|
| 657 |
+
API密钥
|
| 658 |
+
{editingChannel.isProtected && (
|
| 659 |
+
<span className="ml-2 text-xs text-blue-400">(受保护,无法修改)</span>
|
| 660 |
+
)}
|
| 661 |
+
</label>
|
| 662 |
<div className="relative">
|
| 663 |
+
{editingChannel.isProtected ? (
|
| 664 |
+
<div className="w-full bg-gray-600 text-gray-400 p-2 rounded text-sm flex items-center space-x-2">
|
| 665 |
+
<Lock size={16} />
|
| 666 |
+
<span>●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● (受保护)</span>
|
| 667 |
+
</div>
|
| 668 |
+
) : (
|
| 669 |
+
<input
|
| 670 |
+
type="password"
|
| 671 |
+
value={editingChannel.apiKey || ''}
|
| 672 |
+
onChange={(e) => setEditingChannel({ ...editingChannel, apiKey: e.target.value })}
|
| 673 |
+
className="w-full bg-gray-700 text-white p-2 rounded text-sm"
|
| 674 |
+
placeholder="输入API密钥"
|
| 675 |
+
/>
|
| 676 |
+
)}
|
| 677 |
</div>
|
|
|
|
|
|
|
|
|
|
| 678 |
</div>
|
| 679 |
|
| 680 |
<div>
|
|
|
|
| 699 |
/>
|
| 700 |
</div>
|
| 701 |
|
| 702 |
+
<div className="space-y-2">
|
| 703 |
+
<label className="flex items-center space-x-2">
|
| 704 |
+
<input
|
| 705 |
+
type="checkbox"
|
| 706 |
+
checked={editingChannel.isDefault || false}
|
| 707 |
+
onChange={(e) => setEditingChannel({ ...editingChannel, isDefault: e.target.checked })}
|
| 708 |
+
className="rounded"
|
| 709 |
+
/>
|
| 710 |
+
<span className="text-gray-300 text-sm">设为默认渠道</span>
|
| 711 |
+
</label>
|
| 712 |
+
</div>
|
|
|
|
|
|
|
| 713 |
|
| 714 |
<div className="flex justify-end space-x-2 pt-4">
|
| 715 |
<button
|
|
|
|
| 753 |
category: 'GPT-4系列',
|
| 754 |
maxTokens: 4096,
|
| 755 |
temperature: 0.7,
|
| 756 |
+
channelId: channels.length > 0 ? channels[0].id : ''
|
| 757 |
})}
|
| 758 |
className="px-3 py-1 bg-sky-600 hover:bg-sky-700 text-white rounded text-sm flex items-center space-x-1"
|
| 759 |
>
|
|
|
|
| 774 |
<p className="text-gray-500 text-xs">{model.category}</p>
|
| 775 |
<p className="text-gray-500 text-xs">
|
| 776 |
渠道: {channel?.name || '未找到渠道'}
|
|
|
|
| 777 |
</p>
|
| 778 |
</div>
|
| 779 |
<div className="flex space-x-1">
|
|
|
|
| 869 |
<option value="">选择渠道</option>
|
| 870 |
{channels.map((channel) => (
|
| 871 |
<option key={channel.id} value={channel.id}>
|
| 872 |
+
{channel.name} {channel.isProtected && '(受保护)'}
|
| 873 |
</option>
|
| 874 |
))}
|
| 875 |
</select>
|
|
|
|
| 1001 |
</p>
|
| 1002 |
<p className="text-gray-500 text-xs">
|
| 1003 |
渠道: {associatedChannel?.name || '未找到渠道'}
|
| 1004 |
+
{associatedChannel?.isProtected && (
|
| 1005 |
+
<span className="ml-1 text-green-400">(受保护)</span>
|
| 1006 |
+
)}
|
| 1007 |
</p>
|
| 1008 |
</div>
|
| 1009 |
<div className="flex space-x-1">
|
|
|
|
| 1077 |
const channel = channels.find(ch => ch.id === model.channelId);
|
| 1078 |
return (
|
| 1079 |
<option key={model.id} value={model.id}>
|
| 1080 |
+
{model.name} ({channel?.name || '未知渠道'})
|
| 1081 |
+
{channel?.isProtected && ' - 受保护'}
|
| 1082 |
</option>
|
| 1083 |
);
|
| 1084 |
})}
|
|
|
|
| 1165 |
<div className="space-y-2">
|
| 1166 |
<div className="flex justify-between text-sm">
|
| 1167 |
<span className="text-gray-300">API渠道:</span>
|
| 1168 |
+
<span className="text-white">{storageInfo.channels}</span>
|
| 1169 |
</div>
|
| 1170 |
<div className="flex justify-between text-sm">
|
| 1171 |
<span className="text-gray-300">模型数量:</span>
|
|
|
|
| 1185 |
<p className="text-gray-300 text-sm">
|
| 1186 |
所有配置数据存储在浏览器的localStorage中。清除浏览器数据可能会导致配置丢失。建议定期导出配置进行备份。
|
| 1187 |
</p>
|
| 1188 |
+
<p className="text-blue-300 text-sm">
|
| 1189 |
+
注意:受保护的预置API密钥不会包含在导出的配置中,以确保安全性。
|
| 1190 |
+
</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1191 |
<div className="flex space-x-3">
|
| 1192 |
<button
|
| 1193 |
onClick={handleReset}
|