2026年了,不会还有人觉得"会用Copilot写注释"就等于掌握AI编程吧?

说实话,我在找实习的过程中发现,面试官现在最喜欢问的问题已经从"你用过什么框架"变成了"你用过什么AI编程工具?怎么用的?用的怎么样?"

今天这篇博客,不整虚的,直接带你用 Cursor + React + TypeScript + Tailwind CSS 从零搭建一个完整的电商后台管理系统。手把手的那种,有截图、有代码、有踩坑记录。

读完这篇,你收获的不只是一个项目,更是一套AI时代的开发方法论

一、项目介绍:为什么选这个场景?

1.1 解决什么问题

传统后台管理系统开发有几个痛点:

  • 表单组件重复性高(CRUD表单、筛选面板、分页器)
  • 状态管理逻辑复杂(筛选、排序、批量操作)
  • 样式开发耗时(响应式布局、深浅色切换)
  • 代码规范难以统一(多人协作时尤其明显)

用AI辅助开发,可以把 70%的重复代码 交给AI处理,开发者专注于业务逻辑和架构设计。

1.2 技术栈选择

技术 选择理由
React 19 支持新特性(Compiler、Server Components概念)
TypeScript 5.5 类型安全,减少AI生成代码的运行时错误
Tailwind CSS 4.0 AI生成样式代码最友好,CSS-in-JS容易被AI写崩
Zustand 状态管理简洁,AI生成代码质量高
Cursor 支持整个项目的AI协作,不是单文件补全

1.3 最终效果

我们最终会实现:

  • 商品管理(列表、新增、编辑、删除)
  • 订单管理(状态流转、筛选)
  • 数据看板(图表、统计)
  • 深浅色模式切换
  • 响应式布局

二、环境准备:Cursor配置

2.1 安装与基础配置

bash

# 创建项目
npm create vite@latest admin-dashboard -- --template react-ts
cd admin-dashboard
npm install

# 安装依赖
npm install tailwindcss @tailwindcss/vite postcss autoprefixer
npm install zustand axios @tanstack/react-query
npm install lucide-react clsx tailwind-merge
npm install -D @types/node

2.2 Cursor配置文件

在项目根目录创建 .cursorrules 文件,这是让AI理解你项目规范的关键:

markdown

# 语言和框架
- 框架:React 19 + TypeScript 5.5
- 样式:Tailwind CSS 4.0
- 状态管理:Zustand

# 代码规范
- 组件使用 PascalCase(如 ProductList.tsx)
- Hooks 使用 camelCase,use开头(如 useProducts.ts)
- 工具函数使用 camelCase(如 formatCurrency.ts)
- 类型定义单独放在 types/ 目录
- 组件放在 components/ 目录,按功能模块分组

# Tailwind CSS 规范
- 使用 clsx + tailwind-merge 处理动态类名
- 颜色使用 CSS 变量,便于主题切换
- 响应式断点:sm(640px)、md(768px)、lg(1024px)、xl(1280px)

# AI 生成规范
- 生成代码必须包含完整的类型定义
- 组件必须添加 JSDoc 注释说明用途
- Hooks 必须标注依赖项和副作用
- 错误处理必须完整,不能 try-catch 后空着

# 禁止事项
- 禁止使用 any 类型(除非绝对必要)
- 禁止内联复杂样式逻辑
- 禁止生成重复的代码(先检查是否有可复用组件)

2.3 tsconfig.json 配置优化

