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

一、项目概述

运行效果图

image-20260405105602982

image-20260405105609677

image-20260405105620062

image-20260405105630413

1.1 应用简介

报销管理是一款专为企业员工设计的费用报销管理工具。无论是差旅费用、餐饮支出还是办公用品采购,都能轻松创建报销申请并追踪审批进度。应用支持多级审批流程,费用明细清晰记录,让报销工作规范高效。

应用采用直观的卡片式布局,待审批和已通过金额一目了然。支持按状态和类型筛选,快速定位报销记录。审批功能支持通过和拒绝操作,拒绝时可填写原因,流程透明可追溯。

1.2 核心功能

功能模块 功能描述 实现方式
报销列表 展示所有报销记录 ListView + Card
新建报销 创建新的报销申请 表单页面
编辑报销 修改已有报销记录 表单预填充
删除报销 删除不需要的记录 确认对话框
详情查看 查看报销完整信息 详情页面
审批功能 通过/拒绝报销申请 状态更新
状态筛选 按审批状态筛选 DropdownButton
类型筛选 按报销类型筛选 DropdownButton
费用明细 多项费用详细记录 列表管理

1.3 报销记录字段

字段 类型 说明
报销标题 String 报销单标题,必填
申请人 String 报销申请人,必填
报销类型 ExpenseCategory 差旅/餐饮/交通/办公/其他
审批状态 ExpenseStatus 待审批/已通过/已拒绝
报销日期 DateTime 费用发生日期
报销金额 double 费用合计金额
报销说明 String 报销详细说明
费用明细 List 各项费用详情
审批人 String 审批操作人
审批时间 DateTime 审批操作时间
拒绝原因 String 拒绝时的原因说明

1.4 技术栈

技术领域 技术选型 版本要求
开发框架 Flutter >= 3.0.0
编程语言 Dart >= 2.17.0
设计规范 Material Design 3 -
状态管理 setState -
目标平台 鸿蒙OS API 21+

1.5 项目结构

lib/
└── main_expense_claim.dart
    ├── ExpenseClaimApp         # 应用入口
    ├── ExpenseStatus           # 审批状态枚举
    ├── ExpenseCategory         # 报销类型枚举
    ├── ExpenseClaim            # 报销单模型
    ├── ExpenseItem             # 费用明细模型
    ├── ExpenseClaimPage        # 主列表页面
    │   ├── _buildStatisticsSection() # 统计区域
    │   ├── _buildFilterSection()     # 筛选区域
    │   ├── _buildClaimsList()        # 记录列表
    │   └── _buildClaimCard()         # 记录卡片
    ├── ExpenseClaimEditPage    # 编辑页面
    │   ├── _selectDate()              # 日期选择
    │   ├── _addItem()                 # 添加费用项
    │   └── _save()                    # 保存记录
    └── ExpenseClaimDetailPage  # 详情页面
        ├── _buildSection()            # 信息区块
        └── _buildInfoRow()            # 信息行

二、系统架构

2.1 整体架构图

Business Logic

Presentation Layer

Data Layer

ExpenseClaim
报销单模型

ExpenseItem
费用明细模型

ExpenseStatus
审批状态枚举

ExpenseCategory
报销类型枚举

列表页面

统计卡片

筛选区域

报销卡片列表

编辑页面

表单输入

日期选择

费用明细管理

详情页面

状态展示

费用明细

报销管理
增删改查

审批操作
通过/拒绝

筛选过滤
_filteredClaims

金额统计
_totalPending/_totalApproved

2.2 类图设计

contains

manages

contains

has

has

navigates

navigates

ExpenseClaimApp

+Widget build()

«enumeration»

ExpenseStatus

pending

approved

rejected

«enumeration»

ExpenseCategory

travel

meal

transport

office

other

ExpenseClaim

+String id

+String title

+double amount

+ExpenseCategory category

+ExpenseStatus status

+DateTime date

+String description

+String applicant

+String? approver

+DateTime? approvedAt

+String? rejectReason

+List<ExpenseItem> items

+String statusText

+String categoryText

ExpenseItem

+String id

+String name

+double amount

+String description

ExpenseClaimPage

-List<ExpenseClaim> _claims

-String _selectedStatus

-String _selectedCategory

-String _searchQuery

-List<ExpenseClaim> _filteredClaims

