批量重命名工具应用


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

适配的第三方库地址:

  • file_selector: https://pub.dev/packages/file_selector
  • cross_file: https://pub.dev/packages/cross_file
  • shared_preferences: https://pub.dev/packages/shared_preferences
  • animations: https://pub.dev/packages/animations

一、项目概述

运行效果图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.1 应用简介

批量重命名工具是一款专注于文件批量管理的实用工具应用,旨在帮助用户高效地重命名大量文件。应用支持多种重命名规则、实时预览对比、一键撤销操作,让文件管理变得轻松便捷。

应用以优雅紫色为主色调,象征创意与效率。涵盖规则设置、文件预览、批量执行、历史记录四大核心模块。用户可以设置前缀后缀、查找替换、序号命名、日期命名等多种规则,实时预览重命名效果,一键执行批量操作。

1.2 核心功能

功能模块 功能描述 实现方式
文件选择 选择需要重命名的文件 文件选择器
规则设置 设置重命名规则模板 规则引擎
实时预览 预览重命名前后对比 UI渲染
批量执行 一键执行重命名操作 文件操作
撤销操作 撤销最近的重命名操作 历史记录
冲突检测 检测文件名冲突 算法检测
规则保存 保存常用重命名规则 本地存储
历史记录 查看操作历史记录 数据存储

1.3 重命名规则定义

序号 规则类型 图标 描述 示例
1 添加前缀 在文件名前添加指定文本 file → new_file
2 添加后缀 在文件名后添加指定文本 file → file_backup
3 查找替换 🔍 查找并替换指定文本 IMG → Photo
4 序号命名 🔢 按序号批量命名 001, 002, 003…
5 日期命名 📅 使用日期作为文件名 20240115
6 大小写转换 🔤 转换文件名大小写 FILE → file
7 删除字符 删除指定数量字符 filename → name
8 修改扩展名 📎 批量修改文件扩展名 .txt → .md

1.4 大小写转换类型

序号 转换类型 描述 示例
1 全部大写 所有字母转为大写 file → FILE
2 全部小写 所有字母转为小写 FILE → file
3 首字母大写 首字母大写其余小写 file → File
4 句首大写 每个单词首字母大写 my file → My File

1.5 日期格式定义

序号 格式代码 示例输出 描述
1 yyyyMMdd 20240115 年月日
2 yyyy-MM-dd 2024-01-15 年-月-日
3 yyyyMMdd_HHmmss 20240115_143025 年月日_时分秒
4 MMdd 0115 月日

1.6 序号格式定义

序号 位数 示例 描述
1 2位 01, 02, 03… 01-99
2 3位 001, 002, 003… 001-999
3 4位 0001, 0002, 0003… 0001-9999

1.7 技术栈

技术领域 技术选型 版本要求
开发框架 Flutter >= 3.0.0
编程语言 Dart >= 2.17.0
设计规范 Material Design 3 -
状态管理 StatefulWidget -
文件选择 File Selector >= 1.0.0
文件操作 Cross File >= 1.0.0
规则存储 SharedPreferences >= 2.0.0
动画效果 Animations >= 2.0.0
目标平台 鸿蒙OS / Web API 21+

1.8 项目结构

lib/
└── main_batch_rename.dart
    ├── BatchRenameApp               # 应用入口
    ├── RenameRuleType               # 重命名规则枚举
    ├── CaseType                     # 大小写类型枚举
    ├── FileItem                     # 文件项模型
    ├── RenameRule                   # 重命名规则模型
    ├── RenameHistory                # 操作历史模型
    ├── BatchRenameHomePage          # 主页面
    ├── _buildRulePanel              # 规则面板
    ├── _buildMainPanel              # 主面板
    └── _buildFileList               # 文件列表

二、系统架构

2.1 整体架构图

Data Layer

Business Layer

Presentation Layer

主页面
BatchRenameHomePage

规则面板

主面板

底部栏

规则列表

规则配置

文件列表

预览对比

冲突提示

历史记录

执行按钮

规则引擎
RuleEngine

文件管理器
FileManager

冲突检测器
ConflictDetector

历史管理器
HistoryManager

FileItem
文件项

RenameRule
重命名规则

RenameHistory
操作历史

SharedPreferences
本地存储

2.2 类图设计

applies

records

contains

BatchRenameApp

+Widget build()

«enumeration»

RenameRuleType

+String label

+IconData icon

+prefix()

+suffix()

+replace()

+sequence()

+date()

+caseChange()

+remove()

