完善skills;测试用例生成页面功能初步实现

This commit is contained in:
2026-05-05 19:45:33 +08:00
parent 0c2ed67e2a
commit 69b49d28b2
35 changed files with 4396 additions and 658 deletions

View File

@@ -1,29 +1,32 @@
"use client";
import { useState, useEffect } from "react";
import { useEffect, useState } from "react";
import { usePathname, useRouter } from "next/navigation";
import Link from "next/link";
import {
Book,
ChevronRight,
FileText,
MessageSquare,
LogOut,
Menu,
MessageSquare,
Search,
User,
} from "lucide-react";
import Breadcrumb from "@/components/ui/breadcrumb";
import { cn } from "@/lib/utils";
type NavigationChild = {
name: string;
href: string;
};
type NavigationItem = {
name: string;
icon: React.ComponentType<{ className?: string }>;
href?: string;
children?: Array<{
name: string;
href: string;
}>;
children?: NavigationChild[];
defaultOpen?: boolean;
};
export default function DashboardLayout({
@@ -34,9 +37,10 @@ export default function DashboardLayout({
const router = useRouter();
const pathname = usePathname();
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [isDocProcessingOpen, setIsDocProcessingOpen] = useState(
pathname.startsWith("/dashboard/doc-processing")
);
const [openGroups, setOpenGroups] = useState<Record<string, boolean>>({
knowledge: pathname.startsWith("/dashboard/knowledge"),
"doc-processing": pathname.startsWith("/dashboard/doc-processing"),
});
useEffect(() => {
const token = localStorage.getItem("token");
@@ -51,13 +55,31 @@ export default function DashboardLayout({
};
useEffect(() => {
if (pathname.startsWith("/dashboard/knowledge")) {
setOpenGroups((prev) => ({ ...prev, knowledge: true }));
}
if (pathname.startsWith("/dashboard/doc-processing")) {
setIsDocProcessingOpen(true);
setOpenGroups((prev) => ({ ...prev, "doc-processing": true }));
}
}, [pathname]);
const toggleGroup = (key: string) => {
setOpenGroups((prev) => ({
...prev,
[key]: !prev[key],
}));
};
const navigation: NavigationItem[] = [
{ name: "知识库", href: "/dashboard/knowledge", icon: Book },
{
name: "知识库",
icon: Book,
defaultOpen: true,
children: [
{ name: "文档知识库", href: "/dashboard/knowledge/document" },
{ name: "代码知识库", href: "/dashboard/knowledge/code" },
],
},
{ name: "对话", href: "/dashboard/chat", icon: MessageSquare },
{
name: "文档处理",
@@ -80,42 +102,39 @@ export default function DashboardLayout({
return (
<div className="min-h-screen bg-background">
{/* Mobile menu button */}
<div className="lg:hidden fixed top-0 left-0 m-4 z-50">
<div className="fixed left-0 top-0 z-50 m-4 lg:hidden">
<button
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="p-2 rounded-md bg-primary text-primary-foreground"
className="rounded-md bg-primary p-2 text-primary-foreground"
>
<Menu className="h-6 w-6" />
</button>
</div>
{/* Sidebar */}
<div
className={`fixed inset-y-0 left-0 z-40 w-64 transform bg-card border-r transition-transform duration-200 ease-in-out lg:translate-x-0 ${
className={`fixed inset-y-0 left-0 z-40 w-64 transform border-r bg-card transition-transform duration-200 ease-in-out lg:translate-x-0 ${
isMobileMenuOpen ? "translate-x-0" : "-translate-x-full"
}`}
>
<div className="flex h-full flex-col">
{/* Sidebar header */}
<div className="flex h-16 items-center border-b pl-8">
<Link
href="/dashboard"
className="flex items-center text-lg font-semibold hover:text-primary transition-colors"
className="flex items-center text-lg font-semibold transition-colors hover:text-primary"
>
<img
src="/logo.svg"
alt="标志"
className="w-16 h-16 rounded-lg"
/>
<img src="/logo.svg" alt="标志" className="h-16 w-16 rounded-lg" />
RAG
</Link>
</div>
{/* Navigation */}
<nav className="flex-1 space-y-2 px-4 py-6">
{navigation.map((item) => {
if (item.children) {
const groupKey =
item.children[0]?.href.split("/")[2] === "knowledge"
? "knowledge"
: "doc-processing";
const isOpen = openGroups[groupKey] ?? item.defaultOpen ?? false;
const hasActiveChild = item.children.some((child) =>
pathname.startsWith(child.href)
);
@@ -124,7 +143,7 @@ export default function DashboardLayout({
<div key={item.name} className="space-y-2">
<button
type="button"
onClick={() => setIsDocProcessingOpen((prev) => !prev)}
onClick={() => toggleGroup(groupKey)}
className={cn(
"group flex w-full items-center rounded-lg px-4 py-3 text-sm font-medium transition-all duration-200",
hasActiveChild
@@ -136,7 +155,7 @@ export default function DashboardLayout({
className={cn(
"mr-3 h-5 w-5 transition-transform duration-200",
hasActiveChild
? "text-primary scale-110"
? "scale-110 text-primary"
: "group-hover:scale-110"
)}
/>
@@ -144,12 +163,12 @@ export default function DashboardLayout({
<ChevronRight
className={cn(
"ml-auto h-4 w-4 transition-transform duration-200",
isDocProcessingOpen ? "rotate-90" : ""
isOpen && "rotate-90"
)}
/>
</button>
{isDocProcessingOpen && (
{isOpen && (
<div className="ml-7 space-y-1 border-l pl-3">
{item.children.map((child) => {
const isChildActive = pathname.startsWith(child.href);
@@ -188,18 +207,20 @@ export default function DashboardLayout({
<Link
key={item.name}
href={item.href || "/dashboard"}
className={`group flex items-center rounded-lg px-4 py-3 text-sm font-medium transition-all duration-200 ${
className={cn(
"group flex items-center rounded-lg px-4 py-3 text-sm font-medium transition-all duration-200",
isActive
? "bg-gradient-to-r from-primary/10 to-primary/5 text-primary shadow-sm"
: "text-muted-foreground hover:bg-accent/50 hover:text-foreground hover:shadow-sm"
}`}
)}
>
<item.icon
className={`mr-3 h-5 w-5 transition-transform duration-200 ${
className={cn(
"mr-3 h-5 w-5 transition-transform duration-200",
isActive
? "text-primary scale-110"
? "scale-110 text-primary"
: "group-hover:scale-110"
}`}
)}
/>
<span className="font-medium">{item.name}</span>
{isActive && (
@@ -209,11 +230,11 @@ export default function DashboardLayout({
);
})}
</nav>
{/* User profile and logout */}
<div className="border-t p-4 space-y-4">
<div className="space-y-4 border-t p-4">
<button
onClick={handleLogout}
className="flex w-full items-center rounded-lg px-3 py-2.5 text-sm font-medium text-destructive hover:bg-destructive/10 transition-colors duration-200"
className="flex w-full items-center rounded-lg px-3 py-2.5 text-sm font-medium text-destructive transition-colors duration-200 hover:bg-destructive/10"
>
<LogOut className="mr-3 h-4 w-4" />
退
@@ -222,9 +243,8 @@ export default function DashboardLayout({
</div>
</div>
{/* Main content */}
<div className="lg:pl-64">
<main className="min-h-screen py-6 px-4 sm:px-6 lg:px-8">
<main className="min-h-screen px-4 py-6 sm:px-6 lg:px-8">
<Breadcrumb />
{children}
</main>
@@ -237,10 +257,15 @@ export const dashboardConfig = {
mainNav: [],
sidebarNav: [
{
title: "知识库",
href: "/dashboard/knowledge",
title: "文档知识库",
href: "/dashboard/knowledge/document",
icon: "database",
},
{
title: "代码知识库",
href: "/dashboard/knowledge/code",
icon: "braces",
},
{
title: "对话",
href: "/dashboard/chat",