数据网格应用


欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net

适配的第三方库地址:

  • shared_preferences: https://pub.dev/packages/shared_preferences
  • file_selector: https://pub.dev/packages/file_selector
  • path_provider: https://pub.dev/packages/path_provider
  • intl: https://pub.dev/packages/intl

一、项目概述

运行效果图

image-20260412155412685

image-20260412155424787

image-20260412155431334

image-20260412155435206

image-20260412155439600

1.1 应用简介

数据网格应用是一款专业的数据管理工具,为用户提供高效的数据展示、筛选、编辑和导出功能。应用采用现代化的表格界面设计,支持多列数据展示、点击列头排序、多条件筛选、单元格编辑等核心功能,满足用户日常数据管理需求。

应用以清新的青绿色为主色调,象征数据的专业与可靠。涵盖数据网格、数据筛选、数据统计、系统设置四大模块。用户可以快速浏览数据、筛选目标记录、编辑数据内容、导出CSV文件,实现数据的高效管理。

1.2 核心功能

功能模块功能描述实现方式
数据展示多列数据表格展示DataTable
数据排序点击列头升序/降序排序排序算法
数据筛选多条件组合筛选筛选引擎
数据编辑点击单元格编辑数据TextField
数据导出导出为CSV文件文件写入
数据导入导入CSV文件文件解析
数据统计数据汇总统计分析统计引擎
数据持久化本地数据存储SharedPreferences

1.3 筛选类型定义

序号筛选类型Emoji描述适用数据
1包含🔍包含指定文本文本/数字
2等于⚖️完全匹配文本/数字
3开头为🔤以指定文本开头文本
4结尾为📝以指定文本结尾文本
5大于⬆️数值大于指定值数字
6小于⬇️数值小于指定值数字

1.4 排序方向定义

序号排序方向Emoji描述图标
1升序⬆️从小到大排列arrow_upward
2降序⬇️从大到小排列arrow_downward

1.5 数据列定义

序号列名数据类型可编辑宽度
1ID数字80
2姓名文本100
3部门文本120
4职位文本120
5薪资数字100
6入职日期日期110
7状态文本80
8邮箱文本180
9电话文本120

1.6 技术栈

技术领域技术选型版本要求
开发框架Flutter>= 3.0.0
编程语言Dart>= 2.17.0
设计规范Material Design 3-
数据存储SharedPreferences>= 2.0.0
文件选择file_selector>= 1.0.0
文件路径path_provider>= 2.0.0
日期格式intl>= 0.18.0
目标平台鸿蒙OS / WebAPI 21+

1.7 项目结构

lib/
└── main_data_grid.dart
    ├── DataGridApp                      # 应用入口
    ├── SortDirection                    # 排序方向枚举
    ├── FilterType                       # 筛选类型枚举
    ├── GridColumn                       # 网格列模型
    ├── GridRow                          # 网格行模型
    ├── DataGridHomePage                 # 主页面(底部导航)
    ├── _buildGridPage                   # 数据网格页
    ├── _buildFilterPage                 # 数据筛选页
    ├── _buildStatsPage                  # 数据统计页
    └── _buildSettingsPage               # 设置页

二、系统架构

2.1 整体架构图

Data Layer

Business Layer

Presentation Layer

主页面
DataGridHomePage

数据网格页

数据筛选页

数据统计页

设置页

数据表格

搜索栏

操作按钮

筛选条件

筛选结果

统计卡片

部门分布

关于应用

数据管理

数据管理器
DataManager

筛选引擎
FilterEngine

排序引擎
SortEngine

统计引擎
StatsEngine

GridColumn
网格列

GridRow
网格行

SharedPreferences
本地存储

2.2 类图设计

uses

uses

manages

manages

DataGridApp

+Widget build()

«enumeration»

SortDirection

+ascending

+descending

«enumeration»

FilterType

+String label

+contains()

+equals()

+startsWith()

+endsWith()

+greaterThan()

+lessThan()

GridColumn

+String key

+String label

+bool isNumeric

+bool isEditable

+double width

GridRow

+String id

+Map<String,dynamic> values

+DateTime createdAt

+DateTime? updatedAt

+copyWith()

DataGridHomePage

-int _currentIndex

-List<GridColumn> _columns

-List<GridRow> _rows

-List<GridRow> _filteredRows

