跳转至

布局 API 参考

Layout 类管理数据看板内组件的排列。本参考涵盖了布局的创建、配置和高级使用模式。

目录

概述

什么是布局?

布局定义了组件在数据看板上的排列方式:

  1. 网格系统: 12列响应式网格
  2. 行与列: 组件按行排列,具有列宽度
  3. 响应式设计: 适应不同屏幕尺寸
  4. 组件管理: 组织和定位组件

布局 Concepts

  • 网格列: 每行总共12列
  • 组件宽度: 组件占据的列数 (1-12)
  • : 组件的水平容器
  • 行高度: 行的固定或自动高度
  • 嵌套: 组件可以包含子布局 (高级)

导入路径

    from django_admin_dashboards.base import Layout

类参考

Layout

    class Layout:
        """Layout manager for dashboard"""

        def __init__(self, columns=12):
            """Initialize layout with column count"""

        def add_row(self, components, height="auto"):
            """Add a row of components"""

        def add_component(self, component, width=12):
            """Add a single component as a row"""

构造函数

__init__(columns=12)

参数:

  • columns (int): 网格中的列数 (默认: 12)

返回: Layout 实例

描述:

使用指定的列数创建新的布局管理器。

使用:

    # Default 12-column layout (recommended)

    layout = Layout()

    # Custom column count (advanced use)

    layout = Layout(columns=16)

方法

add_row(components, height="auto")

参数: - components (list): (component, width) 元组列表 - height (str): 行高度,CSS单位或"auto"

返回: self (用于方法链式调用)

描述:

添加包含多个组件的行。

组件 Format:

    components = [
        (component1, width1),  # component, column width (1-12)

        (component2, width2),
        # ...

    ]

高度值: - "auto": 高度由内容决定 - "400px": 固定高度(像素) - "50vh": 视口高度的百分比 - "min-content": 所需最小高度

使用:

    layout.add_row([
        (card1, 6),   # 6 columns (50% width)

        (card2, 6)    # 6 columns (50% width)

    ], height="auto")

add_component(component, width=12)

参数: - component (Component): 组件实例 - width (int): 列宽度 (1-12, 默认: 12)

返回: self (用于方法链式调用)

描述:

将单个组件添加为完整行。

使用:

    # Full-width component

    layout.add_component(full_width_chart)

    # Half-width component (creates a row with one component)

    layout.add_component(half_width_card, width=6)

属性

属性 类型 描述
columns int 网格中的列数
rows list 行字典列表

行 Structure

Layout.rows 中的每一行具有以下结构:

    row = {
        "components": [
            (component_instance, column_width),
            # ... more components

        ],
        "height": "400px"  # or "auto", "50vh", etc.

    }

基础 使用

创建布局

    from django_admin_dashboards.base import Layout, CardComponent, ChartComponent

    # 创建布局

    layout = Layout(columns=12)

    # 添加组件

    card1 = CardComponent(title="Card 1", value=100)
    card2 = CardComponent(title="Card 2", value=200)
    chart = ChartComponent(title="Chart", chart_type="line", data={})

    # 添加包含两张卡片的行 (每张6列)

    layout.add_row([
        (card1, 6),
        (card2, 6)
    ])

    # 添加全宽图表

    layout.add_row([
        (chart, 12)
    ])

    # 替代方案:添加单个组件

    layout.add_component(card1, width=12)

数据看板中的布局

    from django_admin_dashboards.base import Dashboard, Layout, CardComponent

    class MyDashboard(Dashboard):

        def get_layout(self):
            layout = Layout(columns=12)

            # 创建组件

            users_card = CardComponent(
                title="Total Users",
                value=User.objects.count()
            )

            sales_card = CardComponent(
                title="Today's Sales",
                value=1500,
                change="+12%"
            )

            # 在布局中排列

            layout.add_row([
                (users_card, 6),
                (sales_card, 6)
            ])

            return layout

方法链式调用

布局方法支持链式调用:

    layout = Layout() \
        .add_row([(card1, 4), (card2, 4), (card3, 4)]) \
        .add_row([(chart1, 8), (chart2, 4)]) \
        .add_component(full_width_table)

验证列宽度

