mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 09:38:02 +00:00
@@ -41,6 +41,26 @@
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400 sm:mt-2">名称不可修改</p>
|
||||
</div>
|
||||
|
||||
<!-- 所有者选择 -->
|
||||
<div>
|
||||
<label
|
||||
class="mb-1.5 block text-xs font-semibold text-gray-700 dark:text-gray-300 sm:mb-3 sm:text-sm"
|
||||
>所有者</label
|
||||
>
|
||||
<select
|
||||
v-model="form.ownerId"
|
||||
class="form-input w-full dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200"
|
||||
>
|
||||
<option v-for="user in availableUsers" :key="user.id" :value="user.id">
|
||||
{{ user.displayName }} ({{ user.username }})
|
||||
<span v-if="user.role === 'admin'" class="text-gray-500">- 管理员</span>
|
||||
</option>
|
||||
</select>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400 sm:mt-2">
|
||||
分配此 API Key 给指定用户或管理员,管理员分配时不受用户 API Key 数量限制
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 标签 -->
|
||||
<div>
|
||||
<label
|
||||
@@ -666,6 +686,9 @@ const localAccounts = ref({
|
||||
// 支持的客户端列表
|
||||
const supportedClients = ref([])
|
||||
|
||||
// 可用用户列表
|
||||
const availableUsers = ref([])
|
||||
|
||||
// 标签相关
|
||||
const newTag = ref('')
|
||||
const availableTags = ref([])
|
||||
@@ -696,7 +719,8 @@ const form = reactive({
|
||||
enableClientRestriction: false,
|
||||
allowedClients: [],
|
||||
tags: [],
|
||||
isActive: true
|
||||
isActive: true,
|
||||
ownerId: '' // 新增:所有者ID
|
||||
})
|
||||
|
||||
// 添加限制的模型
|
||||
@@ -856,6 +880,11 @@ const updateApiKey = async () => {
|
||||
// 活跃状态
|
||||
data.isActive = form.isActive
|
||||
|
||||
// 所有者
|
||||
if (form.ownerId !== undefined) {
|
||||
data.ownerId = form.ownerId
|
||||
}
|
||||
|
||||
const result = await apiClient.put(`/admin/api-keys/${props.apiKey.id}`, data)
|
||||
|
||||
if (result.success) {
|
||||
@@ -947,11 +976,45 @@ const refreshAccounts = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 加载用户列表
|
||||
const loadUsers = async () => {
|
||||
try {
|
||||
const response = await apiClient.get('/admin/users')
|
||||
if (response.success) {
|
||||
availableUsers.value = response.data || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load users:', error)
|
||||
availableUsers.value = [
|
||||
{
|
||||
id: 'admin',
|
||||
username: 'admin',
|
||||
displayName: 'Admin',
|
||||
email: '',
|
||||
role: 'admin'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化表单数据
|
||||
onMounted(async () => {
|
||||
// 加载支持的客户端和已存在的标签
|
||||
supportedClients.value = await clientsStore.loadSupportedClients()
|
||||
availableTags.value = await apiKeysStore.fetchTags()
|
||||
try {
|
||||
// 并行加载所有需要的数据
|
||||
const [clients, tags] = await Promise.all([
|
||||
clientsStore.loadSupportedClients(),
|
||||
apiKeysStore.fetchTags(),
|
||||
loadUsers()
|
||||
])
|
||||
|
||||
supportedClients.value = clients || []
|
||||
availableTags.value = tags || []
|
||||
} catch (error) {
|
||||
console.error('Error loading initial data:', error)
|
||||
// Fallback to empty arrays if loading fails
|
||||
supportedClients.value = []
|
||||
availableTags.value = []
|
||||
}
|
||||
|
||||
// 初始化账号数据
|
||||
if (props.accounts) {
|
||||
@@ -1001,6 +1064,9 @@ onMounted(async () => {
|
||||
form.enableClientRestriction = props.apiKey.enableClientRestriction || false
|
||||
// 初始化活跃状态,默认为 true
|
||||
form.isActive = props.apiKey.isActive !== undefined ? props.apiKey.isActive : true
|
||||
|
||||
// 初始化所有者
|
||||
form.ownerId = props.apiKey.userId || 'admin'
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -159,7 +159,11 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-if="!(apiKey.isDeleted === 'true' || apiKey.deletedAt) && apiKey.isActive"
|
||||
v-if="
|
||||
!(apiKey.isDeleted === 'true' || apiKey.deletedAt) &&
|
||||
apiKey.isActive &&
|
||||
allowUserDeleteApiKeys
|
||||
"
|
||||
class="inline-flex items-center rounded border border-transparent p-1 text-red-400 hover:text-red-600"
|
||||
title="Delete API Key"
|
||||
@click="deleteApiKey(apiKey)"
|
||||
@@ -255,6 +259,7 @@ const userStore = useUserStore()
|
||||
const loading = ref(true)
|
||||
const apiKeys = ref([])
|
||||
const maxApiKeys = computed(() => userStore.config?.maxApiKeysPerUser || 5)
|
||||
const allowUserDeleteApiKeys = computed(() => userStore.config?.allowUserDeleteApiKeys === true)
|
||||
|
||||
const showCreateModal = ref(false)
|
||||
const showViewModal = ref(false)
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
<input
|
||||
v-model="searchKeyword"
|
||||
class="w-full rounded-lg border border-gray-200 bg-white px-3 py-2 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="搜索名称..."
|
||||
placeholder="搜索名称或所有者..."
|
||||
type="text"
|
||||
@input="currentPage = 1"
|
||||
/>
|
||||
@@ -404,6 +404,11 @@
|
||||
使用共享池
|
||||
</div>
|
||||
</div>
|
||||
<!-- 显示所有者信息 -->
|
||||
<div v-if="key.ownerDisplayName" class="mt-1 text-xs text-red-600">
|
||||
<i class="fas fa-user mr-1" />
|
||||
{{ key.ownerDisplayName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -1025,6 +1030,11 @@
|
||||
<i class="fas fa-share-alt mr-1" />
|
||||
使用共享池
|
||||
</div>
|
||||
<!-- 显示所有者信息 -->
|
||||
<div v-if="key.ownerDisplayName" class="text-xs text-red-600">
|
||||
<i class="fas fa-user mr-1" />
|
||||
{{ key.ownerDisplayName }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计信息 -->
|
||||
@@ -1647,12 +1657,18 @@ const sortedApiKeys = computed(() => {
|
||||
)
|
||||
}
|
||||
|
||||
// 然后进行名称搜索
|
||||
// 然后进行名称搜索(搜索API Key名称和所有者名称)
|
||||
if (searchKeyword.value) {
|
||||
const keyword = searchKeyword.value.toLowerCase().trim()
|
||||
filteredKeys = filteredKeys.filter(
|
||||
(key) => key.name && key.name.toLowerCase().includes(keyword)
|
||||
)
|
||||
filteredKeys = filteredKeys.filter((key) => {
|
||||
// 搜索API Key名称
|
||||
const nameMatch = key.name && key.name.toLowerCase().includes(keyword)
|
||||
// 搜索所有者名称
|
||||
const ownerMatch =
|
||||
key.ownerDisplayName && key.ownerDisplayName.toLowerCase().includes(keyword)
|
||||
// 如果API Key名称或所有者名称匹配,则包含该条目
|
||||
return nameMatch || ownerMatch
|
||||
})
|
||||
}
|
||||
|
||||
// 如果没有排序字段,返回筛选后的结果
|
||||
|
||||
Reference in New Issue
Block a user