d2-03-main-menu

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Day 2 · Step 3 — Main menu with capability-driven menu items

Day 2 · Step 3 — 基于功能驱动菜单项的主菜单

Before starting: Ask for:
  1. Entity name (PascalCase, e.g.,
    Product
    ,
    Order
    ,
    Employee
    ) — REQUIRED
  2. Available capabilities (select one or more:
    search
    ,
    create
    ,
    save
    ,
    delete
    ) — REQUIRED
    • search
      : Browse/list all records
    • create
      : Create new records
    • save
      : Save to database (used with create/edit)
    • delete
      : Delete records
Use exactly as specified. Menu buttons will be generated based on selected capabilities.
Routing Setup (
app.routes.ts
) — COMPLETE ROUTING TABLE FOR ALL PAGES:
  • Import MenuComponent, all <Entity>SearchComponents, and all <Entity>FormComponents
  • Add menu as the default/landing page route (path: '')
  • Define entity routes for each program: search, create, edit
  • This route table is shared by d2-03 (menu), d2-04 (search), and d2-05 (form)
typescript
import { Routes } from '@angular/router';
import { MenuComponent } from './pages/menu/menu.component';
import { <Entity>SearchComponent } from './pages/<entity>-search/<entity>-search.component';
import { <Entity>FormComponent } from './pages/<entity>-form/<entity>-form.component';
// Add more imports for additional entities as needed

export const routes: Routes = [
  // Landing page (menu comes first!)
  { path: '', component: MenuComponent },

  // <Entity> CRUD routes
  { path: '<entity>', component: <Entity>SearchComponent },        // Search/list page
  { path: '<entity>/new', component: <Entity>FormComponent },      // Create new record
  { path: '<entity>/:id/edit', component: <Entity>FormComponent }, // Edit existing record

  // Add more entity routes here for additional programs:
  // { path: 'customer', component: CustomerSearchComponent },
  // { path: 'customer/new', component: CustomerFormComponent },
  // { path: 'customer/:id/edit', component: CustomerFormComponent },
];
Route Usage:
  • d2-03-main-menu uses
    { path: '', component: MenuComponent }
    — landing page
  • d2-04-search-delete-page uses
    /<entity>
    route and navigates via
    router.navigate(['/<entity>/new'])
    ,
    router.navigate(['/<entity>', id, 'edit'])
  • d2-05-form-page uses
    /<entity>/new
    and
    /<entity>/:id/edit
    routes
Component TypeScript (capability-driven):
typescript
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { CommonModule } from '@angular/common';

interface MenuItem {
  name: string;
  icon: string;
  path: string;
}

interface Program {
  name: string;
  entity: string;
  icon: string;
  description: string;
  capabilities: string[];
  menuItems: MenuItem[];
}

@Component({
  selector: 'app-menu',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './menu.component.html',
  styleUrls: ['./menu.component.css']
})
export class MenuComponent {
  appTitle = 'CRUD Dashboard';
  appSubtitle = 'Manage your data with ease';

  programs: Program[] = [];

  constructor(private router: Router) {
    this.programs = [
      {
        name: '<Entity>',
        entity: '<entity>',
        icon: '📦',
        description: 'Manage <entity> records',
        capabilities: ['search', 'create', 'save', 'delete'],
        menuItems: this.generateMenuItems('<entity>', ['search', 'create', 'save', 'delete'])
      },
      // Add more programs here:
      // { name: 'Customer', entity: 'customer', icon: '👥', description: 'Manage customer data', capabilities: [...], menuItems: ... }
    ];
  }

  generateMenuItems(entity: string, capabilities: string[]): MenuItem[] {
    const items: MenuItem[] = [];
    
    if (capabilities.includes('search')) {
      items.push({ name: 'Browse', icon: '📋', path: `/${entity}` });
    }
    if (capabilities.includes('create')) {
      items.push({ name: 'Create', icon: '✚', path: `/${entity}/new` });
    }
    
    return items;
  }