布局系统验证列宽度:

    # 有效:总和为12

    layout.add_row([(c1, 6), (c2, 6)])      # 6 + 6 = 12 ✓

    layout.add_row([(c1, 4), (c2, 4), (c3, 4)])  # 4 + 4 + 4 = 12 ✓

    # 无效:总和不为12

    # layout.add_row([(c1, 6), (c2, 8)])    # 6 + 8 = 14 ✗ (would raise error)

    # 单个组件可以是1-12之间的任意宽度

    layout.add_component(c1, width=8)       # Valid ✓

布局模式

等宽列

    # 两等宽列

    layout.add_row([(component1, 6), (component2, 6)])

    # 三等宽列

    layout.add_row([(c1, 4), (c2, 4), (c3, 4)])

    # 四等宽列

    layout.add_row([(c1, 3), (c2, 3), (c3, 3), (c4, 3)])

    # 六等宽列

    layout.add_row([(c1, 2), (c2, 2), (c3, 2), (c4, 2), (c5, 2), (c6, 2)])

非对称布局

    # 主次布局 (8-4)

    layout.add_row([(main_content, 8), (sidebar, 4)])

    # 三分之二对三分之一 (8-4)

    layout.add_row([(primary, 8), (secondary, 4)])

    # 四分之三对四分之一 (9-3)

    layout.add_row([(main, 9), (aside, 3)])

    # 黄金比例近似 (7-5)

    layout.add_row([(golden_main, 7), (golden_side, 5)])

混合宽度模式

    # 数据看板标题模式

    layout.add_row([(title_card, 12)])                    # 全宽标题

    layout.add_row([(kpi1, 3), (kpi2, 3), (kpi3, 3), (kpi4, 3)])  # 4个卡片

    layout.add_row([(main_chart, 8), (sidebar_stats, 4)]) # 带统计的图表

    layout.add_row([(table, 12)])                         # 全宽表格

垂直堆叠

    # 垂直堆叠组件(每个全宽)

    layout.add_component(component1)      # width=12 by default

    layout.add_component(component2)
    layout.add_component(component3)

水平流

    # 所有组件在一行中(必须总和为12)

    layout.add_row([
        (small1, 2),
        (small2, 2),
        (medium1, 4),
        (medium2, 4)
    ])

高级技术

动态布局

基于数据或条件创建布局:

    def get_layout(self):
        layout = Layout(columns=12)

        # 基于用户角色添加组件

        if self.request.user.is_superuser:
            components = self.get_admin_components()
        else:
            components = self.get_user_components()

        # 动态排列组件

        row = []
        total_width = 0

        for component in components:
            width = component.preferred_width or 6

            if total_width + width > 12:
                # 开始新行

                layout.add_row(row)
                row = [(component, width)]
                total_width = width
            else:
                # 添加到当前行

                row.append((component, width))
                total_width += width

        # 添加最后一行

        if row:
            layout.add_row(row)

        return layout

嵌套布局(概念性)

虽然不直接支持,但您可以模拟嵌套布局:

    def create_nested_layout(self):
        """创建具有概念性嵌套的布局"""
        layout = Layout(columns=12)

        # 顶层:标题

        layout.add_row([(header, 12)])

        # 中间:主要内容区域

        main_layout = self.create_main_content_layout()
        layout.add_row([(main_layout_component, 12)])

        # 底部:页脚

        layout.add_row([(footer, 12)])

        return layout

    def create_main_content_layout(self):
        """为主要内容区域创建布局"""
        sub_layout = Layout(columns=12)

        # 侧边栏(概念性嵌套)

        sidebar_components = self.get_sidebar_components()
        for component in sidebar_components:
            sub_layout.add_component(component, width=3)

        # 主要内容(概念性嵌套)

        main_components = self.get_main_components()
        for component in main_components:
            sub_layout.add_component(component, width=9)

        return sub_layout

条件行

基于条件显示/隐藏行:

    def get_layout(self):
        layout = Layout(columns=12)

        # 始终显示标题

        layout.add_row([(header_card, 12)])

        # 条件行:仅适用于管理员

        if self.request.user.is_staff:
            layout.add_row([(admin_stats, 12)])

        # 基于数据的条件

        if self.has_alerts():
            layout.add_row([(alerts_panel, 12)])

        # 基于视图模式显示不同的布局

        view_mode = self.request.GET.get('view', 'standard')

        if view_mode == 'minimal':
            return self.get_minimal_layout()
        elif view_mode == 'detailed':
            return self.get_detailed_layout()
        else:
            # 标准布局

            layout.add_row([(main_content, 12)])

        return layout