-String? _sortColumn

-SortDirection _sortDirection

-Map<String,String> _filters

-Set<String> _selectedRows

+initState()

+_loadData()

+_saveData()

+_applyFiltersAndSort()

+_sort()

+_addRow()

+_deleteSelectedRows()

+_exportData()

+_importData()

2.3 页面导航流程

点击筛选Tab

点击统计Tab

点击设置Tab

添加数据

选择行

导出数据

导入数据

应用启动

数据网格页

用户操作

数据筛选页

设置筛选条件

应用筛选

数据统计页

查看统计数据

设置页

管理数据

新增行

编辑数据

选中数据

删除选中

导出CSV

保存文件

选择文件

解析CSV

2.4 数据操作流程

数据存储排序引擎筛选引擎数据网格用户数据存储排序引擎筛选引擎数据网格用户打开应用加载数据返回数据输入搜索关键词应用搜索筛选返回筛选结果点击列头排序执行排序返回排序结果编辑单元格更新数据保存数据导出数据生成CSV保存文件

三、核心模块设计

3.1 数据模型设计

3.1.1 筛选类型枚举 (FilterType)
enum FilterType {
  contains(label: '包含'),
  equals(label: '等于'),
  startsWith(label: '开头为'),
  endsWith(label: '结尾为'),
  greaterThan(label: '大于'),
  lessThan(label: '小于');

  final String label;
  const FilterType({required this.label});
}
3.1.2 排序方向枚举 (SortDirection)
enum SortDirection {
  ascending,
  descending,
}
3.1.3 网格列模型 (GridColumn)
class GridColumn {
  final String key;
  final String label;
  final bool isNumeric;
  final bool isEditable;
  final double width;

  const GridColumn({
    required this.key,
    required this.label,
    this.isNumeric = false,
    this.isEditable = true,
    this.width = 120,
  });
}
3.1.4 网格行模型 (GridRow)
class GridRow {
  final String id;
  final Map<String, dynamic> values;
  final DateTime createdAt;
  final DateTime? updatedAt;

  const GridRow({
    required this.id,
    required this.values,
    required this.createdAt,
    this.updatedAt,
  });

  GridRow copyWith({
    Map<String, dynamic>? values,
    DateTime? updatedAt,
  }) {
    return GridRow(
      id: id,
      values: values ?? this.values,
      createdAt: createdAt,
      updatedAt: updatedAt ?? this.updatedAt,
    );
  }
}
3.1.5 筛选类型分布
40%20%15%10%10%5%筛选类型使用分布示例包含等于开头为结尾为大于小于

3.2 页面结构设计

3.2.1 主页面布局

DataGridHomePage

IndexedStack

数据网格页

数据筛选页

数据统计页

设置页

NavigationBar

数据 Tab

筛选 Tab

统计 Tab

设置 Tab

3.2.2 数据网格页结构

数据网格页

顶部栏

选择提示栏

数据表格

标题

操作按钮

搜索框

添加行

删除选中

导出数据

导入数据

列头

数据行

单元格

3.2.3 数据筛选页结构

数据筛选页

SliverAppBar

筛选条件卡片

筛选结果卡片

字段筛选器

筛选类型选择

筛选值输入

清除按钮

结果数量

数据预览

3.2.4 数据统计页结构

数据统计页

SliverAppBar

统计卡片组

部门分布卡片

总记录数

总薪资

平均薪资

部门数

部门列表

进度条

百分比

3.3 筛选引擎逻辑

包含

等于

开头为

结尾为

大于

小于

开始筛选

复制数据列表

搜索关键词?

应用搜索筛选

遍历筛选条件

筛选值非空?

获取筛选类型

跳过此条件

筛选类型?

包含匹配

完全匹配

开头匹配

结尾匹配

数值比较

数值比较

过滤数据

还有条件?

返回结果

3.4 排序引擎逻辑

相同列

不同列

数字

文本

升序

降序

点击列头

当前排序列?

切换排序方向

设置新排序列

设为升序

应用排序

数据类型?

数值比较

字符串比较

比较大小

比较字符

排序方向?

正序排列

倒序排列

更新显示


四、UI设计规范

4.1 配色方案

应用以清新的青绿色为主色调,象征数据的专业与可靠:

