From 3c3acba451f7f17244cfdf083ea04c0548f039a1 Mon Sep 17 00:00:00 2001 From: Gsh <15170702455@163.com> Date: Sat, 14 Feb 2026 23:12:18 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=89=B9=E9=87=8F=E5=88=A0=E9=99=A4apik?= =?UTF-8?q?ey?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/APIKeyManagement.vue | 480 +++++++++++++++++- 1 file changed, 460 insertions(+), 20 deletions(-) diff --git a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/APIKeyManagement.vue b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/APIKeyManagement.vue index b32f0ad4..34369395 100644 --- a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/APIKeyManagement.vue +++ b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/APIKeyManagement.vue @@ -49,6 +49,17 @@ const router = useRouter(); // TokenFormDialog 组件引用 const tokenFormDialogRef = ref | null>(null); +// 批量选择相关 +const isBatchDeleteMode = ref(false); // 批量删除模式 +const selectedTokenIds = ref([]); +const selectAll = ref(false); + +// 批量删除预览对话框 +const showDeletePreviewDialog = ref(false); +const deleteItems = ref([]); +const deleteStatusList = ref>([]); +const isDeleting = ref(false); + // 移动端检测 const isMobile = ref(false); @@ -261,6 +272,108 @@ async function handleDelete(row: TokenItem) { } } +// 批量删除Token +async function handleBatchDelete() { + if (isBatchDeleteMode.value) { + // 已选择项目,显示预览对话框 + if (selectedTokenIds.value.length === 0) { + ElMessage.warning('请先选择要删除的API密钥'); + return; + } + + // 获取选中的 token + deleteItems.value = tokenList.value.filter(t => selectedTokenIds.value.includes(t.id)); + // 初始化删除状态 + deleteStatusList.value = deleteItems.value.map(item => ({ + id: item.id, + name: item.name, + status: 'pending' as const, + })); + showDeletePreviewDialog.value = true; + } else { + // 进入批量删除模式 + isBatchDeleteMode.value = true; + } +} + +// 确认批量删除 +async function confirmBatchDelete() { + isDeleting.value = true; + + // 并发删除所有选中的 token + const promises = deleteItems.value.map(async (item, index) => { + try { + deleteStatusList.value[index].status = 'deleting'; + await deleteToken(item.id); + deleteStatusList.value[index].status = 'success'; + } catch (error: any) { + console.error(`删除 "${item.name}" 失败:`, error); + deleteStatusList.value[index].status = 'failed'; + deleteStatusList.value[index].error = error?.message || '删除失败'; + throw error; + } + }); + + await Promise.allSettled(promises); + + // 删除完成后刷新列表 + await fetchTokenList(); + + // 关闭对话框并重置状态 + showDeletePreviewDialog.value = false; + isDeleting.value = false; + isBatchDeleteMode.value = false; + selectedTokenIds.value = []; + selectAll.value = false; + deleteItems.value = []; + deleteStatusList.value = []; +} + +// 取消批量删除预览 +function cancelBatchDeletePreview() { + if (isDeleting.value) return; + showDeletePreviewDialog.value = false; + deleteItems.value = []; + deleteStatusList.value = []; +} + +// 退出批量删除模式 +function exitBatchDeleteMode() { + isBatchDeleteMode.value = false; + selectedTokenIds.value = []; + selectAll.value = false; +} + +// 处理复选框选择变化 +function handleSelectionChange() { + // 如果所有项目都被选中,则全选按钮也被选中 + selectAll.value = tokenList.value.length > 0 + && selectedTokenIds.value.length === tokenList.value.length; +} + +// 处理全选/取消全选 +function handleSelectAllChange() { + if (selectAll.value) { + selectedTokenIds.value = tokenList.value.map(t => t.id); + } else { + selectedTokenIds.value = []; + } +} + +// 处理单个项目的选择变化 +function handleItemSelectChange(id: string, checked: boolean) { + if (checked) { + if (!selectedTokenIds.value.includes(id)) { + selectedTokenIds.value.push(id); + } + } else { + selectedTokenIds.value = selectedTokenIds.value.filter(itemId => itemId !== id); + } + // 更新全选状态 + selectAll.value = tokenList.value.length > 0 + && selectedTokenIds.value.length === tokenList.value.length; +} + // 启用/禁用Token(带防抖) async function handleToggle(row: TokenItem) { if (operatingTokenId.value === row.id) @@ -383,6 +496,27 @@ function isOperating(tokenId: string) { return operatingTokenId.value === tokenId; } +// 删除进度统计 +const deleteProgress = computed(() => { + const total = deleteStatusList.value.length; + const success = deleteStatusList.value.filter(s => s.status === 'success').length; + const failed = deleteStatusList.value.filter(s => s.status === 'failed').length; + const pending = deleteStatusList.value.filter(s => s.status === 'pending' || s.status === 'deleting').length; + return { total, success, failed, pending }; +}); + +// 删除预览对话框标题 +const deletePreviewDialogTitle = computed(() => { + if (isDeleting.value) { + const { success, failed, pending } = deleteProgress.value; + if (pending > 0) { + return `删除中... (${success}/${deleteProgress.value.total})`; + } + return `删除完成`; + } + return '批量删除预览'; +}); + onMounted(async () => { checkMobile(); window.addEventListener('resize', checkMobile); @@ -424,6 +558,36 @@ onUnmounted(() => { 新增 API密钥 + + + 批量删除 + + +
@@ -445,6 +609,29 @@ onUnmounted(() => { :header-cell-style="{ background: '#fafafa', color: '#262626', fontWeight: '600' }" @sort-change="handleSortChange" > + + + + + + { > {{ row.isDisabled ? '启用' : '禁用' }} - - 删除 -
@@ -641,9 +818,36 @@ onUnmounted(() => {
+ +
+ 已选 {{ selectedTokenIds.length }} 项 + + 确认删除 + + + 取消 + +
+
+ {{ token.name }}
@@ -720,15 +924,6 @@ onUnmounted(() => { > {{ token.isDisabled ? '启用' : '禁用' }} - - 删除 -
@@ -795,6 +990,91 @@ onUnmounted(() => { @confirm="handleFormSubmit" @batch-create="handleBatchCreate" /> + + + +
+ +
+
+ + + + + + + + +
+ +
+ + +
+ + 即将删除 {{ deleteItems.length }} 个 API 密钥,删除后将无法恢复,请确认以下信息: +
+ + +
+
+ {{ index + 1 }} + {{ item.name }} + + + + + + + + + + + + +
+
+
+ + +