This commit is contained in:
zpc 2026-01-06 01:12:59 +08:00
parent 914cda7219
commit 0e5e9fb140
2 changed files with 240 additions and 11 deletions

View File

@ -30,6 +30,11 @@ namespace WorkCameraExport.Forms
{
components = new System.ComponentModel.Container();
// 图片右键菜单
contextMenuImage = new ContextMenuStrip(components);
menuItemDownloadImage = new ToolStripMenuItem();
menuItemCopyUrl = new ToolStripMenuItem();
// 主面板
panelMain = new Panel();
@ -365,6 +370,7 @@ namespace WorkCameraExport.Forms
btnAdd.Text = " 新增";
btnAdd.UseVisualStyleBackColor = false;
btnAdd.Click += btnAdd_Click;
btnAdd.Enabled = false; // 禁用新增按钮
//
// btnExportAll
@ -471,6 +477,7 @@ namespace WorkCameraExport.Forms
dgvWorkRecords.CellClick += dgvWorkRecords_CellClick;
dgvWorkRecords.CellContentClick += dgvWorkRecords_CellContentClick;
dgvWorkRecords.CellPainting += dgvWorkRecords_CellPainting;
dgvWorkRecords.CellMouseClick += dgvWorkRecords_CellMouseClick;
//
// colSelect - 选择列
@ -560,6 +567,7 @@ namespace WorkCameraExport.Forms
colOperations.Text = "编辑";
colOperations.UseColumnTextForButtonValue = true;
colOperations.Width = 70;
colOperations.ReadOnly = true; // 禁用编辑列
//
// panelPaging - 分页控件区域
@ -760,6 +768,29 @@ namespace WorkCameraExport.Forms
//
// WorkRecordForm
//
// contextMenuImage
//
contextMenuImage.Items.AddRange(new ToolStripItem[] { menuItemDownloadImage, menuItemCopyUrl });
contextMenuImage.Name = "contextMenuImage";
contextMenuImage.Size = new Size(150, 48);
//
// menuItemDownloadImage
//
menuItemDownloadImage.Name = "menuItemDownloadImage";
menuItemDownloadImage.Size = new Size(149, 22);
menuItemDownloadImage.Text = "下载图片";
menuItemDownloadImage.Click += menuItemDownloadImage_Click;
//
// menuItemCopyUrl
//
menuItemCopyUrl.Name = "menuItemCopyUrl";
menuItemCopyUrl.Size = new Size(149, 22);
menuItemCopyUrl.Text = "复制URL";
menuItemCopyUrl.Click += menuItemCopyUrl_Click;
//
AutoScaleDimensions = new SizeF(7F, 17F);
AutoScaleMode = AutoScaleMode.Font;
@ -782,6 +813,7 @@ namespace WorkCameraExport.Forms
panelPaging.ResumeLayout(false);
panelPaging.PerformLayout();
panelProgress.ResumeLayout(false);
contextMenuImage.ResumeLayout(false);
ResumeLayout(false);
}
@ -850,5 +882,10 @@ namespace WorkCameraExport.Forms
private ProgressBar progressBar;
private Label lblProgress;
private Button btnCancelExport;
// 图片右键菜单
private ContextMenuStrip contextMenuImage;
private ToolStripMenuItem menuItemDownloadImage;
private ToolStripMenuItem menuItemCopyUrl;
}
}

View File