json

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@/components/*": ["src/components/*"],
      "@/hooks/*": ["src/hooks/*"],
      "@/stores/*": ["src/stores/*"],
      "@/types/*": ["src/types/*"]
    }
  }
}

三、核心功能开发:手把手实操

3.1 商品列表页:AI生成组件

Step 1:用AI分析需求,生成技术方案

在Cursor中输入以下提示词:

plaintext

我正在开发一个电商后台管理系统的商品管理模块,需要你帮我分析需求并生成技术方案。

需求:
1. 商品列表展示(图片、名称、价格、评分、库存状态、上下架状态)
2. 筛选功能(按分类、价格区间、评分、上下架状态筛选)
3. 排序功能(按价格、评分、销量排序)
4. 分页功能(支持页码跳转和每页数量选择)
5. 批量操作(批量上下架、批量删除)
6. 响应式设计(桌面、平板、移动端适配)

请给出:
1. 组件拆分方案
2. 状态管理方案(哪些数据放Zustand,哪些放组件state)
3. API接口设计(模拟RESTful风格)
4. TypeScript类型定义

AI输出的技术方案:

typescript

Step 2:让AI生成商品卡片组件

继续在Cursor中输入:

plaintext

基于上面的类型定义,请帮我生成 ProductCard 组件。

要求:
1. 使用 Tailwind CSS 实现样式
2. 支持深色模式(使用 dark: 前缀)
3. 点击卡片跳转到编辑页
4. 悬停显示快捷操作按钮(上架/下架、编辑、删除)
5. 库存不足时显示红色警告
6. 图片使用懒加载,加载中显示骨架屏
7. 使用 useCartStore 管理收藏状态(zustand store)

请输出完整的组件代码,包含所有导入和类型定义。

AI生成的代码(关键部分):

tsx

// src/components/product/ProductCard.tsx
import React, { memo } from 'react';
import { Star, Edit2, Trash2, Eye, EyeOff } from 'lucide-react';
import { clsx } from 'clsx';
import type { Product } from '@/types/product';
import { useProductStore } from '@/stores/productStore';

interface ProductCardProps {
  product: Product;
  onEdit: (product: Product) => void;
  onDelete: (product: Product) => void;
  onToggleStatus: (product: Product) => void;
}

export const ProductCard = memo<ProductCardProps>(({
  product,
  onEdit,
  onDelete,
  onToggleStatus,
}) => {
  const isLowStock = product.stock < 10;
  const discount = Math.round(
    ((product.originalPrice - product.price) / product.originalPrice) * 100
  );

  return (
    <div
      className={clsx(
        'group relative bg-white dark:bg-gray-800 rounded-xl shadow-sm',
        'border border-gray-200 dark:border-gray-700',
        'hover:shadow-lg transition-all duration-200',
        'hover:border-blue-300 dark:hover:border-blue-600'
      )}
    >
      {/* 图片区域 */}
      <div className="relative aspect-square overflow-hidden rounded-t-xl bg-gray-100 dark:bg-gray-700">
        <img
          src={product.images[0] || '/placeholder.png'}
          alt={product.name}
          loading="lazy"
          className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
        />
        
        {/* 折扣标签 */}
        {discount > 0 && (
          <span className="absolute top-3 left-3 bg-red-500 text-white text-xs font-bold px-2 py-1 rounded-full">
            -{discount}%
          </span>
        )}

        {/* 状态标签 */}
        <span
          className={clsx(
            'absolute top-3 right-3 text-xs font-medium px-2 py-1 rounded-full',
            product.status === 'active'
              ? 'bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300'
              : 'bg-gray-100 text-gray-600 dark:bg-gray-600 dark:text-gray-300'
          )}
        >
          {product.status === 'active' ? '上架' : '下架'}
        </span>

        {/* 悬停操作按钮 */}
        <div
          className={clsx(
            'absolute inset-0 bg-black/50 flex items-center justify-center gap-2',
            'opacity-0 group-hover:opacity-100 transition-opacity'
          )}
        >
          <button
            onClick={() => onEdit(product)}
            className="p-2 bg-white rounded-lg hover:bg-gray-100 transition-colors"
            title="编辑"
          >
            <Edit2 className="w-5 h-5 text-gray-700" />
          </button>
          <button
            onClick={() => onToggleStatus(product)}
            className="p-2 bg-white rounded-lg hover:bg-gray-100 transition-colors"
            title={product.status === 'active' ? '下架' : '上架'}
          >
            {product.status === 'active' ? (
              <EyeOff className="w-5 h-5 text-gray-700" />
            ) : (
              <Eye className="w-5 h-5 text-gray-700" />
            )}
          </button>
          <button
            onClick={() => onDelete(product)}
            className="p-2 bg-red-500 rounded-lg hover:bg-red-600 transition-colors"
            title="删除"
          >
            <Trash2 className="w-5 h-5 text-white" />
          </button>
        </div>
      </div>

      {/* 商品信息 */}
      <div className="p-4">
        <h3 className="text-lg font-semibold text-gray-900 dark:text-white line-clamp-2 mb-2">
          {product.name}
        </h3>

        {/* 价格和评分 */}
        <div className="flex items-center justify-between mb-2">
          <div className="flex items-baseline gap-2">
            <span className="text-xl font-bold text-red-500">
              ¥{product.price.toFixed(2)}
            </span>
            <span className="text-sm text-gray-400 line-through">
              ¥{product.originalPrice.toFixed(2)}
            </span>
          </div>
          <div className="flex items-center gap-1">
            <Star className="w-4 h-4 text-yellow-400 fill-yellow-400" />
            <span className="text-sm text-gray-600 dark:text-gray-400">
              {product.rating.toFixed(1)}
            </span>
          </div>
        </div>

        {/* 库存状态 */}
        <div className="flex items-center justify-between text-sm">
          <span className="text-gray-500 dark:text-gray-400">
            销量 {product.salesCount}
          </span>
          {isLowStock ? (
            <span className="text-red-500 font-medium">
              库存不足 ({product.stock})
            </span>
          ) : (
            <span className="text-gray-500 dark:text-gray-400">
              库存 {product.stock}
            </span>
          )}
        </div>
      </div>
    </div>
  );
});

