From 3e148c390eba0a1bbf90a8d2cfd55b23f7279b55 Mon Sep 17 00:00:00 2001 From: xiaohei Date: Sun, 28 Dec 2025 16:35:05 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E7=A7=9F=E6=88=B7=E5=92=8C=E7=AE=A1=E7=90=86=E5=91=98=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在渠道管理页面添加删除租户按钮和确认对话框 - 在渠道设置页面添加删除管理员按钮和确认对话框 - 在渠道编辑对话框中添加删除管理员功能 - 添加完整的删除确认流程,包含警告提示 - 删除成功后自动刷新列表 - 添加API调用调试日志,便于排查问题 - 支持中英文双语显示 --- .../dashboard/components/ChannelsTab.tsx | 104 +++++++- app/channel/dashboard/page.tsx | 246 +++++++++++++++++- lib/api-client.ts | 9 +- 3 files changed, 351 insertions(+), 8 deletions(-) diff --git a/app/admin/dashboard/components/ChannelsTab.tsx b/app/admin/dashboard/components/ChannelsTab.tsx index d04f5367..18743718 100644 --- a/app/admin/dashboard/components/ChannelsTab.tsx +++ b/app/admin/dashboard/components/ChannelsTab.tsx @@ -101,6 +101,9 @@ export function ChannelsTab({ const [isChannelAccessDialogOpen, setIsChannelAccessDialogOpen] = useState(false) const [isApprovalDialogOpen, setIsApprovalDialogOpen] = useState(false) const [isAgentApprovalDialogOpen, setIsAgentApprovalDialogOpen] = useState(false) + const [showDeleteChannelAdminDialog, setShowDeleteChannelAdminDialog] = useState(false) + const [selectedAdminForDelete, setSelectedAdminForDelete] = useState(null) + const [isDeletingChannelAdmin, setIsDeletingChannelAdmin] = useState(false) // Data states const [selectedChannel, setSelectedChannel] = useState(null) @@ -480,12 +483,35 @@ export function ChannelsTab({ console.log("Change password for tenant:", tenantId) } - const handleRemoveAdmin = async (adminId: string) => { - if (!selectedChannel) return + const handleRemoveAdmin = (admin: any) => { + setSelectedAdminForDelete(admin) + setShowDeleteChannelAdminDialog(true) + } + + const handleConfirmDeleteChannelAdmin = async () => { + if (!selectedAdminForDelete || !selectedChannel) return + try { - console.log(`Removing admin from channel:`, selectedChannel.id) + setIsDeletingChannelAdmin(true) + const response = await TaijiAPIClient.deleteAdmin(selectedAdminForDelete.id) + if (response.success) { + // Refresh channel admins list + const adminsResponse = await TaijiAPIClient.getChannelAdmins() + if (adminsResponse.success && adminsResponse.data?.admins) { + // Filter admins for current channel + const channelAdminsList = adminsResponse.data.admins.filter( + (admin: any) => admin.channelId === selectedChannel.id + ) + setChannelAdmins(channelAdminsList) + } + console.log(`Successfully removed admin from channel:`, selectedChannel.id) + } } catch (error) { console.error("Failed to remove admin:", error) + } finally { + setIsDeletingChannelAdmin(false) + setShowDeleteChannelAdminDialog(false) + setSelectedAdminForDelete(null) } } @@ -1353,7 +1379,7 @@ export function ChannelsTab({ + + + + ) } diff --git a/app/channel/dashboard/page.tsx b/app/channel/dashboard/page.tsx index 76e8664a..5d6d4df5 100644 --- a/app/channel/dashboard/page.tsx +++ b/app/channel/dashboard/page.tsx @@ -99,6 +99,9 @@ export default function ChannelDashboard() { const [isAddAdminOpen, setIsAddAdminOpen] = useState(false) const [channelAdmins, setChannelAdmins] = useState([]) const [isLoadingAdmins, setIsLoadingAdmins] = useState(false) + const [showDeleteAdminDialog, setShowDeleteAdminDialog] = useState(false) + const [selectedAdminForDelete, setSelectedAdminForDelete] = useState(null) + const [isDeletingAdmin, setIsDeletingAdmin] = useState(false) const [createAdminForm, setCreateAdminForm] = useState({ name: "", email: "", @@ -130,6 +133,8 @@ export default function ChannelDashboard() { const [showTenantDetailsDialog, setShowTenantDetailsDialog] = useState(false) const [selectedTenantForDetails, setSelectedTenantForDetails] = useState(null) const [isDeletingTenant, setIsDeletingTenant] = useState(false) + const [showDeleteTenantDialog, setShowDeleteTenantDialog] = useState(false) + const [selectedTenantForDelete, setSelectedTenantForDelete] = useState(null) // API Data states const [tenantsData, setTenantsData] = useState([]) @@ -341,16 +346,100 @@ export default function ChannelDashboard() { } } + // Handle delete tenant + const handleDeleteTenant = async () => { + if (!selectedTenantForDelete) return + + try { + setIsDeletingTenant(true) + const response = await TaijiAPIClient.deleteTenant(selectedTenantForDelete.id) + if (response.success) { + toast({ + title: language === "zh" ? "删除成功" : "Deleted Successfully", + description: language === "zh" + ? `租户 "${selectedTenantForDelete.name}" 已被删除` + : `Tenant "${selectedTenantForDelete.name}" has been deleted`, + }) + // Refresh the tenants list + await loadChannelData() + } else { + toast({ + title: language === "zh" ? "删除失败" : "Delete Failed", + description: response.error?.message || (language === "zh" ? "无法删除租户" : "Failed to delete tenant"), + variant: "destructive", + }) + } + } catch (error: any) { + toast({ + title: language === "zh" ? "删除失败" : "Delete Failed", + description: error.message || (language === "zh" ? "删除租户时发生错误" : "Error occurred while deleting tenant"), + variant: "destructive", + }) + } finally { + setIsDeletingTenant(false) + setShowDeleteTenantDialog(false) + setSelectedTenantForDelete(null) + } + } + + // Handle delete admin + const handleDeleteAdmin = async () => { + if (!selectedAdminForDelete) return + + try { + setIsDeletingAdmin(true) + const response = await TaijiAPIClient.deleteAdmin(selectedAdminForDelete.id) + if (response.success) { + toast({ + title: language === "zh" ? "删除成功" : "Deleted Successfully", + description: language === "zh" + ? `管理员 "${selectedAdminForDelete.name}" 已被删除` + : `Administrator "${selectedAdminForDelete.name}" has been deleted`, + }) + // Refresh the admins list + await loadChannelAdmins() + } else { + toast({ + title: language === "zh" ? "删除失败" : "Delete Failed", + description: response.error?.message || (language === "zh" ? "无法删除管理员" : "Failed to delete administrator"), + variant: "destructive", + }) + } + } catch (error: any) { + toast({ + title: language === "zh" ? "删除失败" : "Delete Failed", + description: error.message || (language === "zh" ? "删除管理员时发生错误" : "Error occurred while deleting administrator"), + variant: "destructive", + }) + } finally { + setIsDeletingAdmin(false) + setShowDeleteAdminDialog(false) + setSelectedAdminForDelete(null) + } + } + // Load channel administrators const loadChannelAdmins = async () => { try { setIsLoadingAdmins(true) + console.log("🔍 正在加载渠道管理员列表...") const response = await TaijiAPIClient.getChannelAdmins() + console.log("📥 获取管理员列表响应:", response) if (response.success && response.data?.admins) { + console.log("✅ 成功获取管理员列表:", response.data.admins) setChannelAdmins(response.data.admins) + } else { + console.warn("⚠️ 响应格式异常或无管理员数据:", response) } } catch (error) { - console.error("Failed to load channel admins:", error) + console.error("❌ 加载渠道管理员失败:", error) + toast({ + title: language === "zh" ? "加载失败" : "Failed to Load", + description: language === "zh" + ? "无法加载管理员列表,请检查网络连接和登录状态" + : "Failed to load administrators. Please check network connection and login status.", + variant: "destructive", + }) } finally { setIsLoadingAdmins(false) } @@ -957,6 +1046,16 @@ export default function ChannelDashboard() { {language === "zh" ? "管理计费" : "Manage Billing"} + { + setSelectedTenantForDelete(tenant) + setShowDeleteTenantDialog(true) + }} + className="text-destructive focus:text-destructive" + > + + {language === "zh" ? "删除租户" : "Delete Tenant"} + @@ -1716,6 +1815,17 @@ export default function ChannelDashboard() { : (language === "zh" ? "已停用" : "Inactive")} )} + @@ -2628,6 +2738,140 @@ export default function ChannelDashboard() { + {/* Delete Tenant Confirmation Dialog */} + + + + + {language === "zh" ? "删除租户" : "Delete Tenant"} + + + {language === "zh" + ? "此操作将永久删除该租户及其所有数据。此操作无法撤销。" + : "This action will permanently delete this tenant and all its data. This action cannot be undone."} + + + {selectedTenantForDelete && ( +
+
+
+ +
+

+ {language === "zh" ? "将要删除的租户:" : "Tenant to be deleted:"} +

+

{selectedTenantForDelete.name}

+
+

• {language === "zh" ? "用户数" : "Users"}: {selectedTenantForDelete.users}

+

• {language === "zh" ? "当前方案" : "Current Plan"}: {selectedTenantForDelete.plan}

+

• {language === "zh" ? "状态" : "Status"}: {selectedTenantForDelete.status}

+
+
+
+
+
+

+ ⚠️ {language === "zh" + ? "警告:删除后将无法恢复租户数据,请谨慎操作。" + : "Warning: Tenant data cannot be recovered after deletion. Please proceed with caution."} +

+
+
+ )} + + + + +
+
+ + {/* Delete Admin Confirmation Dialog */} + + + + + {language === "zh" ? "删除管理员" : "Delete Administrator"} + + + {language === "zh" + ? "此操作将永久删除该管理员账号。此操作无法撤销。" + : "This action will permanently delete this administrator account. This action cannot be undone."} + + + {selectedAdminForDelete && ( +
+
+
+ +
+

+ {language === "zh" ? "将要删除的管理员:" : "Administrator to be deleted:"} +

+

{selectedAdminForDelete.name}

+
+

• {language === "zh" ? "邮箱" : "Email"}: {selectedAdminForDelete.email}

+

• {language === "zh" ? "角色" : "Role"}: { + selectedAdminForDelete.role === "channel_admin" + ? (language === "zh" ? "渠道管理员" : "Channel Admin") + : selectedAdminForDelete.role === "billing_admin" + ? (language === "zh" ? "计费管理员" : "Billing Admin") + : (language === "zh" ? "运营管理员" : "Operations Admin") + }

+ {selectedAdminForDelete.status && ( +

• {language === "zh" ? "状态" : "Status"}: { + selectedAdminForDelete.status === "active" + ? (language === "zh" ? "活跃" : "Active") + : (language === "zh" ? "已停用" : "Inactive") + }

+ )} +
+
+
+
+
+

+ ⚠️ {language === "zh" + ? "警告:删除后将无法恢复管理员账号及其权限,请谨慎操作。" + : "Warning: Administrator account and permissions cannot be recovered after deletion. Please proceed with caution."} +

+
+
+ )} + + + + +
+
+ ) diff --git a/lib/api-client.ts b/lib/api-client.ts index e4439539..e9fe844b 100644 --- a/lib/api-client.ts +++ b/lib/api-client.ts @@ -699,9 +699,12 @@ export class TaijiAPIClient { * GET /api/channel/admins */ static async getChannelAdmins() { - const response = await fetch(`${API_BASE_URLS.mcpServer}/api/channel/admins`, { - headers: buildHeaders(), - }) + const url = `${API_BASE_URLS.mcpServer}/api/channel/admins` + console.log("🌐 API请求: GET", url) + const headers = buildHeaders() + console.log("📋 请求头:", headers) + const response = await fetch(url, { headers }) + console.log("📨 响应状态:", response.status, response.statusText) return handleResponse(response) }