前情回顾

在上一篇中,我们使用 Trae 成功初始化了 Next.js 16 + Tailwind CSS 的项目环境,并配置好了 PostgreSQL 数据库连接。

本篇目标:

  1. 安装并配置 Shadcn UI(目前 React 生态最流行的组件库)。
  2. 梳理 Next.js App Router 的文件目录结构
  3. 手写代码:实现响应式的 侧边栏 (Sidebar)Dashboard 布局

1. 为什么选择 Shadcn UI?

很多初学者习惯用 Ant Design 或 Material UI。但 Shadcn UI 不同,它不是一个通过 npm 安装的黑盒库,而是一个代码生成器

当你运行命令时,它会把组件的源码直接“复制”到你的项目中。

  • 优点:你可以随意修改组件代码,拥有 100% 的控制权。
  • 风格:极简、现代,完美契合 Tailwind CSS。

1.1 初始化 Shadcn

打开 Trae 的终端,输入:

npx shadcn@latest init

在这里插入图片描述

按照以下选项进行配置(这是教培管家系统的设计风格):

Which color would you like to use as base color? › Zinc (中性灰,高级感)

在这里插入图片描述

初始化完成后,你会发现项目根目录多了一个 components.json,且 lib/utils.ts 文件被自动创建了(用于处理 CSS 类名合并)。

在这里插入图片描述
在这里插入图片描述

1.2 安装基础图标库

我们需要一套漂亮的图标,这里使用 Lucide React(Shadcn 官方推荐):

npm install lucide-react

在这里插入图片描述


2. 目录结构规划

在 Next.js 16 (App Router) 中,文件放哪里很有讲究。为了保证项目后期不乱,我们先建好“房间”:

app 目录下,创建以下文件夹:

  • app/ui:存放所有的 UI 组件(侧边栏、卡片、按钮等)。
  • app/lib:存放数据定义、工具函数。
  • app/dashboard核心业务页面(在这个文件夹下的 page.tsx 只有登录后才能访问)。

在这里插入图片描述


3. 实战:搭建 Dashboard 布局

我们要实现的是经典的 “左侧固定侧边栏 + 右侧内容自适应” 布局。

3.1 第一步:编写侧边栏组件 (SideNav)

新建文件 app/ui/dashboard/sidenav.tsx

我们将使用 Next.js 的 <Link> 组件来实现无刷新跳转,用 Tailwind 实现样式。

import Link from 'next/link';
import { 
  LayoutDashboard, 
  Building2, 
  Users, 
  ShieldCheck, 
  Menu, 
  GraduationCap, 
  LogOut 
} from 'lucide-react';

// 定义菜单数据(暂时写死,后续会从数据库读取)
const links = [
  { name: '工作台', href: '/dashboard', icon: LayoutDashboard },
  { name: '机构管理', href: '/dashboard/dept', icon: Building2 },
  { name: '人员管理', href: '/dashboard/user', icon: Users },
  { name: '角色管理', href: '/dashboard/role', icon: ShieldCheck },
  { name: '菜单配置', href: '/dashboard/menu', icon: Menu },
];

export default function SideNav() {
  return (
    <div className="flex h-full flex-col px-3 py-4 md:px-2">
      {/* 1. Logo 区域 */}
      <Link
        className="mb-2 flex h-20 items-end justify-start rounded-md bg-zinc-900 p-4 md:h-40"
        href="/"
      >
        <div className="w-32 text-white md:w-40 flex items-center gap-2">
          <GraduationCap className="h-8 w-8" />
          <span className="text-lg font-bold">教培管家</span>
        </div>
      </Link>

      {/* 2. 导航链接区域 */}
      <div className="flex grow flex-row justify-between space-x-2 md:flex-col md:space-x-0 md:space-y-2">
        {links.map((link) => {
          const LinkIcon = link.icon;
          return (
            <Link
              key={link.name}
              href={link.href}
              className="flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3"
            >
              <LinkIcon className="w-6" />
              <p className="hidden md:block">{link.name}</p>
            </Link>
          );
        })}
        
        {/* 占位符,把登出按钮顶到底部 */}
        <div className="hidden h-auto w-full grow rounded-md bg-gray-50 md:block"></div>
        
        {/* 3. 登出按钮 */}
        <form>
          <button className="flex h-[48px] w-full grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3">
            <LogOut className="w-6" />
            <div className="hidden md:block">退出登录</div>
          </button>
        </form>
      </div>
    </div>
  );
}

