You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于日期排序数据并避免前端日期重复展示

解决方案

要实现按日期分组、仅显示一次日期的效果,需要分数据处理和UI组件调整两部分修改代码:


1. 数据处理:按日期分组并排序

首先需要将原始薪资数据按日期分组,同时保证日期有序。推荐在Provider中提前处理,避免build方法重复计算:

修改Provider添加分组逻辑

class EmployeeWageAccountsProvider extends ChangeNotifier {
  List<EmployeeWageAccountsModel>? _employeeAccountsData;
  
  // 新增:获取分组并排序后的薪资数据
  Map<String, List<EmployeeWageAccountsModel>>? get groupedSortedWageData {
    if (_employeeAccountsData == null) return null;
    
    // 1. 按日期排序原始数据
    final sortedList = List.from(_employeeAccountsData!)
      ..sort((a, b) => a.date!.compareTo(b.date!));
    
    // 2. 按日期分组
    final Map<String, List<EmployeeWageAccountsModel>> groupedData = {};
    for (final item in sortedList) {
      groupedData.putIfAbsent(item.date!, () => []).add(item);
    }
    
    return groupedData;
  }
  
  // 其他原有代码...
}

如果不想修改Provider,也可以在列表构建时临时处理:

// 在Consumer内部执行
var wageAccountsData = data.getEmployeeAccountsData!;
// 排序
wageAccountsData.sort((a, b) => a.date!.compareTo(b.date!));
// 分组
Map<String, List<EmployeeWageAccountsModel>> groupedData = {};
for (var item in wageAccountsData) {
  groupedData.putIfAbsent(item.date!, () => []).add(item);
}
// 转为有序列表
var sortedGroups = groupedData.entries.toList()
  ..sort((a, b) => a.key.compareTo(b.key));

2. 拆分UI组件:分离组头和明细项

原有组件同时包含日期和明细,现在拆分为两个独立组件:

组头组件:WageGroupHeader

class WageGroupHeader extends StatelessWidget {
  final String date;
  final String shiftName;

  const WageGroupHeader({
    Key? key,
    required this.date,
    required this.shiftName,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.only(top: 10, left: 10, right: 10),
      padding: const EdgeInsets.all(8),
      decoration: CustomBoxDecoration.cardDecoration(context, shadow: true),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(shiftName, style: wageTextStyle),
          Text(date, style: wageTextStyle),
        ],
      ),
    );
  }
}

明细项组件:WageItem

class WageItem extends StatelessWidget {
  final String name;
  final String balance;

  const WageItem({
    Key? key,
    required this.name,
    required this.balance,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.only(left: 10, right: 10, bottom: 10),
      padding: const EdgeInsets.all(8),
      decoration: CustomBoxDecoration.cardDecoration(context, shadow: true),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(S.of(context).wage_type, style: cTextStyle),
              Text(S.of(context).balance, style: cTextStyle),
            ],
          ),
          const Divider(color: Colors.black),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(name, style: cTextStyle),
              Text(balance, style: cTextStyle),
            ],
          ),
          SizedBox(height: Styles.height(context) * 0.01),
        ],
      ),
    );
  }
}

3. 修改列表构建代码

使用分组后的数据构建ListView,每个分组对应一个组头+多个明细项:

Expanded(
  child: Consumer<EmployeeWageAccountsProvider>(
    builder: (context, data, child) {
      if (!data.isLoading) {
        final groupedData = data.groupedSortedWageData;
        if (groupedData != null && groupedData.isNotEmpty) {
          final sortedGroups = groupedData.entries.toList()
            ..sort((a, b) => a.key.compareTo(b.key));
          
          return ListView.builder(
            itemCount: sortedGroups.length,
            shrinkWrap: true,
            scrollDirection: Axis.vertical,
            itemBuilder: (context, groupIndex) {
              final group = sortedGroups[groupIndex];
              final formattedDate = Helper.formatStringDate(group.key);
              // 取该组第一个非空班次(同一日期班次应一致)
              final shiftName = group.value
                  .firstWhere((item) => item.shiftName!.isNotEmpty, 
                      orElse: () => group.value.first)
                  .shiftName!;
              
              return Column(
                children: [
                  WageGroupHeader(date: formattedDate, shiftName: shiftName),
                  ...group.value.map((item) => 
                      WageItem(name: item.name!, balance: item.balance!))
                      .toList(),
                ],
              );
            },
          );
        }
        return noDataFound(context, 50);
      }
      return const WageAccountsShimmer();
    },
  ),
)

关键说明

  • 分组逻辑保证同一日期的薪资项归为一组,避免日期重复显示;
  • 排序逻辑确保列表按日期升序展示,如需降序可修改compareTo的顺序;
  • 拆分组件后UI结构更清晰,方便后续单独调整样式;
  • 班次处理:若同一日期班次唯一,可直接取group.value.first.shiftName!。

内容的提问来源于stack exchange,提问作者Arslan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 01:55:13