mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-19 03:00:49 +08:00
feat: add excluded models support for Codex/Claude providers and fix header alignment
- Add excludedModels field to ProviderKeyConfig type for Codex and Claude providers - Add excluded models textarea input in Codex/Claude edit modal - Display excluded models badges in Codex and Claude provider cards - Fix header connection status badge vertical alignment with IP address - Update dark theme to use pure black color scheme
This commit is contained in:
@@ -132,13 +132,15 @@ export function AiProvidersPage() {
|
|||||||
excludedModels: [],
|
excludedModels: [],
|
||||||
excludedText: ''
|
excludedText: ''
|
||||||
});
|
});
|
||||||
const [providerForm, setProviderForm] = useState<ProviderKeyConfig & { modelEntries: ModelEntry[] }>({
|
const [providerForm, setProviderForm] = useState<ProviderKeyConfig & { modelEntries: ModelEntry[]; excludedText: string }>({
|
||||||
apiKey: '',
|
apiKey: '',
|
||||||
baseUrl: '',
|
baseUrl: '',
|
||||||
proxyUrl: '',
|
proxyUrl: '',
|
||||||
headers: {},
|
headers: {},
|
||||||
models: [],
|
models: [],
|
||||||
modelEntries: [{ name: '', alias: '' }]
|
excludedModels: [],
|
||||||
|
modelEntries: [{ name: '', alias: '' }],
|
||||||
|
excludedText: ''
|
||||||
});
|
});
|
||||||
const [openaiForm, setOpenaiForm] = useState<OpenAIFormState>({
|
const [openaiForm, setOpenaiForm] = useState<OpenAIFormState>({
|
||||||
name: '',
|
name: '',
|
||||||
@@ -231,7 +233,9 @@ export function AiProvidersPage() {
|
|||||||
proxyUrl: '',
|
proxyUrl: '',
|
||||||
headers: {},
|
headers: {},
|
||||||
models: [],
|
models: [],
|
||||||
modelEntries: [{ name: '', alias: '' }]
|
excludedModels: [],
|
||||||
|
modelEntries: [{ name: '', alias: '' }],
|
||||||
|
excludedText: ''
|
||||||
});
|
});
|
||||||
setOpenaiForm({
|
setOpenaiForm({
|
||||||
name: '',
|
name: '',
|
||||||
@@ -269,7 +273,8 @@ export function AiProvidersPage() {
|
|||||||
const entry = source[index];
|
const entry = source[index];
|
||||||
setProviderForm({
|
setProviderForm({
|
||||||
...entry,
|
...entry,
|
||||||
modelEntries: modelsToEntries(entry?.models)
|
modelEntries: modelsToEntries(entry?.models),
|
||||||
|
excludedText: excludedModelsToText(entry?.excludedModels)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setModal({ type, index });
|
setModal({ type, index });
|
||||||
@@ -558,7 +563,8 @@ export function AiProvidersPage() {
|
|||||||
baseUrl,
|
baseUrl,
|
||||||
proxyUrl: providerForm.proxyUrl?.trim() || undefined,
|
proxyUrl: providerForm.proxyUrl?.trim() || undefined,
|
||||||
headers: buildHeaderObject(headersToEntries(providerForm.headers as any)),
|
headers: buildHeaderObject(headersToEntries(providerForm.headers as any)),
|
||||||
models: entriesToModels(providerForm.modelEntries)
|
models: entriesToModels(providerForm.modelEntries),
|
||||||
|
excludedModels: parseExcludedModels(providerForm.excludedText)
|
||||||
};
|
};
|
||||||
|
|
||||||
const source = type === 'codex' ? codexConfigs : claudeConfigs;
|
const source = type === 'codex' ? codexConfigs : claudeConfigs;
|
||||||
@@ -888,6 +894,21 @@ export function AiProvidersPage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{/* 排除模型徽章 */}
|
||||||
|
{item.excludedModels?.length ? (
|
||||||
|
<div className={styles.excludedModelsSection}>
|
||||||
|
<div className={styles.excludedModelsLabel}>
|
||||||
|
{t('ai_providers.excluded_models_count', { count: item.excludedModels.length })}
|
||||||
|
</div>
|
||||||
|
<div className={styles.modelTagList}>
|
||||||
|
{item.excludedModels.map((model) => (
|
||||||
|
<span key={model} className={`${styles.modelTag} ${styles.excludedModelTag}`}>
|
||||||
|
<span className={styles.modelName}>{model}</span>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
{/* 成功/失败统计 */}
|
{/* 成功/失败统计 */}
|
||||||
<div className={styles.cardStats}>
|
<div className={styles.cardStats}>
|
||||||
<span className={`${styles.statPill} ${styles.statSuccess}`}>
|
<span className={`${styles.statPill} ${styles.statSuccess}`}>
|
||||||
@@ -970,6 +991,21 @@ export function AiProvidersPage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
{/* 排除模型徽章 */}
|
||||||
|
{item.excludedModels?.length ? (
|
||||||
|
<div className={styles.excludedModelsSection}>
|
||||||
|
<div className={styles.excludedModelsLabel}>
|
||||||
|
{t('ai_providers.excluded_models_count', { count: item.excludedModels.length })}
|
||||||
|
</div>
|
||||||
|
<div className={styles.modelTagList}>
|
||||||
|
{item.excludedModels.map((model) => (
|
||||||
|
<span key={model} className={`${styles.modelTag} ${styles.excludedModelTag}`}>
|
||||||
|
<span className={styles.modelName}>{model}</span>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
{/* 成功/失败统计 */}
|
{/* 成功/失败统计 */}
|
||||||
<div className={styles.cardStats}>
|
<div className={styles.cardStats}>
|
||||||
<span className={`${styles.statPill} ${styles.statSuccess}`}>
|
<span className={`${styles.statPill} ${styles.statSuccess}`}>
|
||||||
@@ -1213,6 +1249,17 @@ export function AiProvidersPage() {
|
|||||||
disabled={saving}
|
disabled={saving}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>{t('ai_providers.excluded_models_label')}</label>
|
||||||
|
<textarea
|
||||||
|
className="input"
|
||||||
|
placeholder={t('ai_providers.excluded_models_placeholder')}
|
||||||
|
value={providerForm.excludedText}
|
||||||
|
onChange={(e) => setProviderForm((prev) => ({ ...prev, excludedText: e.target.value }))}
|
||||||
|
rows={4}
|
||||||
|
/>
|
||||||
|
<div className="hint">{t('ai_providers.excluded_models_hint')}</div>
|
||||||
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
{/* OpenAI Modal */}
|
{/* OpenAI Modal */}
|
||||||
|
|||||||
@@ -135,6 +135,12 @@ textarea {
|
|||||||
border-radius: $radius-full;
|
border-radius: $radius-full;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
|
margin-bottom: $spacing-md;
|
||||||
|
|
||||||
|
// 确保后续内容换行显示
|
||||||
|
+ * {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
&.success {
|
&.success {
|
||||||
color: $success-color;
|
color: $success-color;
|
||||||
|
|||||||
@@ -195,6 +195,7 @@
|
|||||||
.status-badge {
|
.status-badge {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.base {
|
.base {
|
||||||
|
|||||||
@@ -36,18 +36,18 @@
|
|||||||
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
|
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 深色主题
|
// 深色主题(纯黑)
|
||||||
[data-theme='dark'] {
|
[data-theme='dark'] {
|
||||||
--bg-primary: #1f2937;
|
--bg-primary: #0a0a0a;
|
||||||
--bg-secondary: #111827;
|
--bg-secondary: #000000;
|
||||||
--bg-tertiary: #374151;
|
--bg-tertiary: #171717;
|
||||||
|
|
||||||
--text-primary: #f9fafb;
|
--text-primary: #fafafa;
|
||||||
--text-secondary: #d1d5db;
|
--text-secondary: #a3a3a3;
|
||||||
--text-tertiary: #9ca3af;
|
--text-tertiary: #737373;
|
||||||
|
|
||||||
--border-color: #374151;
|
--border-color: #262626;
|
||||||
--border-hover: #4b5563;
|
--border-hover: #404040;
|
||||||
|
|
||||||
--primary-color: #3b82f6;
|
--primary-color: #3b82f6;
|
||||||
--primary-hover: #60a5fa;
|
--primary-hover: #60a5fa;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export interface ProviderKeyConfig {
|
|||||||
proxyUrl?: string;
|
proxyUrl?: string;
|
||||||
headers?: Record<string, string>;
|
headers?: Record<string, string>;
|
||||||
models?: ModelAlias[];
|
models?: ModelAlias[];
|
||||||
|
excludedModels?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OpenAIProviderConfig {
|
export interface OpenAIProviderConfig {
|
||||||
|
|||||||
Reference in New Issue
Block a user