AI 编程小技巧:
如果你不理解上面的 Tailwind 类名(如 md:hidden),可以在 Trae 的聊天框里选中代码问 Kimi:“解释一下这段代码中 Tailwind 类名的作用,特别是响应式部分。

在这里插入图片描述

3.2 第二步:创建布局文件 (Layout)

Next.js 的 layout.tsx 是一个非常强大的特性。它允许我们在多个页面之间共享 UI(比如侧边栏),当页面切换时,侧边栏不会重新渲染,状态得以保留。

新建文件 app/dashboard/layout.tsx

import SideNav from '@/app/ui/dashboard/sidenav';
 
export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <div className="flex h-screen flex-col md:flex-row md:overflow-hidden">
      {/* 侧边栏区域:在移动端是顶部导航,在桌面端是左侧固定 */}
      <div className="w-full flex-none md:w-64 bg-white border-r">
        <SideNav />
      </div>
      
      {/* 主内容区域:可滚动 */}
      <div className="flex-grow p-6 md:overflow-y-auto md:p-12 bg-gray-50">
        {children}
      </div>
    </div>
  );
}

在这里插入图片描述

3.3 第三步:创建第一个页面

为了验证效果,我们需要一个页面。
新建 app/dashboard/page.tsx

export default function Page() {
  return (
    <div>
      <h1 className="text-2xl font-bold mb-4">工作台 Dashboard</h1>
      <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
        {/* 这里以后放统计卡片 */}
        <div className="h-32 rounded-xl bg-white p-4 shadow-sm border border-gray-100">
           <p className="text-sm text-gray-500">总学员数</p>
           <p className="text-2xl font-bold">1,203</p>
        </div>
        <div className="h-32 rounded-xl bg-white p-4 shadow-sm border border-gray-100">
           <p className="text-sm text-gray-500">本月营收</p>
           <p className="text-2xl font-bold">¥ 45,231</p>
        </div>
      </div>
    </div>
  );
}

在这里插入图片描述


4. 见证奇迹

现在,在终端运行:

npm run dev

在这里插入图片描述

打开浏览器访问 http://localhost:3000/dashboard

你应该能看到一个专业的后台管理界面:

  1. 左侧是黑色的 Logo 和 灰色的导航菜单。
  2. 右侧是灰色的背景和白色的数据卡片。
  3. 尝试缩小浏览器窗口,你会发现侧边栏自动变为了顶部的移动端导航(这是 Tailwind md: 前缀的功劳)。

在这里插入图片描述


5 vibe coding

怎么说呢,默认生成的不是太好看,我们来vibe coding一下

先在trae里打开我们的页面,然后用选中元素选中我们左侧的工作台菜单,这样右边的对话框可以看到一个a标签,输入提示词

工作台增加一个选中的效果

在这里插入图片描述
点击发送的箭头,让ai帮我们优化一下

ai调整好了之后,点击代码审查,可以看一下他帮我们加了什么
在这里插入图片描述
确认无误后,点击对号接受修改,再次刷新页面,当前工作台就高亮显示了

在这里插入图片描述
再一个我们的LOGO有点过于高了,我们让AI优化一下,告诉他调整到合适的高度
在这里插入图片描述
优化之后,看起来就顺眼多了
在这里插入图片描述
在当下,大模型的多模态能力,结合trae的选中元素功能,组合起来,只要你把意图表达明白,大模型就可以又好又快的实现功能。

确认修改后,我们让kimi把代码提交到我们的远程仓库里
在这里插入图片描述
有些命令是需要我们点击运行的,点运行,一路按照提示,就可以看到代码被正确的提交到了仓库里
在这里插入图片描述

下一步预告

现在的页面虽然好看,但数据是“死”的,菜单也是写死的。

下一篇 中,我们将深入后端核心:

  1. 使用 Seed 脚本 填充数据库(包含真实的部门、角色、菜单数据)。
  2. 学习如何用 Next.js 的 Server Actions 从数据库读取数据。
  3. 让左侧菜单变成从数据库动态加载的“活”菜单。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