+Widget build()

-void _addClaim()

-void _editClaim()

-void _deleteClaim()

-void _approveClaim()

-void _rejectClaim()

ExpenseClaimEditPage

-ExpenseClaim? claim

-TextEditingController _titleController

-List<ExpenseItem> _items

+Widget build()

-void _save()

-void _addItem()

-void _calculateTotal()

ExpenseClaimDetailPage

2.3 数据流程图

新建

编辑

删除

查看

审批

筛选

用户操作

操作类型

打开编辑页面

打开编辑页面-预填充

确认对话框

打开详情页面

通过/拒绝操作

过滤记录列表

填写表单

添加费用明细

保存报销单

更新记录列表

更新状态

2.4 审批流程

审批人 报销系统 申请人 审批人 报销系统 申请人 alt [审批通过] [审批拒绝] 提交报销申请 创建报销单(待审批) 通知待审批 点击通过 更新状态为已通过 通知审批通过 点击拒绝+填写原因 更新状态为已拒绝 通知审批拒绝+原因

三、核心模块设计

3.1 数据模型设计

3.1.1 审批状态枚举 (ExpenseStatus)
enum ExpenseStatus {
  pending,   // 待审批
  approved,  // 已通过
  rejected,  // 已拒绝
}
3.1.2 报销类型枚举 (ExpenseCategory)
enum ExpenseCategory {
  travel,    // 差旅
  meal,      // 餐饮
  transport, // 交通
  office,    // 办公
  other,     // 其他
}
3.1.3 报销单模型 (ExpenseClaim)
class ExpenseClaim {
  final String id;              // 唯一标识
  String title;                 // 报销标题
  double amount;                // 报销金额
  ExpenseCategory category;     // 报销类型
  ExpenseStatus status;         // 审批状态
  DateTime date;                // 报销日期
  String description;           // 报销说明
  String applicant;             // 申请人
  String? approver;             // 审批人
  DateTime? approvedAt;         // 审批时间
  String? rejectReason;         // 拒绝原因
  List<ExpenseItem> items;      // 费用明细
  DateTime createdAt;           // 创建时间
  DateTime updatedAt;           // 更新时间
}
3.1.4 费用明细模型 (ExpenseItem)
class ExpenseItem {
  final String id;      // 唯一标识
  String name;          // 费用名称
  double amount;        // 费用金额
  String description;   // 费用说明
}

3.2 筛选过滤算法

3.2.1 过滤流程

获取全部记录

是否选择状态?

按状态过滤

保留全部

是否选择类型?

按类型过滤

是否有搜索词?

关键词匹配过滤

按时间排序

返回过滤结果

3.2.2 过滤实现
List<ExpenseClaim> get _filteredClaims {
  var claims = _claims.toList();

  // 状态过滤
  if (_selectedStatus != '全部') {
    ExpenseStatus? status;
    switch (_selectedStatus) {
      case '待审批': status = ExpenseStatus.pending; break;
      case '已通过': status = ExpenseStatus.approved; break;
      case '已拒绝': status = ExpenseStatus.rejected; break;
    }
    if (status != null) {
      claims = claims.where((c) => c.status == status).toList();
    }
  }

  // 类型过滤
  if (_selectedCategory != '全部') {
    // ... 类型过滤逻辑
  }

  // 关键词搜索
  if (_searchQuery.isNotEmpty) {
    claims = claims.where((c) {
      return c.title.toLowerCase().contains(_searchQuery.toLowerCase()) ||
          c.description.toLowerCase().contains(_searchQuery.toLowerCase());
    }).toList();
  }

  claims.sort((a, b) => b.createdAt.compareTo(a.createdAt));
  return claims;
}

3.3 金额统计算法

3.3.1 统计计算
// 待审批金额
double get _totalPending {
  return _claims
      .where((c) => c.status == ExpenseStatus.pending)
      .fold(0.0, (sum, c) => sum + c.amount);
}

// 已通过金额
double get _totalApproved {
  return _claims
      .where((c) => c.status == ExpenseStatus.approved)
      .fold(0.0, (sum, c) => sum + c.amount);
}
3.3.2 统计展示

报销记录列表

筛选待审批

筛选已通过

累加金额

统计卡片展示

3.4 页面结构设计

3.4.1 列表页面布局

