在这里插入图片描述

1. 这个功能解决什么问题

工单列表是巡检人员的核心工作入口,作为日常巡检任务的核心操作载体,需满足多维度的使用需求:

  • 状态筛选:支持待分派、执行中、待复核、已完成四类核心状态的精准筛选,适配巡检全流程的任务管理
  • 列表展示:清晰呈现工单标题、所属片区、关联井盖编号、截止时间等核心信息,便于快速识别任务
  • 状态指示:通过彩色圆点视觉化标识工单状态,无需阅读文字即可快速判断任务进度
  • 详情跳转:点击列表项可直达工单详情页,满足查看任务详情、执行操作的深层需求

这个页面是典型的“列表+筛选”组合,适合做 PopupMenuButton + ListView.separated 的最佳实践示例,既符合Flutter组件设计规范,又能适配开源鸿蒙的跨平台特性。

2. 相关文件一览

  • lib/feature_pages.dartWorkOrdersPage):工单列表页面的核心布局与交互逻辑实现文件
  • lib/mock_data.dartbuildMockWorkOrders):模拟工单数据构造文件,为功能开发提供测试数据支撑

3. Mock 数据构造

lib/mock_data.dart 中提供了工单的 Mock 数据,模拟数据的设计需兼顾真实性与覆盖性:

List<WorkOrder> buildMockWorkOrders() {
  const statuses = ['待分派', '执行中', '待复核', '已完成'];
  const districts = ['东城区', '西城区', '南城区', '北城区', '高新区'];
  return List<WorkOrder>.generate(24, (i) {
    final status = statuses[i % statuses.length];
    final d = districts[i % districts.length];
    return WorkOrder(
      id: _uuid.v4(),
      title: '井盖巡检工单 #${i + 1}',
      status: status,
      district: d,
      dueAt: DateTime.now().add(Duration(hours: (i + 1) * 6)),
      coverCode: 'MH-${(1000 + (i % 18)).toString()}',
    );
  });
}
  • 状态覆盖设计:通过 i % statuses.length 循环取值,确保四类工单状态都有对应模拟数据,满足筛选功能测试需求
  • 时间模拟逻辑:dueAt 采用当前时间叠加递增小时数的方式,模拟不同工单的截止时间,贴近真实业务场景
  • 区域与编号设计:片区按固定数组循环分配,井盖编号按规则生成,保证数据格式统一且具备辨识度

4. 状态定义

WorkOrdersPage 中需先定义核心状态变量,为筛选逻辑提供基础支撑:

class _WorkOrdersPageState extends State<WorkOrdersPage> {
  late final List<WorkOrder> _all = buildMockWorkOrders();
  String _status = '全部';
}
  • 数据初始化:_all 使用 late final 修饰,保证页面加载时仅构造一次Mock数据,避免重复初始化造成性能损耗
  • 筛选默认值:_status 初始值设为“全部”,确保页面首次加载时展示所有状态的工单,符合用户默认查看全量数据的习惯

5. 状态筛选逻辑

筛选逻辑需在 build 方法中实现,保证状态变化时实时更新列表数据:

final statuses = <String>['全部', ...{for (final e in _all) e.status}];
final list = _all
    .where((e) => _status == '全部' || e.status == _status)
    .toList(growable: false);
  • 去重处理:通过 Set 集合特性对工单状态去重后,再拼接“全部”选项,确保筛选菜单无重复选项
  • 过滤规则:where 方法实现核心筛选逻辑,“全部”状态下匹配所有工单,其他状态仅匹配对应工单
  • 不可变列表:toList(growable: false) 创建不可变列表,减少内存占用,提升列表渲染性能

6. 状态筛选 UI

页面右上角通过 PopupMenuButton 实现状态筛选的交互入口,UI设计需兼顾易用性与直观性:

AppBar(
  title: const Text('工单列表'),
  actions: [
    PopupMenuButton<String>(
      initialValue: _status,
      onSelected: (v) => setState(() => _status = v),
      itemBuilder: (context) => statuses
          .map((e) => PopupMenuItem(value: e, child: Text('筛选: $e')))
          .toList(growable: false),
    ),
  ],
)
  • 初始值展示:initialValue 绑定当前筛选状态,用户可直观看到当前筛选条件
  • 状态更新:onSelected 回调中通过 setState 更新 _status,触发页面重建与列表重新过滤
  • 菜单文案设计:PopupMenuItem 文案添加“筛选:”前缀,明确菜单功能,提升交互语义清晰度

7. 状态指示器

设计 _statusDot 函数实现状态与颜色的映射,通过视觉元素强化状态识别:

Widget _statusDot(String status) {
  Color c;
  switch (status) {
    case '待分派':
      c = Colors.grey;
      break;
    case '执行中':
      c = Colors.blue;
      break;
    case '待复核':
      c = Colors.orange;
      break;
    default:
      c = Colors.green;
  }
  return Container(
    width: 10,
    height: 10,
    decoration: BoxDecoration(color: c, shape: BoxShape.circle),
  );
}
  • 颜色映射规则:按行业通用视觉规范设计,待分派(灰色)、执行中(蓝色)、待复核(橙色)、已完成(绿色)
  • 视觉样式:Container 设置10px固定宽高,配合圆形形状,打造小巧醒目的状态圆点
  • 扩展性:采用 switch 语句实现状态匹配,后续新增状态时可快速扩展颜色配置

