目录
- [1 环境准备](#1 环境准备)
- [2 初始化 Next.js](#2 初始化 Next.js)
- [3 集成 Shadcn UI](#3 集成 Shadcn UI)
- [4 清理与重置](#4 清理与重置)
-
- [4.1 清空全局样式](#4.1 清空全局样式)
- [4.2 重置首页](#4.2 重置首页)
- [5 目录结构规划](#5 目录结构规划)
- [6. 启动验证](#6. 启动验证)
我们的低代码小程序已经搭建的差不多了,为了对比,我们再用纯代码重新架构一遍。第一篇先搭建脚手架
1 环境准备
纯代码我们的技术栈还是基于javascript,首先确保你安装了nodejs,打开cmd命令行查看一下安装的版本
bash
node -v

2 初始化 Next.js
nextjs作为react的全栈框架非常适合作为我们的框架使用,使用命令行安装nextjs
bash
npx create-next-app@latest hospital-booking-system

3 集成 Shadcn UI
用trae打开我们按照好的目录,新建一个终端

运行初始化命令:
bash
npx shadcn@latest init

安装基础组件
bash
npx shadcn@latest add button input card

4 清理与重置
Next.js 默认生成的首页太花哨了,我们需要一个干净的画板。
4.1 清空全局样式
打开 src/app/globals.css,删除所有内容,贴入如下代码:
bash
@import "tailwindcss";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
:root {
--muted: oklch(0.967 0.001 286.375);
--muted-foreground: oklch(0.552 0.016 285.938);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.141 0.005 285.823);
--card: oklch(1 0 0);
--card-foreground: oklch(0.141 0.005 285.823);
--border: oklch(0.92 0.004 286.32);
--input: oklch(0.92 0.004 286.32);
--primary: oklch(0.21 0.006 285.885);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.967 0.001 286.375);
--secondary-foreground: oklch(0.21 0.006 285.885);
--accent: oklch(0.967 0.001 286.375);
--accent-foreground: oklch(0.21 0.006 285.885);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: 0% 0% 98%;
--ring: oklch(0.705 0.015 286.067);
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.141 0.005 285.823);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.141 0.005 285.823);
--sidebar-primary: oklch(0.21 0.006 285.885);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.967 0.001 286.375);
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
--sidebar-border: oklch(0.92 0.004 286.32);
--sidebar-ring: oklch(0.705 0.015 286.067);
}
.dark {
--background: oklch(0.141 0.005 285.823);
--foreground: oklch(0.985 0 0);
--muted: oklch(0.274 0.006 286.033);
--muted-foreground: oklch(0.705 0.015 286.067);
--popover: oklch(0.21 0.006 285.885);
--popover-foreground: oklch(0.985 0 0);
--card: oklch(0.21 0.006 285.885);
--card-foreground: oklch(0.985 0 0);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--primary: oklch(0.92 0.004 286.32);
--primary-foreground: oklch(0.21 0.006 285.885);
--secondary: oklch(0.274 0.006 286.033);
--secondary-foreground: oklch(0.985 0 0);
--accent: oklch(0.274 0.006 286.033);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--destructive-foreground: 0% 0% 98%;
--ring: oklch(0.552 0.016 285.938);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.21 0.006 285.885);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.274 0.006 286.033);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.552 0.016 285.938);
}
@theme {
--color-background: oklch(var(--background));
--color-foreground: oklch(var(--foreground));
--color-muted: oklch(var(--muted));
--color-muted-foreground: oklch(var(--muted-foreground));
--color-popover: oklch(var(--popover));
--color-popover-foreground: oklch(var(--popover-foreground));
--color-card: oklch(var(--card));
--color-card-foreground: oklch(var(--card-foreground));
--color-border: oklch(var(--border));
--color-input: oklch(var(--input));
--color-primary: oklch(var(--primary));
--color-primary-foreground: oklch(var(--primary-foreground));
--color-secondary: oklch(var(--secondary));
--color-secondary-foreground: oklch(var(--secondary-foreground));
--color-accent: oklch(var(--accent));
--color-accent-foreground: oklch(var(--accent-foreground));
--color-destructive: oklch(var(--destructive));
--color-destructive-foreground: oklch(var(--destructive-foreground));
--color-ring: oklch(var(--ring));
--radius: var(--radius);
}
@theme inline {
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}

4.2 重置首页
打开 src/app/page.tsx,替换为以下简单的代码。我们顺便用一下刚才安装的 Button,验证 Shadcn 是否生效。
bash
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
export default function Home() {
return (
<div className="flex h-screen w-full items-center justify-center bg-gray-50">
<Card className="w-[350px]">
<CardHeader>
<CardTitle>系统初始化成功</CardTitle>
</CardHeader>
<CardContent>
<p className="mb-4 text-sm text-muted-foreground">
Next.js + Shadcn 医院预约系统环境已准备就绪。
</p>
<Button className="w-full">进入系统</Button>
</CardContent>
</Card>
</div>
);
}

5 目录结构规划
在开始写业务逻辑前,我们需要按照**"PC端与移动端分离"**的策略规划路由。在 src/app 下,我们将建立完全隔离的布局体系。
请在手动创建以下文件夹结构:
bash
src/
├── app/
│ ├── (auth)/ # 路由组:登录注册(不影响URL路径)
│ │ ├── login/ # localhost:3000/login
│ │ └── register/ # localhost:3000/register
│ ├── admin/ # PC管理端
│ │ ├── dashboard/ # localhost:3000/admin/dashboard
│ │ ├── department/ # 科室管理
│ │ ├── doctors/ # 医生管理
│ │ └── layout.tsx # ★ PC端专用布局(侧边栏+顶栏)
│ ├── mobile/ # 移动端/小程序H5
│ │ ├── home/ # localhost:3000/mobile/home
│ │ └── layout.tsx # ★ 移动端专用布局(底部导航)
│ ├── api/ # 后端接口
│ ├── layout.tsx # 全局根布局
│ └── page.tsx # 根入口(通常重定向到 login 或 home)
├── components/
│ ├── ui/ # shadcn 通用组件
│ ├── admin/ # PC端专用业务组件 (如 Sidebar)
│ └── mobile/ # 移动端专用业务组件 (如 TabBar)
└── lib/
├── db.ts # 数据库连接单例
└── utils.ts # 工具函数

6. 启动验证
最后,让我们启动开发服务器:
bash
npm run dev

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