列表页面

统计区域

筛选区域

报销列表

浮动按钮

待审批金额卡片

已通过金额卡片

搜索框

状态下拉

类型下拉

报销卡片1

报销卡片2

...

类型图标

标题/申请人

状态标签

金额/日期

操作按钮

3.4.2 编辑页面布局
┌─────────────────────────────────────────────────────────────┐
│  AppBar: 新建/编辑报销单                      [💾 保存]     │
├─────────────────────────────────────────────────────────────┤
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 📝 报销标题 *                                       │   │
│  │ ┌─────────────────────────────────────────────────┐ │   │
│  │ │                                                 │ │   │
│  │ └─────────────────────────────────────────────────┘ │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 👤 申请人 * | 📅 报销日期 | 📁 报销类型              │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 📋 报销说明                                         │   │
│  │ ┌─────────────────────────────────────────────────┐ │   │
│  │ │              多行文本输入                        │ │   │
│  │ └─────────────────────────────────────────────────┘ │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  费用明细                                    [+ 添加]       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 机票                                    ¥1,800.00   │   │
│  │ 酒店                                    ¥1,200.00   │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 合计金额                              ¥3,580.00     │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

3.5 状态管理

3.5.1 核心状态变量
class _ExpenseClaimPageState extends State<ExpenseClaimPage> {
  final List<ExpenseClaim> _claims = [];   // 所有报销记录
  String _selectedStatus = '全部';          // 选中的状态筛选
  String _selectedCategory = '全部';        // 选中的类型筛选
  String _searchQuery = '';                 // 搜索关键词
}

class _ExpenseClaimEditPageState extends State<ExpenseClaimEditPage> {
  final _formKey = GlobalKey<FormState>();          // 表单Key
  late TextEditingController _titleController;       // 标题控制器
  late TextEditingController _descriptionController; // 说明控制器
  late TextEditingController _applicantController;   // 申请人控制器
  late double _totalAmount;                          // 总金额
  late String _selectedCategory;                     // 选中的类型
  late DateTime _selectedDate;                       // 选中的日期
  late List<ExpenseItem> _items;                     // 费用明细列表
}
3.5.2 状态更新流程
// 审批通过
void _approveClaim(ExpenseClaim claim) {
  setState(() {
    claim.status = ExpenseStatus.approved;
    claim.approver = '当前用户';
    claim.approvedAt = DateTime.now();
    claim.updatedAt = DateTime.now();
  });
}

// 审批拒绝
void _rejectClaim(ExpenseClaim claim, String reason) {
  setState(() {
    claim.status = ExpenseStatus.rejected;
    claim.rejectReason = reason;
    claim.approver = '当前用户';
    claim.updatedAt = DateTime.now();
  });
}

四、UI设计规范

4.1 配色方案

应用采用蓝色主题风格:

颜色类型 色值 用途
主色 Blue AppBar、按钮、金额
待审批 Orange 待审批状态标签
已通过 Green 已通过状态标签
已拒绝 Red 已拒绝状态标签
差旅 Purple 差旅类型图标
餐饮 Orange 餐饮类型图标
交通 Blue 交通类型图标
办公 Teal 办公类型图标

4.2 状态标签样式

4.2.1 颜色对照表
状态 颜色 示例
待审批 橙色 待审批
已通过 绿色 已通过
已拒绝 红色 已拒绝

4.3 类型图标样式

类型 图标 颜色
差旅 flight 紫色
餐饮 restaurant 橙色
交通 directions_car 蓝色
办公 business_center 青色
其他 more_horiz 灰色

4.4 组件规范

4.4.1 统计卡片
┌─────────────────────────────────────┐
│  📋 待审批                          │
│                                     │
│  ¥3,580.00                          │
└─────────────────────────────────────┘
4.4.2 报销卡片
┌─────────────────────────────────────────────────────────────┐
│  ✈️  北京出差报销                              [待审批]     │
│      差旅 | 张三                                            │
│                                                             │
│  ¥3,580.00                              2024-01-15          │
│                                                             │
│                          [拒绝]  [通过]                     │
└─────────────────────────────────────────────────────────────┘

4.5 交互设计