@ -249,7 +249,7 @@ namespace WorkCameraExport.Forms
/// <summary>
/// 单元格绘制事件 - 用于绘制图片缩略图
/// </summary>
private async void dgvWorkRecords_CellPainting(object? sender, DataGridViewCellPaintingEventArgs e)
private void dgvWorkRecords_CellPainting(object? sender, DataGridViewCellPaintingEventArgs e)
{
// 只处理图片列
if (e.ColumnIndex != colImages.Index || e.RowIndex < 0)
@ -272,7 +272,6 @@ namespace WorkCameraExport.Forms
var startX = cellBounds.X + 5;
var startY = cellBounds.Y + (cellBounds.Height - ThumbnailHeight) / 2;
// 绘制缩略图占位符
using var placeholderBrush = new SolidBrush(Color.FromArgb(230, 230, 230));
using var borderPen = new Pen(Color.FromArgb(200, 200, 200));
using var textBrush = new SolidBrush(Color.FromArgb(102, 102, 102));
@ -283,16 +282,35 @@ namespace WorkCameraExport.Forms
var x = startX + i * (ThumbnailWidth + ThumbnailSpacing);
var rect = new Rectangle(x, startY, ThumbnailWidth, ThumbnailHeight);
var imageUrl = record.Images[i].ImageUrl;
// 检查缓存中是否有图片
if (_thumbnailCache.TryGetValue(imageUrl, out var cachedImage) && cachedImage != null)
{
// 绘制缓存的图片
e.Graphics.DrawImage(cachedImage, rect);
e.Graphics.DrawRectangle(borderPen, rect);
}
else
{
// 绘制占位符背景
e.Graphics.FillRectangle(placeholderBrush, rect);
e.Graphics.DrawRectangle(borderPen, rect);
// 绘制图片序号
var text = $"图{i + 1}";
// 绘制加载中文字
var text = "加载中";
var textSize = e.Graphics.MeasureString(text, font);
var textX = x + (ThumbnailWidth - textSize.Width) / 2;
var textY = startY + (ThumbnailHeight - textSize.Height) / 2;
e.Graphics.DrawString(text, font, textBrush, textX, textY);
// 异步加载图片(只触发一次)
if (!_thumbnailCache.ContainsKey(imageUrl))
{
_thumbnailCache[imageUrl] = null; // 标记为正在加载
_ = LoadThumbnailAsync(imageUrl, e.RowIndex);
}
}
}
// 绘制徽章(显示剩余图片数量)
@ -316,6 +334,58 @@ namespace WorkCameraExport.Forms
e.Handled = true;
}
/// <summary>
/// 异步加载缩略图
/// </summary>
private async Task LoadThumbnailAsync(string imageUrl, int rowIndex)
{
try
{
var (success, imageData, _) = await _apiService.DownloadImageAsync(imageUrl);
if (success && imageData != null && imageData.Length > 0)
{
using var ms = new MemoryStream(imageData);
var originalImage = Image.FromStream(ms);
// 创建缩略图
var thumbnail = new Bitmap(ThumbnailWidth, ThumbnailHeight);
using (var g = Graphics.FromImage(thumbnail))
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImage(originalImage, 0, 0, ThumbnailWidth, ThumbnailHeight);
}
originalImage.Dispose();
_thumbnailCache[imageUrl] = thumbnail;
// 刷新对应行
if (InvokeRequired)
{
Invoke(() => InvalidateRow(rowIndex));
}
else
{
InvalidateRow(rowIndex);
}
}
}
catch (Exception ex)
{
_logService?.Warn($"加载缩略图失败: {imageUrl}, {ex.Message}");
}
}
/// <summary>
/// 刷新指定行
/// </summary>
private void InvalidateRow(int rowIndex)
{
if (rowIndex >= 0 && rowIndex < dgvWorkRecords.Rows.Count)
{
dgvWorkRecords.InvalidateRow(rowIndex);
}
}
/// <summary>
/// 单元格点击事件 - 处理图片点击打开查看器
/// </summary>
@ -345,6 +415,128 @@ namespace WorkCameraExport.Forms
}
}
// 当前右键选中的图片信息
private string? _contextMenuImageUrl;
private int _contextMenuRowIndex = -1;
/// <summary>
/// 单元格鼠标点击事件 - 处理右键菜单
/// </summary>
private void dgvWorkRecords_CellMouseClick(object? sender, DataGridViewCellMouseEventArgs e)
{
if (e.RowIndex < 0 || e.Button != MouseButtons.Right)
return;
// 只处理图片列的右键
if (e.ColumnIndex == colImages.Index)
{
var row = dgvWorkRecords.Rows[e.RowIndex];
var record = row.Tag as WorkRecordDto;
if (record?.Images != null && record.Images.Count > 0)
{
// 计算点击的是哪张图片
var cellBounds = dgvWorkRecords.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false);
var clickX = e.X;
var imageIndex = clickX / (ThumbnailWidth + ThumbnailSpacing);
if (imageIndex >= 0 && imageIndex < record.Images.Count)
{
_contextMenuImageUrl = record.Images[imageIndex].ImageUrl;
_contextMenuRowIndex = e.RowIndex;
// 显示右键菜单
var screenPoint = dgvWorkRecords.PointToScreen(new Point(
cellBounds.X + e.X,
cellBounds.Y + e.Y));
contextMenuImage.Show(screenPoint);
}
}
}
}
/// <summary>
/// 下载图片菜单项点击
/// </summary>
private async void menuItemDownloadImage_Click(object? sender, EventArgs e)
{
if (string.IsNullOrEmpty(_contextMenuImageUrl))
return;
try
{
// 创建 image 目录
var imageDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "image");
if (!Directory.Exists(imageDir))
{
Directory.CreateDirectory(imageDir);
}
// 生成文件名
var fileName = Path.GetFileName(_contextMenuImageUrl);
if (string.IsNullOrEmpty(fileName) || !fileName.Contains('.'))
{
fileName = $"image_{DateTime.Now:yyyyMMdd_HHmmss}.jpg";
}
var savePath = Path.Combine(imageDir, fileName);
// 确保文件名唯一
var counter = 1;
var baseName = Path.GetFileNameWithoutExtension(savePath);
var ext = Path.GetExtension(savePath);
while (File.Exists(savePath))
{
savePath = Path.Combine(imageDir, $"{baseName}_{counter}{ext}");
counter++;
}
lblStatusBar.Text = "正在下载图片...";
lblStatusBar.ForeColor = Color.Blue;
var (success, imageData, message) = await _apiService.DownloadImageAsync(_contextMenuImageUrl);
if (success && imageData != null)
{
await File.WriteAllBytesAsync(savePath, imageData);
lblStatusBar.Text = $"图片已保存: {savePath}";
lblStatusBar.ForeColor = Color.Green;
_logService?.Info($"图片下载成功: {savePath}");
}
else
{
lblStatusBar.Text = $"下载失败: {message}";
lblStatusBar.ForeColor = Color.Red;
}
}
catch (Exception ex)
{
lblStatusBar.Text = $"下载失败: {ex.Message}";
lblStatusBar.ForeColor = Color.Red;
_logService?.Error($"下载图片失败: {ex.Message}", ex);
}
}
/// <summary>
/// 复制URL菜单项点击
/// </summary>
private void menuItemCopyUrl_Click(object? sender, EventArgs e)
{
if (string.IsNullOrEmpty(_contextMenuImageUrl))
return;
try
{
Clipboard.SetText(_contextMenuImageUrl);
lblStatusBar.Text = "URL已复制到剪贴板";
lblStatusBar.ForeColor = Color.Green;
}
catch (Exception ex)
{
lblStatusBar.Text = $"复制失败: {ex.Message}";
lblStatusBar.ForeColor = Color.Red;
}
}
/// <summary>
/// 单元格内容点击事件 - 处理操作按钮
/// </summary>