  navigate(path: string) {
    this.router.navigate([path]);
  }
}
Template (capability-driven menu with responsive card grid, standard Tailwind classes):
html
<div class="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 p-6">
  <!-- Header -->
  <div class="max-w-6xl mx-auto mb-12">
    <div class="text-center mb-8">
      <h1 class="text-5xl font-bold text-gray-900">{{ appTitle }}</h1>
      <p class="text-gray-600 text-xl mt-4">{{ appSubtitle }}</p>
    </div>
  </div>

  <!-- Program Cards Grid -->
  <div class="max-w-6xl mx-auto">
    <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
      @for (program of programs; track program.entity) {
        <div class="bg-white rounded-xl shadow-md hover:shadow-xl transition-shadow p-6">
          <!-- Card Header -->
          <div class="flex items-center mb-4">
            <span class="text-5xl mr-4">{{ program.icon }}</span>
            <div>
              <h3 class="text-2xl font-bold text-gray-900">{{ program.name }}</h3>
              <p class="text-gray-600 text-sm">{{ program.description }}</p>
            </div>
          </div>

          <!-- Capabilities Badge -->
          <div class="flex flex-wrap gap-2 mb-4">
            @for (cap of program.capabilities; track cap) {
              <span class="bg-gray-100 text-gray-700 text-xs font-semibold px-3 py-1 rounded-full">
                {{ cap }}
              </span>
            }
          </div>

          <!-- Divider -->
          <div class="border-b border-gray-200 my-4"></div>

          <!-- Dynamic Menu Buttons -->
          <div class="flex flex-col gap-4">
            @for (item of program.menuItems; track item.name) {
              <button
                (click)="navigate(item.path)"
                [class]="item.name === 'Browse' ? 'w-full text-white font-semibold px-6 py-3 rounded-lg transition bg-blue-500 hover:bg-blue-600' : 'w-full text-white font-semibold px-6 py-3 rounded-lg transition bg-green-500 hover:bg-green-600'"
              >
                {{ item.icon }} {{ item.name }}
              </button>
            }
          </div>

          @if (program.menuItems.length === 0) {
            <p class="text-gray-600 text-sm text-center">No actions available for this program.</p>
          }
        </div>
      }
    </div>
  </div>

  <!-- Footer -->
  <div class="max-w-6xl mx-auto mt-12 text-center">
    <p class="text-gray-600 text-sm">Built with Angular + Tailwind CSS</p>
  </div>
</div>
Design System (Standard Tailwind):
  • Spacing: p-6 (padding), mb-4/8/12, gap-2/4/8
  • Colors: bg-blue-500/600, bg-green-500/600, text-gray-600, bg-gray-100
  • Shadows: shadow-md, hover:shadow-xl (card elevation)
  • Typography: text-5xl/2xl, font-bold/semibold
  • Layout: grid responsive (1-2-3 columns), flex flex-col
  • Interactions: rounded-lg, transition, hover states
开始前:请询问以下信息:
  1. 实体名称(采用PascalCase格式,例如
    Product
    Order
    Employee
    )—— 必填项
  2. 可用功能(选择一个或多个:
    search
    create
    save
    delete
    )—— 必填项
    • search
      :浏览/列出所有记录
    • create
      :创建新记录
    • save
      :保存到数据库(与创建/编辑配合使用)
    • delete
      :删除记录
请严格按照指定要求操作。菜单按钮将根据所选功能生成。
路由设置 (
app.routes.ts
) —— 所有页面的完整路由表:
  • 导入MenuComponent、所有<Entity>SearchComponents以及所有<Entity>FormComponents
  • 将菜单设置为默认/首页路由(路径: '')
  • 为每个程序定义实体路由:搜索、创建、编辑
  • 该路由表由d2-03(菜单)、d2-04(搜索)和d2-05(表单)共享
typescript
import { Routes } from '@angular/router';
import { MenuComponent } from './pages/menu/menu.component';
import { <Entity>SearchComponent } from './pages/<entity>-search/<entity>-search.component';
import { <Entity>FormComponent } from './pages/<entity>-form/<entity>-form.component';
// Add more imports for additional entities as needed

export const routes: Routes = [
  // Landing page (menu comes first!)
  { path: '', component: MenuComponent },

  // <Entity> CRUD routes
  { path: '<entity>', component: <Entity>SearchComponent },        // Search/list page
  { path: '<entity>/new', component: <Entity>FormComponent },      // Create new record
  { path: '<entity>/:id/edit', component: <Entity>FormComponent }, // Edit existing record

  // Add more entity routes here for additional programs:
  // { path: 'customer', component: CustomerSearchComponent },
  // { path: 'customer/new', component: CustomerFormComponent },
  // { path: 'customer/:id/edit', component: CustomerFormComponent },
];
路由使用说明:
  • d2-03-main-menu 使用
    { path: '', component: MenuComponent }
    —— 首页
  • d2-04-search-delete-page 使用
    /<entity>
    路由,并通过
    router.navigate(['/<entity>/new'])
    router.navigate(['/<entity>', id, 'edit'])
    进行导航
  • d2-05-form-page 使用
    /<entity>/new
    /<entity>/:id/edit
    路由
