软件雏形
This commit is contained in:
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"python-envs.defaultEnvManager": "ms-python.python:conda",
|
||||
"python-envs.defaultPackageManager": "ms-python.python:conda",
|
||||
"python-envs.pythonProjects": []
|
||||
}
|
||||
@@ -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
83
AppConfig.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
151
FileService.cs
Normal 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
12
IFileService.cs
Normal 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
11
ILogger.cs
Normal 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
32
Logger.cs
Normal 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]");
|
||||
}
|
||||
}
|
||||
}
|
||||
221
MainWindow.xaml
221
MainWindow.xaml
@@ -1,12 +1,215 @@
|
||||
<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>
|
||||
<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:local="clr-namespace:CopyRou"
|
||||
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>
|
||||
@@ -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
87
README.md
Normal 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
108
demo/copy_rou.py
Normal 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()
|
||||
Reference in New Issue
Block a user