布局模板

创建可重用的布局模式:

    class LayoutTemplates:
        """可重用的布局模式"""

        @staticmethod
        def card_dashboard(cards):
            """卡片数据看板布局:一行4张卡片"""
            layout = Layout(columns=12)

            if len(cards) == 4:
                layout.add_row([
                    (cards[0], 3),
                    (cards[1], 3),
                    (cards[2], 3),
                    (cards[3], 3)
                ])
             elif len(cards) == 6:
                # 第一行:3张卡片

                layout.add_row([
                    (cards[0], 4),
                    (cards[1], 4),
                     (cards[2], 4)
                ])
                # 第二行:3张卡片

                layout.add_row([
                    (cards[3], 4),
                    (cards[4], 4),
                    (cards[5], 4)
                ])

            return layout

        @staticmethod
        def main_with_sidebar(main_component, sidebar_components):
            """带有主要内容和侧边栏的布局"""
            layout = Layout(columns=12)

            # 添加侧边栏组件

            sidebar_layout = Layout(columns=12)
            for component in sidebar_components:
                sidebar_layout.add_component(component, width=12)

            # Main layout (8 columns main, 4 columns sidebar)

            layout.add_row([
                (main_component, 8),
                (sidebar_layout, 4)  # Note: passing layout as component

            ])

            return layout

    # Usage

    layout = LayoutTemplates.card_dashboard(cards)

布局 Inheritance

Extend existing layouts:

    class BaseLayout(Layout):
        """Base layout with common configuration"""

        def __init__(self, columns=12):
            super().__init__(columns)
            self.common_rows = []

        def add_common_header(self):
            """Add common header to layout"""
            header = CardComponent(title="Dashboard", value="Overview")
            self.add_row([(header, 12)])
            return self

        def add_common_footer(self):
            """Add common footer to layout"""
            footer = CardComponent(title="Last Updated", value=timezone.now())
            self.add_row([(footer, 12)])
            return self

    class CustomLayout(BaseLayout):
        """Custom layout extending base"""

        def create_dashboard_layout(self, components):
            """Create complete dashboard layout"""
            self.add_common_header()

            # Add components in rows of 3

            for i in range(0, len(components), 3):
                row_components = components[i:i+3]
                widths = [4, 4, 4][:len(row_components)]
                self.add_row(list(zip(row_components, widths)))

            self.add_common_footer()
            return self

响应式设计

CSS 网格基础

布局系统使用CSS网格实现响应式设计:

    /* 基础网格系统 */
    .dashboard-grid {
        display: grid;
        grid-template-columns: repeat(12, 1fr);
        gap: 20px;
        width: 100%;
    }

    /* 列跨度 */
    .col-span-1 { grid-column: span 1; }
    .col-span-2 { grid-column: span 2; }
    .col-span-3 { grid-column: span 3; }
    .col-span-4 { grid-column: span 4; }
    .col-span-5 { grid-column: span 5; }
    .col-span-6 { grid-column: span 6; }
    .col-span-7 { grid-column: span 7; }
    .col-span-8 { grid-column: span 8; }
    .col-span-9 { grid-column: span 9; }
    .col-span-10 { grid-column: span 10; }
    .col-span-11 { grid-column: span 11; }
    .col-span-12 { grid-column: span 12; }

响应式断点

内置响应式行为:

屏幕尺寸 列行为


桌面 (> 1200px) 完整12列布局 平板 (768px - 1199px) 组件可能堆叠 移动设备 (\< 768px) 单列布局

自定义响应式规则

使用自定义CSS覆盖响应式行为:

    /* dashboard.css - 响应式覆盖 */

    /* 平板每行2列 */
    @media (max-width: 1199px) and (min-width: 768px)

        .dashboard-col.col-span-3 {
            grid-column: span 6 !important; /* 3  6 columns */
        }
    }

    /* Mobile: single column */
    @media (max-width: 767px)
    }

    /* Large desktop: more spacing */
    @media (min-width: 1400px)
    }

组件-aware Responsiveness

