布局系统¶
Django Admin Dashboards 中的布局系统提供了一种灵活的、基于响应式网格的方法来在您的数据看板上排列组件。本指南涵盖从基础布局到高级响应式设计的所有内容。
目录¶
布局基础¶
什么是布局?¶
布局定义了组件在数据看板上的排列方式。它包含:
- 网格系统: 12-列 响应式 网格
- 行: 组件的水平容器
- 列: 行内的垂直划分
- 组件: 放置在网格单元格中的可视化元素
创建基础布局¶
from django_admin_dashboards.base import Dashboard, Layout, CardComponent
class BasicDashboard(Dashboard):
def get_layout(self):
# 创建 12 列布局
layout = Layout(columns=12)
# 添加一行,包含一个全宽度组件
layout.add_row([
(CardComponent(title="Welcome", value="Dashboard"), 12)
], height="auto")
return layout
网格系统¶
12-列 网格¶
布局系统使用 12 列网格,可自动适应不同的屏幕尺寸:
layout = Layout(columns=12) # Always 12 columns
# 组件可以跨越1-12列
# 每行的列跨度总和应等于12
# 有效:4 + 4 + 4 = 12
layout.add_row([(c1, 4), (c2, 4), (c3, 4)], height="auto")
# 有效:8 + 4 = 12
layout.add_row([(c1, 8), (c2, 4)], height="auto")
# 有效:6 + 3 + 3 = 12
layout.add_row([(c1, 6), (c2, 3), (c3, 3)], height="auto")
# 无效:6 + 6 + 6 = 18(会导致布局问题)
列跨度示例¶
def get_layout(self):
layout = Layout(columns=12)
# 单列(很少使用)
layout.add_row([(component, 1)], height="auto")
# 两列(6 + 6)
layout.add_row([(left, 6), (right, 6)], height="auto")
# 三列(4 + 4 + 4)
layout.add_row([(col1, 4), (col2, 4), (col3, 4)], height="auto")
# 四列(3 + 3 + 3 + 3)
layout.add_row([(c1, 3), (c2, 3), (c3, 3), (c4, 3)], height="auto")
# 六列(2 + 2 + 2 + 2 + 2 + 2)
layout.add_row([(c1, 2), (c2, 2), (c3, 2), (c4, 2), (c5, 2), (c6, 2)], height="auto")
# 混合布局(8 + 4)
layout.add_row([
(main_content, 8), # Main content area
(sidebar, 4), # Sidebar
], height="auto")
# 混合布局(9 + 3)
layout.add_row([
(content, 9), # Content
(widgets, 3), # Widget sidebar
], height="auto")
return layout
网格可视化¶
12列网格分解:
[ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ][ 7 ][ 8 ][ 9 ][10][11][12]
常见布局:
全宽度: [ 12 ]
两列: [ 6 ][ 6 ]
三列: [ 4 ][ 4 ][ 4 ]
四列: [ 3 ][ 3 ][ 3 ][ 3 ]
侧边栏布局: [ 8 ][ 4 ]
主内容 + 侧边栏: [ 9 ][ 3 ]
行管理¶
创建行¶
行使用 add_row() 方法创建:
def get_layout(self):
layout = Layout(columns=12)
# 第1行:标题
layout.add_row([
(self.get_header_component(), 12)
], height="auto")
# 第2行:主要指标
layout.add_row([
(self.get_metric_card("Revenue"), 4),
(self.get_metric_card("Users"), 4),
(self.get_metric_card("Orders"), 4),
], height="auto")
# 第3行:图表
layout.add_row([
(self.get_sales_chart(), 8),
(self.get_conversion_chart(), 4),
], height="400px")
# 第4行:表格
layout.add_row([
(self.get_recent_orders(), 6),
(self.get_top_products(), 6),
], height="auto")
return layout
行高度选项¶
# 自动高度(内容决定高度)
layout.add_row([...], height="auto")
# 固定高度(像素)
layout.add_row([...], height="300px")
layout.add_row([...], height="500px")
# 视口高度(响应式)
layout.add_row([...], height="50vh") # 视口高度的50%
layout.add_row([...], height="75vh") # 视口高度的75%
# 最小高度
layout.add_row([...], height="min-height: 200px")
# 最大高度
layout.add_row([...], height="max-height: 600px")
# Clamp函数(带限制的响应式)
layout.add_row([...], height="clamp(300px, 50vh, 800px)")
# Calc函数(计算高度)
layout.add_row([...], height="calc(100vh - 200px)")
动态行高度¶
def get_dynamic_row_height(self, component_count):
"""基于组件数量计算行高度"""
if component_count >= 4:
return "250px" # 多个组件时较小
elif component_count == 3:
return "300px" # 中等高度
else:
return "400px" # 较少组件时较大
def get_layout(self):
layout = Layout(columns=12)
charts = self.get_charts()
chart_count = len(charts)
# 根据数量将图表分布到行中
if chart_count == 1:
layout.add_row([(charts[0], 12)], height=self.get_dynamic_row_height(1))
elif chart_count == 2:
layout.add_row([
(charts[0], 6),
(charts[1], 6),
], height=self.get_dynamic_row_height(2))
elif chart_count == 3:
layout.add_row([
(charts[0], 4),
(charts[1], 4),
(charts[2], 4),
], height=self.get_dynamic_row_height(3))
return layout
行间距和边距¶
def get_layout_with_spacing(self):
layout = Layout(columns=12)
# 添加自定义间距的CSS类
layout.add_row([
(self.get_header(), 12)
], height="auto", css_class="header-row")
layout.add_row([
(self.get_metrics(), 12)
], height="auto", css_class="metrics-row")
return layout
自定义CSS间距:
/* 自定义行间距 */
.header-row {
margin-bottom: 2rem;
}
.metrics-row {
margin-bottom: 1.5rem;
gap: 1rem; /* 行内组件之间的间隙 */
}
/* 移除特定行的默认边距 */
.no-margin-row {
margin: 0;
padding: 0;
}
响应式设计¶
内置响应式¶
网格系统默认是响应式的。组件会根据屏幕尺寸自动调整布局:
# 这一行将显示为:
# - 大屏幕上并排显示3列
# - 中等屏幕上2列 + 1列
# - 小屏幕上每行1列
layout.add_row([
(card1, 4),
(card2, 4),
(card3, 4),
], height="auto")
基于断点的布局¶
def get_responsive_layout(self):
layout = Layout(columns=12)
# Desktop: 4 columns
# Tablet: 2 columns
# Mobile: 1 column
layout.add_row([
(self.get_card("Card 1"), {"default": 4, "md": 6, "sm": 12}),
(self.get_card("Card 2"), {"default": 4, "md": 6, "sm": 12}),
(self.get_card("Card 3"), {"default": 4, "md": 12, "sm": 12}),
], height="auto")
return layout
基于屏幕尺寸的条件布局¶
def get_adaptive_layout(self, request):
layout = Layout(columns=12)
# 检查移动设备的用户代理
user_agent = request.META.get('HTTP_USER_AGENT', '').lower()
is_mobile = any(mobile in user_agent for mobile in ['mobile', 'android', 'iphone'])
if is_mobile:
# 移动布局:单列
layout.add_row([(self.get_mobile_header(), 12)], height="auto")
layout.add_row([(self.get_mobile_metrics(), 12)], height="auto")
layout.add_row([(self.get_mobile_chart(), 12)], height="300px")
else:
# 桌面布局:多列
layout.add_row([
(self.get_desktop_header(), 12)
], height="auto")
layout.add_row([
(self.get_metric_card("Revenue"), 4),
(self.get_metric_card("Users"), 4),
(self.get_metric_card("Orders"), 4),
], height="auto")
layout.add_row([
(self.get_main_chart(), 8),
(self.get_sidebar_charts(), 4),
], height="400px")
return layout
响应式高度调整¶
def get_responsive_height_layout(self):
layout = Layout(columns=12)
# 不同屏幕尺寸的不同高度
layout.add_row([
(self.get_main_chart(), 12)
], height={
"default": "500px", # 桌面
"md": "400px", # 平板
"sm": "300px", # 移动
})
# 移动设备上自动高度,桌面设备上固定高度
layout.add_row([
(self.get_data_table(), 12)
], height={
"default": "400px",
"sm": "auto", # 小屏幕上自动高度
})
return layout
高级布局模式¶
嵌套布局¶
def get_nested_layout(self):
"""创建具有嵌套行的复杂布局"""
layout = Layout(columns=12)
# 主标题
layout.add_row([(self.get_header(), 12)], height="auto")
# 具有嵌套布局的主要内容区域
main_content = Layout(columns=12)
# 嵌套:顶部区域
main_content.add_row([
(self.get_stats_summary(), 8),
(self.get_quick_actions(), 4),
], height="auto")
# 嵌套:中间区域
main_content.add_row([
(self.get_primary_chart(), 12)
], height="400px")
# 嵌套:底部区域
main_content.add_row([
(self.get_data_table(), 12)
], height="auto")
# 将嵌套布局添加为组件
layout.add_row([(main_content, 12)], height="auto")
return layout
选项卡布局¶
def get_tabbed_layout(self):
"""带有选项卡界面的布局"""
layout = Layout(columns=12)
# 选项卡导航
layout.add_row([
(self.get_tab_navigation(), 12)
], height="auto")
# 选项卡内容(基于活动选项卡的条件显示)
active_tab = self.request.GET.get('tab', 'overview')
if active_tab == 'overview':
layout.add_row([
(self.get_overview_content(), 12)
], height="auto")
elif active_tab == 'analytics':
layout.add_row([
(self.get_analytics_content(), 12)
], height="auto")
elif active_tab == 'reports':
layout.add_row([
(self.get_reports_content(), 12)
], height="auto")
return layout
手风琴布局¶
def get_accordion_layout(self):
"""带有可折叠区域的布局"""
layout = Layout(columns=12)
# 区域1(默认展开)
layout.add_row([
(self.get_accordion_header("Section 1", expanded=True), 12)
], height="auto")
layout.add_row([
(self.get_section1_content(), 12)
], height="auto", css_class="accordion-content", id="section1")
# 区域2(默认折叠)
layout.add_row([
(self.get_accordion_header("Section 2", expanded=False), 12)
], height="auto")
layout.add_row([
(self.get_section2_content(), 12)
], height="auto", css_class="accordion-content hidden", id="section2")
# 区域3
layout.add_row([
(self.get_accordion_header("Section 3", expanded=False), 12)
], height="auto")
layout.add_row([
(self.get_section3_content(), 12)
], height="auto", css_class="accordion-content hidden", id="section3")
return layout
瀑布流布局¶
def get_masonry_layout(self):
"""具有不同组件高度的瀑布流式布局"""
layout = Layout(columns=12)
# 创建瀑布流网格
# 组件将流入可用空间
masonry_grid = []
# 添加不同高度的组件
masonry_grid.append((self.get_tall_card(), 6)) # 2列,高
masonry_grid.append((self.get_short_card(), 3)) # 1列,矮
masonry_grid.append((self.get_medium_card(), 3)) # 1列,中等
masonry_grid.append((self.get_short_card(), 3)) # 1列,矮
masonry_grid.append((self.get_tall_card(), 6)) # 2列,高
masonry_grid.append((self.get_medium_card(), 3)) # 1列,中等
# 将所有组件添加在一行中
# CSS将处理瀑布流布局
layout.add_row(masonry_grid, height="auto", css_class="masonry-grid")
return layout
带侧边栏的数据看板¶
def get_sidebar_layout(self):
"""经典侧边栏布局"""
layout = Layout(columns=12)
# 侧边栏(固定宽度)
sidebar_components = [
self.get_user_profile(),
self.get_quick_stats(),
self.get_navigation(),
self.get_filters(),
]
# 主要内容(流动宽度)
main_components = [
self.get_header(),
self.get_cards(),
self.get_main_chart(),
self.get_data_table(),
]
# 创建两列布局
layout.add_row([
# 侧边栏列
(self.create_vertical_stack(sidebar_components), 3),
# 主要内容列
(self.create_vertical_stack(main_components), 9),
], height="auto")
return layout
def create_vertical_stack(self, components):
"""垂直堆叠组件的辅助函数"""
stack = Layout(columns=12)
for component in components:
stack.add_row([(component, 12)], height="auto")
return stack
布局性能¶
优化布局性能¶
def get_optimized_layout(self):
"""具有性能优化的布局"""
layout = Layout(columns=12)
# 1. 懒加载重型组件
layout.add_row([
(self.get_lazy_loaded_chart(), 12)
], height="400px")
# 2. 缓存静态布局
cache_key = f"layout_{self.__class__.__name__}"
cached_layout = cache.get(cache_key)
if cached_layout:
return cached_layout
# 3. 高效构建布局
# 从快速渲染的组件开始
layout.add_row([
(self.get_quick_cards(), 12)
], height="auto")
# 然后添加较重的组件
layout.add_row([
(self.get_complex_chart(), 12)
], height="500px")
# 缓存布局
cache.set(cache_key, layout, timeout=300) # 5分钟
return layout
渐进增强¶
def get_progressive_layout(self):
"""渐进加载的布局"""
layout = Layout(columns=12)
# 阶段1:立即加载的内容(快速)
layout.add_row([
(self.get_loading_indicator(), 12)
], height="auto")
layout.add_row([
(self.get_basic_cards(), 12)
], height="auto")
# 阶段2:延迟内容(初始渲染后加载)
deferred_chart = self.get_main_chart()
deferred_chart.deferred = True
deferred_chart.load_timeout = 1000 # 1秒后加载
layout.add_row([
(deferred_chart, 12)
], height="400px")
# 阶段3:懒加载内容(可见时加载)
lazy_table = self.get_data_table()
lazy_table.lazy = True
layout.add_row([
(lazy_table, 12)
], height="auto")
return layout
组件优先级¶
def get_prioritized_layout(self):
"""具有组件加载优先级的布局"""
layout = Layout(columns=12)
# 优先级1:首屏内容(立即加载)
layout.add_row([
(self.get_critical_metrics(), 12)
], height="auto", priority=1)
# 优先级2:重要但不关键
layout.add_row([
(self.get_main_chart(), 12)
], height="400px", priority=2)
# 优先级3:首屏以下内容(可以懒加载)
layout.add_row([
(self.get_detailed_table(), 12)
], height="auto", priority=3)
# 优先级4:可选内容
layout.add_row([
(self.get_secondary_charts(), 12)
], height="300px", priority=4)
return layout
常见布局示例¶
高管数据看板¶
def get_executive_dashboard(self):
"""简洁的高层高管数据看板"""
layout = Layout(columns=12)
# 1. 关键指标(顶部行)
layout.add_row([
(self.get_metric_card("Revenue", "primary"), 3),
(self.get_metric_card("Profit", "success"), 3),
(self.get_metric_card("Growth", "warning"), 3),
(self.get_metric_card("ROI", "info"), 3),
], height="auto")
# 2. 主要性能图表
layout.add_row([
(self.get_performance_chart(), 12)
], height="500px")
# 3. 部门指标
layout.add_row([
(self.get_department_card("Sales"), 4),
(self.get_department_card("Marketing"), 4),
(self.get_department_card("Operations"), 4),
], height="auto")
# 4. 快速洞察
layout.add_row([
(self.get_insights_panel(), 12)
], height="auto")
return layout
数据看板¶
def get_operational_dashboard(self):
"""详细的运营数据看板"""
layout = Layout(columns=12)
# 1. 实时指标
layout.add_row([
(self.get_realtime_card("Active Users"), 3),
(self.get_realtime_card("Orders/Hour"), 3),
(self.get_realtime_card("Error Rate"), 3),
(self.get_realtime_card("Response Time"), 3),
], height="auto")
# 2. 系统状态
layout.add_row([
(self.get_system_status(), 12)
], height="auto")
# 3. 监控图表
layout.add_row([
(self.get_traffic_chart(), 6),
(self.get_error_chart(), 6),
], height="300px")
# 4. 最近事件
layout.add_row([
(self.get_event_log(), 8),
(self.get_alerts_panel(), 4),
], height="400px")
# 5. 性能指标
layout.add_row([
(self.get_performance_table(), 12)
], height="auto")
return layout
数据看板¶
def get_analytics_dashboard(self):
"""数据数据看板"""
layout = Layout(columns=12)
# 1. 过滤器
layout.add_row([
(self.get_analytics_filters(), 12)
], height="auto")
# 2. 摘要卡片
layout.add_row([
(self.get_summary_card("Sessions"), 3),
(self.get_summary_card("Users"), 3),
(self.get_summary_card("Pageviews"), 3),
(self.get_summary_card("Bounce Rate"), 3),
], height="auto")
# 3. 趋势分析
layout.add_row([
(self.get_trend_chart(), 12)
], height="400px")
# 4. 细分
layout.add_row([
(self.get_segmentation_chart(), 6),
(self.get_demographics_chart(), 6),
], height="300px")
# 5. 转化漏斗
layout.add_row([
(self.get_conversion_funnel(), 12)
], height="300px")
# 6. 详细报告
layout.add_row([
(self.get_top_pages(), 6),
(self.get_traffic_sources(), 6),
], height="auto")
return layout
移动优化数据看板¶
def get_mobile_dashboard(self):
"""为移动设备优化的数据看板"""
layout = Layout(columns=12)
# 移动设备的单列布局
layout.add_row([(self.get_mobile_header(), 12)], height="auto")
# 堆叠卡片(每行一个)
for card in self.get_mobile_cards():
layout.add_row([(card, 12)], height="auto")
# 单一图表(为移动设备简化)
layout.add_row([(self.get_mobile_chart(), 12)], height="250px")
# 可折叠区域用于附加数据
layout.add_row([(self.get_collapsible_table(), 12)], height="auto")
# 底部导航
layout.add_row([(self.get_mobile_navigation(), 12)], height="auto")
return layout
打印优化布局¶
def get_print_layout(self):
"""为打印优化的布局"""
layout = Layout(columns=12)
# 打印专用样式
layout.print_css = """
@media print
.page-break { page-break-after: always; }
.dashboard { padding: 0; margin: 0; }
.card { break-inside: avoid; }
}
"""
# 带有打印信息的标题
layout.add_row([
(self.get_print_header(), 12)
], height="auto", css_class="no-print")
# 关键指标(避免分页)
layout.add_row([
(self.get_print_metric("Revenue"), 4),
(self.get_print_metric("Cost"), 4),
(self.get_print_metric("Profit"), 4),
], height="auto", css_class="avoid-break")
# 图表前分页
layout.add_row([
(self.get_print_chart(), 12)
], height="300px", css_class="page-break")
# 表格(确保它们不会跨页分割)
layout.add_row([
(self.get_print_table(), 12)
], height="auto", css_class="avoid-break")
return layout
布局工具¶
布局辅助函数¶
class LayoutHelpers:
"""常见布局模式的辅助函数"""
@staticmethod
def create_grid(components, columns_per_row=3):
"""创建响应式组件网格"""
layout = Layout(columns=12)
column_width = 12 // columns_per_row
for i in range(0, len(components), columns_per_row):
row_components = components[i:i + columns_per_row]
row_items = [(comp, column_width) for comp in row_components]
# 如果最后一行不完整,则调整
if len(row_items) < columns_per_row:
last_item_span = 12 - (column_width * (len(row_items) - 1))
row_items[-1] = (row_items[-1][0], last_item_span)
layout.add_row(row_items, height="auto")
return layout
@staticmethod
def create_two_column(left_component, right_component, left_width=8):
"""创建两列布局"""
layout = Layout(columns=12)
right_width = 12 - left_width
layout.add_row([
(left_component, left_width),
(right_component, right_width),
], height="auto")
return layout
@staticmethod
def create_sidebar(main_content, sidebar_content, sidebar_width=3):
"""创建侧边栏布局"""
layout = Layout(columns=12)
main_width = 12 - sidebar_width
layout.add_row([
(main_content, main_width),
(sidebar_content, sidebar_width),
], height="auto")
return layout
@staticmethod
def create_centered(component, width=8):
"""创建居中对齐布局"""
layout = Layout(columns=12)
offset = (12 - width) // 2
# 添加空列以实现偏移
if offset > 0:
layout.add_row([
(Component(component_type="spacer"), offset),
(component, width),
(Component(component_type="spacer"), offset),
], height="auto")
else:
layout.add_row([(component, width)], height="auto")
return layout
布局验证¶
def validate_layout(layout):
"""验证布局结构"""
errors = []
for i, row in enumerate(layout.rows):
total_columns = sum(width for _, width in row["components"])
if total_columns != 12:
errors.append(f"Row {i + 1}: Total columns ({total_columns}) != 12")
for j, (component, width) in enumerate(row["components"]):
if not component:
errors.append(f"Row {i + 1}, Component {j + 1}: Component is None")
if width < 1 or width > 12:
errors.append(f"Row {i + 1}, Component {j + 1}: Invalid width ({width})")
return errors
下一步¶
布局故障排除¶
常见布局问题¶
1. 组件未对齐¶
- 检查每行列跨度总和为12
- 验证组件是否正确实例化
- 检查CSS冲突
2. 响应式问题¶
- 在不同屏幕尺寸上测试
- 检查浏览器开发者工具
- 验证响应式CSS是否已应用
3. 性能问题¶
- 减少每行的组件数量
- 对重型组件实施懒加载
- 优化数据库查询
4. 打印布局问题¶
- 使用打印专用CSS
- 避免关键内容分页
- 在浏览器中测试打印预览
调试布局¶
def debug_layout(self):
"""调试布局创建"""
layout = Layout(columns=12)
print(f"Creating layout for {self.__class__.__name__}")
# 为每一行添加调试信息
for i, row_data in enumerate(self.get_row_data()):
print(f"Adding row {i + 1}: {len(row_data)} components")
row_components = []
for j, component in enumerate(row_data):
print(f" Component {j + 1}: {component.__class__.__name__}")
row_components.append((component, 12 // len(row_data)))
layout.add_row(row_components, height="auto")
print(f"Layout created with {len(layout.rows)} rows")
return layout