From bba7e560ec148d1188073963041efd8e5db3f8e8 Mon Sep 17 00:00:00 2001 From: abingyyds Date: Mon, 13 Apr 2026 19:46:06 +0800 Subject: [PATCH] Add distributor token supported models view --- src/api.js | 1 + src/i18n/locales/en.json | 9 ++++ src/i18n/locales/zh.json | 9 ++++ src/pages/Tokens.jsx | 100 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 118 insertions(+), 1 deletion(-) diff --git a/src/api.js b/src/api.js index 70bee4c..fd62a15 100644 --- a/src/api.js +++ b/src/api.js @@ -62,6 +62,7 @@ export const getUserLogs = (params) => api.get('/api/dist/user/logs', { params } // ===== Tokens ===== export const getTokens = () => api.get('/api/dist/token/list'); +export const getTokenSupportedModels = (id) => api.get(`/api/dist/token/${id}/models`); export const createToken = (data) => api.post('/api/dist/token/create', data); export const updateToken = (id, data) => api.put(`/api/dist/token/${id}`, data); export const deleteToken = (id) => api.delete(`/api/dist/token/${id}`); diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 393f4c3..9a7af26 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -168,6 +168,15 @@ "tokens.unavailable": "Unavailable", "tokens.rmbPerUsd": "RMB / $1", "tokens.myKeys": "My API Keys", + "tokens.viewSupportedModels": "Supported Models", + "tokens.hideSupportedModels": "Hide Models", + "tokens.loadingSupportedModels": "Loading supported models...", + "tokens.loadSupportedModelsFailed": "Failed to load supported models", + "tokens.supportedModels": "Supported Models", + "tokens.supportedProviders": "Providers", + "tokens.noSupportedModels": "No available models for this key", + "tokens.restrictedByModels": "Model limited", + "tokens.restrictedByProviders": "Provider limited", "tokens.selectedGroup": "Selected Group", "tokens.quickStart": "Quick Start", "tokens.quickStartComment": "# Use your API key with OpenAI-compatible endpoint", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 78d347a..46ac656 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -168,6 +168,15 @@ "tokens.unavailable": "暂不可用", "tokens.rmbPerUsd": "块 / 1 美元", "tokens.myKeys": "我的 API Keys", + "tokens.viewSupportedModels": "支持模型", + "tokens.hideSupportedModels": "收起模型", + "tokens.loadingSupportedModels": "正在加载支持模型...", + "tokens.loadSupportedModelsFailed": "加载支持模型失败", + "tokens.supportedModels": "支持模型", + "tokens.supportedProviders": "支持商家", + "tokens.noSupportedModels": "当前没有可用模型", + "tokens.restrictedByModels": "模型限制", + "tokens.restrictedByProviders": "商家限制", "tokens.selectedGroup": "已选分组", "tokens.quickStart": "快速开始", "tokens.quickStartComment": "# 使用 OpenAI 兼容端点调用 API", diff --git a/src/pages/Tokens.jsx b/src/pages/Tokens.jsx index 40ac366..82ae245 100644 --- a/src/pages/Tokens.jsx +++ b/src/pages/Tokens.jsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { getTokens, createToken, updateToken, deleteToken, getSiteKeyGroups } from '../api'; +import { getTokens, createToken, updateToken, deleteToken, getSiteKeyGroups, getTokenSupportedModels } from '../api'; import toast from 'react-hot-toast'; export default function Tokens() { @@ -10,6 +10,8 @@ export default function Tokens() { const [copiedId, setCopiedId] = useState(null); const [newKey, setNewKey] = useState(null); const [deleteConfirm, setDeleteConfirm] = useState(null); + const [expandedTokens, setExpandedTokens] = useState({}); + const [tokenModels, setTokenModels] = useState({}); // Key groups const [keyGroups, setKeyGroups] = useState([]); @@ -128,6 +130,45 @@ export default function Tokens() { try { return JSON.parse(tagsStr || '[]'); } catch { return []; } }; + const handleToggleSupportedModels = async (tokenId) => { + const isExpanded = !!expandedTokens[tokenId]; + setExpandedTokens((prev) => ({ ...prev, [tokenId]: !isExpanded })); + if (isExpanded || tokenModels[tokenId]) return; + + setTokenModels((prev) => ({ + ...prev, + [tokenId]: { loading: true, models: [], count: 0, provider_names: [], restricted_by_providers: false, restricted_by_models: false }, + })); + + try { + const res = await getTokenSupportedModels(tokenId); + if (res.data.success) { + const data = res.data.data || {}; + setTokenModels((prev) => ({ + ...prev, + [tokenId]: { + loading: false, + models: data.models || [], + count: data.count || 0, + provider_names: data.provider_names || [], + restricted_by_providers: Boolean(data.restricted_by_providers), + restricted_by_models: Boolean(data.restricted_by_models), + }, + })); + } else { + setTokenModels((prev) => ({ + ...prev, + [tokenId]: { loading: false, error: true, models: [], count: 0, provider_names: [], restricted_by_providers: false, restricted_by_models: false }, + })); + } + } catch (e) { + setTokenModels((prev) => ({ + ...prev, + [tokenId]: { loading: false, error: true, models: [], count: 0, provider_names: [], restricted_by_providers: false, restricted_by_models: false }, + })); + } + }; + if (loading) { return (
@@ -314,6 +355,12 @@ export default function Tokens() { {token.created_time ? new Date(token.created_time * 1000).toLocaleDateString() : ''}
+
)} + {expandedTokens[token.id] && ( +
+ {tokenModels[token.id]?.loading ? ( +
+
+ {t('tokens.loadingSupportedModels')} +
+ ) : tokenModels[token.id]?.error ? ( +

{t('tokens.loadSupportedModelsFailed')}

+ ) : ( + <> +
+

+ {t('tokens.supportedModels')} ({tokenModels[token.id]?.count || 0}) +

+ {tokenModels[token.id]?.restricted_by_models && ( + + {t('tokens.restrictedByModels')} + + )} + {tokenModels[token.id]?.restricted_by_providers && ( + + {t('tokens.restrictedByProviders')} + + )} +
+ {tokenModels[token.id]?.provider_names?.length > 0 && ( +
+ {t('tokens.supportedProviders')} + {tokenModels[token.id].provider_names.map((name) => ( + + {name} + + ))} +
+ )} + {tokenModels[token.id]?.models?.length > 0 ? ( +
+ {tokenModels[token.id].models.map((modelName) => ( + + {modelName} + + ))} +
+ ) : ( +

{t('tokens.noSupportedModels')}

+ )} + + )} +
+ )}
))}