From 4fb70310609364e270f2bea27cd48a588279632a Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sun, 2 Nov 2025 10:11:15 +0800
Subject: [PATCH] =?UTF-8?q?add:Dlass=E8=81=94=E5=8A=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Helpers/DlassApiClient.cs | 82 +++++++
Ink Canvas/Helpers/DlassNoteUploader.cs | 200 ++++++++++++++++++
.../MainWindow_cs/MW_Save&OpenStrokes.cs | 18 ++
Ink Canvas/MainWindow_cs/MW_Screenshot.cs | 24 +++
Ink Canvas/Resources/Settings.cs | 6 +
Ink Canvas/Windows/DlassSettingsWindow.xaml | 116 ++++++++--
.../Windows/DlassSettingsWindow.xaml.cs | 96 +++++++++
7 files changed, 521 insertions(+), 21 deletions(-)
create mode 100644 Ink Canvas/Helpers/DlassNoteUploader.cs
diff --git a/Ink Canvas/Helpers/DlassApiClient.cs b/Ink Canvas/Helpers/DlassApiClient.cs
index 438c4947..61a04f42 100644
--- a/Ink Canvas/Helpers/DlassApiClient.cs
+++ b/Ink Canvas/Helpers/DlassApiClient.cs
@@ -1,7 +1,9 @@
using Ink_Canvas.Helpers;
using Newtonsoft.Json;
using System;
+using System.IO;
using System.Net.Http;
+using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
@@ -343,6 +345,86 @@ namespace Ink_Canvas.Helpers
}
}
+ ///
+ /// 上传笔记文件
+ ///
+ /// 上传端点
+ /// 文件路径
+ /// 白板ID
+ /// 白板密钥
+ /// 笔记标题(可选)
+ /// 笔记描述(可选)
+ /// 笔记标签(可选)
+ public async Task UploadNoteAsync(string endpoint, string filePath, string boardId, string secretKey, string title = null, string description = null, string tags = null)
+ {
+ try
+ {
+ if (!File.Exists(filePath))
+ {
+ throw new FileNotFoundException($"文件不存在: {filePath}");
+ }
+
+ var request = new HttpRequestMessage(HttpMethod.Post, endpoint);
+
+ // 设置白板认证头
+ request.Headers.Add("X-Board-ID", boardId);
+ request.Headers.Add("X-Secret-Key", secretKey);
+
+ // 创建multipart/form-data内容
+ var content = new MultipartFormDataContent();
+
+ // 添加文件
+ var fileContent = new ByteArrayContent(File.ReadAllBytes(filePath));
+ var fileName = Path.GetFileName(filePath);
+ fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
+ content.Add(fileContent, "file", fileName);
+
+ // 添加可选参数
+ if (!string.IsNullOrEmpty(title))
+ {
+ content.Add(new StringContent(title), "title");
+ }
+ if (!string.IsNullOrEmpty(description))
+ {
+ content.Add(new StringContent(description), "description");
+ }
+ if (!string.IsNullOrEmpty(tags))
+ {
+ content.Add(new StringContent(tags), "tags");
+ }
+
+ request.Content = content;
+
+ var response = await _httpClient.SendAsync(request);
+ var responseContent = await response.Content.ReadAsStringAsync();
+
+ if (response.IsSuccessStatusCode)
+ {
+ if (string.IsNullOrEmpty(responseContent))
+ {
+ return default(T);
+ }
+ return JsonConvert.DeserializeObject(responseContent);
+ }
+ else
+ {
+ throw new Exception($"上传文件失败: {response.StatusCode} - {responseContent}");
+ }
+ }
+ catch (HttpRequestException httpEx)
+ {
+ throw new Exception($"上传文件时网络错误: {httpEx.Message}", httpEx);
+ }
+ catch (TaskCanceledException timeoutEx)
+ {
+ throw new Exception($"上传文件超时: {endpoint}", timeoutEx);
+ }
+ catch (Exception ex)
+ {
+ throw new Exception($"上传文件时出错: {ex.Message}", ex);
+ }
+ }
+
///
/// 释放资源
///
diff --git a/Ink Canvas/Helpers/DlassNoteUploader.cs b/Ink Canvas/Helpers/DlassNoteUploader.cs
new file mode 100644
index 00000000..ed08fece
--- /dev/null
+++ b/Ink Canvas/Helpers/DlassNoteUploader.cs
@@ -0,0 +1,200 @@
+using Ink_Canvas.Helpers;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Ink_Canvas.Helpers
+{
+ ///
+ /// Dlass笔记自动上传辅助类
+ ///
+ public class DlassNoteUploader
+ {
+ private const string APP_ID = "app_WkjocWqsrVY7T6zQV2CfiA";
+ private const string APP_SECRET = "o7dx5b5ASGUMcM72PCpmRQYAhSijqaOVHoGyBK0IxbA";
+
+ ///
+ /// 上传笔记响应模型
+ ///
+ public class UploadNoteResponse
+ {
+ [JsonProperty("success")]
+ public bool Success { get; set; }
+
+ [JsonProperty("message")]
+ public string Message { get; set; }
+
+ [JsonProperty("note_id")]
+ public int? NoteId { get; set; }
+
+ [JsonProperty("filename")]
+ public string Filename { get; set; }
+
+ [JsonProperty("file_path")]
+ public string FilePath { get; set; }
+
+ [JsonProperty("file_url")]
+ public string FileUrl { get; set; }
+ }
+
+ ///
+ /// 白板信息模型(用于查找白板)
+ ///
+ private class WhiteboardInfo
+ {
+ [JsonProperty("id")]
+ public int Id { get; set; }
+
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("board_id")]
+ public string BoardId { get; set; }
+
+ [JsonProperty("secret_key")]
+ public string SecretKey { get; set; }
+
+ [JsonProperty("class_name")]
+ public string ClassName { get; set; }
+
+ [JsonProperty("class_id")]
+ public int ClassId { get; set; }
+ }
+
+ ///
+ /// 认证响应模型
+ ///
+ private class AuthWithTokenResponse
+ {
+ [JsonProperty("success")]
+ public bool Success { get; set; }
+
+ [JsonProperty("whiteboards")]
+ public List Whiteboards { get; set; }
+ }
+
+ ///
+ /// 异步上传PNG文件到Dlass
+ ///
+ /// PNG文件路径
+ /// 是否上传成功
+ public static async Task UploadPngNoteAsync(string pngFilePath)
+ {
+ try
+ {
+ // 检查是否启用自动上传
+ if (MainWindow.Settings?.Dlass?.IsAutoUploadNotes != true)
+ {
+ return false;
+ }
+
+ // 检查文件是否存在
+ if (!File.Exists(pngFilePath))
+ {
+ LogHelper.WriteLogToFile($"上传失败:文件不存在 - {pngFilePath}", LogHelper.LogType.Error);
+ return false;
+ }
+
+ // 检查文件大小(最大10MB)
+ var fileInfo = new FileInfo(pngFilePath);
+ if (fileInfo.Length > 10 * 1024 * 1024)
+ {
+ LogHelper.WriteLogToFile($"上传失败:文件过大({fileInfo.Length / 1024 / 1024}MB),超过10MB限制", LogHelper.LogType.Error);
+ return false;
+ }
+
+ // 获取设置的班级名称
+ var selectedClassName = MainWindow.Settings?.Dlass?.SelectedClassName;
+ if (string.IsNullOrEmpty(selectedClassName))
+ {
+ LogHelper.WriteLogToFile("上传失败:未选择班级", LogHelper.LogType.Error);
+ return false;
+ }
+
+ // 获取用户Token
+ var userToken = MainWindow.Settings?.Dlass?.UserToken;
+ if (string.IsNullOrEmpty(userToken))
+ {
+ LogHelper.WriteLogToFile("上传失败:未设置用户Token", LogHelper.LogType.Error);
+ return false;
+ }
+
+ // 获取API基础URL
+ var apiBaseUrl = MainWindow.Settings?.Dlass?.ApiBaseUrl ?? "https://dlass.tech";
+
+ // 创建API客户端并获取白板信息
+ DlassApiClient apiClient = null;
+ try
+ {
+ apiClient = new DlassApiClient(APP_ID, APP_SECRET, apiBaseUrl, userToken);
+
+ // 调用认证接口获取白板列表
+ var authData = new
+ {
+ app_id = APP_ID,
+ app_secret = APP_SECRET,
+ user_token = userToken
+ };
+
+ var authResult = await apiClient.PostAsync("/api/whiteboard/framework/auth-with-token", authData, requireAuth: false);
+
+ if (authResult == null || !authResult.Success || authResult.Whiteboards == null)
+ {
+ LogHelper.WriteLogToFile("上传失败:无法获取白板信息", LogHelper.LogType.Error);
+ return false;
+ }
+
+ // 查找匹配班级的白板
+ var whiteboard = authResult.Whiteboards
+ .FirstOrDefault(w => !string.IsNullOrEmpty(w.ClassName) && w.ClassName == selectedClassName);
+
+ if (whiteboard == null || string.IsNullOrEmpty(whiteboard.BoardId) || string.IsNullOrEmpty(whiteboard.SecretKey))
+ {
+ LogHelper.WriteLogToFile($"上传失败:未找到班级'{selectedClassName}'对应的白板", LogHelper.LogType.Error);
+ return false;
+ }
+
+ // 准备上传参数
+ var fileName = Path.GetFileNameWithoutExtension(pngFilePath);
+ var title = fileName;
+ var description = $"自动上传的笔记 - {DateTime.Now:yyyy-MM-dd HH:mm:ss}";
+ var tags = "自动上传,笔记";
+
+ // 上传文件
+ var uploadResult = await apiClient.UploadNoteAsync(
+ "/api/whiteboard/upload_note",
+ pngFilePath,
+ whiteboard.BoardId,
+ whiteboard.SecretKey,
+ title,
+ description,
+ tags);
+
+ if (uploadResult != null && uploadResult.Success)
+ {
+ LogHelper.WriteLogToFile($"笔记上传成功:{fileName} -> {uploadResult.FileUrl}", LogHelper.LogType.Event);
+ return true;
+ }
+ else
+ {
+ LogHelper.WriteLogToFile($"上传失败:服务器响应失败 - {uploadResult?.Message ?? "未知错误"}", LogHelper.LogType.Error);
+ return false;
+ }
+ }
+ finally
+ {
+ apiClient?.Dispose();
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"上传笔记时出错: {ex.Message}", LogHelper.LogType.Error);
+ return false;
+ }
+ }
+ }
+}
+
diff --git a/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs b/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs
index b0aae681..bcbc487c 100644
--- a/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs
@@ -7,6 +7,7 @@ using System.Drawing.Imaging;
using System.IO;
using System.IO.Compression;
using System.Text;
+using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms;
@@ -310,6 +311,23 @@ namespace Ink_Canvas
var fs = new FileStream(savePathWithName, FileMode.Create);
inkCanvas.Strokes.Save(fs);
fs.Close();
+
+ _ = Task.Run(async () =>
+ {
+ try
+ {
+ var delayMinutes = Settings?.Dlass?.AutoUploadDelayMinutes ?? 0;
+ if (delayMinutes > 0)
+ {
+ await Task.Delay(TimeSpan.FromMinutes(delayMinutes));
+ }
+
+ await Helpers.DlassNoteUploader.UploadPngNoteAsync(imagePathWithName);
+ }
+ catch (Exception)
+ {
+ }
+ });
}
}
diff --git a/Ink Canvas/MainWindow_cs/MW_Screenshot.cs b/Ink Canvas/MainWindow_cs/MW_Screenshot.cs
index f398a4e9..844c0982 100644
--- a/Ink Canvas/MainWindow_cs/MW_Screenshot.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Screenshot.cs
@@ -3,6 +3,7 @@ using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
+using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
@@ -65,6 +66,29 @@ namespace Ink_Canvas
{
ShowNotification($"截图成功保存至 {savePath}");
}
+ _ = Task.Run(async () =>
+ {
+ try
+ {
+ var delayMinutes = Settings?.Dlass?.AutoUploadDelayMinutes ?? 0;
+ if (delayMinutes > 0)
+ {
+ await Task.Delay(TimeSpan.FromMinutes(delayMinutes));
+ }
+
+ var uploaded = await Helpers.DlassNoteUploader.UploadPngNoteAsync(savePath);
+ if (uploaded && !isHideNotification)
+ {
+ Dispatcher.Invoke(() =>
+ {
+ ShowNotification($"笔记已自动上传到Dlass");
+ });
+ }
+ }
+ catch (Exception)
+ {
+ }
+ });
}
// 获取日期文件夹路径
diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs
index 4ec614da..3941e2ac 100644
--- a/Ink Canvas/Resources/Settings.cs
+++ b/Ink Canvas/Resources/Settings.cs
@@ -740,5 +740,11 @@ namespace Ink_Canvas
[JsonProperty("apiBaseUrl")]
public string ApiBaseUrl { get; set; } = "https://dlass.tech";
+
+ [JsonProperty("isAutoUploadNotes")]
+ public bool IsAutoUploadNotes { get; set; } = false;
+
+ [JsonProperty("autoUploadDelayMinutes")]
+ public int AutoUploadDelayMinutes { get; set; } = 0;
}
}
diff --git a/Ink Canvas/Windows/DlassSettingsWindow.xaml b/Ink Canvas/Windows/DlassSettingsWindow.xaml
index a8e998ee..fe993dd6 100644
--- a/Ink Canvas/Windows/DlassSettingsWindow.xaml
+++ b/Ink Canvas/Windows/DlassSettingsWindow.xaml
@@ -106,22 +106,22 @@
X1="0" Y1="0" X2="1" Y2="0"
Stroke="{StaticResource BorderBrush}"
StrokeThickness="1"
- Margin="0,8,0,12"/>
+ Margin="0,2,0,2"/>
+ Margin="0,0,0,1"/>
+ Margin="0,0,0,2"/>
-
+
+
+
+
+
+
+
+ Margin="0,2,0,1"/>
+ Margin="0,0,0,1"/>
+ Margin="0,0,0,2"/>
+ Margin="0,2,0,1"/>
-
-
-
-
+
+
+
+
+
+
+
+ TextWrapping="Wrap"
+ Margin="0,0,0,2"/>
+
+
+
+
+
+
+
+
+
+
+
+
+ Margin="0,2,0,1"/>
diff --git a/Ink Canvas/Windows/DlassSettingsWindow.xaml.cs b/Ink Canvas/Windows/DlassSettingsWindow.xaml.cs
index 494e17d9..a68d7640 100644
--- a/Ink Canvas/Windows/DlassSettingsWindow.xaml.cs
+++ b/Ink Canvas/Windows/DlassSettingsWindow.xaml.cs
@@ -2,6 +2,7 @@ using Ink_Canvas.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
@@ -34,6 +35,9 @@ namespace Ink_Canvas.Windows
// 加载保存的token
LoadUserToken();
+ // 加载自动上传设置
+ LoadAutoUploadSettings();
+
// 初始化API客户端(优先使用用户token)
InitializeApiClient();
@@ -272,6 +276,98 @@ namespace Ink_Canvas.Windows
}
}
+ ///
+ /// 加载自动上传设置
+ ///
+ private void LoadAutoUploadSettings()
+ {
+ try
+ {
+ if (MainWindow.Settings?.Dlass != null)
+ {
+ ToggleSwitchAutoUploadNotes.IsOn = MainWindow.Settings.Dlass.IsAutoUploadNotes;
+ var delayMinutes = MainWindow.Settings.Dlass.AutoUploadDelayMinutes;
+ if (delayMinutes < 0 || delayMinutes > 60)
+ {
+ delayMinutes = 0;
+ }
+ TxtUploadDelayMinutes.Text = delayMinutes.ToString();
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"加载自动上传设置时出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ ///
+ /// 自动上传开关切换事件
+ ///
+ private void ToggleSwitchAutoUploadNotes_Toggled(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ if (MainWindow.Settings?.Dlass != null)
+ {
+ MainWindow.Settings.Dlass.IsAutoUploadNotes = ToggleSwitchAutoUploadNotes.IsOn;
+ MainWindow.SaveSettingsToFile();
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"保存自动上传设置时出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ ///
+ /// 上传延迟时间输入框文本改变事件
+ ///
+ private void TxtUploadDelayMinutes_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
+ {
+ try
+ {
+ if (MainWindow.Settings?.Dlass != null && int.TryParse(TxtUploadDelayMinutes.Text, out int delayMinutes))
+ {
+ // 限制范围在0-60分钟
+ if (delayMinutes < 0)
+ {
+ delayMinutes = 0;
+ TxtUploadDelayMinutes.Text = "0";
+ }
+ else if (delayMinutes > 60)
+ {
+ delayMinutes = 60;
+ TxtUploadDelayMinutes.Text = "60";
+ }
+
+ MainWindow.Settings.Dlass.AutoUploadDelayMinutes = delayMinutes;
+ MainWindow.SaveSettingsToFile();
+ }
+ else if (string.IsNullOrWhiteSpace(TxtUploadDelayMinutes.Text))
+ {
+ // 空文本时设置为0
+ if (MainWindow.Settings?.Dlass != null)
+ {
+ MainWindow.Settings.Dlass.AutoUploadDelayMinutes = 0;
+ MainWindow.SaveSettingsToFile();
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"保存上传延迟时间时出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ ///
+ /// 上传延迟时间输入框预览文本输入事件(只允许数字)
+ ///
+ private void TxtUploadDelayMinutes_PreviewTextInput(object sender, TextCompositionEventArgs e)
+ {
+ Regex regex = new Regex("[^0-9]+");
+ e.Handled = regex.IsMatch(e.Text);
+ }
+
///
/// 标题栏拖动事件
///