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] =?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
+}
+