mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
feta: apikeys页面支持专属绑定账号筛选
This commit is contained in:
@@ -117,16 +117,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 搜索框 -->
|
<!-- 搜索模式与搜索框 -->
|
||||||
<div class="group relative min-w-[200px]">
|
<div class="flex min-w-[240px] flex-col gap-2 sm:flex-row sm:items-center">
|
||||||
|
<div class="sm:w-44">
|
||||||
|
<CustomDropdown
|
||||||
|
v-model="searchMode"
|
||||||
|
icon="fa-filter"
|
||||||
|
icon-color="text-cyan-500"
|
||||||
|
:options="searchModeOptions"
|
||||||
|
placeholder="选择搜索类型"
|
||||||
|
@change="currentPage = 1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="group relative flex-1">
|
||||||
<div
|
<div
|
||||||
class="absolute -inset-0.5 rounded-lg bg-gradient-to-r from-cyan-500 to-teal-500 opacity-0 blur transition duration-300 group-hover:opacity-20"
|
class="pointer-events-none absolute -inset-0.5 rounded-lg bg-gradient-to-r from-cyan-500 to-teal-500 opacity-0 blur transition duration-300 group-hover:opacity-20"
|
||||||
></div>
|
></div>
|
||||||
<div class="relative flex items-center">
|
<div class="relative flex items-center">
|
||||||
<input
|
<input
|
||||||
v-model="searchKeyword"
|
v-model="searchKeyword"
|
||||||
class="h-10 w-full rounded-lg border border-gray-200 bg-white px-3 pl-9 text-sm text-gray-700 placeholder-gray-400 shadow-sm transition-all duration-200 hover:border-gray-300 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 dark:placeholder-gray-500 dark:hover:border-gray-500"
|
class="h-10 w-full rounded-lg border border-gray-200 bg-white px-3 pl-9 text-sm text-gray-700 placeholder-gray-400 shadow-sm transition-all duration-200 hover:border-gray-300 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 dark:placeholder-gray-500 dark:hover:border-gray-500"
|
||||||
:placeholder="isLdapEnabled ? '搜索名称或所有者...' : '搜索名称...'"
|
:placeholder="
|
||||||
|
searchMode === 'bindingAccount'
|
||||||
|
? '搜索所属账号...'
|
||||||
|
: isLdapEnabled
|
||||||
|
? '搜索名称或所有者...'
|
||||||
|
: '搜索名称...'
|
||||||
|
"
|
||||||
type="text"
|
type="text"
|
||||||
@input="currentPage = 1"
|
@input="currentPage = 1"
|
||||||
/>
|
/>
|
||||||
@@ -141,6 +158,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 右侧:操作按钮组 -->
|
<!-- 右侧:操作按钮组 -->
|
||||||
<div class="flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center sm:gap-3">
|
<div class="flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center sm:gap-3">
|
||||||
@@ -1866,6 +1884,11 @@ const availableTags = ref([])
|
|||||||
|
|
||||||
// 搜索相关
|
// 搜索相关
|
||||||
const searchKeyword = 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 tagOptions = computed(() => {
|
||||||
const options = [{ value: '', label: '所有标签', icon: 'fa-asterisk' }]
|
const options = [{ value: '', label: '所有标签', icon: 'fa-asterisk' }]
|
||||||
@@ -1910,6 +1933,65 @@ const renewingApiKey = ref(null)
|
|||||||
const newApiKeyData = ref(null)
|
const newApiKeyData = ref(null)
|
||||||
const batchApiKeyData = ref([])
|
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
|
// 计算排序后的API Keys
|
||||||
const sortedApiKeys = computed(() => {
|
const sortedApiKeys = computed(() => {
|
||||||
// 先进行标签筛选
|
// 先进行标签筛选
|
||||||
@@ -1920,20 +2002,22 @@ const sortedApiKeys = computed(() => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 然后进行名称搜索(搜索API Key名称和所有者名称)
|
// 然后进行搜索过滤
|
||||||
if (searchKeyword.value) {
|
if (searchKeyword.value) {
|
||||||
const keyword = searchKeyword.value.toLowerCase().trim()
|
const keyword = searchKeyword.value.toLowerCase().trim()
|
||||||
filteredKeys = filteredKeys.filter((key) => {
|
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)
|
const nameMatch = key.name && key.name.toLowerCase().includes(keyword)
|
||||||
// 如果启用了 LDAP,搜索所有者名称
|
|
||||||
if (isLdapEnabled.value) {
|
if (isLdapEnabled.value) {
|
||||||
const ownerMatch =
|
const ownerMatch =
|
||||||
key.ownerDisplayName && key.ownerDisplayName.toLowerCase().includes(keyword)
|
key.ownerDisplayName && key.ownerDisplayName.toLowerCase().includes(keyword)
|
||||||
// 如果API Key名称或所有者名称匹配,则包含该条目
|
|
||||||
return nameMatch || ownerMatch
|
return nameMatch || ownerMatch
|
||||||
}
|
}
|
||||||
// 未启用 LDAP 时只搜索名称
|
|
||||||
return nameMatch
|
return nameMatch
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -3671,6 +3755,12 @@ watch(searchKeyword, () => {
|
|||||||
updateSelectAllState()
|
updateSelectAllState()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 监听搜索模式变化,重置分页并更新选中状态
|
||||||
|
watch(searchMode, () => {
|
||||||
|
currentPage.value = 1
|
||||||
|
updateSelectAllState()
|
||||||
|
})
|
||||||
|
|
||||||
// 监听分页变化,更新全选状态
|
// 监听分页变化,更新全选状态
|
||||||
watch([currentPage, pageSize], () => {
|
watch([currentPage, pageSize], () => {
|
||||||
updateSelectAllState()
|
updateSelectAllState()
|
||||||
|
|||||||
Reference in New Issue
Block a user