+extension()

«enumeration»

CaseType

+String label

+upper()

+lower()

+capitalize()

+sentence()

FileItem

+String id

+String originalName

+String newName

+String extension

+int size

+DateTime modifiedTime

+bool isSelected

+bool hasConflict

+get displayName

+get originalFullName

+copyWith()

RenameRule

+RenameRuleType type

+String prefix

+String suffix

+String findText

+String replaceText

+int startIndex

+int step

+int digits

+String dateFormat

+CaseType caseType

+int removeCount

+bool removeFromStart

+String newExtension

+apply()

RenameHistory

+String id

+DateTime timestamp

+List<FileItem> files

+RenameRule rule

+int renamedCount

2.3 页面导航流程

应用启动

主页面

左侧规则面板

右侧文件列表

选择规则类型

配置规则参数

应用规则

预览重命名效果

有冲突?

解决冲突

执行重命名

保存历史记录

显示成功提示

2.4 重命名执行流程

历史管理器 文件管理器 规则引擎 主页面 用户 历史管理器 文件管理器 规则引擎 主页面 用户 选择规则类型 配置规则参数 返回规则对象 应用规则 应用规则到文件 返回新文件名 显示预览 执行重命名 批量重命名 返回结果 保存操作记录 显示成功提示

三、核心模块设计

3.1 数据模型设计

3.1.1 重命名规则枚举 (RenameRuleType)
enum RenameRuleType {
  prefix('添加前缀', Icons.add_circle_outline),
  suffix('添加后缀', Icons.add_circle),
  replace('查找替换', Icons.find_replace),
  sequence('序号命名', Icons.format_list_numbered),
  date('日期命名', Icons.calendar_today),
  caseChange('大小写转换', Icons.text_fields),
  remove('删除字符', Icons.remove_circle_outline),
  extension('修改扩展名', Icons.attachment);

  final String label;
  final IconData icon;
  const RenameRuleType(this.label, this.icon);
}
3.1.2 文件项模型 (FileItem)
class FileItem {
  final String id;
  final String originalName;
  final String newName;
  final String extension;
  final int size;
  final DateTime modifiedTime;
  final bool isSelected;
  final bool hasConflict;

  const FileItem({
    required this.id,
    required this.originalName,
    required this.newName,
    required this.extension,
    required this.size,
    required this.modifiedTime,
    required this.isSelected,
    required this.hasConflict,
  });

  FileItem copyWith({
    String? newName,
    bool? isSelected,
    bool? hasConflict,
  }) {
    return FileItem(
      id: id,
      originalName: originalName,
      newName: newName ?? this.newName,
      extension: extension,
      size: size,
      modifiedTime: modifiedTime,
      isSelected: isSelected ?? this.isSelected,
      hasConflict: hasConflict ?? this.hasConflict,
    );
  }

  String get displayName => '$newName$extension';
  String get originalFullName => '$originalName$extension';
}
3.1.3 重命名规则模型 (RenameRule)
class RenameRule {
  final RenameRuleType type;
  final String? prefix;
  final String? suffix;
  final String? findText;
  final String? replaceText;
  final int? startIndex;
  final int? step;
  final int? digits;
  final String? dateFormat;
  final CaseType? caseType;
  final int? removeCount;
  final bool? removeFromStart;
  final String? newExtension;

  String apply(String originalName, int index) {
    String result = originalName;

    switch (type) {
      case RenameRuleType.prefix:
        result = '${prefix ?? ''}$result';
        break;
      case RenameRuleType.suffix:
        result = '$result${suffix ?? ''}';
        break;
      case RenameRuleType.replace:
        result = result.replaceAll(findText ?? '', replaceText ?? '');
        break;
      case RenameRuleType.sequence:
        final start = startIndex ?? 1;
        final stepVal = step ?? 1;
        final digitCount = digits ?? 3;
        final number = start + (index * stepVal);
        result = number.toString().padLeft(digitCount, '0');
        break;
      // ... 其他规则
    }

    return result;
  }
}
3.1.4 规则使用分布
30% 25% 20% 15% 10% 重命名规则使用分布示例 添加前缀 添加后缀 序号命名 查找替换 其他规则

3.2 页面结构设计

3.2.1 主页面布局

BatchRenameHomePage

Row

规则面板 280px

主面板 Expanded

规则列表

执行按钮

顶部栏

文件列表

底部栏

3.2.2 规则面板结构

规则面板

标题栏

规则列表

操作按钮

图标

标题文字