颜色类型色值用途
主色#00897B (Teal)导航、主题元素
辅助色#4DB6AC数据表格
第三色#80CBC4筛选页面
强调色#B2DFDB统计页面
背景色#FAFAFA页面背景
卡片背景#FFFFFF信息卡片
数字颜色#2196F3数值显示
警告色#FF9800删除警告
成功色#4CAF50操作成功

4.2 统计卡片配色

统计项色值图标
总记录数#2196F3 (Blue)table_rows
总薪资#4CAF50 (Green)attach_money
平均薪资#FF9800 (Orange)trending_up
部门数#9C27B0 (Purple)business

4.3 字体规范

元素字号字重颜色
页面标题24pxBold主色
表格标题14pxBold#000000
表格内容14pxRegular#000000
数字内容14pxRegular#2196F3
统计数值22pxBold对应颜色
提示文字14pxRegular#666666

4.4 组件规范

4.4.1 数据表格
┌─────────────────────────────────────────────────────────────┐
│  数据网格应用              [+] [🗑] [⬇] [⬆]                   │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 🔍 搜索数据...                                       │   │
│  └─────────────────────────────────────────────────────┘   │
├─────────────────────────────────────────────────────────────┤
│  ID ⬆ │ 姓名   │ 部门   │ 职位       │ 薪资    │ 状态    │
├─────────────────────────────────────────────────────────────┤
│  ☐ 1  │ 张三   │ 技术部 │ 高级工程师 │ 25000   │ 在职    │
│  ☑ 2  │ 李四   │ 产品部 │ 产品经理   │ 22000   │ 在职    │
│  ☐ 3  │ 王五   │ 技术部 │ 前端工程师 │ 18000   │ 在职    │
│  ☐ 4  │ 赵六   │ 市场部 │ 市场专员   │ 15000   │ 离职    │
└─────────────────────────────────────────────────────────────┘
4.4.2 筛选条件
┌─────────────────────────────────────────────────────────────┐
│  筛选条件                              [清除全部]            │
├─────────────────────────────────────────────────────────────┤
│  姓名     [包含 ▼]  [输入筛选值...        ]                  │
│  部门     [等于 ▼]  [技术部              ]                  │
│  薪资     [大于 ▼]  [15000               ]                  │
│  状态     [包含 ▼]  [在职                ]                  │
└─────────────────────────────────────────────────────────────┘
4.4.3 统计卡片
┌─────────────────────────────────────────────────────────────┐
│  ┌───────────────┐  ┌───────────────┐                      │
│  │  📊 8         │  │  💰 151000    │                      │
│  │  总记录数      │  │  总薪资        │                      │
│  └───────────────┘  └───────────────┘                      │
│  ┌───────────────┐  ┌───────────────┐                      │
│  │  📈 18888     │  │  🏢 5         │                      │
│  │  平均薪资      │  │  部门数        │                      │
│  └───────────────┘  └───────────────┘                      │
└─────────────────────────────────────────────────────────────┘
4.4.4 部门分布
┌─────────────────────────────────────────────────────────────┐
│  部门分布                                                    │
├─────────────────────────────────────────────────────────────┤
│  技术部          3 人 (37.5%)                                │
│  ═══════════════════════════════░░░░░░░░░░░░░░░░░░░░░░░░░   │
│                                                              │
│  产品部          2 人 (25.0%)                                │
│  ═════════════════════════░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░   │
│                                                              │
│  市场部          1 人 (12.5%)                                │
│  ═══════════░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░   │
└─────────────────────────────────────────────────────────────┘

五、核心功能实现

5.1 数据加载实现

Future<void> _loadData() async {
  final prefs = await SharedPreferences.getInstance();
  final dataJson = prefs.getString('grid_data');

  if (dataJson != null) {
    try {
      final List<dynamic> decoded = jsonDecode(dataJson);
      setState(() {
        _rows.clear();
        for (var item in decoded) {
          _rows.add(GridRow(
            id: item['id'] ?? '',
            values: Map<String, dynamic>.from(item['values'] ?? {}),
            createdAt: DateTime.tryParse(item['createdAt'] ?? '') ?? DateTime.now(),
            updatedAt: item['updatedAt'] != null
                ? DateTime.tryParse(item['updatedAt'])
                : null,
          ));
        }
        _applyFiltersAndSort();
      });
    } catch (e) {
      _loadSampleData();
    }
  } else {
    _loadSampleData();
  }
}

