软件雏形

This commit is contained in:
2025-12-18 20:21:02 +08:00
parent 9b81cacea5
commit 18a2c9b4b2
12 changed files with 973 additions and 20 deletions

5
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"python-envs.defaultEnvManager": "ms-python.python:conda",
"python-envs.defaultPackageManager": "ms-python.python:conda",
"python-envs.pythonProjects": []
}

View File

@@ -1,4 +1,4 @@
using System.Configuration;
using System.Configuration;
using System.Data;
using System.Windows;
@@ -7,7 +7,7 @@ namespace CopyRou
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
public partial class App : System.Windows.Application
{
}

83
AppConfig.cs Normal file
View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
namespace CopyRou
{
public class AppConfig
{
public List<string> SourcePaths { get; set; } = new();
public string DestPath { get; set; } = string.Empty;
private static readonly string ConfigPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"CopyRou", "config.json");
public static AppConfig Load()
{
try
{
if (File.Exists(ConfigPath))
{
var json = File.ReadAllText(ConfigPath);
return JsonSerializer.Deserialize<AppConfig>(json) ?? new AppConfig();
}
}
catch (Exception ex)
{
// 如果加载失败,返回默认配置
System.Diagnostics.Debug.WriteLine($"加载配置失败: {ex.Message}");
}
return new AppConfig
{
SourcePaths = new List<string>
{
@"Z:\Routing\A1-ROUTING\NEW-ROUTING",
@"Z:\Routing\A2-ROUTING\NEW-ROUTING"
},
DestPath = @"D:\ARPTWork\rou"
};
}
public void Save()
{
try
{
var directory = Path.GetDirectoryName(ConfigPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
Directory.CreateDirectory(directory);
var json = JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(ConfigPath, json);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"保存配置失败: {ex.Message}");
}
}
public string GetSourcePathsText()
{
return string.Join(Environment.NewLine, SourcePaths);
}
public void SetSourcePathsFromText(string text)
{
SourcePaths.Clear();
if (!string.IsNullOrWhiteSpace(text))
{
var paths = text.Split(new[] { Environment.NewLine, "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries);
foreach (var path in paths)
{
var trimmedPath = path.Trim();
if (!string.IsNullOrEmpty(trimmedPath))
{
SourcePaths.Add(trimmedPath);
}
}
}
}
}
}

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
@@ -6,6 +6,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<Platforms>AnyCPU;x86</Platforms>
</PropertyGroup>

151
FileService.cs Normal file
View File

@@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace CopyRou
{
public class FileService : IFileService
{
public bool MatchFolderName(string folderName, string numPart, string versionPart)
{
if (string.IsNullOrEmpty(folderName) ||
string.IsNullOrEmpty(numPart) ||
string.IsNullOrEmpty(versionPart))
return false;
var folderNameLower = folderName.ToLower();
var numLength = numPart.Length;
// 检查文件夹名长度是否足够
if (folderNameLower.Length < 3 + numLength)
return false;
// 从第4个字符开始匹配数字编号索引3
var numInName = folderNameLower.Substring(3, numLength);
if (numInName != numPart.ToLower())
return false;
// 检查版本部分
var versionInName = folderNameLower.Substring(3 + numLength);
return versionInName.StartsWith(versionPart.ToLower());
}
public List<string> CopyRouFiles(string sourceFolder, string number, string destPath, ILogger logger)
{
var copiedFiles = new List<string>();
var edFoldersFound = 0;
var rouExtensions = new[] { ".rou", ".rou1", ".rou2", ".rou3" };
try
{
// 确保目标目录存在
Directory.CreateDirectory(destPath);
foreach (var item in Directory.GetDirectories(sourceFolder))
{
var folderName = Path.GetFileName(item);
if (folderName.StartsWith("ED", StringComparison.OrdinalIgnoreCase) ||
folderName.StartsWith("ROU", StringComparison.OrdinalIgnoreCase))
{
edFoldersFound++;
logger?.Log($"正在处理ED文件夹: {folderName}");
// 递归查找所有.rou文件
var rouFiles = Directory.GetFiles(item, "*.*", SearchOption.AllDirectories)
.Where(file => rouExtensions.Contains(Path.GetExtension(file).ToLower()))
.ToList();
foreach (var srcFile in rouFiles)
{
var fileName = Path.GetFileName(srcFile);
var baseName = $"{number}_{folderName}_{fileName}";
var destFile = Path.Combine(destPath, baseName);
File.Copy(srcFile, destFile, true);
copiedFiles.Add(baseName);
}
}
}
if (edFoldersFound == 0)
{
logger?.LogWarning("未找到任何以ED或ROU开头的文件夹");
}
else if (!copiedFiles.Any())
{
logger?.LogWarning("在ED文件夹中未找到任何.rou系列文件");
}
else
{
logger?.Log($"成功从 {edFoldersFound} 个ED文件夹复制 {copiedFiles.Count} 个文件");
}
}
catch (Exception ex)
{
logger?.LogError($"复制文件时发生错误: {ex.Message}");
}
return copiedFiles;
}
public async Task ProcessCodes(List<string> codes, List<string> sourcePaths, string destPath,
IProgress<int> progress, ILogger logger)
{
var totalCodes = codes.Count;
var processedCodes = 0;
foreach (var code in codes)
{
var trimmedCode = code.Trim();
if (string.IsNullOrEmpty(trimmedCode))
continue;
logger?.Log($"\n处理编号: {trimmedCode}");
var numPart = trimmedCode.Length >= 5 ? trimmedCode.Substring(0, 5) : trimmedCode;
var versionPart = trimmedCode.Length > 5 ? trimmedCode.Substring(5) : "";
var found = false;
foreach (var sourceRoot in sourcePaths)
{
var numFolder = Path.Combine(sourceRoot, numPart);
if (!Directory.Exists(numFolder))
continue;
try
{
var folders = Directory.GetDirectories(numFolder);
foreach (var folder in folders)
{
var folderName = Path.GetFileName(folder);
if (MatchFolderName(folderName, numPart, versionPart))
{
logger?.Log($"找到匹配文件夹: {folder}");
CopyRouFiles(folder, numPart, destPath, logger ?? new Logger());
found = true;
}
}
}
catch (Exception ex)
{
logger?.LogError($"访问文件夹 {numFolder} 时发生错误: {ex.Message}");
}
}
if (!found)
{
logger?.Log("未找到匹配的文件夹");
}
processedCodes++;
progress?.Report((int)((double)processedCodes / totalCodes * 100));
// 添加小延迟以避免UI阻塞
await Task.Delay(10);
}
}
}
}

12
IFileService.cs Normal file
View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace CopyRou
{
public interface IFileService
{
bool MatchFolderName(string folderName, string numPart, string versionPart);
List<string> CopyRouFiles(string sourceFolder, string number, string destPath, ILogger logger);
Task ProcessCodes(List<string> codes, List<string> sourcePaths, string destPath, IProgress<int> progress, ILogger logger);
}
}

11
ILogger.cs Normal file
View File

@@ -0,0 +1,11 @@
namespace CopyRou
{
public interface ILogger
{
void Log(string message);
void LogError(string message);
void LogWarning(string message);
void Clear();
event System.EventHandler<string>? MessageLogged;
}
}

32
Logger.cs Normal file
View File

@@ -0,0 +1,32 @@
using System;
namespace CopyRou
{
public class Logger : ILogger
{
public event EventHandler<string>? MessageLogged;
public void Log(string message)
{
var timestampedMessage = $"[{DateTime.Now:HH:mm:ss}] {message}";
MessageLogged?.Invoke(this, timestampedMessage);
}
public void LogError(string message)
{
var timestampedMessage = $"[{DateTime.Now:HH:mm:ss}] [错误] {message}";
MessageLogged?.Invoke(this, timestampedMessage);
}
public void LogWarning(string message)
{
var timestampedMessage = $"[{DateTime.Now:HH:mm:ss}] [警告] {message}";
MessageLogged?.Invoke(this, timestampedMessage);
}
public void Clear()
{
MessageLogged?.Invoke(this, "[LOG CLEARED]");
}
}
}

View File

@@ -1,12 +1,215 @@
<Window x:Class="CopyRou.MainWindow"
<Window
x:Class="CopyRou.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CopyRou"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="ROU文件复制工具"
Width="586"
Height="631"
MinWidth="586"
MinHeight="631"
mc:Ignorable="d">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- 配置区域 -->
<GroupBox
Grid.Row="0"
Margin="0,0,0,10"
Header="路径配置">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Grid.Row="0"
Grid.Column="0"
Margin="5"
VerticalAlignment="Top"
Content="源路径:" />
<TextBox
x:Name="SourcePathsTextBox"
Grid.Row="0"
Grid.Column="1"
Height="60"
Margin="5"
AcceptsReturn="True"
IsReadOnly="True"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto" />
<Button
x:Name="AddSourceButton"
Grid.Row="0"
Grid.Column="2"
Width="60"
Height="25"
Margin="5"
Click="AddSourceButton_Click"
Content="添加" />
<Label
Grid.Row="1"
Grid.Column="0"
Margin="5"
Content="目标路径:" />
<TextBox
x:Name="DestPathTextBox"
Grid.Row="1"
Grid.Column="1"
Margin="5"
IsReadOnly="True" />
<Button
x:Name="BrowseDestButton"
Grid.Row="1"
Grid.Column="2"
Width="60"
Height="25"
Margin="5"
Click="BrowseDestButton_Click"
Content="浏览" />
<!-- 保存配置按钮 -->
<Button
x:Name="SaveConfigButton"
Grid.Row="2"
Grid.Column="1"
Grid.ColumnSpan="2"
Width="100"
Height="25"
Margin="5"
HorizontalAlignment="Right"
Click="SaveConfigButton_Click"
Content="保存配置" />
</Grid>
</GroupBox>
<!-- 输入区域 -->
<Grid Grid.Row="1" Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1.2*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- 单个料号输入区域 -->
<GroupBox
Grid.Column="0"
Margin="0,0,5,0"
Header="单个料号输入">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Margin="5"
Foreground="Gray"
Text="输入单个料号27362a" />
<StackPanel Margin="0,20,0,0" Orientation="Horizontal">
<TextBox
x:Name="SingleCodeTextBox"
Grid.Row="1"
MinWidth="150"
Margin="5"
KeyDown="SingleCodeTextBox_KeyDown" />
<Button
x:Name="AddSingleCodeButton"
Grid.Row="2"
Width="80"
Height="25"
Margin="5"
HorizontalAlignment="Left"
Click="AddSingleCodeButton_Click"
Content="添加" />
</StackPanel>
</Grid>
</GroupBox>
<!-- 批量料号输入区域 -->
<GroupBox
Grid.Column="1"
Margin="5,0,0,0"
Header="批量料号输入">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Margin="5"
Foreground="Gray"
Text="每行输入一个料号27362a" />
<TextBox
x:Name="CodesTextBox"
Grid.Row="1"
Height="100"
Margin="5"
AcceptsReturn="True"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto" />
<!-- 操作按钮 -->
<StackPanel
Grid.Row="2"
Margin="5"
HorizontalAlignment="Center"
Orientation="Horizontal">
<Button
x:Name="StartButton"
Width="80"
Height="25"
Margin="5"
Click="StartButton_Click"
Content="开始处理" />
<Button
x:Name="ClearButton"
Width="80"
Height="25"
Margin="5"
Click="ClearButton_Click"
Content="清空" />
</StackPanel>
</Grid>
</GroupBox>
</Grid>
<!-- 进度条 -->
<ProgressBar
x:Name="ProgressBar"
Grid.Row="3"
Height="20"
Margin="0,0,0,10"
Maximum="100"
Minimum="0"
Value="0" />
<!-- 日志区域 -->
<GroupBox Grid.Row="4" Header="处理日志">
<TextBox
x:Name="LogTextBox"
Height="150"
Margin="5"
IsReadOnly="True"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto" />
</GroupBox>
</Grid>
</Window>

View File

@@ -1,13 +1,13 @@
using System.Text;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using MessageBox = System.Windows.MessageBox;
namespace CopyRou
{
@@ -16,9 +16,269 @@ namespace CopyRou
/// </summary>
public partial class MainWindow : Window
{
private readonly IFileService _fileService;
private readonly ILogger _logger;
private AppConfig _config;
private bool _isProcessing = false;
public MainWindow()
{
InitializeComponent();
_fileService = new FileService();
_logger = new Logger();
_config = AppConfig.Load();
InitializeUI();
SubscribeToEvents();
}
private void InitializeUI()
{
// 加载配置到UI
SourcePathsTextBox.Text = _config.GetSourcePathsText();
DestPathTextBox.Text = _config.DestPath;
// 设置初始状态
StartButton.IsEnabled = true;
ProgressBar.Value = 0;
}
private void SubscribeToEvents()
{
// 订阅日志事件
_logger.MessageLogged += (sender, message) =>
{
Dispatcher.Invoke(() =>
{
LogTextBox.AppendText(message + Environment.NewLine);
LogTextBox.ScrollToEnd();
});
};
}
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
if (_isProcessing)
return;
var codesText = CodesTextBox.Text;
if (string.IsNullOrWhiteSpace(codesText))
{
System.Windows.MessageBox.Show("请输入要处理的料号", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
var sourcePaths = GetSourcePaths();
if (!sourcePaths.Any())
{
System.Windows.MessageBox.Show("请至少配置一个源路径", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
var destPath = DestPathTextBox.Text;
if (string.IsNullOrWhiteSpace(destPath))
{
System.Windows.MessageBox.Show("请配置目标路径", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
_isProcessing = true;
StartButton.IsEnabled = false;
ProgressBar.Value = 0;
LogTextBox.Clear();
try
{
_logger.Log("=== 开始处理 ===");
_logger.Log($"源路径: {string.Join(", ", sourcePaths)}");
_logger.Log($"目标路径: {destPath}");
var materialNumbers = codesText.Split(new[] { Environment.NewLine, "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries)
.ToList();
var progress = new Progress<int>(value =>
{
ProgressBar.Value = value;
});
await _fileService.ProcessCodes(materialNumbers, sourcePaths, destPath, progress, _logger);
_logger.Log("=== 处理完成 ===");
System.Windows.MessageBox.Show("处理完成!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
_logger.LogError($"处理过程中发生错误: {ex.Message}");
System.Windows.MessageBox.Show($"处理过程中发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
_isProcessing = false;
StartButton.IsEnabled = true;
}
}
private void ClearButton_Click(object sender, RoutedEventArgs e)
{
CodesTextBox.Clear();
LogTextBox.Clear();
ProgressBar.Value = 0;
}
private void SaveConfigButton_Click(object sender, RoutedEventArgs e)
{
_config.SetSourcePathsFromText(SourcePathsTextBox.Text);
_config.DestPath = DestPathTextBox.Text;
_config.Save();
System.Windows.MessageBox.Show("配置已保存", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
private void AddSourceButton_Click(object sender, RoutedEventArgs e)
{
var dialog = new System.Windows.Forms.FolderBrowserDialog();
dialog.Description = "选择源路径";
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
var currentPaths = GetSourcePaths();
if (!currentPaths.Contains(dialog.SelectedPath))
{
currentPaths.Add(dialog.SelectedPath);
SourcePathsTextBox.Text = string.Join(Environment.NewLine, currentPaths);
}
}
}
private void BrowseDestButton_Click(object sender, RoutedEventArgs e)
{
var dialog = new System.Windows.Forms.FolderBrowserDialog();
dialog.Description = "选择目标路径";
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
DestPathTextBox.Text = dialog.SelectedPath;
}
}
private List<string> GetSourcePaths()
{
var paths = new List<string>();
if (!string.IsNullOrWhiteSpace(SourcePathsTextBox.Text))
{
paths = SourcePathsTextBox.Text.Split(new[] { Environment.NewLine, "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Trim())
.Where(p => !string.IsNullOrEmpty(p))
.ToList();
}
return paths;
}
private void SingleCodeTextBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Enter)
{
ProcessSingleCode();
}
}
private async void AddSingleCodeButton_Click(object sender, RoutedEventArgs e)
{
await ProcessSingleCodeAsync();
}
private void ProcessSingleCode()
{
if (_isProcessing)
return;
var code = SingleCodeTextBox.Text.Trim();
if (string.IsNullOrWhiteSpace(code))
{
System.Windows.MessageBox.Show("请输入要处理的料号", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 异步处理单个料号
_ = ProcessSingleCodeAsync();
}
private async Task ProcessSingleCodeAsync()
{
if (_isProcessing)
return;
var code = SingleCodeTextBox.Text.Trim();
if (string.IsNullOrWhiteSpace(code))
{
System.Windows.MessageBox.Show("请输入要处理的料号", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
var sourcePaths = GetSourcePaths();
if (!sourcePaths.Any())
{
System.Windows.MessageBox.Show("请至少配置一个源路径", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
var destPath = DestPathTextBox.Text;
if (string.IsNullOrWhiteSpace(destPath))
{
System.Windows.MessageBox.Show("请配置目标路径", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
_isProcessing = true;
AddSingleCodeButton.IsEnabled = false;
SingleCodeTextBox.IsEnabled = false;
try
{
_logger.Log($"=== 开始处理单个料号: {code} ===");
_logger.Log($"源路径: {string.Join(", ", sourcePaths)}");
_logger.Log($"目标路径: {destPath}");
var materialNumbers = new List<string> { code };
// 创建一个简单的进度报告器单个料号处理时直接设置为100%
var progress = new Progress<int>(value =>
{
// 对于单个料号,我们可以直接设置进度条
ProgressBar.Value = value;
});
await _fileService.ProcessCodes(materialNumbers, sourcePaths, destPath, progress, _logger);
_logger.Log("=== 单个料号处理完成 ===");
// 清空输入框
SingleCodeTextBox.Clear();
// 将焦点返回到输入框,方便继续输入
SingleCodeTextBox.Focus();
}
catch (Exception ex)
{
_logger.LogError($"处理单个料号时发生错误: {ex.Message}");
System.Windows.MessageBox.Show($"处理单个料号时发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
_isProcessing = false;
AddSingleCodeButton.IsEnabled = true;
SingleCodeTextBox.IsEnabled = true;
}
}
protected override void OnClosed(EventArgs e)
{
// 自动保存配置
_config.SetSourcePathsFromText(SourcePathsTextBox.Text);
_config.DestPath = DestPathTextBox.Text;
_config.Save();
base.OnClosed(e);
}
}
}

87
README.md Normal file
View File

@@ -0,0 +1,87 @@
# ROU文件复制工具
这是一个基于WPF的C#应用程序,用于根据编号和版本匹配文件夹,然后复制其中的.rou文件到目标位置。
## 功能特性
- 根据输入的料号(如"27362a")自动匹配特定格式的文件夹
- 批量处理多个料号
- 复制匹配文件夹中的.rou、.rou1、.rou2、.rou3文件
- 实时显示处理进度和日志信息
- 配置管理(源路径、目标路径)
- 简洁易用的用户界面
## 使用方法
### 1. 配置路径
- **源路径**:在"源路径"文本框中输入要搜索的根目录,每行一个路径
- 默认路径:
- `Z:\Routing\A1-ROUTING\NEW-ROUTING`
- `Z:\Routing\A2-ROUTING\NEW-ROUTING`
- **目标路径**:在"目标路径"文本框中输入复制文件的目标位置
- 默认路径:`D:\ARPTWork\rou`
### 2. 输入料号
在"料号输入"文本框中输入要处理的料号,每行一个,例如:
```
27362a
27363b
27364c
```
### 3. 开始处理
点击"开始处理"按钮,应用程序将:
1. 根据每个料号的前5位作为数字部分剩余部分作为版本部分
2. 在源路径中查找匹配的文件夹
3. 复制找到的.rou系列文件到目标路径
4. 显示处理进度和详细日志
### 4. 其他功能
- **清空**:清空输入的料号和日志
- **保存配置**:保存当前的路径配置
- **添加**:通过文件夹浏览器添加源路径
- **浏览**:通过文件夹浏览器选择目标路径
## 文件夹匹配规则
应用程序使用以下规则匹配文件夹:
- 料号格式前5位为数字部分剩余部分为版本部分
- 文件夹匹配从文件夹名第4个字符开始匹配数字编号
- 搜索范围:在源路径下的数字编号文件夹中查找匹配的子文件夹
## 文件复制规则
- 只处理以"ED"或"ROU"开头的文件夹
- 复制扩展名为.rou、.rou1、.rou2、.rou3的文件
- 目标文件名格式:`{数字部分}_{文件夹名}_{原文件名}`
## 技术架构
- **框架**.NET 6.0 WPF
- **架构模式**:分层架构,使用接口和依赖注入
- **主要组件**
- `FileService`:核心文件操作逻辑
- `AppConfig`:配置管理
- `Logger`:日志记录
- `MainWindow`:用户界面
## 构建和运行
```bash
# 构建项目
dotnet build
# 运行应用程序
dotnet run
```
## 注意事项
- 确保有足够的权限访问源路径和目标路径
- 目标路径会自动创建(如果不存在)
- 处理过程中请勿关闭应用程序
- 配置会自动保存,下次启动时会自动加载

108
demo/copy_rou.py Normal file
View File

@@ -0,0 +1,108 @@
import os
import shutil
SOURCE_PATHS = [
r"Z:\Routing\A1-ROUTING\NEW-ROUTING",
r"Z:\Routing\A2-ROUTING\NEW-ROUTING"
]
DEST_PATH = r"D:\ARPTWork\rou"
def match_folder_name(folder_name, num_part, version_part):
"""匹配逻辑:从第四个字符开始匹配数字编号"""
folder_name_lower = folder_name.lower()
num_length = len(num_part)
if len(folder_name_lower) < 3 + num_length:
return False
num_in_name = folder_name_lower[4 : 4+num_length]
if num_in_name != num_part.lower():
return False
version_in_name = folder_name_lower[4+num_length:]
return version_in_name.startswith(version_part.lower())
def copy_rou_files(source_folder, number):
"""复制所有.rou系列文件"""
os.makedirs(DEST_PATH, exist_ok=True)
copied_files = []
ed_folders_found = 0
for item in os.listdir(source_folder):
item_path = os.path.join(source_folder, item)
if os.path.isdir(item_path) and (item.lower().startswith('ed') or item.lower().startswith('rou')):
ed_folders_found += 1
print(f"正在处理ED文件夹: {item}")
for root, _, files in os.walk(item_path):
for file in files:
if file.lower().endswith(('.rou', '.rou1', '.rou2', '.rou3')):
src_file = os.path.join(root, file)
base_name = f"{number}_{item}_{file}"
dest_file = os.path.join(DEST_PATH, base_name)
shutil.copy2(src_file, dest_file)
copied_files.append(base_name)
if ed_folders_found == 0:
print("警告: 未找到任何以ED开头的文件夹")
elif not copied_files:
print("警告: 在ED文件夹中未找到任何.rou系列文件")
else:
print(f"成功从 {ed_folders_found} 个ED文件夹复制 {len(copied_files)} 个文件:")
def process_codes(code_list):
"""处理多个代码"""
for code in code_list:
code = code.strip()
if not code:
continue
print(f"\n处理编号: {code}")
num_part = code[:5]
version_part = code[len(num_part):]
found = False
for source_root in SOURCE_PATHS:
num_folder = os.path.join(source_root, num_part)
if not os.path.exists(num_folder):
continue
for folder in os.listdir(num_folder):
if match_folder_name(folder, num_part, version_part):
full_path = os.path.join(num_folder, folder)
print(f"找到匹配文件夹: {full_path}")
copy_rou_files(full_path, num_part)
found = True
if not found:
print("未找到匹配的文件夹")
def main():
print("=== 文件复制工具 ===")
print(f"搜索路径: {SOURCE_PATHS}")
print(f"目标路径: {DEST_PATH}")
print("输入说明:")
print("1. 直接输入单个编号,如: 27362a")
print("2. 输入多行编号(每行一个)最后输入end结束")
print("3. 输入exit退出程序")
while True:
print("\n请输入编号(支持多行输入):")
user_inputs = []
while True:
line = input().strip()
if line.lower() == 'end':
break
if line.lower() == 'exit':
return
if line:
user_inputs.append(line)
if user_inputs:
process_codes(user_inputs)
if __name__ == "__main__":
main()