Make components 响应式 基于 screen size:

    class ResponsiveDashboard(Dashboard):

        def get_layout(self):
            layout = Layout(columns=12)

            # Detect screen size from request (simplified)

            user_agent = self.request.META.get('HTTP_USER_AGENT', '')
            is_mobile = 'Mobile' in user_agent

            if is_mobile:
                # Mobile layout: single column

                for component in self.get_components():
                    layout.add_component(component, width=12)
            else:
                # Desktop layout: multiple columns

                layout.add_row([
                    (self.get_card1(), 3),
                    (self.get_card2(), 3),
                    (self.get_card3(), 3),
                    (self.get_card4(), 3)
                ])

            return layout

最佳实践

1. 规划您的布局

在编码前草图绘制您的布局:

    # 布局草图:

    # ┌─────────────┬─────────────┐

    # │   Card 1    │   Card 2    │  (6 columns each)

    # ├─────────────┴─────────────┤

    # │        Full Chart         │  (12 columns)

    # ├─────────────┬─────────────┤

    # │  Table 1    │  Table 2    │  (6 columns each)

    # └─────────────┴─────────────┘

    def get_layout(self):
        layout = Layout(columns=12)

        # 第1行:两张卡片

        layout.add_row([
            (self.create_card1(), 6),
            (self.create_card2(), 6)
        ])

        # 第2行:全宽图表

        layout.add_row([
            (self.create_main_chart(), 12)
        ])

        # 第3行:两个表格

        layout.add_row([
            (self.create_table1(), 6),
            (self.create_table2(), 6)
        ])

        return layout

2. 平衡列宽度

保持列宽度平衡且合理:

    # 良好:平衡且总和为12

    layout.add_row([(c1, 4), (c2, 4), (c3, 4)])  # 4+4+4=12 ✓

    layout.add_row([(c1, 6), (c2, 6)])           # 6+6=12 ✓

    # 避免:不平衡且难以阅读

    layout.add_row([(c1, 2), (c2, 3), (c3, 7)])  # 2+3+7=12 but unbalanced

3. 使用一致的高度

保持行高度一致以实现视觉和谐:

    # 一致的高度

    layout.add_row([(card1, 6), (card2, 6)], height="200px")
    layout.add_row([(chart1, 8), (chart2, 4)], height="400px")
    layout.add_row([(table, 12)], height="auto")  # Let content determine height

    # 避免在同一数据看板中混合固定高度和自动高度

4. 分组相关组件

将相关组件保持在一起:

    def get_layout(self):
        layout = Layout(columns=12)

        # 用户统计分组

        layout.add_row([(user_header, 12)])
        layout.add_row([
            (total_users, 3),
            (active_users, 3),
            (new_users, 3),
            (churned_users, 3)
        ])

        # Sales group

        layout.add_row([(sales_header, 12)])
        layout.add_row([
            (revenue_chart, 8),
            (sales_card, 4)
        ])

        return layout

5. 优化性能

最小化布局计算:

    class OptimizedDashboard(Dashboard):

        def __init__(self, request=None):
            super().__init__(request)
            self._layout_cache = None

        def get_layout(self):
            # 缓存布局以避免重复计算

            if self._layout_cache is None:
                self._layout_cache = self._create_layout()

            return self._layout_cache

        def _create_layout(self):
            """实际创建布局(仅调用一次)"""
            layout = Layout(columns=12)

            # 构建布局一次

            # ... layout building code ...

            return layout

6. 测试布局响应性

在不同的屏幕尺寸下测试您的布局:

    from django.test import TestCase
    from django.test.client import RequestFactory

    class LayoutTests(TestCase):

        def test_layout_creation(self):
            layout = Layout(columns=12)
            self.assertEqual(layout.columns, 12)

        def test_row_creation(self):
            layout = Layout()
            card = CardComponent(title="Test", value=100)

            layout.add_row([(card, 12)])
            self.assertEqual(len(layout.rows), 1)
            self.assertEqual(layout.rows[0]["components"][0][1], 12)

        def test_column_sum_validation(self):
            layout = Layout()
            card1 = CardComponent(title="Test1", value=100)
            card2 = CardComponent(title="Test2", value=200)

            # 这应该能工作

            layout.add_row([(card1, 6), (card2, 6)])

            # 这可能会失败(取决于实现)

            # layout.add_row([(card1, 7), (card2, 6)])  # 7+6=13 > 12

7. 记录布局决策