添加前缀

添加后缀

查找替换

序号命名

日期命名

大小写转换

删除字符

修改扩展名

执行重命名

重置

3.2.3 文件列表结构

文件列表

表头

文件项列表

复选框

原文件名

新文件名

大小

修改时间

文件项

文件项

...

3.3 规则应用逻辑

选择规则类型

显示规则配置

输入规则参数

点击应用规则

遍历选中文件

应用规则计算新名称

更新文件项

还有文件?

检测冲突

有冲突?

标记冲突文件

正常显示

提示用户解决

显示预览

3.4 冲突检测逻辑

开始检测

创建名称映射表

遍历所有文件

获取新文件名

更新映射计数

还有文件?

检查映射表

计数>1?

标记为冲突

标记为正常

显示冲突提示

允许执行


四、UI设计规范

4.1 配色方案

应用以优雅紫色为主色调,象征创意与效率:

颜色类型 色值 用途
主色 #9C27B0 (Purple) 导航、主题元素
辅助色 #BA68C8 卡片背景
第三色 #CE93D8 进度条背景
强调色 #E1BEE7 分组页面
背景色 #FAFAFA 页面背景
卡片背景 #FFFFFF 信息卡片
成功色 #4CAF50 重命名成功
冲突色 #F44336 文件名冲突

4.2 状态配色

状态 色值 视觉效果
未修改 默认 白色背景
已修改 #E8F5E9 浅绿背景
冲突 #FFEBEE 浅红背景
选中 #F3E5F5 浅紫背景

4.3 字体规范

元素 字号 字重 颜色
页面标题 20px Bold 主色
规则名称 16px Medium #000000
文件名 14px Regular #000000
提示文字 12px Regular #666666
冲突提示 12px Medium #F44336

4.4 组件规范

4.4.1 规则面板
┌─────────────────────────────────────┐
│  ✏️ 重命名规则                        │
├─────────────────────────────────────┤
│  ┌─────────────────────────────┐   │
│  │ ➕ 添加前缀                  │   │
│  └─────────────────────────────┘   │
│  ┌─────────────────────────────┐   │
│  │ ➕ 添加后缀                  │   │
│  └─────────────────────────────┘   │
│  ┌─────────────────────────────┐   │
│  │ 🔍 查找替换                  │   │
│  └─────────────────────────────┘   │
│  ...                                │
├─────────────────────────────────────┤
│  ┌─────────────────────────────┐   │
│  │     ✓ 执行重命名             │   │
│  └─────────────────────────────┘   │
│  ┌─────────────────────────────┐   │
│  │     ↻ 重置                   │   │
│  └─────────────────────────────┘   │
└─────────────────────────────────────┘
4.4.2 文件列表项
┌─────────────────────────────────────────────────────────┐
│ ☑ IMG001.jpg    →    Photo001.jpg     ✓  2.5 MB   1/15 │
│   (删除线)              (绿色加粗)                        │
└─────────────────────────────────────────────────────────┘
4.4.3 冲突提示
┌─────────────────────────────────────────────────────────┐
│ ☑ file.txt      →    newfile.txt      ⚠  1.2 MB   1/15 │
│   (删除线)              (红色警告)                        │
└─────────────────────────────────────────────────────────┘
4.4.4 规则配置面板
┌─────────────────────────────────────┐
│  ➕ 添加前缀                          │
│  ─────────────────────────────────  │
│  ┌─────────────────────────────┐   │
│  │ 前缀内容                     │   │
│  │ 输入要添加的前缀              │   │
│  └─────────────────────────────┘   │
│                                     │
│  ┌─────────────────────────────┐   │
│  │       应用规则               │   │
│  └─────────────────────────────┘   │
└─────────────────────────────────────┘

五、核心功能实现

5.1 规则引擎实现

class RuleEngine {
  String applyRule(RenameRule rule, String originalName, int index) {
    String result = originalName;

    switch (rule.type) {
      case RenameRuleType.prefix:
        result = '${rule.prefix ?? ''}$result';
        break;
      case RenameRuleType.suffix:
        result = '$result${rule.suffix ?? ''}';
        break;
      case RenameRuleType.replace:
        result = result.replaceAll(rule.findText ?? '', rule.replaceText ?? '');
        break;
      case RenameRuleType.sequence:
        final start = rule.startIndex ?? 1;
        final step = rule.step ?? 1;
        final digits = rule.digits ?? 3;
        final number = start + (index * step);
        result = number.toString().padLeft(digits, '0');
        break;
      case RenameRuleType.date:
        result = _formatDate(rule.dateFormat ?? 'yyyyMMdd');
        break;
      case RenameRuleType.caseChange:
        result = _applyCaseChange(result, rule.caseType);
        break;
      case RenameRuleType.remove:
        result = _removeChars(result, rule.removeCount ?? 0, rule.removeFromStart ?? true);
        break;
      case RenameRuleType.extension:
        break;
    }

    return result;
  }

