From 72ba1a9f588951942bf933fea692dc82596af266 Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sun, 2 Nov 2025 09:29:06 +0800
Subject: [PATCH 01/25] =?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 | 374 +++++++++++++
Ink Canvas/MainWindow.xaml | 11 +
Ink Canvas/MainWindow_cs/MW_Settings.cs | 56 ++
Ink Canvas/Resources/Settings.cs | 14 +
Ink Canvas/Windows/DlassSettingsWindow.xaml | 388 ++++++++++++++
.../Windows/DlassSettingsWindow.xaml.cs | 490 ++++++++++++++++++
6 files changed, 1333 insertions(+)
create mode 100644 Ink Canvas/Helpers/DlassApiClient.cs
create mode 100644 Ink Canvas/Windows/DlassSettingsWindow.xaml
create mode 100644 Ink Canvas/Windows/DlassSettingsWindow.xaml.cs
diff --git a/Ink Canvas/Helpers/DlassApiClient.cs b/Ink Canvas/Helpers/DlassApiClient.cs
new file mode 100644
index 00000000..438c4947
--- /dev/null
+++ b/Ink Canvas/Helpers/DlassApiClient.cs
@@ -0,0 +1,374 @@
+using Ink_Canvas.Helpers;
+using Newtonsoft.Json;
+using System;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ink_Canvas.Helpers
+{
+ ///
+ /// Dlass API 客户端,用于与服务端通信
+ ///
+ public class DlassApiClient
+ {
+ private const string DEFAULT_BASE_URL = "https://dlass.tech";
+ private readonly string _appId;
+ private readonly string _appSecret;
+ private readonly string _baseUrl;
+ private HttpClient _httpClient;
+ private string _accessToken;
+ private DateTime _tokenExpiresAt;
+
+ private string _userToken;
+
+ ///
+ /// 初始化 Dlass API 客户端
+ ///
+ /// 应用ID
+ /// 应用密钥
+ /// API基础URL,如果为空则使用默认URL
+ /// 用户Token,如果提供则优先使用用户token而不是App Secret
+ public DlassApiClient(string appId, string appSecret, string baseUrl = null, string userToken = null)
+ {
+ _appId = appId ?? throw new ArgumentNullException(nameof(appId));
+ _appSecret = appSecret ?? throw new ArgumentNullException(nameof(appSecret));
+ _userToken = userToken;
+ _baseUrl = baseUrl ?? DEFAULT_BASE_URL;
+
+ _baseUrl = _baseUrl.TrimEnd('/');
+ if (!_baseUrl.StartsWith("http://") && !_baseUrl.StartsWith("https://"))
+ {
+ _baseUrl = "https://" + _baseUrl;
+ }
+
+ _httpClient = new HttpClient
+ {
+ BaseAddress = new Uri(_baseUrl),
+ Timeout = TimeSpan.FromSeconds(30)
+ };
+ _httpClient.DefaultRequestHeaders.Add("User-Agent", "InkCanvas/1.0");
+ }
+
+ ///
+ /// 获取访问令牌(Access Token)
+ ///
+ public async Task GetAccessTokenAsync()
+ {
+ if (!string.IsNullOrEmpty(_userToken))
+ {
+ return _userToken;
+ }
+
+ if (!string.IsNullOrEmpty(_accessToken) && DateTime.Now < _tokenExpiresAt.AddMinutes(-5))
+ {
+ return _accessToken;
+ }
+
+ try
+ {
+ var requestData = new
+ {
+ app_id = _appId,
+ app_secret = _appSecret,
+ grant_type = "client_credentials"
+ };
+
+ var json = JsonConvert.SerializeObject(requestData);
+ var content = new StringContent(json, Encoding.UTF8, "application/json");
+
+ var response = await _httpClient.PostAsync("/oauth/token", content);
+ var responseContent = await response.Content.ReadAsStringAsync();
+
+ if (response.IsSuccessStatusCode)
+ {
+ var tokenResponse = JsonConvert.DeserializeObject(responseContent);
+ _accessToken = tokenResponse.AccessToken;
+ _tokenExpiresAt = DateTime.Now.AddSeconds(tokenResponse.ExpiresIn ?? 3600);
+ return _accessToken;
+ }
+ else
+ {
+ throw new Exception($"获取Access Token失败: {response.StatusCode}");
+ }
+ }
+ catch (HttpRequestException httpEx)
+ {
+ throw new Exception($"获取Access Token时网络错误: {httpEx.Message}", httpEx);
+ }
+ catch (TaskCanceledException timeoutEx)
+ {
+ throw new Exception("获取Access Token时请求超时", timeoutEx);
+ }
+ catch (Exception ex)
+ {
+ throw new Exception($"获取Access Token时出错: {ex.Message}", ex);
+ }
+ }
+
+ ///
+ /// 发送GET请求
+ ///
+ public async Task GetAsync(string endpoint, bool requireAuth = true)
+ {
+ try
+ {
+ string token = null;
+ if (requireAuth)
+ {
+ token = await GetAccessTokenAsync();
+ }
+
+ var request = new HttpRequestMessage(HttpMethod.Get, endpoint);
+
+ if (requireAuth && !string.IsNullOrEmpty(token))
+ {
+ if (!string.IsNullOrEmpty(_userToken))
+ {
+ request.Headers.Add("X-User-Token", token);
+ }
+ else
+ {
+ request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
+ }
+ }
+
+ var response = await _httpClient.SendAsync(request);
+ var content = await response.Content.ReadAsStringAsync();
+
+ if (response.IsSuccessStatusCode)
+ {
+ if (string.IsNullOrEmpty(content))
+ {
+ return default(T);
+ }
+ return JsonConvert.DeserializeObject(content);
+ }
+ else
+ {
+ throw new Exception($"API请求失败: {response.StatusCode} - {content}");
+ }
+ }
+ 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);
+ }
+ }
+
+ ///
+ /// 发送POST请求
+ ///
+ public async Task PostAsync(string endpoint, object data = null, bool requireAuth = true)
+ {
+ try
+ {
+ string token = null;
+ if (requireAuth)
+ {
+ token = await GetAccessTokenAsync();
+ }
+
+ var request = new HttpRequestMessage(HttpMethod.Post, endpoint);
+
+ if (requireAuth && !string.IsNullOrEmpty(token))
+ {
+ if (!string.IsNullOrEmpty(_userToken))
+ {
+ request.Headers.Add("X-User-Token", token);
+ }
+ else
+ {
+ request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
+ }
+ }
+
+ if (data != null)
+ {
+ var json = JsonConvert.SerializeObject(data);
+ request.Content = new StringContent(json, Encoding.UTF8, "application/json");
+ }
+
+ var response = await _httpClient.SendAsync(request);
+ var content = await response.Content.ReadAsStringAsync();
+
+ if (response.IsSuccessStatusCode)
+ {
+ if (string.IsNullOrEmpty(content))
+ {
+ return default(T);
+ }
+ return JsonConvert.DeserializeObject(content);
+ }
+ else
+ {
+ throw new Exception($"API请求失败: {response.StatusCode} - {content}");
+ }
+ }
+ 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);
+ }
+ }
+
+ ///
+ /// 发送PUT请求
+ ///
+ public async Task PutAsync(string endpoint, object data = null, bool requireAuth = true)
+ {
+ try
+ {
+ string token = null;
+ if (requireAuth)
+ {
+ token = await GetAccessTokenAsync();
+ }
+
+ var request = new HttpRequestMessage(HttpMethod.Put, endpoint);
+
+ if (requireAuth && !string.IsNullOrEmpty(token))
+ {
+ // 如果是用户token,使用X-User-Token header
+ if (!string.IsNullOrEmpty(_userToken))
+ {
+ request.Headers.Add("X-User-Token", token);
+ }
+ else
+ {
+ request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
+ }
+ }
+
+ if (data != null)
+ {
+ var json = JsonConvert.SerializeObject(data);
+ request.Content = new StringContent(json, Encoding.UTF8, "application/json");
+ }
+
+ var response = await _httpClient.SendAsync(request);
+ var content = await response.Content.ReadAsStringAsync();
+
+ if (response.IsSuccessStatusCode)
+ {
+ if (string.IsNullOrEmpty(content))
+ {
+ return default(T);
+ }
+ return JsonConvert.DeserializeObject(content);
+ }
+ else
+ {
+ throw new Exception($"API请求失败: {response.StatusCode} - {content}");
+ }
+ }
+ 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);
+ }
+ }
+
+ ///
+ /// 发送DELETE请求
+ ///
+ public async Task DeleteAsync(string endpoint, bool requireAuth = true)
+ {
+ try
+ {
+ string token = null;
+ if (requireAuth)
+ {
+ token = await GetAccessTokenAsync();
+ }
+
+ var request = new HttpRequestMessage(HttpMethod.Delete, endpoint);
+
+ if (requireAuth && !string.IsNullOrEmpty(token))
+ {
+ // 如果是用户token,使用X-User-Token header
+ if (!string.IsNullOrEmpty(_userToken))
+ {
+ request.Headers.Add("X-User-Token", token);
+ }
+ else
+ {
+ request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
+ }
+ }
+
+ var response = await _httpClient.SendAsync(request);
+
+ if (response.IsSuccessStatusCode)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ catch (HttpRequestException httpEx)
+ {
+ return false;
+ }
+ catch (TaskCanceledException timeoutEx)
+ {
+ return false;
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// 释放资源
+ ///
+ public void Dispose()
+ {
+ _httpClient?.Dispose();
+ }
+
+ #region 内部类
+
+ ///
+ /// Token响应模型
+ ///
+ private class TokenResponse
+ {
+ [JsonProperty("access_token")]
+ public string AccessToken { get; set; }
+
+ [JsonProperty("expires_in")]
+ public int? ExpiresIn { get; set; }
+
+ [JsonProperty("token_type")]
+ public string TokenType { get; set; }
+ }
+
+ #endregion
+ }
+}
+
diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml
index 32a65bc6..beaa21df 100644
--- a/Ink Canvas/MainWindow.xaml
+++ b/Ink Canvas/MainWindow.xaml
@@ -3185,6 +3185,17 @@
FontSize="14" Margin="8,0,0,0" />
+
+
+
+
+
+
diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs
index 9f7b9907..97c55672 100644
--- a/Ink Canvas/MainWindow_cs/MW_Settings.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs
@@ -1846,6 +1846,62 @@ namespace Ink_Canvas
SaveSettingsToFile();
}
+ private void BtnDlassSettingsManage_Click(object sender, RoutedEventArgs e)
+ {
+ if (isOpeningOrHidingSettingsPane) return;
+ HideSubPanels();
+ try
+ {
+ // 检查是否是第一次打开(检查用户是否已设置Token)
+ bool hasToken = !string.IsNullOrEmpty(Settings?.Dlass?.UserToken?.Trim());
+ bool isFirstTime = !hasToken;
+
+ if (isFirstTime)
+ {
+ // 第一次打开,询问用户是否已注册
+ var result = MessageBox.Show(
+ "您是否已经注册了Dlass账号?\n\n" +
+ "• 如果已注册:将直接打开设置管理页面\n" +
+ "• 如果未注册:将打开浏览器跳转到注册页面",
+ "Dlass账号注册",
+ MessageBoxButton.YesNo,
+ MessageBoxImage.Question);
+
+ if (result == MessageBoxResult.No)
+ {
+ // 用户未注册,打开浏览器
+ try
+ {
+ Process.Start(new ProcessStartInfo
+ {
+ FileName = "https://dlass.tech/dashboard",
+ UseShellExecute = true
+ });
+ LogHelper.WriteLogToFile("已打开浏览器跳转到Dlass注册页面", LogHelper.LogType.Event);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"打开浏览器时出错: {ex.Message}", LogHelper.LogType.Error);
+ MessageBox.Show($"无法打开浏览器。请手动访问: https://dlass.tech/dashboard",
+ "提示", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ return; // 不打开设置窗口
+ }
+ // 如果用户选择"是",继续打开设置窗口
+ }
+
+ // 打开设置管理窗口
+ var dlassSettingsWindow = new Windows.DlassSettingsWindow();
+ dlassSettingsWindow.Owner = this;
+ dlassSettingsWindow.ShowDialog();
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"打开Dlass设置管理窗口时出错: {ex.Message}", LogHelper.LogType.Error);
+ MessageBox.Show($"打开Dlass设置管理窗口时发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
private void ToggleSwitchAutoDelSavedFiles_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs
index ed819bd1..ccb2615f 100644
--- a/Ink Canvas/Resources/Settings.cs
+++ b/Ink Canvas/Resources/Settings.cs
@@ -29,6 +29,8 @@ namespace Ink_Canvas
public ModeSettings ModeSettings { get; set; } = new ModeSettings();
[JsonProperty("camera")]
public CameraSettings Camera { get; set; } = new CameraSettings();
+ [JsonProperty("dlass")]
+ public DlassSettings Dlass { get; set; } = new DlassSettings();
}
public class Canvas
@@ -724,4 +726,16 @@ namespace Ink_Canvas
[JsonProperty("selectedCameraIndex")]
public int SelectedCameraIndex { get; set; } = 0;
}
+
+ public class DlassSettings
+ {
+ [JsonProperty("userToken")]
+ public string UserToken { get; set; } = string.Empty;
+
+ [JsonProperty("savedTokens")]
+ public List SavedTokens { get; set; } = new List();
+
+ [JsonProperty("apiBaseUrl")]
+ public string ApiBaseUrl { get; set; } = "https://dlass.tech";
+ }
}
diff --git a/Ink Canvas/Windows/DlassSettingsWindow.xaml b/Ink Canvas/Windows/DlassSettingsWindow.xaml
new file mode 100644
index 00000000..0ec103df
--- /dev/null
+++ b/Ink Canvas/Windows/DlassSettingsWindow.xaml
@@ -0,0 +1,388 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Ink Canvas/Windows/DlassSettingsWindow.xaml.cs b/Ink Canvas/Windows/DlassSettingsWindow.xaml.cs
new file mode 100644
index 00000000..3f80c958
--- /dev/null
+++ b/Ink Canvas/Windows/DlassSettingsWindow.xaml.cs
@@ -0,0 +1,490 @@
+using Ink_Canvas.Helpers;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
+
+namespace Ink_Canvas.Windows
+{
+ ///
+ /// DlassSettingsWindow.xaml 的交互逻辑
+ ///
+ public partial class DlassSettingsWindow : Window
+ {
+ private const string APP_ID = "app_WkjocWqsrVY7T6zQV2CfiA";
+ private const string APP_SECRET = "o7dx5b5ASGUMcM72PCpmRQYAhSijqaOVHoGyBK0IxbA";
+
+ private DlassApiClient _apiClient;
+
+ public DlassSettingsWindow(MainWindow mainWindow = null)
+ {
+ InitializeComponent();
+
+ // 加载保存的token
+ LoadUserToken();
+
+ // 初始化API客户端(优先使用用户token)
+ InitializeApiClient();
+
+ // 窗口关闭时释放资源
+ Closed += (s, e) => _apiClient?.Dispose();
+
+ // 测试连接
+ _ = TestConnectionAsync();
+ }
+
+ ///
+ /// 初始化API客户端
+ ///
+ private void InitializeApiClient()
+ {
+ var userToken = GetUserToken();
+ var apiBaseUrl = MainWindow.Settings?.Dlass?.ApiBaseUrl;
+
+ if (string.IsNullOrEmpty(apiBaseUrl) || apiBaseUrl.Contains("api.dlass.tech"))
+ {
+ apiBaseUrl = "https://dlass.tech";
+ if (MainWindow.Settings?.Dlass != null)
+ {
+ MainWindow.Settings.Dlass.ApiBaseUrl = apiBaseUrl;
+ MainWindow.SaveSettingsToFile();
+ }
+ }
+
+ if (!string.IsNullOrEmpty(userToken))
+ {
+ _apiClient = new DlassApiClient(APP_ID, APP_SECRET, baseUrl: apiBaseUrl, userToken: userToken);
+ }
+ else
+ {
+ _apiClient = new DlassApiClient(APP_ID, APP_SECRET, baseUrl: apiBaseUrl);
+ }
+ }
+
+ ///
+ /// 获取用户token
+ ///
+ private string GetUserToken()
+ {
+ if (MainWindow.Settings?.Dlass != null)
+ {
+ return MainWindow.Settings.Dlass.UserToken ?? string.Empty;
+ }
+ return string.Empty;
+ }
+
+ ///
+ /// 获取保存的Token列表
+ ///
+ private List GetSavedTokens()
+ {
+ if (MainWindow.Settings?.Dlass != null)
+ {
+ return MainWindow.Settings.Dlass.SavedTokens ?? new List();
+ }
+ return new List();
+ }
+
+ ///
+ /// 加载用户token到UI
+ ///
+ private void LoadUserToken()
+ {
+ var savedTokens = GetSavedTokens();
+ var currentToken = GetUserToken();
+
+ CmbSavedTokens.Items.Clear();
+ if (savedTokens.Count > 0)
+ {
+ foreach (var token in savedTokens)
+ {
+ CmbSavedTokens.Items.Add(token);
+ }
+ if (!string.IsNullOrEmpty(currentToken))
+ {
+ var index = savedTokens.IndexOf(currentToken);
+ if (index >= 0)
+ {
+ CmbSavedTokens.SelectedIndex = index;
+ }
+ else
+ {
+ CmbSavedTokens.SelectedIndex = 0;
+ }
+ }
+ else if (CmbSavedTokens.Items.Count > 0)
+ {
+ CmbSavedTokens.SelectedIndex = 0;
+ }
+ }
+ else
+ {
+ CmbSavedTokens.Items.Add("(无保存的Token)");
+ CmbSavedTokens.SelectedIndex = 0;
+ CmbSavedTokens.IsEnabled = false;
+ }
+
+ TxtNewToken.Text = string.Empty;
+
+ if (!string.IsNullOrEmpty(currentToken))
+ {
+ TxtTokenStatus.Text = "已选择Token";
+ TxtTokenStatus.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(34, 197, 94));
+ }
+ else
+ {
+ TxtTokenStatus.Text = "未设置Token";
+ TxtTokenStatus.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(161, 161, 170));
+ }
+ }
+
+ ///
+ /// 保存用户token
+ ///
+ private void SaveUserToken(string token)
+ {
+ if (MainWindow.Settings?.Dlass != null)
+ {
+ MainWindow.Settings.Dlass.UserToken = token ?? string.Empty;
+ MainWindow.SaveSettingsToFile();
+ }
+ }
+
+ ///
+ /// 添加Token到保存列表
+ ///
+ private void AddTokenToList(string token)
+ {
+ if (MainWindow.Settings?.Dlass != null)
+ {
+ if (MainWindow.Settings.Dlass.SavedTokens == null)
+ {
+ MainWindow.Settings.Dlass.SavedTokens = new List();
+ }
+
+ if (!string.IsNullOrEmpty(token) && !MainWindow.Settings.Dlass.SavedTokens.Contains(token))
+ {
+ MainWindow.Settings.Dlass.SavedTokens.Add(token);
+ MainWindow.SaveSettingsToFile();
+ }
+ }
+ }
+
+ ///
+ /// 从列表删除Token
+ ///
+ private void RemoveTokenFromList(string token)
+ {
+ if (MainWindow.Settings?.Dlass != null && MainWindow.Settings.Dlass.SavedTokens != null)
+ {
+ MainWindow.Settings.Dlass.SavedTokens.Remove(token);
+ MainWindow.SaveSettingsToFile();
+ }
+ }
+
+ ///
+ /// 标题栏拖动事件
+ ///
+ private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ if (e.ButtonState == MouseButtonState.Pressed)
+ {
+ DragMove();
+ }
+ }
+
+ ///
+ /// 关闭按钮点击事件
+ ///
+ private void BtnClose_Click(object sender, RoutedEventArgs e)
+ {
+ Close();
+ }
+
+ ///
+ /// 下拉框选择改变事件
+ ///
+ private void CmbSavedTokens_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
+ {
+ try
+ {
+ if (CmbSavedTokens.SelectedItem != null && CmbSavedTokens.SelectedItem.ToString() != "(无保存的Token)")
+ {
+ var selectedToken = CmbSavedTokens.SelectedItem.ToString();
+ SaveUserToken(selectedToken);
+
+ _apiClient?.Dispose();
+ InitializeApiClient();
+
+ TxtTokenStatus.Text = "已选择Token";
+ TxtTokenStatus.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(34, 197, 94));
+
+ _ = TestConnectionAsync();
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"选择Token时出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ ///
+ /// 保存Token按钮点击事件
+ ///
+ private void BtnSaveToken_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ var token = TxtNewToken.Text?.Trim() ?? string.Empty;
+ if (string.IsNullOrEmpty(token))
+ {
+ MessageBox.Show("请输入新的用户Token", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
+ return;
+ }
+
+ AddTokenToList(token);
+ SaveUserToken(token);
+
+ _apiClient?.Dispose();
+ InitializeApiClient();
+
+ LoadUserToken();
+
+ MessageBox.Show("Token已成功保存并已选择", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
+
+ _ = TestConnectionAsync();
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"保存Token时出错: {ex.Message}", LogHelper.LogType.Error);
+ MessageBox.Show($"保存Token时发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ ///
+ /// 清除Token按钮点击事件
+ ///
+ private void BtnClearToken_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ if (CmbSavedTokens.SelectedItem == null || CmbSavedTokens.SelectedItem.ToString() == "(无保存的Token)")
+ {
+ MessageBox.Show("请先选择一个Token", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
+ return;
+ }
+
+ var selectedToken = CmbSavedTokens.SelectedItem.ToString();
+ var result = MessageBox.Show($"确定要删除已选中的Token吗?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Question);
+ if (result == MessageBoxResult.Yes)
+ {
+ RemoveTokenFromList(selectedToken);
+
+ if (GetUserToken() == selectedToken)
+ {
+ SaveUserToken(string.Empty);
+ }
+
+ _apiClient?.Dispose();
+ InitializeApiClient();
+
+ LoadUserToken();
+
+ TxtConnectionStatus.Text = "未连接";
+ TxtConnectionStatus.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(161, 161, 170));
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"删除Token时出错: {ex.Message}", LogHelper.LogType.Error);
+ MessageBox.Show($"删除Token时发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ ///
+ /// 测试Token连接按钮点击事件
+ ///
+ private async void BtnTestToken_Click(object sender, RoutedEventArgs e)
+ {
+ await TestConnectionAsync();
+ }
+
+ ///
+ /// 保存按钮点击事件
+ ///
+ private async void BtnSave_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ // TODO: 根据实际API文档实现保存逻辑
+ // 示例:保存设置到服务器
+ // var settings = new { ... };
+ // await _apiClient.PostAsync("/api/settings", settings);
+
+ MessageBox.Show("设置已保存", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
+ Close();
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"保存设置时出错: {ex.Message}", LogHelper.LogType.Error);
+ MessageBox.Show($"保存设置时发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ ///
+ /// 取消按钮点击事件
+ ///
+ private void BtnCancel_Click(object sender, RoutedEventArgs e)
+ {
+ Close();
+ }
+
+ ///
+ /// 测试API连接
+ ///
+ private async Task TestConnectionAsync()
+ {
+ Dispatcher.Invoke(() =>
+ {
+ TxtConnectionStatus.Text = "测试中...";
+ TxtConnectionStatus.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(161, 161, 170)); // 灰色
+ });
+
+ try
+ {
+ var userToken = GetUserToken();
+ if (string.IsNullOrEmpty(userToken))
+ {
+ Dispatcher.Invoke(() =>
+ {
+ TxtConnectionStatus.Text = "未设置Token";
+ TxtConnectionStatus.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(239, 68, 68)); // 红色
+ });
+ return;
+ }
+
+ // 根据文档,使用 auth-with-token 接口验证token
+ // 此接口需要POST请求,包含app_id, app_secret和user_token
+ try
+ {
+ var authData = new
+ {
+ app_id = APP_ID,
+ app_secret = APP_SECRET,
+ user_token = userToken
+ };
+
+ var result = await _apiClient.PostAsync("/api/whiteboard/framework/auth-with-token", authData, requireAuth: false);
+
+ if (result != null && result.Success)
+ {
+ var whiteboardCount = result.Whiteboards?.Count ?? 0;
+
+ Dispatcher.Invoke(() =>
+ {
+ TxtConnectionStatus.Text = $"已连接 (找到 {whiteboardCount} 个白板)";
+ TxtConnectionStatus.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(34, 197, 94));
+ });
+ }
+ else
+ {
+ throw new Exception("认证响应失败");
+ }
+ }
+ catch (Exception ex)
+ {
+ if (userToken.Length < 10)
+ {
+ throw new Exception("Token格式可能不正确(长度过短,至少需要10个字符)");
+ }
+
+ LogHelper.WriteLogToFile($"Token验证失败: {ex.Message}", LogHelper.LogType.Error);
+ throw;
+ }
+
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"Dlass API连接测试失败: {ex.Message}", LogHelper.LogType.Error);
+ Dispatcher.Invoke(() =>
+ {
+ TxtConnectionStatus.Text = "连接失败";
+ TxtConnectionStatus.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(239, 68, 68)); // 红色
+ });
+ }
+ }
+ }
+
+ #region API响应模型
+
+ ///
+ /// auth-with-token接口响应模型
+ ///
+ public class AuthWithTokenResponse
+ {
+ [Newtonsoft.Json.JsonProperty("success")]
+ public bool Success { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("whiteboards")]
+ public List Whiteboards { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("count")]
+ public int Count { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("user")]
+ public UserInfo User { get; set; }
+ }
+
+ ///
+ /// 白板信息模型
+ ///
+ public class WhiteboardInfo
+ {
+ [Newtonsoft.Json.JsonProperty("id")]
+ public int Id { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("name")]
+ public string Name { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("board_id")]
+ public string BoardId { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("secret_key")]
+ public string SecretKey { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("class_name")]
+ public string ClassName { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("class_id")]
+ public int ClassId { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("is_online")]
+ public bool IsOnline { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("last_heartbeat")]
+ public string LastHeartbeat { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("created_at")]
+ public string CreatedAt { get; set; }
+ }
+
+ ///
+ /// 用户信息模型
+ ///
+ public class UserInfo
+ {
+ [Newtonsoft.Json.JsonProperty("id")]
+ public int Id { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("username")]
+ public string Username { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("email")]
+ public string Email { get; set; }
+ }
+
+ #endregion
+}
+
From 4ef77c2e72f0150cbf53a36ac63ad0141b190f23 Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sun, 2 Nov 2025 09:41:53 +0800
Subject: [PATCH 02/25] =?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/Resources/Settings.cs | 3 +
Ink Canvas/Windows/DlassSettingsWindow.xaml | 32 +++++
.../Windows/DlassSettingsWindow.xaml.cs | 127 +++++++++++++++++-
3 files changed, 160 insertions(+), 2 deletions(-)
diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs
index ccb2615f..4ec614da 100644
--- a/Ink Canvas/Resources/Settings.cs
+++ b/Ink Canvas/Resources/Settings.cs
@@ -735,6 +735,9 @@ namespace Ink_Canvas
[JsonProperty("savedTokens")]
public List SavedTokens { get; set; } = new List();
+ [JsonProperty("selectedClassName")]
+ public string SelectedClassName { get; set; } = string.Empty;
+
[JsonProperty("apiBaseUrl")]
public string ApiBaseUrl { get; set; } = "https://dlass.tech";
}
diff --git a/Ink Canvas/Windows/DlassSettingsWindow.xaml b/Ink Canvas/Windows/DlassSettingsWindow.xaml
index 0ec103df..a8e998ee 100644
--- a/Ink Canvas/Windows/DlassSettingsWindow.xaml
+++ b/Ink Canvas/Windows/DlassSettingsWindow.xaml
@@ -280,6 +280,38 @@
Margin="0,4,0,0"/>
+
+
+
+
+
+
+
+
+
+
_currentWhiteboards = new List();
+ private UserInfo _currentUser;
public DlassSettingsWindow(MainWindow mainWindow = null)
{
InitializeComponent();
+ // 初始化班级下拉框
+ CmbClassSelection.Items.Clear();
+ CmbClassSelection.Items.Add("(等待连接)");
+ CmbClassSelection.SelectedIndex = 0;
+ CmbClassSelection.IsEnabled = false;
+
// 加载保存的token
LoadUserToken();
@@ -184,6 +193,85 @@ namespace Ink_Canvas.Windows
}
}
+ ///
+ /// 加载班级列表到下拉框
+ ///
+ private void LoadClasses(List whiteboards, UserInfo user = null)
+ {
+ CmbClassSelection.Items.Clear();
+
+ if (whiteboards != null && whiteboards.Count > 0)
+ {
+ var teacherName = user?.Username ?? "未知教师";
+ var classGroups = whiteboards
+ .Where(w => !string.IsNullOrEmpty(w.ClassName))
+ .GroupBy(w => w.ClassName)
+ .OrderBy(g => g.Key)
+ .ToList();
+
+ foreach (var group in classGroups)
+ {
+ var className = group.Key;
+ var displayText = $"{teacherName} - {className}";
+ CmbClassSelection.Items.Add(new ClassSelectionItem
+ {
+ DisplayText = displayText,
+ ClassName = className,
+ TeacherName = teacherName
+ });
+ }
+
+ var savedClassName = MainWindow.Settings?.Dlass?.SelectedClassName ?? string.Empty;
+ if (!string.IsNullOrEmpty(savedClassName))
+ {
+ var savedItem = CmbClassSelection.Items.Cast()
+ .FirstOrDefault(item => item.ClassName == savedClassName);
+ if (savedItem != null)
+ {
+ CmbClassSelection.SelectedItem = savedItem;
+ }
+ else if (CmbClassSelection.Items.Count > 0)
+ {
+ CmbClassSelection.SelectedIndex = 0;
+ }
+ }
+ else if (CmbClassSelection.Items.Count > 0)
+ {
+ CmbClassSelection.SelectedIndex = 0;
+ }
+
+ CmbClassSelection.IsEnabled = true;
+ }
+ else
+ {
+ CmbClassSelection.Items.Add("(无可用班级)");
+ CmbClassSelection.SelectedIndex = 0;
+ CmbClassSelection.IsEnabled = false;
+ }
+ }
+
+ ///
+ /// 班级选择改变事件
+ ///
+ private void CmbClassSelection_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
+ {
+ try
+ {
+ if (CmbClassSelection.SelectedItem is ClassSelectionItem selectedItem)
+ {
+ if (MainWindow.Settings?.Dlass != null)
+ {
+ MainWindow.Settings.Dlass.SelectedClassName = selectedItem.ClassName;
+ MainWindow.SaveSettingsToFile();
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"选择班级时出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
///
/// 标题栏拖动事件
///
@@ -292,6 +380,13 @@ namespace Ink_Canvas.Windows
LoadUserToken();
+ CmbClassSelection.Items.Clear();
+ CmbClassSelection.Items.Add("(等待连接)");
+ CmbClassSelection.SelectedIndex = 0;
+ CmbClassSelection.IsEnabled = false;
+ _currentWhiteboards.Clear();
+ _currentUser = null;
+
TxtConnectionStatus.Text = "未连接";
TxtConnectionStatus.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(161, 161, 170));
}
@@ -380,12 +475,18 @@ namespace Ink_Canvas.Windows
if (result != null && result.Success)
{
- var whiteboardCount = result.Whiteboards?.Count ?? 0;
+ var whiteboards = result.Whiteboards ?? new List();
+ _currentWhiteboards = whiteboards;
+ _currentUser = result.User;
+ var whiteboardCount = whiteboards.Count;
Dispatcher.Invoke(() =>
{
TxtConnectionStatus.Text = $"已连接 (找到 {whiteboardCount} 个白板)";
TxtConnectionStatus.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(34, 197, 94));
+
+ // 加载班级列表
+ LoadClasses(whiteboards, result.User);
});
}
else
@@ -411,7 +512,14 @@ namespace Ink_Canvas.Windows
Dispatcher.Invoke(() =>
{
TxtConnectionStatus.Text = "连接失败";
- TxtConnectionStatus.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(239, 68, 68)); // 红色
+ TxtConnectionStatus.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(239, 68, 68));
+
+ // 清空班级列表
+ CmbClassSelection.Items.Clear();
+ CmbClassSelection.Items.Add("(无可用班级)");
+ CmbClassSelection.SelectedIndex = 0;
+ CmbClassSelection.IsEnabled = false;
+ _currentWhiteboards.Clear();
});
}
}
@@ -485,6 +593,21 @@ namespace Ink_Canvas.Windows
public string Email { get; set; }
}
+ ///
+ /// 班级选择项
+ ///
+ public class ClassSelectionItem
+ {
+ public string DisplayText { get; set; }
+ public string ClassName { get; set; }
+ public string TeacherName { get; set; }
+
+ public override string ToString()
+ {
+ return DisplayText;
+ }
+ }
+
#endregion
}
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 03/25] =?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);
+ }
+
///
/// 标题栏拖动事件
///
From b6020481866bc91b43fd2db1c0e377311505978f Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sun, 2 Nov 2025 10:16:31 +0800
Subject: [PATCH 04/25] =?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/DlassNoteUploader.cs | 23 +++++++++++++++++--
.../MainWindow_cs/MW_Save&OpenStrokes.cs | 19 ++++++++++++++-
Ink Canvas/MainWindow_cs/MW_Screenshot.cs | 9 +-------
Ink Canvas/Windows/DlassSettingsWindow.xaml | 4 ++--
4 files changed, 42 insertions(+), 13 deletions(-)
diff --git a/Ink Canvas/Helpers/DlassNoteUploader.cs b/Ink Canvas/Helpers/DlassNoteUploader.cs
index ed08fece..30da0f51 100644
--- a/Ink Canvas/Helpers/DlassNoteUploader.cs
+++ b/Ink Canvas/Helpers/DlassNoteUploader.cs
@@ -76,6 +76,16 @@ namespace Ink_Canvas.Helpers
public List Whiteboards { get; set; }
}
+ ///
+ /// 异步上传笔记文件到Dlass(支持PNG和ICSTK格式)
+ ///
+ /// 文件路径(支持PNG和ICSTK)
+ /// 是否上传成功
+ public static async Task UploadNoteFileAsync(string filePath)
+ {
+ return await UploadPngNoteAsync(filePath);
+ }
+
///
/// 异步上传PNG文件到Dlass
///
@@ -98,6 +108,14 @@ namespace Ink_Canvas.Helpers
return false;
}
+ // 检查文件扩展名
+ var fileExtension = Path.GetExtension(pngFilePath).ToLower();
+ if (fileExtension != ".png" && fileExtension != ".icstk")
+ {
+ LogHelper.WriteLogToFile($"上传失败:不支持的文件格式 - {fileExtension},仅支持PNG和ICSTK", LogHelper.LogType.Error);
+ return false;
+ }
+
// 检查文件大小(最大10MB)
var fileInfo = new FileInfo(pngFilePath);
if (fileInfo.Length > 10 * 1024 * 1024)
@@ -160,8 +178,9 @@ namespace Ink_Canvas.Helpers
// 准备上传参数
var fileName = Path.GetFileNameWithoutExtension(pngFilePath);
var title = fileName;
- var description = $"自动上传的笔记 - {DateTime.Now:yyyy-MM-dd HH:mm:ss}";
- var tags = "自动上传,笔记";
+ var fileType = fileExtension == ".icstk" ? "墨迹文件" : "笔记";
+ var description = $"自动上传的{fileType} - {DateTime.Now:yyyy-MM-dd HH:mm:ss}";
+ var tags = fileExtension == ".icstk" ? "自动上传,墨迹,icstk" : "自动上传,笔记,png";
// 上传文件
var uploadResult = await apiClient.UploadNoteAsync(
diff --git a/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs b/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs
index bcbc487c..0e3b6f12 100644
--- a/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs
@@ -133,6 +133,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.UploadNoteFileAsync(savePathWithName);
+ }
+ catch (Exception)
+ {
+ }
+ });
+
// 保存元素信息
var elementInfos = new List();
foreach (var child in inkCanvas.Children)
@@ -322,7 +339,7 @@ namespace Ink_Canvas
await Task.Delay(TimeSpan.FromMinutes(delayMinutes));
}
- await Helpers.DlassNoteUploader.UploadPngNoteAsync(imagePathWithName);
+ await Helpers.DlassNoteUploader.UploadNoteFileAsync(imagePathWithName);
}
catch (Exception)
{
diff --git a/Ink Canvas/MainWindow_cs/MW_Screenshot.cs b/Ink Canvas/MainWindow_cs/MW_Screenshot.cs
index 844c0982..18ef9fec 100644
--- a/Ink Canvas/MainWindow_cs/MW_Screenshot.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Screenshot.cs
@@ -76,14 +76,7 @@ namespace Ink_Canvas
await Task.Delay(TimeSpan.FromMinutes(delayMinutes));
}
- var uploaded = await Helpers.DlassNoteUploader.UploadPngNoteAsync(savePath);
- if (uploaded && !isHideNotification)
- {
- Dispatcher.Invoke(() =>
- {
- ShowNotification($"笔记已自动上传到Dlass");
- });
- }
+ await Helpers.DlassNoteUploader.UploadNoteFileAsync(savePath);
}
catch (Exception)
{
diff --git a/Ink Canvas/Windows/DlassSettingsWindow.xaml b/Ink Canvas/Windows/DlassSettingsWindow.xaml
index fe993dd6..9591776b 100644
--- a/Ink Canvas/Windows/DlassSettingsWindow.xaml
+++ b/Ink Canvas/Windows/DlassSettingsWindow.xaml
@@ -341,13 +341,13 @@
-
Date: Sun, 2 Nov 2025 10:30:36 +0800
Subject: [PATCH 05/25] =?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 | 2 +-
Ink Canvas/Helpers/DlassNoteUploader.cs | 301 +++++++++++++++++++-----
2 files changed, 239 insertions(+), 64 deletions(-)
diff --git a/Ink Canvas/Helpers/DlassApiClient.cs b/Ink Canvas/Helpers/DlassApiClient.cs
index 61a04f42..d77bed80 100644
--- a/Ink Canvas/Helpers/DlassApiClient.cs
+++ b/Ink Canvas/Helpers/DlassApiClient.cs
@@ -12,7 +12,7 @@ namespace Ink_Canvas.Helpers
///
/// Dlass API 客户端,用于与服务端通信
///
- public class DlassApiClient
+ public class DlassApiClient : IDisposable
{
private const string DEFAULT_BASE_URL = "https://dlass.tech";
private readonly string _appId;
diff --git a/Ink Canvas/Helpers/DlassNoteUploader.cs b/Ink Canvas/Helpers/DlassNoteUploader.cs
index 30da0f51..0d67e3a2 100644
--- a/Ink Canvas/Helpers/DlassNoteUploader.cs
+++ b/Ink Canvas/Helpers/DlassNoteUploader.cs
@@ -1,9 +1,11 @@
using Ink_Canvas.Helpers;
using Newtonsoft.Json;
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
namespace Ink_Canvas.Helpers
@@ -15,6 +17,17 @@ namespace Ink_Canvas.Helpers
{
private const string APP_ID = "app_WkjocWqsrVY7T6zQV2CfiA";
private const string APP_SECRET = "o7dx5b5ASGUMcM72PCpmRQYAhSijqaOVHoGyBK0IxbA";
+ private const int BATCH_SIZE = 10; // 批量上传大小
+
+ ///
+ /// 上传队列(线程安全)
+ ///
+ private static readonly ConcurrentQueue _uploadQueue = new ConcurrentQueue();
+
+ ///
+ /// 队列处理锁,防止并发处理
+ ///
+ private static readonly SemaphoreSlim _queueProcessingLock = new SemaphoreSlim(1, 1);
///
/// 上传笔记响应模型
@@ -80,18 +93,8 @@ namespace Ink_Canvas.Helpers
/// 异步上传笔记文件到Dlass(支持PNG和ICSTK格式)
///
/// 文件路径(支持PNG和ICSTK)
- /// 是否上传成功
+ /// 是否成功加入队列(不等待实际上传完成)
public static async Task UploadNoteFileAsync(string filePath)
- {
- return await UploadPngNoteAsync(filePath);
- }
-
- ///
- /// 异步上传PNG文件到Dlass
- ///
- /// PNG文件路径
- /// 是否上传成功
- public static async Task UploadPngNoteAsync(string pngFilePath)
{
try
{
@@ -101,91 +104,267 @@ namespace Ink_Canvas.Helpers
return false;
}
- // 检查文件是否存在
- if (!File.Exists(pngFilePath))
+ // 基本验证
+ if (!File.Exists(filePath))
{
- LogHelper.WriteLogToFile($"上传失败:文件不存在 - {pngFilePath}", LogHelper.LogType.Error);
+ LogHelper.WriteLogToFile($"上传失败:文件不存在 - {filePath}", LogHelper.LogType.Error);
return false;
}
- // 检查文件扩展名
- var fileExtension = Path.GetExtension(pngFilePath).ToLower();
+ var fileExtension = Path.GetExtension(filePath).ToLower();
if (fileExtension != ".png" && fileExtension != ".icstk")
{
- LogHelper.WriteLogToFile($"上传失败:不支持的文件格式 - {fileExtension},仅支持PNG和ICSTK", LogHelper.LogType.Error);
return false;
}
- // 检查文件大小(最大10MB)
- var fileInfo = new FileInfo(pngFilePath);
+ var fileInfo = new FileInfo(filePath);
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))
+ // 获取上传延迟时间(分钟)
+ var delayMinutes = MainWindow.Settings?.Dlass?.AutoUploadDelayMinutes ?? 0;
+
+ // 如果设置了延迟时间,在后台任务中等待后再加入队列
+ if (delayMinutes > 0)
{
- LogHelper.WriteLogToFile("上传失败:未选择班级", LogHelper.LogType.Error);
- return false;
+ _ = Task.Run(async () =>
+ {
+ await Task.Delay(TimeSpan.FromMinutes(delayMinutes));
+ EnqueueFile(filePath);
+ });
+ }
+ else
+ {
+ EnqueueFile(filePath);
}
- // 获取用户Token
- var userToken = MainWindow.Settings?.Dlass?.UserToken;
- if (string.IsNullOrEmpty(userToken))
+ return true;
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"加入上传队列时出错: {ex.Message}", LogHelper.LogType.Error);
+ return false;
+ }
+ }
+
+ ///
+ /// 将文件加入上传队列
+ ///
+ private static void EnqueueFile(string filePath)
+ {
+ _uploadQueue.Enqueue(filePath);
+
+ // 如果队列达到批量大小,触发批量上传
+ if (_uploadQueue.Count >= BATCH_SIZE)
+ {
+ _ = ProcessUploadQueueAsync();
+ }
+ }
+
+ ///
+ /// 处理上传队列,批量上传文件
+ ///
+ private static async Task ProcessUploadQueueAsync()
+ {
+ // 使用信号量防止并发处理
+ if (!await _queueProcessingLock.WaitAsync(0))
+ {
+ return; // 已有处理任务在运行
+ }
+
+ try
+ {
+ var filesToUpload = new List();
+
+ // 从队列中取出最多BATCH_SIZE个文件
+ while (filesToUpload.Count < BATCH_SIZE && _uploadQueue.TryDequeue(out string filePath))
{
- LogHelper.WriteLogToFile("上传失败:未设置用户Token", LogHelper.LogType.Error);
- return false;
+ // 再次检查文件是否存在(可能在队列中时被删除)
+ if (File.Exists(filePath))
+ {
+ filesToUpload.Add(filePath);
+ }
}
- // 获取API基础URL
- var apiBaseUrl = MainWindow.Settings?.Dlass?.ApiBaseUrl ?? "https://dlass.tech";
+ if (filesToUpload.Count == 0)
+ {
+ return;
+ }
- // 创建API客户端并获取白板信息
- DlassApiClient apiClient = null;
+ // 获取共享的白板信息(同一批次的所有文件共享认证信息)
+ WhiteboardInfo sharedWhiteboard = null;
+ string apiBaseUrl = null;
+ string userToken = null;
+
try
{
- apiClient = new DlassApiClient(APP_ID, APP_SECRET, apiBaseUrl, userToken);
-
- // 调用认证接口获取白板列表
- var authData = new
+ var selectedClassName = MainWindow.Settings?.Dlass?.SelectedClassName;
+ if (string.IsNullOrEmpty(selectedClassName))
{
- app_id = APP_ID,
- app_secret = APP_SECRET,
- user_token = userToken
- };
+ LogHelper.WriteLogToFile("上传失败:未选择班级", LogHelper.LogType.Error);
+ return;
+ }
- var authResult = await apiClient.PostAsync("/api/whiteboard/framework/auth-with-token", authData, requireAuth: false);
-
- if (authResult == null || !authResult.Success || authResult.Whiteboards == null)
+ userToken = MainWindow.Settings?.Dlass?.UserToken;
+ if (string.IsNullOrEmpty(userToken))
{
- LogHelper.WriteLogToFile("上传失败:无法获取白板信息", LogHelper.LogType.Error);
+ LogHelper.WriteLogToFile("上传失败:未设置用户Token", LogHelper.LogType.Error);
+ return;
+ }
+
+ apiBaseUrl = MainWindow.Settings?.Dlass?.ApiBaseUrl ?? "https://dlass.tech";
+
+ // 获取白板信息(只获取一次,所有文件共享)
+ using (var 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;
+ }
+
+ sharedWhiteboard = authResult.Whiteboards
+ .FirstOrDefault(w => !string.IsNullOrEmpty(w.ClassName) && w.ClassName == selectedClassName);
+
+ if (sharedWhiteboard == null || string.IsNullOrEmpty(sharedWhiteboard.BoardId) || string.IsNullOrEmpty(sharedWhiteboard.SecretKey))
+ {
+ LogHelper.WriteLogToFile($"上传失败:未找到班级'{selectedClassName}'对应的白板", LogHelper.LogType.Error);
+ return;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"批量上传获取白板信息时出错: {ex.Message}", LogHelper.LogType.Error);
+ return;
+ }
+
+ // 并发上传所有文件(共享白板信息)
+ var uploadTasks = filesToUpload.Select(filePath => UploadFileInternalAsync(filePath, sharedWhiteboard, apiBaseUrl, userToken));
+ await Task.WhenAll(uploadTasks);
+
+ // 如果队列中还有文件,继续处理
+ if (_uploadQueue.Count >= BATCH_SIZE)
+ {
+ _ = ProcessUploadQueueAsync();
+ }
+ }
+ finally
+ {
+ _queueProcessingLock.Release();
+ }
+ }
+
+ ///
+ /// 内部上传方法,执行实际上传操作
+ ///
+ /// 文件路径
+ /// 白板信息(如果为null则重新获取)
+ /// API基础URL(如果为null则从设置获取)
+ /// 用户Token(如果为null则从设置获取)
+ private static async Task UploadFileInternalAsync(string filePath, WhiteboardInfo whiteboard = null, string apiBaseUrl = null, string userToken = null)
+ {
+ try
+ {
+ // 再次检查文件是否存在(可能在队列等待时被删除)
+ if (!File.Exists(filePath))
+ {
+ return false;
+ }
+
+ // 检查文件扩展名
+ var fileExtension = Path.GetExtension(filePath).ToLower();
+ if (fileExtension != ".png" && fileExtension != ".icstk")
+ {
+ return false;
+ }
+
+ // 检查文件大小(最大10MB)
+ var fileInfo = new FileInfo(filePath);
+ if (fileInfo.Length > 10 * 1024 * 1024)
+ {
+ LogHelper.WriteLogToFile($"上传失败:文件过大({fileInfo.Length / 1024 / 1024}MB),超过10MB限制", LogHelper.LogType.Error);
+ return false;
+ }
+
+ // 如果白板信息未提供,则重新获取
+ if (whiteboard == null)
+ {
+ var selectedClassName = MainWindow.Settings?.Dlass?.SelectedClassName;
+ if (string.IsNullOrEmpty(selectedClassName))
+ {
+ 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))
+ userToken = userToken ?? MainWindow.Settings?.Dlass?.UserToken;
+ if (string.IsNullOrEmpty(userToken))
{
- LogHelper.WriteLogToFile($"上传失败:未找到班级'{selectedClassName}'对应的白板", LogHelper.LogType.Error);
+ LogHelper.WriteLogToFile("上传失败:未设置用户Token", LogHelper.LogType.Error);
return false;
}
- // 准备上传参数
- var fileName = Path.GetFileNameWithoutExtension(pngFilePath);
- var title = fileName;
- var fileType = fileExtension == ".icstk" ? "墨迹文件" : "笔记";
- var description = $"自动上传的{fileType} - {DateTime.Now:yyyy-MM-dd HH:mm:ss}";
- var tags = fileExtension == ".icstk" ? "自动上传,墨迹,icstk" : "自动上传,笔记,png";
+ apiBaseUrl = apiBaseUrl ?? MainWindow.Settings?.Dlass?.ApiBaseUrl ?? "https://dlass.tech";
- // 上传文件
+ // 创建API客户端并获取白板信息
+ using (var 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;
+ }
+
+ // 查找匹配班级的白板
+ 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;
+ }
+ }
+ }
+
+ // 获取API基础URL和用户Token(如果未提供)
+ apiBaseUrl = apiBaseUrl ?? MainWindow.Settings?.Dlass?.ApiBaseUrl ?? "https://dlass.tech";
+ userToken = userToken ?? MainWindow.Settings?.Dlass?.UserToken;
+
+ // 准备上传参数
+ var fileName = Path.GetFileNameWithoutExtension(filePath);
+ var title = fileName;
+ var fileType = fileExtension == ".icstk" ? "墨迹文件" : "笔记";
+ var description = $"自动上传的{fileType} - {DateTime.Now:yyyy-MM-dd HH:mm:ss}";
+ var tags = fileExtension == ".icstk" ? "自动上传,墨迹,icstk" : "自动上传,笔记,png";
+
+ // 创建API客户端并上传文件
+ using (var apiClient = new DlassApiClient(APP_ID, APP_SECRET, apiBaseUrl, userToken))
+ {
var uploadResult = await apiClient.UploadNoteAsync(
"/api/whiteboard/upload_note",
- pngFilePath,
+ filePath,
whiteboard.BoardId,
whiteboard.SecretKey,
title,
@@ -203,10 +382,6 @@ namespace Ink_Canvas.Helpers
return false;
}
}
- finally
- {
- apiClient?.Dispose();
- }
}
catch (Exception ex)
{
From d2906476c87ed69c90a3e69496c2faecf940da40 Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sun, 2 Nov 2025 10:46:16 +0800
Subject: [PATCH 06/25] =?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/DlassNoteUploader.cs | 125 +++++++++++++++++++++---
1 file changed, 112 insertions(+), 13 deletions(-)
diff --git a/Ink Canvas/Helpers/DlassNoteUploader.cs b/Ink Canvas/Helpers/DlassNoteUploader.cs
index 0d67e3a2..4265c2bc 100644
--- a/Ink Canvas/Helpers/DlassNoteUploader.cs
+++ b/Ink Canvas/Helpers/DlassNoteUploader.cs
@@ -18,11 +18,21 @@ namespace Ink_Canvas.Helpers
private const string APP_ID = "app_WkjocWqsrVY7T6zQV2CfiA";
private const string APP_SECRET = "o7dx5b5ASGUMcM72PCpmRQYAhSijqaOVHoGyBK0IxbA";
private const int BATCH_SIZE = 10; // 批量上传大小
+ private const int MAX_RETRY_COUNT = 3; // 最大重试次数
///
- /// 上传队列(线程安全)
+ /// 上传队列项
///
- private static readonly ConcurrentQueue _uploadQueue = new ConcurrentQueue();
+ private class UploadQueueItem
+ {
+ public string FilePath { get; set; }
+ public int RetryCount { get; set; }
+ }
+
+ ///
+ /// 上传队列
+ ///
+ private static readonly ConcurrentQueue _uploadQueue = new ConcurrentQueue();
///
/// 队列处理锁,防止并发处理
@@ -153,9 +163,13 @@ namespace Ink_Canvas.Helpers
///
/// 将文件加入上传队列
///
- private static void EnqueueFile(string filePath)
+ private static void EnqueueFile(string filePath, int retryCount = 0)
{
- _uploadQueue.Enqueue(filePath);
+ _uploadQueue.Enqueue(new UploadQueueItem
+ {
+ FilePath = filePath,
+ RetryCount = retryCount
+ });
// 如果队列达到批量大小,触发批量上传
if (_uploadQueue.Count >= BATCH_SIZE)
@@ -177,15 +191,15 @@ namespace Ink_Canvas.Helpers
try
{
- var filesToUpload = new List();
+ var filesToUpload = new List();
// 从队列中取出最多BATCH_SIZE个文件
- while (filesToUpload.Count < BATCH_SIZE && _uploadQueue.TryDequeue(out string filePath))
+ while (filesToUpload.Count < BATCH_SIZE && _uploadQueue.TryDequeue(out UploadQueueItem item))
{
- // 再次检查文件是否存在(可能在队列中时被删除)
- if (File.Exists(filePath))
+ // 再次检查文件是否存在
+ if (File.Exists(item.FilePath))
{
- filesToUpload.Add(filePath);
+ filesToUpload.Add(item);
}
}
@@ -251,11 +265,59 @@ namespace Ink_Canvas.Helpers
return;
}
- // 并发上传所有文件(共享白板信息)
- var uploadTasks = filesToUpload.Select(filePath => UploadFileInternalAsync(filePath, sharedWhiteboard, apiBaseUrl, userToken));
+ // 并发上传所有文件(共享白板信息),并处理失败重试
+ var uploadTasks = filesToUpload.Select(async item =>
+ {
+ try
+ {
+ var success = await UploadFileInternalAsync(item.FilePath, sharedWhiteboard, apiBaseUrl, userToken);
+ if (!success)
+ {
+ // 检查是否是可重试的错误
+ if (IsRetryableError(item.FilePath))
+ {
+ // 检查重试次数
+ if (item.RetryCount < MAX_RETRY_COUNT)
+ {
+ LogHelper.WriteLogToFile($"上传失败,将重试 ({item.RetryCount + 1}/{MAX_RETRY_COUNT}): {Path.GetFileName(item.FilePath)}", LogHelper.LogType.Event);
+ EnqueueFile(item.FilePath, item.RetryCount + 1);
+ }
+ else
+ {
+ LogHelper.WriteLogToFile($"上传失败,已达到最大重试次数: {Path.GetFileName(item.FilePath)}", LogHelper.LogType.Error);
+ }
+ }
+ }
+ return success;
+ }
+ catch (Exception ex)
+ {
+ // 检查是否是可重试的错误(超时、网络错误等)
+ var errorMessage = ex.Message.ToLower();
+ bool isRetryable = errorMessage.Contains("超时") ||
+ errorMessage.Contains("timeout") ||
+ errorMessage.Contains("网络错误") ||
+ errorMessage.Contains("network");
+
+ if (isRetryable && IsRetryableError(item.FilePath))
+ {
+ // 检查重试次数
+ if (item.RetryCount < MAX_RETRY_COUNT)
+ {
+ LogHelper.WriteLogToFile($"上传失败({ex.Message}),将重试 ({item.RetryCount + 1}/{MAX_RETRY_COUNT}): {Path.GetFileName(item.FilePath)}", LogHelper.LogType.Event);
+ EnqueueFile(item.FilePath, item.RetryCount + 1);
+ }
+ else
+ {
+ LogHelper.WriteLogToFile($"上传失败,已达到最大重试次数: {Path.GetFileName(item.FilePath)}", LogHelper.LogType.Error);
+ }
+ }
+ return false;
+ }
+ });
await Task.WhenAll(uploadTasks);
- // 如果队列中还有文件,继续处理
+ // 如果队列达到批量大小,继续处理
if (_uploadQueue.Count >= BATCH_SIZE)
{
_ = ProcessUploadQueueAsync();
@@ -385,10 +447,47 @@ namespace Ink_Canvas.Helpers
}
catch (Exception ex)
{
+ // 记录错误信息,抛出异常以便调用方判断是否可重试
LogHelper.WriteLogToFile($"上传笔记时出错: {ex.Message}", LogHelper.LogType.Error);
- return false;
+ throw;
}
}
+
+ ///
+ /// 判断错误是否可重试(超时、网络错误等)
+ ///
+ private static bool IsRetryableError(string filePath)
+ {
+ // 检查文件是否存在
+ if (!File.Exists(filePath))
+ {
+ return false; // 文件不存在,不可重试
+ }
+
+ // 检查文件扩展名
+ var fileExtension = Path.GetExtension(filePath).ToLower();
+ if (fileExtension != ".png" && fileExtension != ".icstk")
+ {
+ return false; // 文件格式错误,不可重试
+ }
+
+ // 检查文件大小
+ try
+ {
+ var fileInfo = new FileInfo(filePath);
+ if (fileInfo.Length > 10 * 1024 * 1024)
+ {
+ return false; // 文件过大,不可重试
+ }
+ }
+ catch
+ {
+ return false; // 无法读取文件信息,不可重试
+ }
+
+ // 其他错误(超时、网络错误等)可以重试
+ return true;
+ }
}
}
From 24b2bffe8e42b459f5889b42d64889e6e8306259 Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sun, 2 Nov 2025 11:12:13 +0800
Subject: [PATCH 07/25] =?UTF-8?q?improve:=E8=AE=A1=E6=97=B6=E5=99=A8?=
=?UTF-8?q?=E7=AA=97=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Windows/MinimizedTimerWindow.xaml.cs | 40 +++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs b/Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs
index b8004d2b..50731801 100644
--- a/Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs
+++ b/Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs
@@ -28,6 +28,9 @@ namespace Ink_Canvas
this.Left = parent.Left;
this.Top = parent.Top;
+ // 根据分辨率和DPI缩放窗口
+ ScaleWindowForResolution();
+
// 启动更新定时器
updateTimer = new System.Timers.Timer(100); // 100ms更新一次
updateTimer.Elapsed += UpdateTimer_Elapsed;
@@ -39,6 +42,43 @@ namespace Ink_Canvas
ApplyTheme();
}
+ ///
+ /// 根据屏幕分辨率和 DPI 缩放窗口大小(保持原始尺寸,使用Transform缩放)
+ ///
+ private void ScaleWindowForResolution()
+ {
+ try
+ {
+ // 获取屏幕尺寸(考虑 DPI 缩放)
+ double screenWidth = SystemParameters.PrimaryScreenWidth;
+ double screenHeight = SystemParameters.PrimaryScreenHeight;
+
+ // 基准分辨率(1920x1080)
+ const double baseWidth = 1920.0;
+ const double baseHeight = 1080.0;
+
+ // 计算缩放比例(使用较小的比例以保持比例)
+ double scaleX = screenWidth / baseWidth;
+ double scaleY = screenHeight / baseHeight;
+ double scale = Math.Min(scaleX, scaleY);
+
+ // 限制最小和最大缩放,避免过小或过大
+ scale = Math.Max(0.5, Math.Min(2.0, scale));
+
+ // 应用缩放变换到整个窗口内容
+ var scaleTransform = this.FindName("WindowScaleTransform") as ScaleTransform;
+ if (scaleTransform != null)
+ {
+ scaleTransform.ScaleX = scale;
+ scaleTransform.ScaleY = scale;
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"缩放窗口大小时出错: {ex.Message}");
+ }
+ }
+
private void UpdateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (parentWindow != null)
From e7d89e65b22da0ab1a0871673cf377eba4fd151e Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sun, 2 Nov 2025 11:27:46 +0800
Subject: [PATCH 08/25] improve:AutoUpdate
---
Ink Canvas/App.xaml.cs | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/Ink Canvas/App.xaml.cs b/Ink Canvas/App.xaml.cs
index 56385a85..51ea0265 100644
--- a/Ink Canvas/App.xaml.cs
+++ b/Ink Canvas/App.xaml.cs
@@ -706,6 +706,20 @@ namespace Ink_Canvas
{
LogHelper.WriteLogToFile($"App | 清理更新标记文件失败: {ex.Message}", LogHelper.LogType.Warning);
}
+
+ Task.Run(async () =>
+ {
+ try
+ {
+ await Task.Delay(3000);
+ LogHelper.WriteLogToFile("App | 最终应用启动,删除AutoUpdate文件夹");
+ AutoUpdateHelper.DeleteUpdatesFolder();
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"App | 删除AutoUpdate文件夹失败: {ex.Message}", LogHelper.LogType.Warning);
+ }
+ });
}
// 如果不是最终应用启动,才检查更新标记文件
From 74eca093da11eb7b9b07ccf36bc9862ded019f04 Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sun, 2 Nov 2025 11:40:27 +0800
Subject: [PATCH 09/25] =?UTF-8?q?improve:=E6=82=AC=E6=B5=AE=E5=BF=AB?=
=?UTF-8?q?=E6=8A=BD=E6=8C=89=E9=92=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Windows/QuickDrawFloatingButton.cs | 69 ++++++++++++++++++
.../Windows/QuickDrawFloatingButton.xaml | 71 +++++++++++++++----
2 files changed, 127 insertions(+), 13 deletions(-)
diff --git a/Ink Canvas/Windows/QuickDrawFloatingButton.cs b/Ink Canvas/Windows/QuickDrawFloatingButton.cs
index 6c5360bd..61aebc2f 100644
--- a/Ink Canvas/Windows/QuickDrawFloatingButton.cs
+++ b/Ink Canvas/Windows/QuickDrawFloatingButton.cs
@@ -14,6 +14,10 @@ namespace Ink_Canvas
///
public partial class QuickDrawFloatingButton : Window
{
+ private bool isDragging = false;
+ private Point dragStartPoint;
+ private Point windowStartPoint;
+
public QuickDrawFloatingButton()
{
InitializeComponent();
@@ -62,6 +66,9 @@ namespace Ink_Canvas
{
try
{
+ // 如果正在拖动,不触发点击事件
+ if (isDragging) return;
+
// 打开快抽窗口
var quickDrawWindow = new QuickDrawWindow();
quickDrawWindow.ShowDialog();
@@ -72,6 +79,68 @@ namespace Ink_Canvas
}
}
+ private void DragArea_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ isDragging = false;
+ // 记录鼠标在屏幕上的初始位置
+ dragStartPoint = this.PointToScreen(e.GetPosition(this));
+ // 记录窗口的初始位置
+ windowStartPoint = new Point(this.Left, this.Top);
+ ((UIElement)sender).CaptureMouse();
+ e.Handled = true;
+ }
+
+ private void DragArea_MouseMove(object sender, MouseEventArgs e)
+ {
+ if (e.LeftButton == MouseButtonState.Pressed && ((UIElement)sender).IsMouseCaptured)
+ {
+ // 获取鼠标在屏幕上的当前位置
+ Point currentScreenPoint = this.PointToScreen(e.GetPosition(this));
+ Vector diff = currentScreenPoint - dragStartPoint;
+
+ if (!isDragging && (Math.Abs(diff.X) > 3 || Math.Abs(diff.Y) > 3))
+ {
+ isDragging = true;
+ }
+
+ if (isDragging)
+ {
+ // 使用窗口初始位置加上鼠标移动的距离
+ double newLeft = windowStartPoint.X + diff.X;
+ double newTop = windowStartPoint.Y + diff.Y;
+
+ // 限制在屏幕范围内
+ var workingArea = SystemParameters.WorkArea;
+ newLeft = Math.Max(workingArea.Left, Math.Min(newLeft, workingArea.Right - this.Width));
+ newTop = Math.Max(workingArea.Top, Math.Min(newTop, workingArea.Bottom - this.Height));
+
+ this.Left = newLeft;
+ this.Top = newTop;
+ }
+ }
+ }
+
+ private void DragArea_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ if (((UIElement)sender).IsMouseCaptured)
+ {
+ ((UIElement)sender).ReleaseMouseCapture();
+ }
+
+ // 延迟重置拖动状态,避免触发点击事件
+ if (isDragging)
+ {
+ Dispatcher.BeginInvoke(new Action(() => { isDragging = false; }),
+ DispatcherPriority.Background);
+ }
+ else
+ {
+ isDragging = false;
+ }
+
+ e.Handled = true;
+ }
+
diff --git a/Ink Canvas/Windows/QuickDrawFloatingButton.xaml b/Ink Canvas/Windows/QuickDrawFloatingButton.xaml
index d743c383..07786911 100644
--- a/Ink Canvas/Windows/QuickDrawFloatingButton.xaml
+++ b/Ink Canvas/Windows/QuickDrawFloatingButton.xaml
@@ -7,7 +7,7 @@
mc:Ignorable="d" WindowStyle="None" AllowsTransparency="True"
Loaded="FloatingButton_Loaded" WindowStartupLocation="Manual"
ShowInTaskbar="False" Focusable="False"
- Title="快抽悬浮按钮" Height="45" Width="45">
+ Title="快抽悬浮按钮" Height="45" Width="65">
@@ -21,23 +21,68 @@
+ BorderBrush="{DynamicResource QuickDrawFloatingButtonBorderBrush}">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 01009f9e355194b12a274ff2cd372fe2d9054e65 Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sun, 2 Nov 2025 11:47:52 +0800
Subject: [PATCH 10/25] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC?=
=?UTF-8?q?=E5=8F=B7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/AssemblyInfo.cs | 4 ++--
Ink Canvas/Properties/AssemblyInfo.cs | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/Ink Canvas/AssemblyInfo.cs b/Ink Canvas/AssemblyInfo.cs
index 93561d41..604af0dd 100644
--- a/Ink Canvas/AssemblyInfo.cs
+++ b/Ink Canvas/AssemblyInfo.cs
@@ -49,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.7.17.0")]
-[assembly: AssemblyFileVersion("1.7.17.0")]
+[assembly: AssemblyVersion("1.7.17.1")]
+[assembly: AssemblyFileVersion("1.7.17.1")]
diff --git a/Ink Canvas/Properties/AssemblyInfo.cs b/Ink Canvas/Properties/AssemblyInfo.cs
index 93561d41..604af0dd 100644
--- a/Ink Canvas/Properties/AssemblyInfo.cs
+++ b/Ink Canvas/Properties/AssemblyInfo.cs
@@ -49,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.7.17.0")]
-[assembly: AssemblyFileVersion("1.7.17.0")]
+[assembly: AssemblyVersion("1.7.17.1")]
+[assembly: AssemblyFileVersion("1.7.17.1")]
From 92c631d6ce266f5a9ff9a3de5e0d5d82d4ba2e3b Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sun, 2 Nov 2025 12:34:50 +0800
Subject: [PATCH 11/25] =?UTF-8?q?improve:=E6=93=8D=E4=BD=9C=E9=80=BB?=
=?UTF-8?q?=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/MainWindow_cs/MW_Settings.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs
index 97c55672..0c29fcfe 100644
--- a/Ink Canvas/MainWindow_cs/MW_Settings.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs
@@ -2728,8 +2728,8 @@ namespace Ink_Canvas
if (ToggleSwitchEnableOvertimeRedText.IsOn && !ToggleSwitchEnableOvertimeCountUp.IsOn)
{
- ToggleSwitchEnableOvertimeRedText.IsOn = false;
- return;
+ ToggleSwitchEnableOvertimeCountUp.IsOn = true;
+ Settings.RandSettings.EnableOvertimeCountUp = true;
}
Settings.RandSettings.EnableOvertimeRedText = ToggleSwitchEnableOvertimeRedText.IsOn;
From ce1998b70121d1f4f8a87f8fd8280f8f33cfe865 Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Fri, 7 Nov 2025 09:46:21 +0800
Subject: [PATCH 12/25] =?UTF-8?q?improve:Dlass=20=E7=95=8C=E9=9D=A2UI?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Windows/DlassSettingsWindow.xaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Ink Canvas/Windows/DlassSettingsWindow.xaml b/Ink Canvas/Windows/DlassSettingsWindow.xaml
index 9591776b..30be8f9a 100644
--- a/Ink Canvas/Windows/DlassSettingsWindow.xaml
+++ b/Ink Canvas/Windows/DlassSettingsWindow.xaml
@@ -343,7 +343,7 @@
From dfab0d7ddfa462eec50114ac978f92c2a58530d4 Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Fri, 7 Nov 2025 10:35:27 +0800
Subject: [PATCH 13/25] =?UTF-8?q?improvve:=E7=82=B9=E5=90=8D=E5=BF=AB?=
=?UTF-8?q?=E6=8A=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Windows/QuickDrawWindow.cs | 45 ++++++++++++++++-----------
1 file changed, 27 insertions(+), 18 deletions(-)
diff --git a/Ink Canvas/Windows/QuickDrawWindow.cs b/Ink Canvas/Windows/QuickDrawWindow.cs
index eb92a816..e8bfec04 100644
--- a/Ink Canvas/Windows/QuickDrawWindow.cs
+++ b/Ink Canvas/Windows/QuickDrawWindow.cs
@@ -99,7 +99,7 @@ namespace Ink_Canvas
{
const int animationTimes = 100; // 动画次数
const int sleepTime = 5; // 每次动画间隔(毫秒)
-
+
new System.Threading.Thread(() =>
{
if (nameList.Count > 0)
@@ -121,7 +121,7 @@ namespace Ink_Canvas
private void StartNameDrawAnimation(int animationTimes, int sleepTime)
{
List usedNames = new List();
-
+
for (int i = 0; i < animationTimes; i++)
{
// 随机选择一个名字进行动画显示,避免立即重复
@@ -130,25 +130,29 @@ namespace Ink_Canvas
{
randomName = nameList[random.Next(0, nameList.Count)];
} while (usedNames.Count > 0 && usedNames[usedNames.Count - 1] == randomName);
-
+
usedNames.Add(randomName);
-
+
Application.Current.Dispatcher.Invoke(() =>
{
MainResultDisplay.Text = randomName;
});
-
+
System.Threading.Thread.Sleep(sleepTime);
}
-
+
// 动画结束,显示最终结果
Application.Current.Dispatcher.Invoke(() =>
{
- // 随机选择一个最终名字
- string finalName = nameList[random.Next(0, nameList.Count)];
+ // 使用降重抽选方法选择最终名字
+ var selectedNames = NewStyleRollCallWindow.SelectNamesWithML(nameList, 1, random);
+ string finalName = selectedNames.Count > 0 ? selectedNames[0] : nameList[random.Next(0, nameList.Count)];
MainResultDisplay.Text = finalName;
+
+ // 更新历史记录
+ NewStyleRollCallWindow.UpdateRollCallHistory(new List { finalName });
});
-
+
// 显示结果后,等待一段时间让用户看到结果,然后关闭窗口
new System.Threading.Thread(() =>
{
@@ -166,7 +170,7 @@ namespace Ink_Canvas
private void StartNumberDrawAnimation(int animationTimes, int sleepTime)
{
List usedNumbers = new List();
-
+
for (int i = 0; i < animationTimes; i++)
{
// 随机选择一个数字进行动画显示,避免立即重复
@@ -175,25 +179,30 @@ namespace Ink_Canvas
{
randomNumber = random.Next(1, 61); // 1-60
} while (usedNumbers.Count > 0 && usedNumbers[usedNumbers.Count - 1] == randomNumber);
-
+
usedNumbers.Add(randomNumber);
-
+
Application.Current.Dispatcher.Invoke(() =>
{
MainResultDisplay.Text = randomNumber.ToString();
});
-
+
System.Threading.Thread.Sleep(sleepTime);
}
-
+
// 动画结束,显示最终结果
Application.Current.Dispatcher.Invoke(() =>
{
- // 随机选择一个最终数字
- int finalNumber = random.Next(1, 61);
- MainResultDisplay.Text = finalNumber.ToString();
+ // 使用降重抽选方法选择最终数字
+ var numberList = Enumerable.Range(1, 60).Select(n => n.ToString()).ToList();
+ var selectedNumbers = NewStyleRollCallWindow.SelectNamesWithML(numberList, 1, random);
+ string finalNumber = selectedNumbers.Count > 0 ? selectedNumbers[0] : random.Next(1, 61).ToString();
+ MainResultDisplay.Text = finalNumber;
+
+ // 更新历史记录
+ NewStyleRollCallWindow.UpdateRollCallHistory(new List { finalNumber });
});
-
+
// 显示结果后,等待一段时间让用户看到结果,然后关闭窗口
new System.Threading.Thread(() =>
{
From 4b2f29442a2cd3fa9f03c4808300dd3e1066e107 Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Fri, 7 Nov 2025 10:36:24 +0800
Subject: [PATCH 14/25] =?UTF-8?q?improve:=E7=82=B9=E5=90=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Windows/NewStyleRollCallWindow.cs | 89 ++++++++++++--------
1 file changed, 56 insertions(+), 33 deletions(-)
diff --git a/Ink Canvas/Windows/NewStyleRollCallWindow.cs b/Ink Canvas/Windows/NewStyleRollCallWindow.cs
index bfe85d88..84dec3a3 100644
--- a/Ink Canvas/Windows/NewStyleRollCallWindow.cs
+++ b/Ink Canvas/Windows/NewStyleRollCallWindow.cs
@@ -126,9 +126,10 @@ namespace Ink_Canvas
private DateTime lastActivityTime = DateTime.Now;
// 机器学习相关
- private RollCallHistoryData historyData = new RollCallHistoryData();
- private int maxRecentHistory = 20;
- private double avoidanceWeight = 0.8; // 避免重复的权重
+ private static RollCallHistoryData historyData = null;
+ private static readonly object historyLock = new object();
+ private static int maxRecentHistory = 20;
+ private static double avoidanceWeight = 0.8; // 避免重复的权重
private const double FREQUENCY_WEIGHT = 0.2; // 频率平衡的权重
// 单次抽相关
@@ -394,13 +395,13 @@ namespace Ink_Canvas
switch (selectedRollCallMode)
{
case "Random":
- return SelectNamesWithML(availableNames, count);
+ return SelectNamesWithML(availableNames, count, random);
case "Sequential":
return SelectNamesSequentially(availableNames, count);
case "Group":
return SelectNamesInGroups(availableNames, count);
default:
- return SelectNamesWithML(availableNames, count);
+ return SelectNamesWithML(availableNames, count, random);
}
}
@@ -471,18 +472,25 @@ namespace Ink_Canvas
///
/// 可用名单
/// 需要选择的人数
+ /// 随机数生成器
/// 选择的人员名单
- private List SelectNamesWithML(List availableNames, int count)
+ public static List SelectNamesWithML(List availableNames, int count, Random random)
{
if (availableNames == null || availableNames.Count == 0)
return new List();
+ // 确保历史数据已初始化
+ if (historyData == null)
+ {
+ LoadRollCallHistory();
+ }
+
// 检查是否启用机器学习避免重复
bool enableML = MainWindow.Settings?.RandSettings?.EnableMLAvoidance ?? true;
if (!enableML)
{
// 如果禁用机器学习,使用简单随机选择
- return SelectNamesRandomly(availableNames, count);
+ return SelectNamesRandomly(availableNames, count, random);
}
var selectedNames = new List();
@@ -490,7 +498,7 @@ namespace Ink_Canvas
for (int i = 0; i < count && remainingNames.Count > 0; i++)
{
- string selectedName = SelectSingleNameWithML(remainingNames, selectedNames);
+ string selectedName = SelectSingleNameWithML(remainingNames, selectedNames, random);
if (!string.IsNullOrEmpty(selectedName))
{
selectedNames.Add(selectedName);
@@ -504,7 +512,7 @@ namespace Ink_Canvas
///
/// 简单随机选择点名人员
///
- private List SelectNamesRandomly(List availableNames, int count)
+ private static List SelectNamesRandomly(List availableNames, int count, Random random)
{
if (availableNames == null || availableNames.Count == 0)
return new List();
@@ -525,7 +533,7 @@ namespace Ink_Canvas
///
/// 使用机器学习算法选择单个人员
///
- private string SelectSingleNameWithML(List availableNames, List alreadySelected)
+ private static string SelectSingleNameWithML(List availableNames, List alreadySelected, Random random)
{
if (availableNames.Count == 0) return null;
if (availableNames.Count == 1) return availableNames[0];
@@ -554,15 +562,15 @@ namespace Ink_Canvas
}
// 使用加权随机选择
- return WeightedRandomSelection(nameWeights);
+ return WeightedRandomSelection(nameWeights, random);
}
///
/// 计算避免最近重复的权重
///
- private double CalculateRecentAvoidanceWeight(string name)
+ private static double CalculateRecentAvoidanceWeight(string name)
{
- if (historyData.History == null || historyData.History.Count == 0)
+ if (historyData == null || historyData.History == null || historyData.History.Count == 0)
return 0.0;
// 获取最近记录
@@ -576,9 +584,9 @@ namespace Ink_Canvas
///
/// 计算频率平衡权重
///
- private double CalculateFrequencyWeight(string name)
+ private static double CalculateFrequencyWeight(string name)
{
- if (historyData.NameFrequency == null || !historyData.NameFrequency.ContainsKey(name))
+ if (historyData == null || historyData.NameFrequency == null || !historyData.NameFrequency.ContainsKey(name))
return 0.5; // 如果从未被选中,给予中等权重
int totalSelections = historyData.NameFrequency.Values.Sum();
@@ -594,7 +602,7 @@ namespace Ink_Canvas
///
/// 加权随机选择
///
- private string WeightedRandomSelection(Dictionary nameWeights)
+ private static string WeightedRandomSelection(Dictionary nameWeights, Random random)
{
if (nameWeights.Count == 0) return null;
@@ -619,15 +627,23 @@ namespace Ink_Canvas
///
/// 更新点名历史记录
///
- private void UpdateRollCallHistory(List selectedNames)
+ public static void UpdateRollCallHistory(List selectedNames)
{
if (selectedNames == null || selectedNames.Count == 0) return;
- // 更新历史记录
- if (historyData.History == null)
- historyData.History = new List();
+ // 确保历史数据已初始化
+ if (historyData == null)
+ {
+ LoadRollCallHistory();
+ }
- historyData.History.AddRange(selectedNames);
+ lock (historyLock)
+ {
+ // 更新历史记录
+ if (historyData.History == null)
+ historyData.History = new List();
+
+ historyData.History.AddRange(selectedNames);
// 保持历史记录不超过100条
if (historyData.History.Count > 100)
@@ -647,18 +663,20 @@ namespace Ink_Canvas
historyData.NameFrequency[name] = 1;
}
- historyData.LastUpdate = DateTime.Now;
+ historyData.LastUpdate = DateTime.Now;
- // 保存到文件
- SaveRollCallHistory();
+ // 保存到文件
+ SaveRollCallHistory();
+ }
}
+
#endregion
#region 数据持久化
///
/// 加载点名历史记录
///
- private void LoadRollCallHistory()
+ private static void LoadRollCallHistory()
{
try
{
@@ -695,7 +713,7 @@ namespace Ink_Canvas
///
/// 保存点名历史记录
///
- private void SaveRollCallHistory()
+ private static void SaveRollCallHistory()
{
try
{
@@ -1292,8 +1310,9 @@ namespace Ink_Canvas
// 动画结束,显示最终结果
Application.Current.Dispatcher.Invoke(() =>
{
- // 使用60个数字进行抽选
- var selectedNumbers = SelectMultipleNumbers(currentCount);
+ // 使用降重抽选方法选择数字
+ var numberList = Enumerable.Range(1, 60).Select(n => n.ToString()).ToList();
+ var selectedNumbers = SelectNamesWithML(numberList, currentCount, random);
// 更新历史记录
UpdateRollCallHistory(selectedNumbers);
@@ -1384,8 +1403,8 @@ namespace Ink_Canvas
// 动画结束,显示最终结果
Application.Current.Dispatcher.Invoke(() =>
{
- // 根据选择的模式进行不同的点名逻辑
- var selectedNames = SelectNamesByMode(nameList, currentCount);
+ // 使用降重抽选方法
+ var selectedNames = SelectNamesWithML(nameList, currentCount, random);
// 更新历史记录
UpdateRollCallHistory(selectedNames);
@@ -1442,8 +1461,12 @@ namespace Ink_Canvas
// 动画结束,显示最终结果
Application.Current.Dispatcher.Invoke(() =>
{
- // 根据选择的数量进行抽选
- var selectedNumbers = SelectMultipleNumbers(currentCount);
+ // 使用降重抽选方法选择数字
+ var numberList = Enumerable.Range(1, 60).Select(n => n.ToString()).ToList();
+ var selectedNumbers = SelectNamesWithML(numberList, currentCount, random);
+
+ // 更新历史记录
+ UpdateRollCallHistory(selectedNumbers);
if (selectedNumbers.Count == 1)
{
@@ -1482,7 +1505,7 @@ namespace Ink_Canvas
}
///
- /// 选择多个数字(不重复)
+ /// 选择多个数字
///
private List SelectMultipleNumbers(int count)
{
From a8dcbd4af030da2ceb31a86a7d08972887ade3af Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sat, 8 Nov 2025 18:01:56 +0800
Subject: [PATCH 15/25] =?UTF-8?q?add:=E7=82=B9=E5=90=8D=E5=8E=86=E5=8F=B2?=
=?UTF-8?q?=E6=9F=A5=E7=9C=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Windows/NewStyleRollCallWindow.cs | 15 ++
.../Windows/NewStyleRollCallWindow.xaml | 14 +-
Ink Canvas/Windows/RollCallHistoryWindow.xaml | 46 +++++
.../Windows/RollCallHistoryWindow.xaml.cs | 184 ++++++++++++++++++
4 files changed, 258 insertions(+), 1 deletion(-)
create mode 100644 Ink Canvas/Windows/RollCallHistoryWindow.xaml
create mode 100644 Ink Canvas/Windows/RollCallHistoryWindow.xaml.cs
diff --git a/Ink Canvas/Windows/NewStyleRollCallWindow.cs b/Ink Canvas/Windows/NewStyleRollCallWindow.cs
index 84dec3a3..b80090d6 100644
--- a/Ink Canvas/Windows/NewStyleRollCallWindow.cs
+++ b/Ink Canvas/Windows/NewStyleRollCallWindow.cs
@@ -856,6 +856,21 @@ namespace Ink_Canvas
}
}
+ private void ViewHistory_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ // 打开历史记录查看窗口
+ var historyWindow = new RollCallHistoryWindow();
+ historyWindow.ShowDialog();
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"打开历史记录失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
+ LogHelper.WriteLogToFile($"打开历史记录失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
private void LoadNamesFromFile()
{
try
diff --git a/Ink Canvas/Windows/NewStyleRollCallWindow.xaml b/Ink Canvas/Windows/NewStyleRollCallWindow.xaml
index 2fd0d5e9..473ac152 100644
--- a/Ink Canvas/Windows/NewStyleRollCallWindow.xaml
+++ b/Ink Canvas/Windows/NewStyleRollCallWindow.xaml
@@ -418,7 +418,7 @@
Foreground="{DynamicResource NewRollCallWindowButtonForeground}"/>
+
diff --git a/Ink Canvas/Windows/RollCallHistoryWindow.xaml b/Ink Canvas/Windows/RollCallHistoryWindow.xaml
new file mode 100644
index 00000000..b2b92e71
--- /dev/null
+++ b/Ink Canvas/Windows/RollCallHistoryWindow.xaml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Ink Canvas/Windows/RollCallHistoryWindow.xaml.cs b/Ink Canvas/Windows/RollCallHistoryWindow.xaml.cs
new file mode 100644
index 00000000..16c7bc0f
--- /dev/null
+++ b/Ink Canvas/Windows/RollCallHistoryWindow.xaml.cs
@@ -0,0 +1,184 @@
+using Ink_Canvas.Helpers;
+using System;
+using System.IO;
+using System.Linq;
+using System.Windows;
+using System.Windows.Media;
+using Newtonsoft.Json;
+using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
+
+namespace Ink_Canvas
+{
+ ///
+ /// Interaction logic for RollCallHistoryWindow.xaml
+ ///
+ public partial class RollCallHistoryWindow : Window
+ {
+ public RollCallHistoryWindow()
+ {
+ InitializeComponent();
+ AnimationsHelper.ShowWithSlideFromBottomAndFade(this, 0.25);
+ ApplyTheme();
+ }
+
+ private void Window_Loaded(object sender, RoutedEventArgs e)
+ {
+ LoadHistory();
+ }
+
+ private void LoadHistory()
+ {
+ try
+ {
+ string configsFolder = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs");
+ string historyJsonPath = System.IO.Path.Combine(configsFolder, "RollCallHistory.json");
+
+ if (!File.Exists(historyJsonPath))
+ {
+ TextBoxHistory.Text = "暂无历史记录";
+ return;
+ }
+
+ string jsonContent = File.ReadAllText(historyJsonPath);
+ var historyData = JsonConvert.DeserializeObject(jsonContent);
+
+ if (historyData == null || historyData.History == null || historyData.History.Count == 0)
+ {
+ TextBoxHistory.Text = "暂无历史记录";
+ return;
+ }
+
+ // 按时间倒序显示(最新的在上方)
+ // 由于历史记录是按时间顺序添加的,所以直接反转即可
+ var reversedHistory = historyData.History.ToList();
+ reversedHistory.Reverse();
+
+ // 显示历史记录,每行一个
+ TextBoxHistory.Text = string.Join(Environment.NewLine, reversedHistory);
+
+ // 显示统计信息
+ int totalCount = historyData.History.Count;
+ string lastUpdate = historyData.LastUpdate.ToString("yyyy-MM-dd HH:mm:ss");
+ string header = $"共 {totalCount} 条记录,最后更新:{lastUpdate}\n\n";
+ TextBoxHistory.Text = header + TextBoxHistory.Text;
+ }
+ catch (Exception ex)
+ {
+ TextBoxHistory.Text = $"加载历史记录失败: {ex.Message}";
+ LogHelper.WriteLogToFile($"加载点名历史记录失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ private void Button_Click(object sender, RoutedEventArgs e)
+ {
+ Close();
+ }
+
+ private void ApplyTheme()
+ {
+ try
+ {
+ if (MainWindow.Settings != null)
+ {
+ ApplyTheme(MainWindow.Settings);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"应用历史记录窗口主题出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ private void ApplyTheme(Settings settings)
+ {
+ try
+ {
+ if (settings.Appearance.Theme == 0) // 浅色主题
+ {
+ iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Light);
+ ApplyThemeResources("Light");
+ }
+ else if (settings.Appearance.Theme == 1) // 深色主题
+ {
+ iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Dark);
+ ApplyThemeResources("Dark");
+ }
+ else // 跟随系统主题
+ {
+ bool isSystemLight = IsSystemThemeLight();
+ if (isSystemLight)
+ {
+ iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Light);
+ ApplyThemeResources("Light");
+ }
+ else
+ {
+ iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Dark);
+ ApplyThemeResources("Dark");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"应用历史记录窗口主题出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ private void ApplyThemeResources(string theme)
+ {
+ try
+ {
+ var resources = this.Resources;
+
+ if (theme == "Light")
+ {
+ // 应用浅色主题资源
+ resources["RollCallHistoryWindowBackground"] = new SolidColorBrush(Color.FromRgb(255, 255, 255));
+ resources["RollCallHistoryWindowForeground"] = new SolidColorBrush(Color.FromRgb(24, 24, 27));
+ resources["RollCallHistoryWindowButtonBackground"] = new SolidColorBrush(Color.FromRgb(244, 244, 245));
+ resources["RollCallHistoryWindowButtonForeground"] = new SolidColorBrush(Color.FromRgb(24, 24, 27));
+ resources["RollCallHistoryWindowBorderBrush"] = new SolidColorBrush(Color.FromRgb(228, 228, 231));
+ }
+ else
+ {
+ // 应用深色主题资源
+ resources["RollCallHistoryWindowBackground"] = new SolidColorBrush(Color.FromRgb(31, 31, 31)); // #1f1f1f
+ resources["RollCallHistoryWindowForeground"] = new SolidColorBrush(Colors.White);
+ resources["RollCallHistoryWindowButtonBackground"] = new SolidColorBrush(Color.FromRgb(42, 42, 42)); // #2a2a2a
+ resources["RollCallHistoryWindowButtonForeground"] = new SolidColorBrush(Colors.White);
+ resources["RollCallHistoryWindowBorderBrush"] = new SolidColorBrush(Color.FromRgb(224, 224, 224)); // #E0E0E0
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"应用历史记录窗口主题资源出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ private bool IsSystemThemeLight()
+ {
+ var light = false;
+ try
+ {
+ var registryKey = Microsoft.Win32.Registry.CurrentUser;
+ var themeKey = registryKey.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize");
+ if (themeKey != null)
+ {
+ var value = themeKey.GetValue("AppsUseLightTheme");
+ if (value != null)
+ {
+ light = (int)value == 1;
+ }
+ themeKey.Close();
+ }
+ }
+ catch
+ {
+ // 如果无法读取注册表,默认使用浅色主题
+ light = true;
+ }
+ return light;
+ }
+ }
+}
+
From ed58873a82790fb1395e1199d87b67f486cd2f0c Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sat, 8 Nov 2025 19:02:37 +0800
Subject: [PATCH 16/25] fix:issue #272
---
Ink Canvas/MainWindow_cs/MW_PPT.cs | 41 ++++++++++++++++++++----------
1 file changed, 27 insertions(+), 14 deletions(-)
diff --git a/Ink Canvas/MainWindow_cs/MW_PPT.cs b/Ink Canvas/MainWindow_cs/MW_PPT.cs
index b9fc064d..19a7f287 100644
--- a/Ink Canvas/MainWindow_cs/MW_PPT.cs
+++ b/Ink Canvas/MainWindow_cs/MW_PPT.cs
@@ -667,7 +667,23 @@ namespace Ink_Canvas
await Application.Current.Dispatcher.InvokeAsync(() =>
{
- var activePresentation = _pptManager?.GetCurrentActivePresentation();
+ Presentation activePresentation = null;
+ int currentSlide = 0;
+ int totalSlides = 0;
+
+ if (wn?.View != null && wn.Presentation != null)
+ {
+ activePresentation = wn.Presentation;
+ currentSlide = wn.View.CurrentShowPosition;
+ totalSlides = activePresentation.Slides.Count;
+ }
+ else
+ {
+ activePresentation = _pptManager?.GetCurrentActivePresentation();
+ currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0;
+ totalSlides = _pptManager?.SlidesCount ?? 0;
+ }
+
if (activePresentation != null)
{
if (Settings.PowerPointSettings.IsSupportWPS)
@@ -691,8 +707,6 @@ namespace Ink_Canvas
}
// 更新UI状态
- var currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0;
- var totalSlides = _pptManager?.SlidesCount ?? 0;
_pptUIManager?.UpdateSlideShowStatus(true, currentSlide, totalSlides);
// 设置浮动栏透明度和边距
@@ -807,20 +821,19 @@ namespace Ink_Canvas
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
- var activePresentation = _pptManager?.GetCurrentActivePresentation();
- if (activePresentation != null)
+ if (wn?.View == null || wn.Presentation == null)
{
- if (Settings.PowerPointSettings.IsSupportWPS)
- {
- }
- else
- {
- _multiPPTInkManager?.SwitchToPresentation(activePresentation);
- }
+ return;
}
- var currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0;
- var totalSlides = _pptManager?.SlidesCount ?? 0;
+ var currentSlide = wn.View.CurrentShowPosition;
+ var activePresentation = wn.Presentation;
+ var totalSlides = activePresentation.Slides.Count;
+
+ if (!Settings.PowerPointSettings.IsSupportWPS)
+ {
+ _multiPPTInkManager?.SwitchToPresentation(activePresentation);
+ }
// 使用防抖机制处理页面切换
HandleSlideSwitchWithDebounce(currentSlide, totalSlides);
From 58b0a0a3be6207abd55f9b100c5dcd14ef9a19f3 Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sat, 8 Nov 2025 19:15:16 +0800
Subject: [PATCH 17/25] =?UTF-8?q?fix:=E8=B4=9F=E6=95=B0=E5=AF=BC=E8=87=B4?=
=?UTF-8?q?=E7=9A=84=E8=AE=A1=E6=97=B6=E5=99=A8=E5=B4=A9=E6=BA=83?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Windows/FullscreenTimerWindow.xaml.cs | 5 ++---
Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs | 5 ++---
Ink Canvas/Windows/NewStyleTimerWindow.cs | 4 ++--
3 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/Ink Canvas/Windows/FullscreenTimerWindow.xaml.cs b/Ink Canvas/Windows/FullscreenTimerWindow.xaml.cs
index 7221e64e..cee0ce8a 100644
--- a/Ink Canvas/Windows/FullscreenTimerWindow.xaml.cs
+++ b/Ink Canvas/Windows/FullscreenTimerWindow.xaml.cs
@@ -125,9 +125,8 @@ namespace Ink_Canvas
seconds = timeSpan.Seconds;
}
- // 更新小时显示
- SetDigitDisplay("FullHour1Display", hours / 10, shouldShowRed);
- SetDigitDisplay("FullHour2Display", hours % 10, shouldShowRed);
+ SetDigitDisplay("FullHour1Display", Math.Abs(hours / 10) % 10, shouldShowRed);
+ SetDigitDisplay("FullHour2Display", (hours % 10 + 10) % 10, shouldShowRed);
// 更新分钟显示
SetDigitDisplay("FullMinute1Display", minutes / 10, shouldShowRed);
diff --git a/Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs b/Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs
index 50731801..10b8a3aa 100644
--- a/Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs
+++ b/Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs
@@ -169,9 +169,8 @@ namespace Ink_Canvas
seconds = timeSpan.Seconds;
}
- // 更新小时显示
- SetDigitDisplay("MinHour1Display", hours / 10, shouldShowRed);
- SetDigitDisplay("MinHour2Display", hours % 10, shouldShowRed);
+ SetDigitDisplay("MinHour1Display", Math.Abs(hours / 10) % 10, shouldShowRed);
+ SetDigitDisplay("MinHour2Display", (hours % 10 + 10) % 10, shouldShowRed);
// 更新分钟显示
SetDigitDisplay("MinMinute1Display", minutes / 10, shouldShowRed);
diff --git a/Ink Canvas/Windows/NewStyleTimerWindow.cs b/Ink Canvas/Windows/NewStyleTimerWindow.cs
index 7c6dcbb0..a46dae48 100644
--- a/Ink Canvas/Windows/NewStyleTimerWindow.cs
+++ b/Ink Canvas/Windows/NewStyleTimerWindow.cs
@@ -130,8 +130,8 @@ namespace Ink_Canvas
bool shouldShowRed = MainWindow.Settings.RandSettings?.EnableOvertimeRedText == true;
- SetDigitDisplay("Digit1Display", displayHours / 10, shouldShowRed);
- SetDigitDisplay("Digit2Display", displayHours % 10, shouldShowRed);
+ SetDigitDisplay("Digit1Display", Math.Abs(displayHours / 10) % 10, shouldShowRed);
+ SetDigitDisplay("Digit2Display", (displayHours % 10 + 10) % 10, shouldShowRed);
SetDigitDisplay("Digit3Display", overtimeSpan.Minutes / 10, shouldShowRed);
SetDigitDisplay("Digit4Display", overtimeSpan.Minutes % 10, shouldShowRed);
SetDigitDisplay("Digit5Display", overtimeSpan.Seconds / 10, shouldShowRed);
From 24c37f1d3ef9d168c4b0291a03e385c7fcd85ddd Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sat, 8 Nov 2025 19:17:58 +0800
Subject: [PATCH 18/25] =?UTF-8?q?fix:=E8=AE=A1=E6=97=B6=E5=99=A8=E6=97=B6?=
=?UTF-8?q?=E9=97=B4=E4=B8=8D=E4=B8=80=E8=87=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Windows/NewStyleTimerWindow.cs | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/Ink Canvas/Windows/NewStyleTimerWindow.cs b/Ink Canvas/Windows/NewStyleTimerWindow.cs
index a46dae48..97cb8a33 100644
--- a/Ink Canvas/Windows/NewStyleTimerWindow.cs
+++ b/Ink Canvas/Windows/NewStyleTimerWindow.cs
@@ -430,10 +430,12 @@ namespace Ink_Canvas
if (isPaused) return null;
var elapsed = DateTime.Now - startTime;
- var totalSeconds = hour * 3600 + minute * 60 + second;
- var remaining = totalSeconds - elapsed.TotalSeconds;
+ var totalTimeSpan = new TimeSpan(hour, minute, second);
+ var leftTimeSpan = totalTimeSpan - elapsed;
- return TimeSpan.FromSeconds(remaining);
+ if (leftTimeSpan.Milliseconds > 0) leftTimeSpan += new TimeSpan(0, 0, 1);
+
+ return leftTimeSpan;
}
public void StopTimer()
From b89d27411bb938b7843f1fc65ff2bd18c8b5ceed Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sat, 8 Nov 2025 19:20:26 +0800
Subject: [PATCH 19/25] =?UTF-8?q?improve:=E8=AE=A1=E6=97=B6=E5=99=A8?=
=?UTF-8?q?=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Windows/NewStyleTimerWindow.cs | 23 +++++++----------------
1 file changed, 7 insertions(+), 16 deletions(-)
diff --git a/Ink Canvas/Windows/NewStyleTimerWindow.cs b/Ink Canvas/Windows/NewStyleTimerWindow.cs
index 97cb8a33..34c7e928 100644
--- a/Ink Canvas/Windows/NewStyleTimerWindow.cs
+++ b/Ink Canvas/Windows/NewStyleTimerWindow.cs
@@ -798,25 +798,16 @@ namespace Ink_Canvas
private void Reset_Click(object sender, RoutedEventArgs e)
{
- if (!isTimerRunning)
+ if (isTimerRunning)
{
- UpdateDigitDisplays();
- isOvertimeMode = false;
- }
- else if (isTimerRunning && isPaused)
- {
- UpdateDigitDisplays();
- StartPauseIcon.Data = Geometry.Parse(PlayIconData);
- isTimerRunning = false;
timer.Stop();
- isPaused = false;
- isOvertimeMode = false;
- }
- else
- {
- startTime = DateTime.Now;
- Timer_Elapsed(timer, null);
+ isTimerRunning = false;
}
+
+ isPaused = false;
+ isOvertimeMode = false;
+ UpdateDigitDisplays();
+ StartPauseIcon.Data = Geometry.Parse(PlayIconData);
}
private void PlayTimerSound()
From 87b9ebc7e15c0159720644d14f21d538b2126fa8 Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sat, 8 Nov 2025 19:36:05 +0800
Subject: [PATCH 20/25] =?UTF-8?q?improve:PPT=E5=A2=A8=E8=BF=B9=E6=98=BE?=
=?UTF-8?q?=E7=A4=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/MainWindow_cs/MW_PPT.cs | 45 ++++++++++++++----------------
1 file changed, 21 insertions(+), 24 deletions(-)
diff --git a/Ink Canvas/MainWindow_cs/MW_PPT.cs b/Ink Canvas/MainWindow_cs/MW_PPT.cs
index 19a7f287..48435399 100644
--- a/Ink Canvas/MainWindow_cs/MW_PPT.cs
+++ b/Ink Canvas/MainWindow_cs/MW_PPT.cs
@@ -758,37 +758,34 @@ namespace Ink_Canvas
PenIcon_Click(null, null);
// 然后设置颜色
BtnColorRed_Click(null, null);
- Dispatcher.BeginInvoke(new Action(() =>
+ try
{
- try
+ if (inkCanvas.EditingMode == InkCanvasEditingMode.Ink)
{
- if (inkCanvas.EditingMode == InkCanvasEditingMode.Ink)
+ UpdateCurrentToolMode("pen");
+ SetFloatingBarHighlightPosition("pen");
+ if (Settings.Appearance.IsShowQuickColorPalette && QuickColorPalettePanel != null && QuickColorPaletteSingleRowPanel != null)
{
- UpdateCurrentToolMode("pen");
- SetFloatingBarHighlightPosition("pen");
- if (Settings.Appearance.IsShowQuickColorPalette && QuickColorPalettePanel != null && QuickColorPaletteSingleRowPanel != null)
+ // 根据显示模式选择显示哪个面板
+ if (Settings.Appearance.QuickColorPaletteDisplayMode == 0)
{
- // 根据显示模式选择显示哪个面板
- if (Settings.Appearance.QuickColorPaletteDisplayMode == 0)
- {
- // 单行显示模式
- QuickColorPalettePanel.Visibility = Visibility.Collapsed;
- QuickColorPaletteSingleRowPanel.Visibility = Visibility.Visible;
- }
- else
- {
- // 双行显示模式
- QuickColorPalettePanel.Visibility = Visibility.Visible;
- QuickColorPaletteSingleRowPanel.Visibility = Visibility.Collapsed;
- }
+ // 单行显示模式
+ QuickColorPalettePanel.Visibility = Visibility.Collapsed;
+ QuickColorPaletteSingleRowPanel.Visibility = Visibility.Visible;
+ }
+ else
+ {
+ // 双行显示模式
+ QuickColorPalettePanel.Visibility = Visibility.Visible;
+ QuickColorPaletteSingleRowPanel.Visibility = Visibility.Collapsed;
}
}
}
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"PPT进入批注模式后同步浮动栏高光状态失败: {ex.Message}", LogHelper.LogType.Error);
- }
- }), DispatcherPriority.Loaded);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"PPT进入批注模式后同步浮动栏高光状态失败: {ex.Message}", LogHelper.LogType.Error);
+ }
}
isEnteredSlideShowEndEvent = false;
From 11bf8cffb2ce7c92b928b4ccaae229d1bbc2e140 Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sat, 8 Nov 2025 19:55:10 +0800
Subject: [PATCH 21/25] =?UTF-8?q?improve:PPT=E5=A2=A8=E8=BF=B9=E5=8A=A0?=
=?UTF-8?q?=E8=BD=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/MainWindow_cs/MW_PPT.cs | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/Ink Canvas/MainWindow_cs/MW_PPT.cs b/Ink Canvas/MainWindow_cs/MW_PPT.cs
index 48435399..d542b616 100644
--- a/Ink Canvas/MainWindow_cs/MW_PPT.cs
+++ b/Ink Canvas/MainWindow_cs/MW_PPT.cs
@@ -1145,6 +1145,9 @@ namespace Ink_Canvas
{
try
{
+ ClearStrokes(true);
+ timeMachine.ClearStrokeHistory();
+
StrokeCollection strokes = null;
if (Settings.PowerPointSettings.IsSupportWPS)
{
@@ -1155,9 +1158,8 @@ namespace Ink_Canvas
strokes = _multiPPTInkManager?.LoadSlideStrokes(slideIndex);
}
- if (strokes != null)
+ if (strokes != null && strokes.Count > 0)
{
- inkCanvas.Strokes.Clear();
inkCanvas.Strokes.Add(strokes);
}
}
@@ -1288,7 +1290,6 @@ namespace Ink_Canvas
// 获取当前页面索引
var currentSlideIndex = _pptManager?.GetCurrentSlideNumber() ?? 0;
-
// 验证页面索引的有效性
if (newSlideIndex <= 0)
{
@@ -1321,10 +1322,9 @@ namespace Ink_Canvas
}
}
}
- else if (inkCanvas.Strokes.Count > 0 && currentSlideIndex <= 0)
- {
- }
+ ClearStrokes(true);
+ timeMachine.ClearStrokeHistory();
StrokeCollection newStrokes = null;
if (Settings.PowerPointSettings.IsSupportWPS)
{
@@ -1334,9 +1334,9 @@ namespace Ink_Canvas
{
newStrokes = _multiPPTInkManager?.SwitchToSlide(newSlideIndex, null);
}
- if (newStrokes != null)
+
+ if (newStrokes != null && newStrokes.Count > 0)
{
- inkCanvas.Strokes.Clear();
inkCanvas.Strokes.Add(newStrokes);
}
From 7f0d29ebd2b787177aa034e76411112b562e2d5f Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sat, 8 Nov 2025 20:07:00 +0800
Subject: [PATCH 22/25] =?UTF-8?q?improve:=E5=BF=AB=E6=8A=BD=E7=AA=97?=
=?UTF-8?q?=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Windows/QuickDrawFloatingButton.cs | 48 +++++++++++++++----
1 file changed, 40 insertions(+), 8 deletions(-)
diff --git a/Ink Canvas/Windows/QuickDrawFloatingButton.cs b/Ink Canvas/Windows/QuickDrawFloatingButton.cs
index 61aebc2f..062306bf 100644
--- a/Ink Canvas/Windows/QuickDrawFloatingButton.cs
+++ b/Ink Canvas/Windows/QuickDrawFloatingButton.cs
@@ -25,6 +25,14 @@ namespace Ink_Canvas
// 设置无焦点状态
this.Focusable = false;
this.ShowInTaskbar = false;
+
+ // 窗口句柄创建后应用无焦点模式
+ this.SourceInitialized += QuickDrawFloatingButton_SourceInitialized;
+ }
+
+ private void QuickDrawFloatingButton_SourceInitialized(object sender, EventArgs e)
+ {
+ ApplyNoFocusMode();
}
@@ -33,12 +41,13 @@ namespace Ink_Canvas
// 设置位置到屏幕右下角稍微靠近中部
SetPositionToBottomRight();
+ // 应用无焦点模式
+ ApplyNoFocusMode();
+
// 应用置顶
ApplyFloatingButtonTopmost();
- // 如果主窗口在无焦点模式下,启动置顶维护
- if (MainWindow.Settings?.Advanced?.IsNoFocusMode == true &&
- MainWindow.Settings?.Advanced?.EnableUIAccessTopMost != true)
+ if (MainWindow.Settings?.Advanced?.EnableUIAccessTopMost != true)
{
StartTopmostMaintenance();
}
@@ -174,6 +183,7 @@ namespace Ink_Canvas
private static extern uint GetCurrentProcessId();
private const int GWL_EXSTYLE = -20;
+ private const int WS_EX_NOACTIVATE = 0x08000000;
private const int WS_EX_TOPMOST = 0x00000008;
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
@@ -187,6 +197,26 @@ namespace Ink_Canvas
private DispatcherTimer topmostMaintenanceTimer;
private bool isTopmostMaintenanceEnabled;
+ ///
+ /// 应用无焦点模式
+ ///
+ private void ApplyNoFocusMode()
+ {
+ try
+ {
+ var hwnd = new WindowInteropHelper(this).Handle;
+ if (hwnd == IntPtr.Zero) return;
+
+ int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
+
+ // 悬浮快抽窗口始终启用无焦点模式
+ SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_NOACTIVATE);
+ }
+ catch (Exception)
+ {
+ }
+ }
+
///
/// 应用悬浮按钮置顶
///
@@ -263,11 +293,7 @@ namespace Ink_Canvas
return;
}
- if (MainWindow.Settings?.Advanced?.IsNoFocusMode != true)
- {
- StopTopmostMaintenance();
- return;
- }
+ // 悬浮快抽窗口始终启用无焦点模式,不需要检查主窗口设置
var hwnd = new WindowInteropHelper(this).Handle;
if (hwnd == IntPtr.Zero) return;
@@ -302,6 +328,12 @@ namespace Ink_Canvas
{
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
}
+
+ // 确保无焦点模式样式正确
+ if ((exStyle & WS_EX_NOACTIVATE) == 0)
+ {
+ SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_NOACTIVATE);
+ }
}
}
catch (Exception ex)
From 008477d5fa3b3b1a54ac8b2c9d84e0d57c1c7ab5 Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sat, 8 Nov 2025 20:17:51 +0800
Subject: [PATCH 23/25] =?UTF-8?q?improve:=E9=BB=98=E8=AE=A4=E8=AE=BE?=
=?UTF-8?q?=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Resources/Settings.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs
index 3941e2ac..de4c9f4d 100644
--- a/Ink Canvas/Resources/Settings.cs
+++ b/Ink Canvas/Resources/Settings.cs
@@ -123,7 +123,7 @@ namespace Ink_Canvas
[JsonIgnore]
public bool IsEnableTwoFingerGestureTranslateOrRotation => IsEnableTwoFingerTranslate || IsEnableTwoFingerRotation;
[JsonProperty("isEnableMultiTouchMode")]
- public bool IsEnableMultiTouchMode { get; set; } = true;
+ public bool IsEnableMultiTouchMode { get; set; } = false;
[JsonProperty("isEnableTwoFingerZoom")]
public bool IsEnableTwoFingerZoom { get; set; } = true;
[JsonProperty("isEnableTwoFingerTranslate")]
From 803cbbdee959b3fd315331a93f56b5d5147bf581 Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sat, 8 Nov 2025 20:29:18 +0800
Subject: [PATCH 24/25] =?UTF-8?q?improve:=E9=BB=98=E8=AE=A4=E8=AE=BE?=
=?UTF-8?q?=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs
index 0e51b58f..bde4b36b 100644
--- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs
+++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs
@@ -592,7 +592,7 @@ namespace Ink_Canvas
ToggleSwitchEnableTwoFingerTranslate.IsOn = false;
BoardToggleSwitchEnableTwoFingerTranslate.IsOn = false;
Settings.Gesture.IsEnableTwoFingerTranslate = false;
- if (!isInMultiTouchMode) ToggleSwitchEnableMultiTouchMode.IsOn = true;
+ // if (!isInMultiTouchMode) ToggleSwitchEnableMultiTouchMode.IsOn = true;
}
else
{
From de1af12157922cf5fbb974a46f659ed1d18036af Mon Sep 17 00:00:00 2001
From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com>
Date: Sat, 8 Nov 2025 20:41:43 +0800
Subject: [PATCH 25/25] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC?=
=?UTF-8?q?=E5=8F=B7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/AssemblyInfo.cs | 4 ++--
Ink Canvas/Properties/AssemblyInfo.cs | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/Ink Canvas/AssemblyInfo.cs b/Ink Canvas/AssemblyInfo.cs
index 604af0dd..e30061e1 100644
--- a/Ink Canvas/AssemblyInfo.cs
+++ b/Ink Canvas/AssemblyInfo.cs
@@ -49,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.7.17.1")]
-[assembly: AssemblyFileVersion("1.7.17.1")]
+[assembly: AssemblyVersion("1.7.17.2")]
+[assembly: AssemblyFileVersion("1.7.17.2")]
diff --git a/Ink Canvas/Properties/AssemblyInfo.cs b/Ink Canvas/Properties/AssemblyInfo.cs
index 604af0dd..e30061e1 100644
--- a/Ink Canvas/Properties/AssemblyInfo.cs
+++ b/Ink Canvas/Properties/AssemblyInfo.cs
@@ -49,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.7.17.1")]
-[assembly: AssemblyFileVersion("1.7.17.1")]
+[assembly: AssemblyVersion("1.7.17.2")]
+[assembly: AssemblyFileVersion("1.7.17.2")]