feat(ai): 统一 AI 配置入口到 /admin/ai-settings

## 新增
- 创建 /admin/ai-settings 统一配置页(AiProviderSettingsCard + AiUsageDashboard)
- admin 侧边栏新增"AI 配置"菜单项(权限 AI_CONFIGURE,图标 Sparkles)
- 新增 deleteAiProvider 数据访问层(事务删除 + 自动转移默认)
- 新增 deleteAiProviderAction Server Action(Zod 校验 + 权限校验)
- AiProviderSettingsCard 新增删除按钮(AlertDialog 确认 + destructive 变体)
- 新增 i18n 翻译键(delete/deleteConfirm/deleteSuccess 等,zh-CN + en)

## 移除
- 从 /settings 移除 AI 标签页(原 VALID_TABS 含 "ai",现仅 4 标签页)
- 从考试页面移除 AI 配置弹窗(Dialog + AiProviderSettingsCard 内嵌)
- 从 ai-provider-selector.tsx 移除配置弹窗(managePanel/manageOpen props)
- 移除 settings-view.tsx 中 canConfigureAi 逻辑和未使用 import

## 变更
- 考试页面"管理"按钮改为 Link 跳转到 /admin/ai-settings
- ai-provider-selector.tsx"管理"按钮改为 Link 跳转到 /admin/ai-settings
- exam-form.tsx 移除 providerDialogOpen/providerDialogKey 状态
- 修正架构文档 004 中 Action 命名(getAiProvidersAction → getAiProviderSummaries 等)

## 架构文档同步
- 004 更新 settings 模块章节(V3 标记/修正 Action 名称/新增 deleteAiProvider)
- 005 新增 deleteAiProviderAction 节点 + /admin/ai-settings 路由
This commit is contained in:
SpecialX
2026-06-23 19:33:28 +08:00
parent d884c6d513
commit 7e320d78c1
13 changed files with 618 additions and 213 deletions

View File

@@ -1,5 +1,6 @@
"use client"
import Link from "next/link"
import { useTranslations } from "next-intl"
import { Settings } from "lucide-react"
import {
@@ -17,14 +18,6 @@ import {
SelectTrigger,
SelectValue,
} from "@/shared/components/ui/select"
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/shared/components/ui/dialog"
import { Button } from "@/shared/components/ui/button"
import type { Control } from "react-hook-form"
@@ -47,11 +40,6 @@ type AiProviderSelectorProps = {
loading?: boolean
/** Provider 标签映射 */
providerLabels?: Record<string, string>
/** 管理面板触发器 */
managePanel?: React.ReactNode
/** 管理面板打开状态 */
manageOpen?: boolean
onManageOpenChange?: (open: boolean) => void
}
/**
@@ -59,6 +47,8 @@ type AiProviderSelectorProps = {
*
* 可复用的表单字段组件,用于选择 AI Provider。
* 从 exam-ai-generator.tsx 抽取,支持在任何需要 AI Provider 选择的表单中复用。
*
* V3移除内嵌配置弹窗"管理"按钮改为跳转到 /admin/ai-settings 统一配置页。
*/
export function AiProviderSelector({
control,
@@ -66,9 +56,6 @@ export function AiProviderSelector({
providers,
loading = false,
providerLabels,
managePanel,
manageOpen,
onManageOpenChange,
}: AiProviderSelectorProps): React.ReactNode {
const t = useTranslations("ai")
@@ -80,28 +67,12 @@ export function AiProviderSelector({
<FormItem>
<div className="flex items-center justify-between gap-2">
<FormLabel>{t("provider.label")}</FormLabel>
{managePanel ? (
<Dialog open={manageOpen} onOpenChange={onManageOpenChange}>
<DialogTrigger asChild>
<Button
type="button"
variant="ghost"
size="sm"
className="h-7 px-2 text-muted-foreground hover:text-foreground"
>
<Settings className="mr-1 h-3.5 w-3.5" />
{t("provider.manage")}
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[960px]">
<DialogHeader>
<DialogTitle>{t("provider.manageTitle")}</DialogTitle>
<DialogDescription>{t("provider.manageDescription")}</DialogDescription>
</DialogHeader>
{managePanel}
</DialogContent>
</Dialog>
) : null}
<Button asChild type="button" variant="ghost" size="sm" className="h-7 px-2 text-muted-foreground hover:text-foreground">
<Link href="/admin/ai-settings">
<Settings className="mr-1 h-3.5 w-3.5" />
{t("provider.manage")}
</Link>
</Button>
</div>
<Select value={field.value as string} onValueChange={field.onChange} disabled={loading}>
<FormControl>