8. 列表项 UI

通过 ListView.separated 实现工单列表的展示,兼顾布局美观与交互体验:

ListView.separated(
  itemCount: list.length,
  separatorBuilder: (_, __) => const Divider(height: 1),
  itemBuilder: (context, i) {
    final w = list[i];
    final due = DateFormat('MM-dd HH:mm').format(w.dueAt);
    return ListTile(
      leading: _statusDot(w.status),
      title: Text(w.title),
      subtitle: Text('${w.district} · 关联井盖 ${w.coverCode} · 截止 $due'),
      trailing: Text(w.status),
      onTap: () => Navigator.of(context).push(
        MaterialPageRoute(builder: (_) => WorkOrderDetailPage(order: w)),
      ),
    );
  },
)
  • 分隔线设计:separatorBuilder 添加1px高度的分割线,清晰区分列表项,提升页面整洁度
  • 时间格式化:通过 DateFormat 将截止时间格式化为“月-日 时:分”,符合移动端时间展示习惯
  • 交互设计:leading 展示状态圆点,trailing 显示状态文字,双重标识强化状态感知;onTap 实现详情页跳转,传递当前工单对象
  • 信息层级:标题展示工单名称,副标题聚合片区、井盖编号、截止时间,信息排布主次分明

9. 完整页面代码(核心片段)

工单列表页面的核心结构实现,整合上述所有功能模块:

class WorkOrdersPage extends StatefulWidget {
  const WorkOrdersPage({super.key});

  
  State<WorkOrdersPage> createState() => _WorkOrdersPageState();
}

class _WorkOrdersPageState extends State<WorkOrdersPage> {
  late final List<WorkOrder> _all = buildMockWorkOrders();
  String _status = '全部';

  
  Widget build(BuildContext context) {
    final statuses = <String>['全部', ...{for (final e in _all) e.status}];
    final list = _all
        .where((e) => _status == '全部' || e.status == _status)
        .toList(growable: false);

    return Scaffold(
      appBar: AppBar(
        title: const Text('工单列表'),
        actions: [
          PopupMenuButton<String>(
            initialValue: _status,
            onSelected: (v) => setState(() => _status = v),
            itemBuilder: (context) => statuses
                .map((e) => PopupMenuItem(value: e, child: Text('筛选: $e')))
                .toList(growable: false),
          ),
        ],
      ),
  • 页面结构:遵循Flutter经典的StatefulWidget设计模式,分离视图与状态管理
  • 筛选逻辑嵌入:在 build 方法中集成状态筛选逻辑,保证状态变化时实时更新列表
  • 组件复用:直接复用已定义的 PopupMenuButton 筛选组件,保证代码复用性

空状态处理

当筛选后无工单数据时,展示友好的空状态提示,提升用户体验:

  Widget _buildEmptyState() {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(
            Icons.assignment_outlined,
            size: 64,
            color: Colors.grey.shade400,
          ),
          const SizedBox(height: 16),
          Text(
            '暂无工单',
            style: TextStyle(
              fontSize: 18,
              color: Colors.grey.shade600,
              fontWeight: FontWeight.w500,
            ),
          ),
  • 视觉引导:使用64px的大号图标作为视觉核心,快速吸引用户注意力
  • 文案层级:主文案“暂无工单”突出显示,副文案补充说明筛选条件,清晰告知用户空状态原因
  • 布局设计:采用 Center + Column 居中布局,保证空状态在页面中垂直水平居中,视觉协调

增强版工单列表项

设计卡片式工单列表项,提升信息展示丰富度与交互体验:

  Widget _buildWorkOrderItem(WorkOrder workOrder) {
    return Card(
      margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
      child: InkWell(
        onTap: () => onWorkOrderTap(workOrder),
        onLongPress: () => onWorkOrderLongPress(workOrder),
        borderRadius: BorderRadius.circular(8),
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              _buildWorkOrderHeader(workOrder),
              const SizedBox(height: 12),
              _buildWorkOrderContent(workOrder),
            ],
          ),
        ),
      ),
    );
  }
  • 卡片布局:使用 Card 组件打造立体视觉效果,8px左右边距、4px上下边距保证项间距合理
  • 交互扩展:除点击跳转外,新增长按操作回调,为批量操作、更多功能提供扩展入口
  • 内边距设计:16px内边距保证内容与卡片边缘有足够留白,避免文字紧贴边框

工单头部设计

工单头部整合状态、标题、优先级信息,强化核心信息展示:

