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, null); migrationForm.ShowDialog(this); } } }