d2-03-main-menu
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDay 2 · Step 3 — Main menu with capability-driven menu items
Day 2 · Step 3 — 基于功能驱动菜单项的主菜单
Before starting: Ask for:
- Entity name (PascalCase, e.g., ,
Product,Order) — REQUIREDEmployee - Available capabilities (select one or more: ,
search,create,save) — REQUIREDdelete- : Browse/list all records
search - : Create new records
create - : Save to database (used with create/edit)
save - : Delete records
delete
Use exactly as specified. Menu buttons will be generated based on selected capabilities.
Routing Setup () — COMPLETE ROUTING TABLE FOR ALL PAGES:
app.routes.ts- 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 — landing page
{ path: '', component: MenuComponent } - d2-04-search-delete-page uses route and navigates via
/<entity>,router.navigate(['/<entity>/new'])router.navigate(['/<entity>', id, 'edit']) - d2-05-form-page uses and
/<entity>/newroutes/<entity>/:id/edit
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
开始前:请询问以下信息:
- 实体名称(采用PascalCase格式,例如、
Product、Order)—— 必填项Employee - 可用功能(选择一个或多个:、
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状态