ProductCard.displayName = 'ProductCard';

3.2 筛选面板:AI生成表单逻辑

提示词:

plaintext

请帮我生成一个商品筛选面板 FilterPanel 组件。

需求:
1. 支持关键词搜索(带防抖)
2. 分类选择器(使用 Select 组件)
3. 价格区间选择器(两个数字输入框)
4. 评分筛选(星级评分选择,最少0星)
5. 状态筛选(上架/下架/全部)
6. 一键重置筛选条件
7. 移动端折叠为抽屉模式

使用 React Hook Form 管理表单状态,
使用 Zod 进行表单验证(价格必须为正数,区间起始≤结束),
筛选变化时自动触发父组件的搜索。

AI生成的核心逻辑:

tsx

// src/components/product/FilterPanel.tsx
import React, { useEffect, useState, useCallback } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Search, X, ChevronDown, ChevronUp } from 'lucide-react';
import { clsx } from 'clsx';
import type { ProductFilters, ProductCategory, ProductStatus } from '@/types/product';

// 表单验证 Schema
const filterSchema = z.object({
  keyword: z.string().optional(),
  category: z.string().optional(),
  minPrice: z.coerce.number().min(0, '价格不能为负数').optional(),
  maxPrice: z.coerce.number().min(0, '价格不能为负数').optional(),
  minRating: z.coerce.number().min(0).max(5).optional(),
  status: z.string().optional(),
}).refine(
  (data) => {
    if (data.minPrice !== undefined && data.maxPrice !== undefined) {
      return data.minPrice <= data.maxPrice;
    }
    return true;
  },
  { message: '起始价格不能大于结束价格', path: ['minPrice'] }
);

type FilterFormData = z.infer<typeof filterSchema>;

interface FilterPanelProps {
  onFilterChange: (filters: ProductFilters) => void;
  initialFilters?: ProductFilters;
}

export const FilterPanel: React.FC<FilterPanelProps> = ({
  onFilterChange,
  initialFilters = {},
}) => {
  const [isExpanded, setIsExpanded] = useState(true);
  const [debouncedKeyword, setDebouncedKeyword] = useState(initialFilters.keyword);

  const {
    register,
    handleSubmit,
    watch,
    reset,
    formState: { errors },
  } = useForm<FilterFormData>({
    resolver: zodResolver(filterSchema),
    defaultValues: {
      keyword: initialFilters.keyword || '',
      category: initialFilters.category || '',
      minPrice: initialFilters.priceRange?.[0],
      maxPrice: initialFilters.priceRange?.[1],
      minRating: initialFilters.minRating || 0,
      status: initialFilters.status || '',
    },
  });

  // 防抖处理关键词搜索
  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedKeyword(watch('keyword'));
    }, 300);

    return () => clearTimeout(timer);
  }, [watch('keyword')]);

  // 筛选变化时触发搜索
  useEffect(() => {
    const subscription = watch((_, { name }) => {
      if (name !== 'keyword') {
        handleFormSubmit(watch());
      }
    });
    return () => subscription.unsubscribe();
  }, [watch]);

  // 关键词防抖后触发搜索
  useEffect(() => {
    const timer = setTimeout(() => {
      onFilterChange({
        ...watch(),
        keyword: debouncedKeyword,
      });
    }, 300);

    return () => clearTimeout(timer);
  }, [debouncedKeyword]);

  const handleFormSubmit = useCallback((data: FilterFormData) => {
    const filters: ProductFilters = {
      keyword: data.keyword,
      category: (data.category || '') as ProductCategory | '',
      priceRange: data.minPrice && data.maxPrice 
        ? [data.minPrice, data.maxPrice] 
        : undefined,
      minRating: data.minRating,
      status: (data.status || '') as ProductStatus | '',
    };
    onFilterChange(filters);
  }, [onFilterChange]);

  const handleReset = useCallback(() => {
    reset();
    onFilterChange({});
  }, [reset, onFilterChange]);

  // ... 渲染逻辑省略
};