组件TypeScript代码(功能驱动型):
typescript
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { CommonModule } from '@angular/common';

interface MenuItem {
  name: string;
  icon: string;
  path: string;
}

interface Program {
  name: string;
  entity: string;
  icon: string;
  description: string;
  capabilities: string[];
  menuItems: MenuItem[];
}

@Component({
  selector: 'app-menu',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './menu.component.html',
  styleUrls: ['./menu.component.css']
})
export class MenuComponent {
  appTitle = 'CRUD Dashboard';
  appSubtitle = 'Manage your data with ease';

  programs: Program[] = [];

  constructor(private router: Router) {
    this.programs = [
      {
        name: '<Entity>',
        entity: '<entity>',
        icon: '📦',
        description: 'Manage <entity> records',
        capabilities: ['search', 'create', 'save', 'delete'],
        menuItems: this.generateMenuItems('<entity>', ['search', 'create', 'save', 'delete'])
      },
      // Add more programs here:
      // { name: 'Customer', entity: 'customer', icon: '👥', description: 'Manage customer data', capabilities: [...], menuItems: ... }
    ];
  }

  generateMenuItems(entity: string, capabilities: string[]): MenuItem[] {
    const items: MenuItem[] = [];
    
    if (capabilities.includes('search')) {
      items.push({ name: 'Browse', icon: '📋', path: `/${entity}` });
    }
    if (capabilities.includes('create')) {
      items.push({ name: 'Create', icon: '✚', path: `/${entity}/new` });
    }
    
    return items;
  }

  navigate(path: string) {
    this.router.navigate([path]);
  }
}
模板代码(功能驱动菜单+响应式卡片网格,采用标准Tailwind类):
html
<div class="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 p-6">
  <!-- Header -->
  <div class="max-w-6xl mx-auto mb-12">
    <div class="text-center mb-8">
      <h1 class="text-5xl font-bold text-gray-900">{{ appTitle }}</h1>
      <p class="text-gray-600 text-xl mt-4">{{ appSubtitle }}</p>
    </div>
  </div>

  <!-- Program Cards Grid -->
  <div class="max-w-6xl mx-auto">
    <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
      @for (program of programs; track program.entity) {
        <div class="bg-white rounded-xl shadow-md hover:shadow-xl transition-shadow p-6">
          <!-- Card Header -->
          <div class="flex items-center mb-4">
            <span class="text-5xl mr-4">{{ program.icon }}</span>
            <div>
              <h3 class="text-2xl font-bold text-gray-900">{{ program.name }}</h3>
              <p class="text-gray-600 text-sm">{{ program.description }}</p>
            </div>
          </div>

          <!-- Capabilities Badge -->
          <div class="flex flex-wrap gap-2 mb-4">
            @for (cap of program.capabilities; track cap) {
              <span class="bg-gray-100 text-gray-700 text-xs font-semibold px-3 py-1 rounded-full">
                {{ cap }}
              </span>
            }
          </div>

          <!-- Divider -->
          <div class="border-b border-gray-200 my-4"></div>

          <!-- Dynamic Menu Buttons -->
          <div class="flex flex-col gap-4">
            @for (item of program.menuItems; track item.name) {
              <button
                (click)="navigate(item.path)"
                [class]="item.name === 'Browse' ? 'w-full text-white font-semibold px-6 py-3 rounded-lg transition bg-blue-500 hover:bg-blue-600' : 'w-full text-white font-semibold px-6 py-3 rounded-lg transition bg-green-500 hover:bg-green-600'"
              >
                {{ item.icon }} {{ item.name }}
              </button>
            }
          </div>

          @if (program.menuItems.length === 0) {
            <p class="text-gray-600 text-sm text-center">No actions available for this program.</p>
          }
        </div>
      }
    </div>
  </div>

  <!-- Footer -->
  <div class="max-w-6xl mx-auto mt-12 text-center">
    <p class="text-gray-600 text-sm">Built with Angular + Tailwind CSS</p>
  </div>
</div>
设计系统(标准Tailwind规范):
  • 间距: p-6(内边距)、mb-4/8/12、gap-2/4/8
  • 颜色: bg-blue-500/600、bg-green-500/600、text-gray-600、bg-gray-100
  • 阴影: shadow-md、hover:shadow-xl(卡片悬浮效果)
  • 排版: text-5xl/2xl、font-bold/semibold
  • 布局: 响应式网格(1-2-3列)、flex flex-col
  • 交互: rounded-lg、transition、hover状态