记录您选择特定布局的原因:

    class DocumentedDashboard(Dashboard):
        """
        数据看板布局文档:

        1. 标题行:全宽标题
         2. 卡片行:4张卡片(每张3列)- 显示关键指标
        3. 图表行:主图表(8列)带侧边栏(4列)
        4. 表格行:全宽用于详细数据

        响应式行为:
        - 桌面:12列网格
        - 平板:每行2列
        - 移动设备:每行1列
        """

        def get_layout(self):
            # 实现遵循上述文档

            pass

常见问题与解决方案

组件溢出

问题: 组件不适合12列网格。

解决方案: 在添加前验证列总和:

    def add_validated_row(self, layout, components_with_widths):
        """添加带验证的行"""
        total_width = sum(width for _, width in components_with_widths)

        if total_width != 12:
            # 按比例调整宽度

            adjusted = []
            for component, width in components_with_widths:
                adjusted_width = int((width / total_width) * 12)
                adjusted.append((component, adjusted_width))

            components_with_widths = adjusted

        layout.add_row(components_with_widths)
        return layout

组件高度不均匀

问题: 同一行中的组件高度不同。

解决方案: 1. 为行设置固定高度 2. 使用CSS平衡高度 3. 将相似高度的组件分组在一起

    # 设置固定高度

    layout.add_row([(tall_component, 6), (short_component, 6)], height="300px")

    # 或使用CSS

    .dashboard-row {
        display: grid;
        grid-template-columns: repeat(12, 1fr);
        align-items: stretch;  /* 使子元素等高 */
    }

    .dashboard-col {
        height: 100%;  /* Take full height of row */
    }

响应式布局断裂

问题: 布局不能很好地适应移动设备。

解决方案: 使用CSS媒体查询并彻底测试:

    /* 在移动设备上强制单列 */
    @media (max-width: 767px)

        .dashboard-row {
            display: block;  /* 从网格切换到块 */
        }
    }

多组件性能

问题: 包含许多组件的数据看板加载缓慢。

解决方案: 1. 分页或懒加载组件 2. 缓存布局和数据 3. 使用较小的初始布局,按需扩展

    class LazyDashboard(Dashboard):

        def get_initial_layout(self):
            """返回最小初始布局"""
            layout = Layout(columns=12)
            layout.add_row([(self.get_essential_cards(), 12)])
            return layout

        def get_full_layout(self):
            """返回完整布局(按需加载)"""
            layout = Layout(columns=12)
            # ... all components ...

            return layout

布局 示例 参考

分析 数据看板

    def create_analytics_layout(self):
        """数据看板布局"""
        layout = Layout(columns=12)

        # 1. Header with key metrics

        layout.add_row([(self.create_dashboard_title(), 12)])

        # 2. Top cards (4 cards)

        cards = self.get_cards()
        layout.add_row([
            (cards[0], 3),
            (cards[1], 3),
            (cards[2], 3),
            (cards[3], 3)
        ])

        # 3. Main charts (2 charts side by side)

        layout.add_row([
            (self.create_main_chart(), 8),
            (self.create_secondary_chart(), 4)
        ])

        # 4. Detailed data tables

        layout.add_row([(self.create_data_table(), 12)])

        # 5. Footer with insights

        layout.add_row([(self.create_insights_panel(), 12)])

        return layout

Monitoring 数据看板

    def create_monitoring_layout(self):
        """系统监控布局"""
        layout = Layout(columns=12)

        # 全宽状态横幅

        layout.add_row([(self.create_status_banner(), 12)])

        # 网格中的系统指标

        metrics = self.get_system_metrics()
        for i in range(0, len(metrics), 3):
            row_metrics = metrics[i:i+3]
            layout.add_row([
                (row_metrics[0], 4),
                (row_metrics[1], 4),
                (row_metrics[2], 4)
            ])

        # 警报面板和图表

        layout.add_row([
            (self.create_alerts_panel(), 4),
            (self.create_performance_chart(), 8)
        ])

        return layout

Executive 数据看板

    def create_executive_layout(self):
        """为高管简化的布局"""
        layout = Layout(columns=12)

        # 简洁、极简的设计

        layout.add_row([(self.create_executive_summary(), 12)], height="auto")

        # 仅关键指标

        key_metrics = self.get_key_metrics()
        layout.add_row([
            (key_metrics[0], 4),
            (key_metrics[1], 4),
            (key_metrics[2], 4)
        ], height="150px")

        # 单个重要图表

        layout.add_row([(self.create_primary_chart(), 12)], height="500px")

        return layout

相关文档