3.3 状态管理:Zustand Store

提示词:

plaintext

请帮我生成商品管理的 Zustand Store。

需要管理的状态:
1. 商品列表(Product[])
2. 筛选条件(ProductFilters)
3. 分页信息(page, pageSize, total)
4. 加载状态(isLoading, isFetching)
5. 错误信息(error)
6. 选中的商品(用于批量操作)

需要实现的 Actions:
1. fetchProducts - 获取商品列表(调用 API)
2. setFilters - 更新筛选条件并重新获取
3. setPage / setPageSize - 分页操作
4. toggleProductStatus - 切换商品上下架状态
5. deleteProduct - 删除单个商品
6. batchDelete - 批量删除
7. batchToggleStatus - 批量切换状态

使用 @tanstack/react-query 处理数据请求和缓存,
Zustand 只负责 UI 状态管理。

AI生成的代码:

typescript

// src/stores/productStore.ts
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
import type { Product, ProductFilters, ProductListResponse } from '@/types/product';
import { productApi } from '@/api/product';

interface ProductState {
  // 数据状态
  products: Product[];
  selectedIds: Set<string>;
  
  // 筛选和分页
  filters: ProductFilters;
  pagination: {
    page: number;
    pageSize: number;
    total: number;
    totalPages: number;
  };
  
  // UI 状态
  isLoading: boolean;
  isFetching: boolean;
  error: string | null;
  
  // Actions
  fetchProducts: () => Promise<void>;
  setFilters: (filters: ProductFilters) => void;
  setPage: (page: number) => void;
  setPageSize: (pageSize: number) => void;
  toggleProductStatus: (product: Product) => Promise<void>;
  deleteProduct: (product: Product) => Promise<void>;
  batchDelete: () => Promise<void>;
  batchToggleStatus: () => Promise<void>;
  toggleSelect: (id: string) => void;
  selectAll: () => void;
  clearSelection: () => void;
}