4.5.1 操作方式
操作 手势 效果
查看详情 点击卡片 跳转详情页
新建报销 点击浮动按钮 跳转编辑页
编辑报销 点击编辑按钮 跳转编辑页
删除报销 点击删除按钮 确认后删除
审批通过 点击通过按钮 更新状态
审批拒绝 点击拒绝按钮 填写原因后更新
筛选 选择下拉选项 过滤列表
4.5.2 视觉反馈

点击卡片

跳转详情页

审批通过

状态变为绿色

审批拒绝

状态变为红色

删除成功

显示删除提示


五、核心功能实现

5.1 列表页面构建


Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: const Text('报销管理')),
    body: Column(
      children: [
        _buildStatisticsSection(),  // 统计区域
        _buildFilterSection(),       // 筛选区域
        Expanded(
          child: _filteredClaims.isEmpty
              ? _buildEmptyState()
              : _buildClaimsList(),
        ),
      ],
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: _addClaim,
      child: const Icon(Icons.add),
    ),
  );
}

5.2 审批功能实现

// 通过审批
void _approveClaim(ExpenseClaim claim) {
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('审批通过'),
      content: const Text('确定要通过此报销申请吗?'),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: const Text('取消'),
        ),
        FilledButton(
          onPressed: () {
            setState(() {
              claim.status = ExpenseStatus.approved;
              claim.approver = '当前用户';
              claim.approvedAt = DateTime.now();
            });
            Navigator.pop(context);
          },
          child: const Text('确定'),
        ),
      ],
    ),
  );
}

// 拒绝审批
void _rejectClaim(ExpenseClaim claim) {
  final reasonController = TextEditingController();
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('拒绝原因'),
      content: TextField(
        controller: reasonController,
        decoration: const InputDecoration(labelText: '请输入拒绝原因'),
        maxLines: 3,
      ),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: const Text('取消'),
        ),
        FilledButton(
          onPressed: () {
            setState(() {
              claim.status = ExpenseStatus.rejected;
              claim.rejectReason = reasonController.text;
            });
            Navigator.pop(context);
          },
          child: const Text('确定拒绝'),
        ),
      ],
    ),
  );
}

5.3 费用明细管理

// 添加费用项
void _addItem() {
  showDialog(
    context: context,
    builder: (context) {
      final nameController = TextEditingController();
      final amountController = TextEditingController();
      
      return AlertDialog(
        title: const Text('添加费用项'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            TextField(
              controller: nameController,
              decoration: const InputDecoration(labelText: '费用名称'),
            ),
            TextField(
              controller: amountController,
              decoration: const InputDecoration(
                labelText: '金额',
                prefixText: '¥',
              ),
              keyboardType: TextInputType.number,
            ),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('取消'),
          ),
          FilledButton(
            onPressed: () {
              setState(() {
                _items.add(ExpenseItem(
                  id: DateTime.now().millisecondsSinceEpoch.toString(),
                  name: nameController.text,
                  amount: double.parse(amountController.text),
                ));
                _calculateTotal();
              });
              Navigator.pop(context);
            },
            child: const Text('添加'),
          ),
        ],
      );
    },
  );
}

// 计算总金额
void _calculateTotal() {
  _totalAmount = _items.fold(0.0, (sum, item) => sum + item.amount);
}

5.4 表单验证与保存

void _save() {
  if (_formKey.currentState!.validate()) {
    final now = DateTime.now();
    
    // 类型转换
    ExpenseCategory category;
    switch (_selectedCategory) {
      case '差旅': category = ExpenseCategory.travel; break;
      case '餐饮': category = ExpenseCategory.meal; break;
      case '交通': category = ExpenseCategory.transport; break;
      case '办公': category = ExpenseCategory.office; break;
      default: category = ExpenseCategory.other;
    }
    
    final claim = ExpenseClaim(
      id: widget.claim?.id ?? DateTime.now().millisecondsSinceEpoch.toString(),
      title: _titleController.text,
      amount: _totalAmount,
      category: category,
      status: widget.claim?.status ?? ExpenseStatus.pending,
      date: _selectedDate,
      description: _descriptionController.text,
      applicant: _applicantController.text,
      items: _items,
      createdAt: widget.claim?.createdAt ?? now,
      updatedAt: now,
    );
    
    Navigator.pop(context, claim);
  }
}

六、报销管理知识

6.1 报销类型划分

6.1.1 常见报销类型

报销类型

差旅费用

餐饮费用