  String _formatDate(String format) {
    final now = DateTime.now();
    return format
        .replaceAll('yyyy', now.year.toString())
        .replaceAll('MM', now.month.toString().padLeft(2, '0'))
        .replaceAll('dd', now.day.toString().padLeft(2, '0'))
        .replaceAll('HH', now.hour.toString().padLeft(2, '0'))
        .replaceAll('mm', now.minute.toString().padLeft(2, '0'))
        .replaceAll('ss', now.second.toString().padLeft(2, '0'));
  }

  String _applyCaseChange(String text, CaseType? caseType) {
    switch (caseType) {
      case CaseType.upper:
        return text.toUpperCase();
      case CaseType.lower:
        return text.toLowerCase();
      case CaseType.capitalize:
        return text.isNotEmpty ? text[0].toUpperCase() + text.substring(1) : text;
      case CaseType.sentence:
        return text.split(' ').map((word) {
          return word.isNotEmpty ? word[0].toUpperCase() + word.substring(1).toLowerCase() : word;
        }).join(' ');
      default:
        return text;
    }
  }

  String _removeChars(String text, int count, bool fromStart) {
    if (fromStart) {
      return text.substring(count.clamp(0, text.length));
    } else {
      return text.substring(0, (text.length - count).clamp(0, text.length));
    }
  }
}

5.2 文件管理器实现

class FileManager {
  final FileSelector _fileSelector = FileSelector();
  List<FileItem> _files = [];

  Future<void> selectFiles() async {
    final result = await _fileSelector.openMultipleFiles();
    
    for (final file in result) {
      final stat = await file.stat();
      _files.add(FileItem(
        id: file.path,
        originalName: file.nameWithoutExtension,
        newName: file.nameWithoutExtension,
        extension: file.extension,
        size: stat.size,
        modifiedTime: stat.modified,
        isSelected: true,
        hasConflict: false,
      ));
    }
  }

  Future<void> renameFiles(List<FileItem> files) async {
    for (final file in files) {
      if (!file.isSelected) continue;
      
      final oldPath = file.id;
      final dir = p.dirname(oldPath);
      final newPath = p.join(dir, file.displayName);
      
      await File(oldPath).rename(newPath);
    }
  }

  List<FileItem> get files => List.unmodifiable(_files);
}

5.3 冲突检测器实现

class ConflictDetector {
  Map<String, List<FileItem>> detectConflicts(List<FileItem> files) {
    final nameMap = <String, List<FileItem>>{};

    for (final file in files) {
      if (!file.isSelected) continue;
      
      final fullName = file.displayName;
      nameMap.putIfAbsent(fullName, () => []).add(file);
    }

    return Map.fromEntries(
      nameMap.entries.where((entry) => entry.value.length > 1),
    );
  }

  List<FileItem> markConflicts(List<FileItem> files) {
    final conflicts = detectConflicts(files);
    final conflictNames = conflicts.keys.toSet();

    return files.map((file) {
      return file.copyWith(
        hasConflict: conflictNames.contains(file.displayName),
      );
    }).toList();
  }

  bool hasConflicts(List<FileItem> files) {
    return detectConflicts(files).isNotEmpty;
  }
}

5.4 历史管理器实现

class HistoryManager {
  final SharedPreferences _prefs;
  static const String _historyKey = 'rename_history';

  HistoryManager(this._prefs);

  Future<void> saveHistory(RenameHistory history) async {
    final histories = await getHistories();
    histories.insert(0, history);
    
    if (histories.length > 50) {
      histories.removeRange(50, histories.length);
    }

    final jsonList = histories.map((h) => h.toJson()).toList();
    await _prefs.setString(_historyKey, jsonEncode(jsonList));
  }

  Future<List<RenameHistory>> getHistories() async {
    final jsonString = _prefs.getString(_historyKey);
    if (jsonString == null) return [];

    final jsonList = jsonDecode(jsonString) as List;
    return jsonList.map((json) => RenameHistory.fromJson(json)).toList();
  }