export const useProductStore = create<ProductState>()(
  devtools(
    (set, get) => ({
      // 初始状态
      products: [],
      selectedIds: new Set(),
      filters: {},
      pagination: {
        page: 1,
        pageSize: 12,
        total: 0,
        totalPages: 0,
      },
      isLoading: false,
      isFetching: false,
      error: null,

      // 获取商品列表
      fetchProducts: async () => {
        const { filters, pagination } = get();
        
        set({ isFetching: true, error: null });
        
        try {
          const response = await productApi.getProducts({
            ...filters,
            page: pagination.page,
            pageSize: pagination.pageSize,
          });
          
          set({
            products: response.items,
            pagination: {
              page: response.page,
              pageSize: response.pageSize,
              total: response.total,
              totalPages: response.totalPages,
            },
            isFetching: false,
          });
        } catch (error) {
          set({
            error: error instanceof Error ? error.message : '获取商品列表失败',
            isFetching: false,
          });
        }
      },

      // 更新筛选条件
      setFilters: (filters) => {
        set({ filters, pagination: { ...get().pagination, page: 1 } });
        get().fetchProducts();
      },

      // 分页操作
      setPage: (page) => {
        set({ pagination: { ...get().pagination, page } });
        get().fetchProducts();
      },

      setPageSize: (pageSize) => {
        set({ 
          pagination: { ...get().pagination, pageSize, page: 1 } 
        });
        get().fetchProducts();
      },

      // 切换商品状态
      toggleProductStatus: async (product) => {
        const newStatus = product.status === 'active' ? 'inactive' : 'active';
        
        // 乐观更新
        set({
          products: get().products.map((p) =>
            p.id === product.id ? { ...p, status: newStatus } : p
          ),
        });

        try {
          await productApi.updateProduct(product.id, { status: newStatus });
        } catch (error) {
          // 回滚
          set({ products: get().products.map((p) =>
            p.id === product.id ? product : p
          )});
          throw error;
        }
      },

      // 删除商品
      deleteProduct: async (product) => {
        set({ isLoading: true });
        
        try {
          await productApi.deleteProduct(product.id);
          set({
            products: get().products.filter((p) => p.id !== product.id),
            pagination: {
              ...get().pagination,
              total: get().pagination.total - 1,
            },
            isLoading: false,
          });
        } catch (error) {
          set({ isLoading: false });
          throw error;
        }
      },

      // 批量删除
      batchDelete: async () => {
        const { selectedIds } = get();
        if (selectedIds.size === 0) return;

        set({ isLoading: true });

        try {
          await productApi.batchDelete(Array.from(selectedIds));
          set({
            products: get().products.filter(
              (p) => !selectedIds.has(p.id)
            ),
            selectedIds: new Set(),
            pagination: {
              ...get().pagination,
              total: get().pagination.total - selectedIds.size,
            },
            isLoading: false,
          });
        } catch (error) {
          set({ isLoading: false });
          throw error;
        }
      },

      // 批量切换状态
      batchToggleStatus: async () => {
        const { selectedIds, products } = get();
        if (selectedIds.size === 0) return;

        const selectedProducts = products.filter((p) => selectedIds.has(p.id));
        const allActive = selectedProducts.every((p) => p.status === 'active');
        const newStatus = allActive ? 'inactive' : 'active';

        // 乐观更新
        set({
          products: get().products.map((p) =>
            selectedIds.has(p.id) ? { ...p, status: newStatus } : p
          ),
        });

        try {
          await productApi.batchUpdateStatus(
            Array.from(selectedIds),
            newStatus
          );
        } catch (error) {
          // 回滚
          set({ products });
          throw error;
        }
      },

      // 选择操作
      toggleSelect: (id) => {
        const newSelected = new Set(get().selectedIds);
        if (newSelected.has(id)) {
          newSelected.delete(id);
        } else {
          newSelected.add(id);
        }
        set({ selectedIds: newSelected });
      },

      selectAll: () => {
        set({
          selectedIds: new Set(get().products.map((p) => p.id)),
        });
      },

      clearSelection: () => {
        set({ selectedIds: new Set() });
      },
    }),
    { name: 'product-store' }
  )
);

四、主题切换:深色模式实战

4.1 Tailwind CSS 深色模式配置

js

// tailwind.config.js
export default {
  darkMode: 'class',  // 使用 class 切换
  content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
  theme: {
    extend: {
      colors: {
        primary: {
          50: '#eff6ff',
          100: '#dbeafe',
          500: '#3b82f6',
          600: '#2563eb',
          900: '#1e3a8a',
        },
      },
    },
  },
  plugins: [],
};

4.2 主题状态管理

typescript

// src/stores/themeStore.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

type Theme = 'light' | 'dark' | 'system';

interface ThemeState {
  theme: Theme;
  resolvedTheme: 'light' | 'dark';
  setTheme: (theme: Theme) => void;
}

export const useThemeStore = create<ThemeState>()(
  persist(
    (set, get) => ({
      theme: 'system',
      resolvedTheme: 'light',

      setTheme: (theme) => {
        const root = document.documentElement;
        
        if (theme === 'system') {
          const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
          root.classList.toggle('dark', systemDark);
          set({ theme, resolvedTheme: systemDark ? 'dark' : 'light' });
        } else {
          root.classList.toggle('dark', theme === 'dark');
          set({ theme, resolvedTheme: theme });
        }
      },
    }),
    {
      name: 'theme-storage',
      onRehydrateStorage: () => (state) => {
        // 页面加载后同步主题
        if (state) {
          const root = document.documentElement;
          if (state.theme !== 'system') {
            root.classList.toggle('dark', state.theme === 'dark');
          }
        }
      },
    }
  )
);

五、面试加分项:

5.1 性能优化