交通费用

办公费用

其他费用

机票/火车票

酒店住宿

出差补贴

商务宴请

工作餐

团队聚餐

出租车

公共交通

停车费

办公用品

设备采购

软件服务

6.1.2 报销流程规范
步骤 操作人 说明
1 申请人 填写报销单并提交
2 系统 生成报销单号
3 审批人 审核报销内容
4 审批人 通过或拒绝
5 财务 处理付款

6.2 审批管理要点

6.2.1 审批要点
要点 说明
票据完整性 检查发票是否齐全
金额准确性 核对金额是否正确
用途合理性 确认费用是否合理
流程合规性 检查是否符合规定
6.2.2 审批状态流转

提交申请

审批通过

审批拒绝

修改后重新提交

待审批

已通过

已拒绝

6.3 费用控制原则

6.3.1 控制原则
原则 说明
预算控制 不超过部门预算
标准控制 符合公司标准
时效控制 及时提交报销
凭证控制 提供有效凭证
6.3.2 常见问题处理

票据缺失

金额不符

超标准

信息错误

报销问题

问题类型

退回补充

核实后调整

部分报销

修改后重提


七、扩展功能规划

7.1 后续版本规划

2024-01-07 2024-01-14 2024-01-21 2024-01-28 2024-02-04 2024-02-11 2024-02-18 2024-02-25 2024-03-03 2024-03-10 2024-03-17 核心报销功能 审批流程功能 筛选搜索功能 数据持久化 图片上传功能 导出报表功能 多级审批流程 预算管理功能 统计分析功能 V1.0 基础版本 V1.1 增强版本 V1.2 进阶版本 报销管理应用开发计划

7.2 功能扩展建议

7.2.1 数据持久化
// 使用SharedPreferences存储
class ExpenseStorage {
  static Future<void> saveClaims(List<ExpenseClaim> claims) async {
    final prefs = await SharedPreferences.getInstance();
    final jsonList = claims.map((c) => c.toJson()).toList();
    await prefs.setString('expense_claims', jsonEncode(jsonList));
  }
}
功能 说明
本地存储 使用SharedPreferences
云端同步 支持多设备同步
数据备份 导出JSON格式
7.2.2 图片上传
功能 说明
拍照上传 拍摄票据照片
相册选择 从相册选择图片
图片压缩 压缩后上传
7.2.3 导出报表
功能 说明
导出Excel 生成Excel报表
导出PDF 生成PDF报表
邮件发送 直接发送邮件

八、注意事项

8.1 开发注意事项

  1. 金额精度:使用double类型注意精度问题

  2. 状态同步:审批后及时更新状态

  3. 数据验证:表单输入要进行验证

  4. 用户体验:操作后给予反馈提示

8.2 用户体验优化

💡 用户体验建议 💡

  • 审批操作简洁明了
  • 费用明细清晰展示
  • 状态变化及时通知
  • 支持快速筛选查找

8.3 常见问题

问题 原因 解决方案
金额显示异常 精度问题 使用toStringAsFixed
状态未更新 setState遗漏 检查状态更新
筛选无效 条件判断错误 检查筛选逻辑
表单验证失败 validator错误 检查验证规则

九、运行说明

9.1 环境要求

环境 版本要求
Flutter SDK >= 3.0.0
Dart SDK >= 2.17.0
鸿蒙OS API 21+

9.2 运行命令

# 查看可用设备
flutter devices

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

# 运行到Windows
flutter run -d windows -t lib/main_expense_claim.dart

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

十、总结

报销管理应用通过清晰的功能设计,帮助企业规范报销流程。应用支持差旅、餐饮、交通、办公等多种报销类型,费用明细详细记录,审批流程透明可追溯。待审批和已通过金额统计一目了然,方便管理者掌握费用情况。

审批功能支持通过和拒绝操作,拒绝时可填写原因,让申请人了解问题所在。筛选和搜索功能帮助用户快速定位报销记录,提高工作效率。费用明细管理支持添加、删除操作,金额自动计算,减少人工计算错误。

界面设计采用蓝色主题风格,状态标签使用不同颜色区分,视觉层次清晰。应用采用Material Design 3设计规范,遵循Flutter最佳实践,代码结构清晰,易于维护和扩展。

报销管理,规范流程,透明审批,高效办公!


Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