  Future<void> clearHistory() async {
    await _prefs.remove(_historyKey);
  }

  Future<void> removeHistory(String historyId) async {
    final histories = await getHistories();
    histories.removeWhere((h) => h.id == historyId);
    
    final jsonList = histories.map((h) => h.toJson()).toList();
    await _prefs.setString(_historyKey, jsonEncode(jsonList));
  }
}

5.5 规则存储实现

class RuleStorage {
  final SharedPreferences _prefs;
  static const String _rulesKey = 'saved_rules';

  RuleStorage(this._prefs);

  Future<void> saveRule(String name, RenameRule rule) async {
    final rules = await getSavedRules();
    rules[name] = rule;
    
    final jsonMap = rules.map((key, value) => MapEntry(key, value.toJson()));
    await _prefs.setString(_rulesKey, jsonEncode(jsonMap));
  }

  Future<Map<String, RenameRule>> getSavedRules() async {
    final jsonString = _prefs.getString(_rulesKey);
    if (jsonString == null) return {};

    final jsonMap = jsonDecode(jsonString) as Map<String, dynamic>;
    return jsonMap.map((key, value) => MapEntry(key, RenameRule.fromJson(value)));
  }

  Future<void> deleteRule(String name) async {
    final rules = await getSavedRules();
    rules.remove(name);
    
    final jsonMap = rules.map((key, value) => MapEntry(key, value.toJson()));
    await _prefs.setString(_rulesKey, jsonEncode(jsonMap));
  }
}

六、交互设计

6.1 规则应用流程

文件列表 规则引擎 规则面板 用户 文件列表 规则引擎 规则面板 用户 loop [每个文件] 选择规则类型 显示配置面板 输入参数并应用 创建规则对象 遍历选中文件 计算新名称 更新文件项 检测冲突 显示预览结果

6.2 执行重命名流程

点击执行

有冲突?

提示解决冲突

确认执行

返回修改

遍历文件

执行重命名

成功?

更新文件状态

记录错误

保存历史记录

显示结果

6.3 撤销操作流程

查看历史

选择记录

确认撤销

恢复文件名

更新历史


七、扩展功能规划

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 2024-03-24 2024-03-31 基础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 常见问题

问题 原因 解决方案
文件名冲突 新名称重复 自动添加序号
权限不足 未获取权限 引导用户授权
文件占用 文件被其他程序使用 提示关闭程序
撤销失败 原文件已删除 提示无法撤销
规则无效 参数设置错误 验证规则参数

8.3 使用技巧

📝 批量重命名工具使用技巧 📝

规则设置

  • 保存常用规则模板
  • 组合使用多种规则
  • 预览后再执行
  • 注意文件名冲突

文件管理

  • 按类型分类处理
  • 先小批量测试
  • 保留原始文件备份
  • 定期清理历史记录

效率提升

  • 使用快捷键操作
  • 善用规则模板
  • 批量选择文件
  • 利用撤销功能

九、运行说明

9.1 环境要求

环境 版本要求
Flutter SDK >= 3.0.0
Dart SDK >= 2.17.0
鸿蒙OS API 21+
Android API 21+
iOS 12.0+
Web浏览器 Chrome 90+

9.2 依赖配置

pubspec.yaml 中添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  file_selector: ^1.0.0
  cross_file: ^1.0.0
  shared_preferences: ^2.0.0
  animations: ^2.0.0
  path: ^1.8.0
  path_provider: ^2.0.0

9.3 权限配置

Android (android/app/src/main/AndroidManifest.xml):

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

iOS (ios/Runner/Info.plist):

<key>NSDocumentsFolderUsageDescription</key>
<string>需要访问文档文件夹以重命名文件</string>
<key>NSDownloadsFolderUsageDescription</key>
<string>需要访问下载文件夹以重命名文件</string>

9.4 运行命令

# 查看可用设备
flutter devices

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

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

# 运行到Android设备
flutter run -d android -t lib/main_batch_rename.dart

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

十、总结

批量重命名工具应用通过多种重命名规则、实时预览对比、一键撤销操作,帮助用户高效地重命名大量文件。应用支持添加前缀、添加后缀、查找替换、序号命名、日期命名、大小写转换、删除字符、修改扩展名8种重命名规则,满足用户多样化的文件管理需求。

应用采用 Material Design 3 设计规范,以优雅紫色为主色调,象征创意与效率。通过本应用,希望能够帮助用户轻松管理文件,让批量重命名变得简单高效。

批量重命名工具——高效管理您的文件


Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