typescript

// 1. 使用 React.memo 避免不必要的重渲染
export const ProductCard = memo(ProductCardComponent, (prev, next) => {
  return (
    prev.product.id === next.product.id &&
    prev.product.status === next.product.status &&
    prev.product.stock === next.product.stock &&
    prev.onEdit === next.onEdit  // 比较回调引用
  );
});

// 2. 虚拟列表(商品数量多时)
import { useVirtualizer } from '@tanstack/react-virtual';

const virtualizer = useVirtualizer({
  count: products.length,
  getScrollElement: () => parentRef.current,
  estimateSize: () => 350,
  overscan: 5,
});

// 3. 图片优化
<img
  src={product.images[0]}
  loading="lazy"
  decoding="async"
  srcSet={`${product.images[0]}?w=200 200w, ${product.images[0]}?w=400 400w`}
  sizes="(max-width: 768px) 50vw, 25vw"
/>

5.2 错误边界

tsx

// src/components/ErrorBoundary.tsx
import React, { Component, type ErrorInfo, type ReactNode } from 'react';

interface Props {
  children: ReactNode;
  fallback?: ReactNode;
}

interface State {
  hasError: boolean;
  error: Error | null;
}

export class ErrorBoundary extends Component<Props, State> {
  state: State = { hasError: false, error: null };

  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error('ErrorBoundary caught an error:', error, errorInfo);
    // 可以上报到监控服务
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback || (
        <div className="flex flex-col items-center justify-center h-full p-8">
          <h2 className="text-xl font-bold text-red-500 mb-4">出错了</h2>
          <p className="text-gray-600 dark:text-gray-400 mb-4">
            {this.state.error?.message}
          </p>
          <button
            onClick={() => this.setState({ hasError: false, error: null })}
            className="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600"
          >
            重试
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

六、延伸思考:AI编程时代的开发者进化

6.1 核心竞争力的转变

很多人担心"AI会取代程序员",但实际上:

plaintext

传统模式:写代码能力 → 核心竞争力
AI时代模式:
  ├── 系统设计能力(AI不擅长架构级设计)
  ├── 业务建模能力(把需求转化为可执行的设计)
  ├── 性能优化能力(AI生成的代码往往只关注功能)
  └── 代码审查能力(识别AI代码中的问题)

6.2 AI编程的正确姿势

plaintext

❌ 错误做法:
1. 直接复制粘贴AI代码,不理解原理
2. 遇到问题就让AI解决,放弃思考
3. 不做代码审查,完全信任AI

✅ 正确做法:
1. AI生成代码后必须人工Review
2. 关键逻辑自己理解,必要时重写
3. 定期复盘:AI哪里写得好,哪里有问题
4. 建立自己的Prompt模板库

6.3 面试时如何展示AI编程能力

面试官真正想看到的是:

  1. 你有体系化的方法论(不是只会用Copilot补全)
  2. 你能驾驭AI而不是被AI驾驭(知道什么时候用AI,什么时候不用)
  3. 你有工程化思维(考虑性能、安全、可维护性)
  4. 你在持续进化(关注新工具、新趋势)

七、总结

今天这篇博客,我们:

  1. 搭建了完整的技术栈:Cursor + React + TypeScript + Tailwind CSS + Zustand
  2. 实现了核心功能:商品CRUD、筛选排序、批量操作、深色模式
  3. 展示了AI协作流程:需求分析 → 类型定义 → 组件生成 → 状态管理
  4. 补充了面试加分项:性能优化、错误边界、主题切换
  5. 沉淀了方法论:AI时代的开发流程和核心竞争力

这个项目放在GitHub上,面试时可以直接演示。比那些"手写一个Promise"的八股文项目有说服力多了。

项目结构:

plaintext

admin-dashboard/
├── src/
│   ├── components/
│   │   ├── common/          # 通用组件(Button, Input, Modal...)
│   │   └── product/         # 商品模块组件
│   ├── hooks/               # 自定义Hooks
│   ├── stores/              # Zustand状态管理
│   ├── types/               # TypeScript类型定义
│   ├── api/                 # API请求封装
│   └── pages/               # 页面组件
├── .cursorrules             # Cursor配置文件
└── package.json

有问题欢迎评论区交流,觉得有用的话点个赞~

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