From 774343d9e26328addd29fd613b8eff671b48d206 Mon Sep 17 00:00:00 2001 From: shaw Date: Sat, 27 Sep 2025 18:08:40 +0800 Subject: [PATCH] =?UTF-8?q?feta:=20apikeys=E9=A1=B5=E9=9D=A2=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E4=B8=93=E5=B1=9E=E7=BB=91=E5=AE=9A=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E7=AD=9B=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/admin-spa/src/views/ApiKeysView.vue | 140 +++++++++++++++++++----- 1 file changed, 115 insertions(+), 25 deletions(-) diff --git a/web/admin-spa/src/views/ApiKeysView.vue b/web/admin-spa/src/views/ApiKeysView.vue index dad79779..794613b0 100644 --- a/web/admin-spa/src/views/ApiKeysView.vue +++ b/web/admin-spa/src/views/ApiKeysView.vue @@ -117,27 +117,45 @@ - -
-
-
- +
+
+ - - +
+
+
+
+ + + +
@@ -1866,6 +1884,11 @@ const availableTags = ref([]) // 搜索相关 const searchKeyword = ref('') +const searchMode = ref('apiKey') +const searchModeOptions = computed(() => [ + { value: 'apiKey', label: '按Key名称', icon: 'fa-key' }, + { value: 'bindingAccount', label: '按所属账号', icon: 'fa-id-badge' } +]) const tagOptions = computed(() => { const options = [{ value: '', label: '所有标签', icon: 'fa-asterisk' }] @@ -1910,6 +1933,65 @@ const renewingApiKey = ref(null) const newApiKeyData = ref(null) const batchApiKeyData = ref([]) +// 提取“所属账号”列直接展示的文本 +const getBindingDisplayStrings = (key) => { + const values = new Set() + + const collect = (...items) => { + items.forEach((item) => { + if (typeof item !== 'string') return + const trimmed = item.trim() + if (trimmed) { + values.add(trimmed) + } + }) + } + + const sanitize = (text) => { + if (typeof text !== 'string') return '' + return text + .replace(/^⚠️\s*/, '') + .replace(/^🔒\s*/, '') + .trim() + } + + const appendBindingRow = (label, info) => { + const infoSanitized = sanitize(info) + collect(label, info, infoSanitized) + if (infoSanitized) { + collect(`${label} ${infoSanitized}`) + } + } + + if (key.claudeAccountId || key.claudeConsoleAccountId) { + appendBindingRow('Claude', getClaudeBindingInfo(key)) + } + + if (key.geminiAccountId) { + appendBindingRow('Gemini', getGeminiBindingInfo(key)) + } + + if (key.openaiAccountId) { + appendBindingRow('OpenAI', getOpenAIBindingInfo(key)) + } + + if (key.bedrockAccountId) { + appendBindingRow('Bedrock', getBedrockBindingInfo(key)) + } + + if ( + !key.claudeAccountId && + !key.claudeConsoleAccountId && + !key.geminiAccountId && + !key.openaiAccountId && + !key.bedrockAccountId + ) { + collect('共享池') + } + + return Array.from(values) +} + // 计算排序后的API Keys const sortedApiKeys = computed(() => { // 先进行标签筛选 @@ -1920,20 +2002,22 @@ const sortedApiKeys = computed(() => { ) } - // 然后进行名称搜索(搜索API Key名称和所有者名称) + // 然后进行搜索过滤 if (searchKeyword.value) { const keyword = searchKeyword.value.toLowerCase().trim() filteredKeys = filteredKeys.filter((key) => { - // 搜索API Key名称 + if (searchMode.value === 'bindingAccount') { + const bindings = getBindingDisplayStrings(key) + if (bindings.length === 0) return false + return bindings.some((text) => text.toLowerCase().includes(keyword)) + } + const nameMatch = key.name && key.name.toLowerCase().includes(keyword) - // 如果启用了 LDAP,搜索所有者名称 if (isLdapEnabled.value) { const ownerMatch = key.ownerDisplayName && key.ownerDisplayName.toLowerCase().includes(keyword) - // 如果API Key名称或所有者名称匹配,则包含该条目 return nameMatch || ownerMatch } - // 未启用 LDAP 时只搜索名称 return nameMatch }) } @@ -3671,6 +3755,12 @@ watch(searchKeyword, () => { updateSelectAllState() }) +// 监听搜索模式变化,重置分页并更新选中状态 +watch(searchMode, () => { + currentPage.value = 1 + updateSelectAllState() +}) + // 监听分页变化,更新全选状态 watch([currentPage, pageSize], () => { updateSelectAllState()