5.2 筛选引擎实现

void _applyFiltersAndSort() {
  List<GridRow> result = List.from(_rows);

  if (_searchQuery.isNotEmpty) {
    result = result.where((row) {
      return row.values.values.any((value) =>
          value.toString().toLowerCase().contains(_searchQuery.toLowerCase()));
    }).toList();
  }

  _filters.forEach((key, filterValue) {
    if (filterValue.isNotEmpty) {
      final filterType = _filterTypes[key] ?? FilterType.contains;
      result = result.where((row) {
        final cellValue = row.values[key]?.toString() ?? '';
        switch (filterType) {
          case FilterType.contains:
            return cellValue.toLowerCase().contains(filterValue.toLowerCase());
          case FilterType.equals:
            return cellValue.toLowerCase() == filterValue.toLowerCase();
          case FilterType.startsWith:
            return cellValue.toLowerCase().startsWith(filterValue.toLowerCase());
          case FilterType.endsWith:
            return cellValue.toLowerCase().endsWith(filterValue.toLowerCase());
          case FilterType.greaterThan:
            final numValue = num.tryParse(cellValue);
            final filterNum = num.tryParse(filterValue);
            return numValue != null && filterNum != null && numValue > filterNum;
          case FilterType.lessThan:
            final numValue = num.tryParse(cellValue);
            final filterNum = num.tryParse(filterValue);
            return numValue != null && filterNum != null && numValue < filterNum;
        }
      }).toList();
    }
  });

  if (_sortColumn != null) {
    result.sort((a, b) {
      final aValue = a.values[_sortColumn];
      final bValue = b.values[_sortColumn];
      int comparison = 0;
      if (aValue is num && bValue is num) {
        comparison = aValue.compareTo(bValue);
      } else {
        comparison = aValue.toString().compareTo(bValue.toString());
      }
      return _sortDirection == SortDirection.ascending ? comparison : -comparison;
    });
  }

  setState(() {
    _filteredRows.clear();
    _filteredRows.addAll(result);
  });
}

5.3 数据导出实现

Future<void> _exportData() async {
  try {
    final buffer = StringBuffer();
    buffer.writeln(_columns.map((c) => c.label).join(','));

    for (var row in _filteredRows) {
      buffer.writeln(_columns.map((c) {
        final value = row.values[c.key]?.toString() ?? '';
        return value.contains(',') ? '"$value"' : value;
      }).join(','));
    }

    final tempDir = await getTemporaryDirectory();
    final file = File('${tempDir.path}/data_export_${DateTime.now().millisecondsSinceEpoch}.csv');
    await file.writeAsString(buffer.toString());

    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('数据已导出到: ${file.path}')),
      );
    }
  } catch (e) {
    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('导出失败: $e')),
      );
    }
  }
}

5.4 数据导入实现

Future<void> _importData() async {
  try {
    final XFile? file = await openFile(acceptedTypeGroups: [
      XTypeGroup(label: 'CSV文件', extensions: ['csv']),
    ]);

    if (file == null) return;

    final content = await file.readAsString();
    final lines = content.split('\n');

    if (lines.isEmpty) return;

    final headers = lines[0].split(',').map((h) => h.trim().replaceAll('"', '')).toList();

    setState(() {
      _rows.clear();
      for (int i = 1; i < lines.length; i++) {
        if (lines[i].trim().isEmpty) continue;

        final values = <String, dynamic>{};
        final cells = lines[i].split(',');

        for (int j = 0; j < headers.length && j < cells.length; j++) {
          values[headers[j]] = cells[j].trim().replaceAll('"', '');
        }

        _rows.add(GridRow(
          id: (i).toString(),
          values: values,
          createdAt: DateTime.now(),
        ));
      }
      _applyFiltersAndSort();
    });

    _saveData();
  } catch (e) {
    // 处理错误
  }
}

5.5 统计计算实现

Widget _buildStatsPage() {
  final totalSalary = _filteredRows.fold<double>(0, (sum, row) {
    return sum + (num.tryParse(row.values['salary']?.toString() ?? '0') ?? 0);
  });
  final avgSalary = _filteredRows.isEmpty ? 0 : totalSalary / _filteredRows.length;

  final departmentStats = <String, int>{};
  for (var row in _filteredRows) {
    final dept = row.values['department']?.toString() ?? '未知';
    departmentStats[dept] = (departmentStats[dept] ?? 0) + 1;
  }
  // ... 渲染统计页面
}

