samlax12 commited on
Commit
39a6b7f
·
verified ·
1 Parent(s): 3d37e4b

Update components/ModelConfigManager.tsx

Browse files
Files changed (1) hide show
  1. 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.isPreset && (
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
- {showApiKeys[channel.id]
 
 
 
 
 
521
  ? channel.apiKey || '未设置'
522
  : '••••••••••••••••••••••••••••••••••••••••'
523
  }
524
  </span>
525
- <button
526
- onClick={() => toggleApiKeyVisibility(channel.id)}
527
- className="text-gray-500 hover:text-gray-300"
528
- >
529
- {showApiKeys[channel.id] ? <EyeOff size={12} /> : <Eye size={12} />}
530
- </button>
 
 
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.isPreset && (
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
- <button
547
- onClick={() => setEditingChannel(channel)}
548
- className="p-1 text-gray-400 hover:text-sky-400"
549
- title="编辑"
550
- >
551
- <Edit3 size={16} />
552
- </button>
553
- {channel.isCustom && !channel.isPreset && (
 
 
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">API密钥</label>
 
 
 
 
 
620
  <div className="relative">
621
- <input
622
- type="password"
623
- value={editingChannel.apiKey || ''}
624
- onChange={(e) => setEditingChannel({ ...editingChannel, apiKey: e.target.value })}
625
- className="w-full bg-gray-700 text-white p-2 rounded text-sm"
626
- placeholder="输入API密钥"
627
- disabled={editingChannel.isPreset}
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
- {!editingChannel.isPreset && (
658
- <div className="space-y-2">
659
- <label className="flex items-center space-x-2">
660
- <input
661
- type="checkbox"
662
- checked={editingChannel.isDefault || false}
663
- onChange={(e) => setEditingChannel({ ...editingChannel, isDefault: e.target.checked })}
664
- className="rounded"
665
- />
666
- <span className="text-gray-300 text-sm">设为默认渠道</span>
667
- </label>
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.find(ch => !ch.isPreset)?.id || channels[0]?.id || ''
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.isPreset && ' (预置)'}
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?.isPreset && <span className="ml-1 text-green-400">(预置)</span>}
 
 
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 || '未知渠道'}{channel?.isPreset ? ' - 预置' : ''})
 
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} {channels.some(ch => ch.isPreset) && '(含预置)'}</span>
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
- {channels.some(ch => ch.isPreset) && (
1144
- <div className="bg-green-800 p-3 rounded">
1145
- <div className="flex items-center space-x-2">
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}