  Widget _buildWorkOrderHeader(WorkOrder workOrder) {
    return Row(
      children: [
        CircleAvatar(
          backgroundColor: _getStatusColor(workOrder.status).withOpacity(0.1),
          child: Icon(
            _getStatusIcon(workOrder.status),
            color: _getStatusColor(workOrder.status),
            size: 20,
          ),
        ),
        const SizedBox(width: 12),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                workOrder.title,
                style: const TextStyle(
                  fontSize: 16,
                  fontWeight: FontWeight.bold,
                ),
              ),
  • 状态图标容器:CircleAvatar 作为状态图标载体,背景色采用状态色的低透明度版本,视觉统一
  • 标题样式:16px加粗字体突出工单标题,作为头部核心信息
  • 布局适配:Expanded 包裹标题区域,保证在不同屏幕宽度下标题可自适应展示

工单内容区域

内容区域展示工单描述、位置、井盖编号等关键业务信息:

  Widget _buildWorkOrderContent(WorkOrder workOrder) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          workOrder.description,
          style: TextStyle(
            color: Colors.grey.shade700,
            fontSize: 14,
          ),
          maxLines: 2,
          overflow: TextOverflow.ellipsis,
        ),
        const SizedBox(height: 8),
        Row(
          children: [
            Icon(
              Icons.location_on,
              size: 16,
              color: Colors.grey.shade500,
            ),
            const SizedBox(width: 4),
            Text(
              workOrder.district,
              style: TextStyle(
                color: Colors.grey.shade600,
                fontSize: 12,
              ),
            ),
  • 描述文本处理:限制2行展示,超出部分省略,避免长文本占用过多页面空间
  • 图标辅助:使用16px的位置、设置图标辅助识别信息类型,提升信息可读性
  • 字体配色:采用灰色系区分次要信息,与标题、状态等核心信息形成视觉层级

工单底部区域

底部区域聚焦时间与附件信息,突出过期提醒:

  Widget _buildWorkOrderFooter(WorkOrder workOrder) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Row(
          children: [
            Icon(
              Icons.schedule,
              size: 16,
              color: _isOverdue(workOrder) ? Colors.red : Colors.grey.shade500,
            ),
            const SizedBox(width: 4),
            Text(
              _formatDate(workOrder.dueAt),
              style: TextStyle(
                color: _isOverdue(workOrder) ? Colors.red : Colors.grey.shade600,
                fontSize: 12,
              ),
            ),
          ],
        ),
  • 过期提醒:通过 _isOverdue 方法判断工单是否过期,过期时图标与文字均改为红色,强化提醒效果
  • 布局对齐:MainAxisAlignment.spaceBetween 实现时间与附件信息左右分布,布局均衡
  • 时间格式化:自定义 _formatDate 方法处理截止时间,提升时间展示的可读性

状态映射辅助方法

封装状态与颜色、图标、文本的映射方法,保证视觉一致性:

  Color _getStatusColor(WorkOrderStatus status) {
    switch (status) {
      case WorkOrderStatus.pending:
        return Colors.grey;
      case WorkOrderStatus.assigned:
        return Colors.blue;
      case WorkOrderStatus.inProgress:
        return Colors.orange;
      case WorkOrderStatus.review:
        return Colors.purple;
      default:
        return Colors.green;
    }
  }

  IconData _getStatusIcon(WorkOrderStatus status) {
    switch (status) {
      case WorkOrderStatus.pending:
        return Icons.pending;
      case WorkOrderStatus.assigned:
        return Icons.person_add;
      case WorkOrderStatus.inProgress:
        return Icons.play_arrow;
      default:
        return Icons.check_circle;
    }
  }
  • 统一管理:将状态相关的视觉映射逻辑封装为独立方法,便于后续统一修改维护
  • 扩展性:switch 语句预留扩展空间,新增工单状态时可快速添加对应配置
  • 规范设计:图标与颜色选择贴合状态语义,如“执行中”使用播放箭头图标,符合用户认知习惯

优先级与时间辅助方法

补充优先级映射与时间处理方法,完善工单信息展示:

  Color _getPriorityColor(WorkOrderPriority priority) {
    switch (priority) {
      case WorkOrderPriority.low:
        return Colors.green;
      case WorkOrderPriority.medium:
        return Colors.orange;
      case WorkOrderPriority.high:
        return Colors.red;
      default:
        return Colors.purple;
    }
  }

  bool _isOverdue(WorkOrder workOrder) {
    return DateTime.now().isAfter(workOrder.dueAt) && 
           workOrder.status != WorkOrderStatus.completed;
  }
  • 优先级配色:按行业通用标准设计,低优先级(绿色)、中优先级(橙色)、高优先级(红色)、紧急(紫色)
  • 过期判断逻辑:仅判断未完成且当前时间晚于截止时间的工单,避免已完成工单误标为过期
  • 复用性:独立封装的方法可在列表项、详情页等多处复用,保证逻辑一致性

13. 小结

工单列表的实现展现了 Flutter 开发的几个重要原则:

状态管理:使用 Provider 管理复杂列表状态和筛选

用户体验:下拉刷新、空状态处理、长按操作

视觉设计:颜色编码、图标使用、卡片布局

组件化:可复用的列表组件和工单项目

数据处理:多条件筛选、状态映射、时间格式化

这样的设计不仅满足了工单管理的基本需求,还为后续的功能扩展(如批量操作、高级搜索、数据导出等)提供了良好的基础架构。


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

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