name: 页面结构设计 description: 在设计页面层次结构、页面模板、布局区域或站点地图结构时使用。覆盖页面集、模板继承、组件区域和头寸CMS架构的页面树API。 allowed-tools: Read, Glob, Grep, Task, Skill
页面结构设计
为头寸CMS设计页面层次结构、模板和模块化页面组合系统的指导。
何时使用此技能
- 设计页面树结构
- 创建带有区域的页面模板
- 实现页面构建器功能
- 规划站点地图生成
- 构建模块化页面组合
页面层次结构模式
基本页面树
public class Page
{
public Guid Id { get; set; }
public string Title { get; set; } = string.Empty;
public string Slug { get; set; } = string.Empty;
// 层级结构
public Guid? ParentId { get; set; }
public Page? Parent { get; set; }
public List<Page> Children { get; set; } = new();
// 计算路径
public string Path { get; set; } = string.Empty; // /about/team/leadership
public int Depth { get; set; }
public int Order { get; set; }
// 模板和内容
public string Template { get; set; } = string.Empty;
public PageContent Content { get; set; } = new();
}
页面集(集合)
// 用于分组相关页面的页面集
public class PageSet
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Slug { get; set; } = string.Empty;
public PageSetType Type { get; set; }
// 配置
public string ItemTemplate { get; set; } = string.Empty;
public string ListTemplate { get; set; } = string.Empty;
public int ItemsPerPage { get; set; } = 10;
// URL 模式
public string UrlPattern { get; set; } = string.Empty; // /blog/{slug}
}
public enum PageSetType
{
Blog, // 按时间顺序的帖子
Portfolio, // 项目展示
Team, // 团队成员
Products, // 产品目录
FAQ, // 问答集合
Custom // 用户定义
}
模板系统
模板定义
public class PageTemplate
{
public string Name { get; set; } = string.Empty;
public string DisplayName { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
// 模板层次结构
public string? ParentTemplate { get; set; }
// 可用的内容区域
public List<TemplateZone> Zones { get; set; } = new();
// 必填字段
public List<TemplateField> Fields { get; set; } = new();
// 适用的页面类型
public List<string> ApplicablePageTypes { get; set; } = new();
}
public class TemplateZone
{
public string Name { get; set; } = string.Empty;
public string DisplayName { get; set; } = string.Empty;
public ZoneType Type { get; set; }
public List<string> AllowedWidgets { get; set; } = new();
public int? MaxWidgets { get; set; }
}
public enum ZoneType
{
Single, // 仅一个部件
Multiple, // 多个部件堆叠
Grid // 网格布局
}
模板继承
基础模板
├── 区域:头部,底部,侧边栏
└── 字段:元标题,元描述
├── 首页模板(继承基础)
│ └── 区域:英雄区域,功能区域,行动召唤区域
│
├── 内容模板(继承基础)
│ └── 区域:主要内容,相关内容
│
└── 着陆页模板(继承基础)
└── 区域:英雄区域,区域块(多个)
页面构建器组件
部件系统
public abstract class Widget
{
public Guid Id { get; set; }
public string Type { get; set; } = string.Empty;
public int Order { get; set; }
public Dictionary<string, object?> Settings { get; set; } = new();
}
public class TextWidget : Widget
{
public string Content { get; set; } = string.Empty;
}
public class ImageWidget : Widget
{
public Guid MediaItemId { get; set; }
public string? Alt { get; set; }
public string? Caption { get; set; }
}
public class CallToActionWidget : Widget
{
public string Heading { get; set; } = string.Empty;
public string? Subheading { get; set; }
public string ButtonText { get; set; } = string.Empty;
public string ButtonUrl { get; set; } = string.Empty;
public string? BackgroundImageId { get; set; }
}
public class CardGridWidget : Widget
{
public List<Card> Cards { get; set; } = new();
public int Columns { get; set; } = 3;
}
页面内容结构
public class PageContent
{
// 基于区域的内容存储
public Dictionary<string, List<Widget>> Zones { get; set; } = new();
// 页面级字段
public string? HeroTitle { get; set; }
public string? HeroSubtitle { get; set; }
public Guid? HeroImageId { get; set; }
// SEO
public string? MetaTitle { get; set; }
public string? MetaDescription { get; set; }
public bool NoIndex { get; set; }
}
站点地图生成
站点地图数据模型
public class SitemapEntry
{
public string Url { get; set; } = string.Empty;
public DateTime LastModified { get; set; }
public ChangeFrequency ChangeFrequency { get; set; }
public decimal Priority { get; set; }
public List<SitemapAlternate>? Alternates { get; set; }
}
public class SitemapAlternate
{
public string Hreflang { get; set; } = string.Empty;
public string Url { get; set; } = string.Empty;
}
public enum ChangeFrequency
{
Always, // 总是
Hourly, // 每小时
Daily, // 每天
Weekly, // 每周
Monthly, // 每月
Yearly, // 每年
Never // 从不
}
站点地图生成服务
public class SitemapService
{
public async Task<List<SitemapEntry>> GenerateSitemapAsync()
{
var entries = new List<SitemapEntry>();
// 添加页面
var pages = await _pageRepository.GetPublishedPagesAsync();
foreach (var page in pages)
{
entries.Add(new SitemapEntry
{
Url = $"{_baseUrl}{page.Path}",
LastModified = page.ModifiedUtc,
ChangeFrequency = GetChangeFrequency(page),
Priority = CalculatePriority(page)
});
}
// 添加页面集项目(博客帖子、产品等)
var pageSets = await _pageSetRepository.GetAllAsync();
foreach (var pageSet in pageSets)
{
var items = await _pageSetRepository.GetItemsAsync(pageSet.Id);
foreach (var item in items)
{
var url = GenerateUrl(pageSet.UrlPattern, item);
entries.Add(new SitemapEntry
{
Url = url,
LastModified = item.ModifiedUtc,
ChangeFrequency = ChangeFrequency.Weekly,
Priority = 0.6m
});
}
}
return entries;
}
private decimal CalculatePriority(Page page)
{
// 首页最高优先级
if (page.Depth == 0) return 1.0m;
// 按深度递减
return Math.Max(0.5m, 1.0m - (page.Depth * 0.1m));
}
}
页面树API
REST 端点
GET /api/pages # 根页面
GET /api/pages/{id} # 单个页面
GET /api/pages/{id}/children # 子页面
GET /api/pages/path/{*path} # 按URL路径获取页面
GET /api/pages/tree # 完整页面树
GET /api/sitemap.xml # XML站点地图
GET /api/sitemap.json # JSON站点地图
页面树响应
{
"data": {
"id": "page-123",
"title": "关于我们",
"slug": "about",
"path": "/about",
"template": "内容模板",
"depth": 1,
"order": 2,
"children": [
{
"id": "page-456",
"title": "我们的团队",
"slug": "team",
"path": "/about/team",
"template": "团队模板",
"children": []
},
{
"id": "page-789",
"title": "职业发展",
"slug": "careers",
"path": "/about/careers",
"template": "内容模板",
"children": []
}
]
},
"breadcrumbs": [
{ "title": "首页", "path": "/" },
{ "title": "关于我们", "path": "/about" }
]
}
最佳实践
页面结构
| 模式 | 何时使用 |
|---|---|
| 扁平结构 | 简单网站,页面少 |
| 2级层次结构 | 大多数企业网站 |
| 深层层次结构 | 文档、大型目录 |
| 页面集 | 博客、作品集、团队页面 |
模板设计
做:
- 保持区域语义化(头部、主要内容、侧边栏)
- 允许灵活部件放置
- 从基础模板继承公共区域
- 提供合理的默认值
不做:
- 创建过于具体的模板
- 在模板中硬编码布局
- 混合内容和呈现关注点
- 创建深层模板继承链
相关技能
navigation-architecture- 菜单和面包屑设计url-routing-patterns- URL结构和路由content-type-modeling- 页面作为内容类型