六、交互设计

6.1 数据编辑流程

数据存储编辑控制器数据表格用户数据存储编辑控制器数据表格用户点击单元格初始化编辑显示输入框输入新值按回车确认获取编辑值更新行数据保存数据保存成功显示新值

6.2 行选择流程

未选中

已选中

点击行选择框

当前状态?

添加到选中集合

从选中集合移除

显示选择提示栏

选中集合为空?

隐藏选择提示栏

更新选中计数

清空选中集合

6.3 数据导出流程

异常

点击导出按钮

生成CSV内容

写入临时文件

显示导出路径

导出失败

显示错误提示


七、扩展功能规划

7.1 后续版本规划

2024-01-072024-01-142024-01-212024-01-282024-02-042024-02-112024-02-182024-02-252024-03-032024-03-102024-03-172024-03-242024-03-312024-04-07基础UI框架 数据表格实现 筛选排序功能 数据导入导出 数据统计功能 数据持久化 多表格支持 数据可视化 云端同步 V1.0 基础版本V1.1 增强版本V1.2 进阶版本数据网格应用开发计划

7.2 功能扩展建议

7.2.1 多表格支持

扩展功能:

  • 创建多个数据表
  • 表格间数据关联
  • 表格切换管理
  • 表格模板保存
7.2.2 数据可视化

可视化功能:

  • 图表展示数据
  • 数据趋势分析
  • 自定义图表类型
  • 图表导出功能
7.2.3 云端同步

同步功能:

  • 数据云端备份
  • 多设备同步
  • 数据版本管理
  • 协作编辑功能

八、注意事项

8.1 开发注意事项

  1. 数据量控制:大数据量时需考虑分页加载

  2. 内存管理:及时释放不需要的数据和控制器

  3. 文件权限:导入导出需要文件读写权限

  4. 数据验证:导入数据时需验证格式正确性

  5. 异常处理:文件操作需完善的异常处理

8.2 常见问题

问题原因解决方案
数据加载失败JSON格式错误捕获异常并加载示例数据
导出文件乱码编码问题使用UTF-8编码
筛选无结果条件过于严格提示用户调整条件
排序异常数据类型不一致统一数据类型处理
导入失败CSV格式错误验证文件格式

8.3 使用技巧

📊 数据网格应用技巧 📊

数据管理

  • 定期导出数据备份
  • 使用筛选快速定位数据
  • 批量选择提高删除效率
  • 合理设置列宽便于查看

筛选技巧

  • 组合多个筛选条件
  • 使用数值比较筛选范围
  • 清除筛选恢复完整数据
  • 搜索框快速全文搜索

数据编辑

  • 点击单元格直接编辑
  • 数字列自动转换为数值
  • 编辑后自动保存
  • 支持撤销操作

九、运行说明

9.1 环境要求

环境版本要求
Flutter SDK>= 3.0.0
Dart SDK>= 2.17.0
鸿蒙OSAPI 21+
Web浏览器Chrome 90+

9.2 运行命令

# 查看可用设备
flutter devices

# 运行到Web服务器
flutter run -d web-server -t lib/main_data_grid.dart --web-port 8144

# 运行到鸿蒙设备
flutter run -d 127.0.0.1:5555 lib/main_data_grid.dart

# 代码分析
flutter analyze lib/main_data_grid.dart

十、总结

数据网格应用是一款专业的数据管理工具,为用户提供高效的数据展示、筛选、编辑和导出功能。应用支持多列数据展示、点击列头排序、多条件筛选、单元格编辑、CSV导入导出等核心功能,满足用户日常数据管理需求。

核心功能涵盖数据展示、数据排序、数据筛选、数据编辑、数据导出、数据导入、数据统计、数据持久化八大模块。用户可以快速浏览数据、筛选目标记录、编辑数据内容、导出CSV文件,实现数据的高效管理。

应用采用 Material Design 3 设计规范,以清新的青绿色为主色调,象征数据的专业与可靠。通过本应用,希望能够为用户提供便捷的数据管理体验,提升工作效率。

数据网格应用——高效管理您的数据


Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