using WorkCameraExport.Models;
using WorkCameraExport.Services;
namespace WorkCameraExport.Forms
{
///
/// 主窗体 - 数据导出功能
///
public partial class MainForm : Form
{
private readonly ApiService _apiService;
private readonly AppSettings _settings;
private CancellationTokenSource? _exportCts;
private bool _isExporting;
// 预览数据
private int _totalRecords;
private int _totalImages;
public MainForm(ApiService apiService)
{
InitializeComponent();
_apiService = apiService;
_settings = AppSettings.Load();
InitializeDefaults();
}
///
/// 初始化默认值
///
private void InitializeDefaults()
{
// 设置默认日期范围(最近30天)
dtpEndDate.Value = DateTime.Today;
dtpStartDate.Value = DateTime.Today.AddDays(-30);
// 设置默认导出路径
if (!string.IsNullOrEmpty(_settings.DefaultExportPath) &&
Directory.Exists(_settings.DefaultExportPath))
{
txtExportPath.Text = _settings.DefaultExportPath;
}
else
{
txtExportPath.Text = PathService.ExportDirectory;
}
UpdateExportButtonState();
}
///
/// 预览按钮点击事件
///
private async void btnPreview_Click(object sender, EventArgs e)
{
await DoPreviewAsync();
}
///
/// 执行预览查询
///
private async Task DoPreviewAsync()
{
SetQueryControlsEnabled(false);
lblStatus.Text = "正在查询...";
lblStatus.ForeColor = Color.Blue;
dgvPreview.Rows.Clear();
_totalRecords = 0;
_totalImages = 0;
try
{
var query = BuildQuery();
query.PageNum = 1;
query.PageSize = 50; // 预览最多显示50条
var (success, message, data) = await _apiService.GetExportListAsync(query);
if (success && data != null)
{
_totalRecords = data.TotalNum;
// 计算图片总数(需要遍历所有记录)
_totalImages = await CalculateTotalImagesAsync(query, data.TotalNum);
// 显示预览数据
foreach (var record in data.Result)
{
dgvPreview.Rows.Add(
record.Id,
record.RecordTime?.ToString("yyyy-MM-dd HH:mm") ?? "",
record.DeptName,
TruncateContent(record.Content, 30),
record.Images.Count
);
}
// 更新统计信息
lblTotalRecords.Text = $"共 {_totalRecords} 条记录";
lblTotalImages.Text = $"约 {_totalImages} 张图片";
lblStatus.Text = $"查询完成,共 {_totalRecords} 条记录";
lblStatus.ForeColor = Color.Green;
}
else
{
ShowError(message);
}
}
catch (Exception ex)
{
ShowError($"查询异常: {ex.Message}");
}
finally
{
SetQueryControlsEnabled(true);
UpdateExportButtonState();
}
}
///
/// 计算图片总数
///
private async Task CalculateTotalImagesAsync(WorkRecordExportQuery query, int totalRecords)
{
if (totalRecords <= 50)
{
// 如果记录数不多,直接从预览数据计算
var (success, _, data) = await _apiService.GetExportListAsync(query);
if (success && data != null)
{
return data.Result.Sum(r => r.Images.Count);
}
}
// 估算:假设平均每条记录2张图片
return totalRecords * 2;
}
///
/// 构建查询参数
///
private WorkRecordExportQuery BuildQuery()
{
return new WorkRecordExportQuery
{
StartDate = dtpStartDate.Value.Date,
EndDate = dtpEndDate.Value.Date.AddDays(1).AddSeconds(-1),
DeptName = string.IsNullOrWhiteSpace(txtDeptName.Text) ? null : txtDeptName.Text.Trim(),
WorkerName = string.IsNullOrWhiteSpace(txtWorkerName.Text) ? null : txtWorkerName.Text.Trim(),
Content = string.IsNullOrWhiteSpace(txtContent.Text) ? null : txtContent.Text.Trim()
};
}
///
/// 浏览导出路径
///
private void btnBrowse_Click(object sender, EventArgs e)
{
using var dialog = new FolderBrowserDialog
{
Description = "选择导出目录",
UseDescriptionForTitle = true,
SelectedPath = txtExportPath.Text
};
if (dialog.ShowDialog() == DialogResult.OK)
{
txtExportPath.Text = dialog.SelectedPath;
_settings.DefaultExportPath = dialog.SelectedPath;
_settings.Save();
}
}
///
/// 导出按钮点击事件
///
private async void btnExport_Click(object sender, EventArgs e)
{
if (_isExporting)
{
// 取消导出
_exportCts?.Cancel();
return;
}
if (_totalRecords == 0)
{
MessageBox.Show("请先预览数据", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
if (string.IsNullOrWhiteSpace(txtExportPath.Text))
{
MessageBox.Show("请选择导出目录", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
if (!Directory.Exists(txtExportPath.Text))
{
MessageBox.Show("导出目录不存在", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
await DoExportAsync();
}
///
/// 执行导出
///
private async Task DoExportAsync()
{
_isExporting = true;
_exportCts = new CancellationTokenSource();
SetExportingState(true);
var exportPath = Path.Combine(
txtExportPath.Text,
$"工作记录导出_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx");
try
{
var query = BuildQuery();
var excelService = new ExcelService();
var allRecords = new List();
var downloadedImages = new Dictionary();
// 阶段1:获取所有数据
lblStatus.Text = "正在获取数据...";
progressBar.Value = 0;
int pageNum = 1;
int totalPages = (int)Math.Ceiling(_totalRecords / 50.0);
while (true)
{
if (_exportCts.Token.IsCancellationRequested)
{
lblStatus.Text = "导出已取消";
lblStatus.ForeColor = Color.Orange;
return;
}
query.PageNum = pageNum;
query.PageSize = 50;
var (success, message, data) = await _apiService.GetExportListAsync(query);
if (!success || data == null)
{
throw new Exception($"获取数据失败: {message}");
}
allRecords.AddRange(data.Result);
// 更新进度
int progress = (int)((pageNum * 100.0) / totalPages * 0.3); // 数据获取占30%
progressBar.Value = Math.Min(progress, 30);
lblProgress.Text = $"获取数据: {pageNum}/{totalPages} 页";
if (data.Result.Count < 50 || pageNum >= data.TotalPage)
break;
pageNum++;
}
// 阶段2:下载图片
lblStatus.Text = "正在下载图片...";
var allImageUrls = allRecords.SelectMany(r => r.Images).Distinct().ToList();
int downloadedCount = 0;
int totalImages = allImageUrls.Count;
// 使用信号量限制并发数为5
using var semaphore = new SemaphoreSlim(5);
var downloadTasks = allImageUrls.Select(async url =>
{
await semaphore.WaitAsync(_exportCts.Token);
try
{
if (_exportCts.Token.IsCancellationRequested)
return;
var (success, imageData, _) = await _apiService.DownloadImageAsync(url, _exportCts.Token);
if (success && imageData != null)
{
lock (downloadedImages)
{
downloadedImages[url] = imageData;
}
}
Interlocked.Increment(ref downloadedCount);
// 更新进度(在UI线程)
this.Invoke(() =>
{
int progress = 30 + (int)((downloadedCount * 100.0) / totalImages * 0.5); // 图片下载占50%
progressBar.Value = Math.Min(progress, 80);
lblProgress.Text = $"下载图片: {downloadedCount}/{totalImages}";
});
}
finally
{
semaphore.Release();
}
});
await Task.WhenAll(downloadTasks);
if (_exportCts.Token.IsCancellationRequested)
{
lblStatus.Text = "导出已取消";
lblStatus.ForeColor = Color.Orange;
return;
}
// 阶段3:生成Excel
lblStatus.Text = "正在生成Excel...";
progressBar.Value = 80;
lblProgress.Text = "生成Excel文件...";
await Task.Run(() =>
{
excelService.GenerateExcel(exportPath, allRecords, downloadedImages,
(current, total) =>
{
this.Invoke(() =>
{
int progress = 80 + (int)((current * 100.0) / total * 0.2); // Excel生成占20%
progressBar.Value = Math.Min(progress, 100);
lblProgress.Text = $"生成Excel: {current}/{total}";
});
});
}, _exportCts.Token);
// 完成
progressBar.Value = 100;
lblProgress.Text = "导出完成";
lblStatus.Text = $"导出完成: {exportPath}";
lblStatus.ForeColor = Color.Green;
// 打开文件所在目录
if (MessageBox.Show("导出完成!是否打开文件所在目录?", "提示",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{exportPath}\"");
}
}
catch (OperationCanceledException)
{
lblStatus.Text = "导出已取消";
lblStatus.ForeColor = Color.Orange;
}
catch (Exception ex)
{
ShowError($"导出失败: {ex.Message}");
}
finally
{
_isExporting = false;
_exportCts?.Dispose();
_exportCts = null;
SetExportingState(false);
}
}
///
/// 设置查询控件启用状态
///
private void SetQueryControlsEnabled(bool enabled)
{
dtpStartDate.Enabled = enabled;
dtpEndDate.Enabled = enabled;
txtDeptName.Enabled = enabled;
txtWorkerName.Enabled = enabled;
txtContent.Enabled = enabled;
btnPreview.Enabled = enabled;
}
///
/// 设置导出状态
///
private void SetExportingState(bool exporting)
{
SetQueryControlsEnabled(!exporting);
txtExportPath.Enabled = !exporting;
btnBrowse.Enabled = !exporting;
if (exporting)
{
btnExport.Text = "取消导出";
btnExport.BackColor = Color.FromArgb(220, 53, 69);
}
else
{
btnExport.Text = "导出Excel";
btnExport.BackColor = Color.FromArgb(40, 167, 69);
progressBar.Value = 0;
lblProgress.Text = "";
}
}
///
/// 更新导出按钮状态
///
private void UpdateExportButtonState()
{
btnExport.Enabled = _totalRecords > 0 || _isExporting;
}
///
/// 显示错误信息
///
private void ShowError(string message)
{
lblStatus.Text = message;
lblStatus.ForeColor = Color.Red;
}
///
/// 截断内容
///
private static string TruncateContent(string content, int maxLength)
{
if (string.IsNullOrEmpty(content)) return "";
return content.Length <= maxLength ? content : content[..maxLength] + "...";
}
///
/// 窗体关闭事件
///
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (_isExporting)
{
var result = MessageBox.Show("正在导出中,确定要取消并退出吗?", "确认",
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result == DialogResult.No)
{
e.Cancel = true;
return;
}
_exportCts?.Cancel();
}
}
///
/// 历史数据迁移按钮点击事件
///
private void btnMigration_Click(object sender, EventArgs e)
{
if (_isExporting)
{
MessageBox.Show("正在导出中,请等待导出完成后再进行迁移操作", "提示",
MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
using var migrationForm = new MigrationForm(_apiService);
migrationForm.ShowDialog(this);
}
}
}