add:Dlass联动
This commit is contained in:
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Dlass API 客户端,用于与服务端通信
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化 Dlass API 客户端
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="appId">应用ID</param>
|
||||||
|
/// <param name="appSecret">应用密钥</param>
|
||||||
|
/// <param name="baseUrl">API基础URL,如果为空则使用默认URL</param>
|
||||||
|
/// <param name="userToken">用户Token,如果提供则优先使用用户token而不是App Secret</param>
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取访问令牌(Access Token)
|
||||||
|
/// </summary>
|
||||||
|
public async Task<string> 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<TokenResponse>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送GET请求
|
||||||
|
/// </summary>
|
||||||
|
public async Task<T> GetAsync<T>(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<T>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送POST请求
|
||||||
|
/// </summary>
|
||||||
|
public async Task<T> PostAsync<T>(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<T>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送PUT请求
|
||||||
|
/// </summary>
|
||||||
|
public async Task<T> PutAsync<T>(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<T>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送DELETE请求
|
||||||
|
/// </summary>
|
||||||
|
public async Task<bool> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 释放资源
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_httpClient?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region 内部类
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token响应模型
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -3185,6 +3185,17 @@
|
|||||||
FontSize="14" Margin="8,0,0,0" />
|
FontSize="14" Margin="8,0,0,0" />
|
||||||
</ui:SimpleStackPanel>
|
</ui:SimpleStackPanel>
|
||||||
|
|
||||||
|
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
|
||||||
|
StrokeThickness="1" Margin="0,8,0,8" />
|
||||||
|
|
||||||
|
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,8,0,0">
|
||||||
|
<Button Name="BtnDlassSettingsManage" Content="Dlass设置管理"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Click="BtnDlassSettingsManage_Click"
|
||||||
|
Padding="15,5"
|
||||||
|
Margin="0,0,0,0"/>
|
||||||
|
</ui:SimpleStackPanel>
|
||||||
|
|
||||||
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
|
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
|
||||||
StrokeThickness="1" Margin="0,8,0,8" />
|
StrokeThickness="1" Margin="0,8,0,8" />
|
||||||
|
|
||||||
|
|||||||
@@ -1846,6 +1846,62 @@ namespace Ink_Canvas
|
|||||||
SaveSettingsToFile();
|
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)
|
private void ToggleSwitchAutoDelSavedFiles_Toggled(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (!isLoaded) return;
|
if (!isLoaded) return;
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ namespace Ink_Canvas
|
|||||||
public ModeSettings ModeSettings { get; set; } = new ModeSettings();
|
public ModeSettings ModeSettings { get; set; } = new ModeSettings();
|
||||||
[JsonProperty("camera")]
|
[JsonProperty("camera")]
|
||||||
public CameraSettings Camera { get; set; } = new CameraSettings();
|
public CameraSettings Camera { get; set; } = new CameraSettings();
|
||||||
|
[JsonProperty("dlass")]
|
||||||
|
public DlassSettings Dlass { get; set; } = new DlassSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Canvas
|
public class Canvas
|
||||||
@@ -724,4 +726,16 @@ namespace Ink_Canvas
|
|||||||
[JsonProperty("selectedCameraIndex")]
|
[JsonProperty("selectedCameraIndex")]
|
||||||
public int SelectedCameraIndex { get; set; } = 0;
|
public int SelectedCameraIndex { get; set; } = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class DlassSettings
|
||||||
|
{
|
||||||
|
[JsonProperty("userToken")]
|
||||||
|
public string UserToken { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonProperty("savedTokens")]
|
||||||
|
public List<string> SavedTokens { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
[JsonProperty("apiBaseUrl")]
|
||||||
|
public string ApiBaseUrl { get; set; } = "https://dlass.tech";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,388 @@
|
|||||||
|
<Window x:Class="Ink_Canvas.Windows.DlassSettingsWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
|
||||||
|
xmlns:controls="clr-namespace:Ink_Canvas.Windows.Controls"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
WindowStyle="None"
|
||||||
|
Title="Dlass设置管理" Height="600" Width="900"
|
||||||
|
WindowStartupLocation="CenterScreen"
|
||||||
|
ResizeMode="CanResize"
|
||||||
|
AllowsTransparency="True"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<Window.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary Source="Controls/WinUI3CloseButton.xaml" />
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
<SolidColorBrush x:Key="WindowBackground" Color="#1e1e1e"/>
|
||||||
|
<SolidColorBrush x:Key="BorderBrush" Color="#3f3f46"/>
|
||||||
|
<SolidColorBrush x:Key="TextForeground" Color="#fafafa"/>
|
||||||
|
<SolidColorBrush x:Key="TextSecondary" Color="#a1a1aa"/>
|
||||||
|
<SolidColorBrush x:Key="AccentColor" Color="#3b82f6"/>
|
||||||
|
<SolidColorBrush x:Key="TitleForeground" Color="#fafafa"/>
|
||||||
|
<SolidColorBrush x:Key="NewTimerWindowButtonForeground" Color="White"/>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Window.Resources>
|
||||||
|
|
||||||
|
<Border Background="{StaticResource WindowBackground}"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="15"
|
||||||
|
Margin="10"
|
||||||
|
x:Name="MainBorder"
|
||||||
|
MouseLeftButtonDown="TitleBar_MouseLeftButtonDown">
|
||||||
|
<Grid>
|
||||||
|
<controls:WinUI3CloseButton x:Name="BtnClose"
|
||||||
|
HorizontalAlignment="Right" VerticalAlignment="Top"
|
||||||
|
Margin="0,0,0,0" Cursor="Hand" Click="BtnClose_Click"
|
||||||
|
Content="✕"/>
|
||||||
|
|
||||||
|
<!-- 主要内容区域 -->
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- 标题栏 -->
|
||||||
|
<Grid Grid.Row="0"
|
||||||
|
Height="50"
|
||||||
|
Background="{StaticResource WindowBackground}"
|
||||||
|
x:Name="TitleBar"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
MouseLeftButtonDown="TitleBar_MouseLeftButtonDown"
|
||||||
|
Margin="0,0,46,0">
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="22,0,0,0">
|
||||||
|
<!-- 设置图标 -->
|
||||||
|
<Path Data="M12 15.5A3.5 3.5 0 0 1 8.5 12A3.5 3.5 0 0 1 12 8.5A3.5 3.5 0 0 1 15.5 12A3.5 3.5 0 0 1 12 15.5M19.43 12.97C19.47 12.65 19.5 12.33 19.5 12C19.5 11.67 19.47 11.34 19.43 11.03L21.54 9.37C21.73 9.22 21.78 8.95 21.66 8.73L19.66 5.27C19.54 5.05 19.27 4.96 19.05 5.05L16.56 6.05C16.04 5.65 15.5 5.32 14.87 5.07L14.5 2.42C14.46 2.18 14.25 2 14 2H10C9.75 2 9.54 2.18 9.5 2.42L9.13 5.07C8.5 5.32 7.96 5.66 7.44 6.05L4.95 5.05C4.73 4.96 4.46 5.05 4.35 5.27L2.35 8.73C2.23 8.95 2.27 9.22 2.46 9.37L4.57 11.03C4.53 11.34 4.5 11.67 4.5 12C4.5 12.33 4.53 12.65 4.57 12.97L2.46 14.63C2.27 14.78 2.23 15.05 2.35 15.27L4.35 18.73C4.46 18.95 4.73 19.03 4.95 18.95L7.44 17.95C7.96 18.34 8.5 18.68 9.13 18.93L9.5 21.58C9.54 21.82 9.75 22 10 22H14C14.25 22 14.46 21.82 14.5 21.58L14.87 18.93C15.5 18.67 16.04 18.34 16.56 17.95L19.05 18.95C19.27 19.03 19.54 18.95 19.66 18.73L21.66 15.27C21.78 15.05 21.73 14.78 21.54 14.63L19.43 12.97Z"
|
||||||
|
Stroke="{StaticResource TitleForeground}"
|
||||||
|
StrokeThickness="1.5"
|
||||||
|
StrokeLineJoin="Round"
|
||||||
|
Fill="Transparent"
|
||||||
|
Width="24" Height="24"
|
||||||
|
Stretch="Uniform"
|
||||||
|
Margin="0,0,8,0"/>
|
||||||
|
<!-- 标题文字 -->
|
||||||
|
<TextBlock Text="Dlass设置管理"
|
||||||
|
FontSize="28"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Foreground="{StaticResource TitleForeground}"
|
||||||
|
x:Name="TitleText"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<!-- 主内容区 -->
|
||||||
|
<Border Grid.Row="1"
|
||||||
|
Background="{StaticResource WindowBackground}"
|
||||||
|
Padding="20,10,20,20">
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||||
|
<ui:SimpleStackPanel Spacing="16">
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<Border BorderThickness="1"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}"
|
||||||
|
CornerRadius="8"
|
||||||
|
Padding="20"
|
||||||
|
Background="#27272a">
|
||||||
|
<ui:SimpleStackPanel Spacing="16">
|
||||||
|
<TextBlock Text="Dlass设置管理"
|
||||||
|
FontSize="18"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="{StaticResource TextForeground}"/>
|
||||||
|
|
||||||
|
<TextBlock Text="管理您的Dlass服务端连接和设置。"
|
||||||
|
FontSize="14"
|
||||||
|
Foreground="{StaticResource TextSecondary}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Margin="0,0,0,8"/>
|
||||||
|
|
||||||
|
<Line HorizontalAlignment="Stretch"
|
||||||
|
X1="0" Y1="0" X2="1" Y2="0"
|
||||||
|
Stroke="{StaticResource BorderBrush}"
|
||||||
|
StrokeThickness="1"
|
||||||
|
Margin="0,8,0,12"/>
|
||||||
|
|
||||||
|
<!-- 用户Token设置 -->
|
||||||
|
<TextBlock Text="用户Token"
|
||||||
|
FontSize="16"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="{StaticResource TextForeground}"
|
||||||
|
Margin="0,0,0,8"/>
|
||||||
|
|
||||||
|
<TextBlock Text="设置您的用户Token以访问Dlass服务端功能。您可以从Dlass平台获取您的用户Token。"
|
||||||
|
FontSize="12"
|
||||||
|
Foreground="{StaticResource TextSecondary}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Margin="0,0,0,12"/>
|
||||||
|
|
||||||
|
<ui:SimpleStackPanel Orientation="Vertical" Spacing="8">
|
||||||
|
<ComboBox x:Name="CmbSavedTokens"
|
||||||
|
FontSize="14"
|
||||||
|
Padding="12,8"
|
||||||
|
Background="#18181b"
|
||||||
|
Foreground="{StaticResource TextForeground}"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}"
|
||||||
|
BorderThickness="1"
|
||||||
|
MinHeight="36"
|
||||||
|
IsEditable="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
SelectionChanged="CmbSavedTokens_SelectionChanged">
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
|
<TextBox x:Name="TxtNewToken"
|
||||||
|
FontSize="14"
|
||||||
|
Padding="12,8"
|
||||||
|
Background="#18181b"
|
||||||
|
Foreground="{StaticResource TextForeground}"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}"
|
||||||
|
BorderThickness="1"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
AcceptsReturn="False"
|
||||||
|
MaxLength="500"
|
||||||
|
MinHeight="36"
|
||||||
|
Tag="输入新的Token">
|
||||||
|
<TextBox.Style>
|
||||||
|
<Style TargetType="TextBox">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="TextBox">
|
||||||
|
<Border Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="6"
|
||||||
|
Padding="{TemplateBinding Padding}">
|
||||||
|
<ScrollViewer x:Name="PART_ContentHost" />
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</TextBox.Style>
|
||||||
|
</TextBox>
|
||||||
|
|
||||||
|
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="8">
|
||||||
|
<Button x:Name="BtnSaveToken"
|
||||||
|
Content="保存Token"
|
||||||
|
Padding="12,6"
|
||||||
|
FontSize="13"
|
||||||
|
Background="{StaticResource AccentColor}"
|
||||||
|
Foreground="White"
|
||||||
|
BorderThickness="0"
|
||||||
|
Cursor="Hand"
|
||||||
|
Click="BtnSaveToken_Click">
|
||||||
|
<Button.Style>
|
||||||
|
<Style TargetType="Button">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border Background="{TemplateBinding Background}"
|
||||||
|
CornerRadius="6"
|
||||||
|
Padding="{TemplateBinding Padding}">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter Property="Background" Value="#2563eb"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsPressed" Value="True">
|
||||||
|
<Setter Property="Background" Value="#1d4ed8"/>
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Button.Style>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button x:Name="BtnClearToken"
|
||||||
|
Content="清除Token"
|
||||||
|
Padding="12,6"
|
||||||
|
FontSize="13"
|
||||||
|
Background="Transparent"
|
||||||
|
Foreground="{StaticResource TextForeground}"
|
||||||
|
BorderThickness="1"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}"
|
||||||
|
Cursor="Hand"
|
||||||
|
Click="BtnClearToken_Click">
|
||||||
|
<Button.Style>
|
||||||
|
<Style TargetType="Button">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="6"
|
||||||
|
Padding="{TemplateBinding Padding}">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter Property="Background" Value="#27272a"/>
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Button.Style>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button x:Name="BtnTestToken"
|
||||||
|
Content="测试连接"
|
||||||
|
Padding="12,6"
|
||||||
|
FontSize="13"
|
||||||
|
Background="Transparent"
|
||||||
|
Foreground="{StaticResource TextForeground}"
|
||||||
|
BorderThickness="1"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}"
|
||||||
|
Cursor="Hand"
|
||||||
|
Click="BtnTestToken_Click">
|
||||||
|
<Button.Style>
|
||||||
|
<Style TargetType="Button">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="6"
|
||||||
|
Padding="{TemplateBinding Padding}">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter Property="Background" Value="#27272a"/>
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Button.Style>
|
||||||
|
</Button>
|
||||||
|
</ui:SimpleStackPanel>
|
||||||
|
|
||||||
|
<TextBlock x:Name="TxtTokenStatus"
|
||||||
|
Text=""
|
||||||
|
FontSize="12"
|
||||||
|
Foreground="{StaticResource TextSecondary}"
|
||||||
|
Margin="0,4,0,0"/>
|
||||||
|
</ui:SimpleStackPanel>
|
||||||
|
|
||||||
|
<Line HorizontalAlignment="Stretch"
|
||||||
|
X1="0" Y1="0" X2="1" Y2="0"
|
||||||
|
Stroke="{StaticResource BorderBrush}"
|
||||||
|
StrokeThickness="1"
|
||||||
|
Margin="0,12,0,8"/>
|
||||||
|
|
||||||
|
<!-- 连接状态 -->
|
||||||
|
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="12">
|
||||||
|
<TextBlock Text="连接状态:"
|
||||||
|
FontSize="14"
|
||||||
|
Foreground="{StaticResource TextForeground}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Width="100"/>
|
||||||
|
<TextBlock x:Name="TxtConnectionStatus"
|
||||||
|
Text="未连接"
|
||||||
|
FontSize="14"
|
||||||
|
Foreground="{StaticResource TextSecondary}"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
</ui:SimpleStackPanel>
|
||||||
|
|
||||||
|
<Line HorizontalAlignment="Stretch"
|
||||||
|
X1="0" Y1="0" X2="1" Y2="0"
|
||||||
|
Stroke="{StaticResource BorderBrush}"
|
||||||
|
StrokeThickness="1"
|
||||||
|
Margin="0,8,0,12"/>
|
||||||
|
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<ui:SimpleStackPanel Orientation="Horizontal" Spacing="12" Margin="0,8,0,0">
|
||||||
|
<Button Content="保存"
|
||||||
|
Padding="16,8"
|
||||||
|
FontSize="14"
|
||||||
|
Background="{StaticResource AccentColor}"
|
||||||
|
Foreground="White"
|
||||||
|
BorderThickness="0"
|
||||||
|
Cursor="Hand"
|
||||||
|
Click="BtnSave_Click">
|
||||||
|
<Button.Style>
|
||||||
|
<Style TargetType="Button">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border Background="{TemplateBinding Background}"
|
||||||
|
CornerRadius="4"
|
||||||
|
Padding="{TemplateBinding Padding}">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter Property="Background" Value="#2563eb"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsPressed" Value="True">
|
||||||
|
<Setter Property="Background" Value="#1d4ed8"/>
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Button.Style>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button Content="取消"
|
||||||
|
Padding="16,8"
|
||||||
|
FontSize="14"
|
||||||
|
Background="Transparent"
|
||||||
|
Foreground="{StaticResource TextForeground}"
|
||||||
|
BorderThickness="1"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}"
|
||||||
|
Cursor="Hand"
|
||||||
|
Click="BtnCancel_Click">
|
||||||
|
<Button.Style>
|
||||||
|
<Style TargetType="Button">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="4"
|
||||||
|
Padding="{TemplateBinding Padding}">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter Property="Background" Value="#27272a"/>
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Button.Style>
|
||||||
|
</Button>
|
||||||
|
</ui:SimpleStackPanel>
|
||||||
|
</ui:SimpleStackPanel>
|
||||||
|
</Border>
|
||||||
|
</ui:SimpleStackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Window>
|
||||||
|
|
||||||
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// DlassSettingsWindow.xaml 的交互逻辑
|
||||||
|
/// </summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化API客户端
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取用户token
|
||||||
|
/// </summary>
|
||||||
|
private string GetUserToken()
|
||||||
|
{
|
||||||
|
if (MainWindow.Settings?.Dlass != null)
|
||||||
|
{
|
||||||
|
return MainWindow.Settings.Dlass.UserToken ?? string.Empty;
|
||||||
|
}
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取保存的Token列表
|
||||||
|
/// </summary>
|
||||||
|
private List<string> GetSavedTokens()
|
||||||
|
{
|
||||||
|
if (MainWindow.Settings?.Dlass != null)
|
||||||
|
{
|
||||||
|
return MainWindow.Settings.Dlass.SavedTokens ?? new List<string>();
|
||||||
|
}
|
||||||
|
return new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加载用户token到UI
|
||||||
|
/// </summary>
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存用户token
|
||||||
|
/// </summary>
|
||||||
|
private void SaveUserToken(string token)
|
||||||
|
{
|
||||||
|
if (MainWindow.Settings?.Dlass != null)
|
||||||
|
{
|
||||||
|
MainWindow.Settings.Dlass.UserToken = token ?? string.Empty;
|
||||||
|
MainWindow.SaveSettingsToFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加Token到保存列表
|
||||||
|
/// </summary>
|
||||||
|
private void AddTokenToList(string token)
|
||||||
|
{
|
||||||
|
if (MainWindow.Settings?.Dlass != null)
|
||||||
|
{
|
||||||
|
if (MainWindow.Settings.Dlass.SavedTokens == null)
|
||||||
|
{
|
||||||
|
MainWindow.Settings.Dlass.SavedTokens = new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(token) && !MainWindow.Settings.Dlass.SavedTokens.Contains(token))
|
||||||
|
{
|
||||||
|
MainWindow.Settings.Dlass.SavedTokens.Add(token);
|
||||||
|
MainWindow.SaveSettingsToFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从列表删除Token
|
||||||
|
/// </summary>
|
||||||
|
private void RemoveTokenFromList(string token)
|
||||||
|
{
|
||||||
|
if (MainWindow.Settings?.Dlass != null && MainWindow.Settings.Dlass.SavedTokens != null)
|
||||||
|
{
|
||||||
|
MainWindow.Settings.Dlass.SavedTokens.Remove(token);
|
||||||
|
MainWindow.SaveSettingsToFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标题栏拖动事件
|
||||||
|
/// </summary>
|
||||||
|
private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.ButtonState == MouseButtonState.Pressed)
|
||||||
|
{
|
||||||
|
DragMove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关闭按钮点击事件
|
||||||
|
/// </summary>
|
||||||
|
private void BtnClose_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下拉框选择改变事件
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存Token按钮点击事件
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清除Token按钮点击事件
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 测试Token连接按钮点击事件
|
||||||
|
/// </summary>
|
||||||
|
private async void BtnTestToken_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
await TestConnectionAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存按钮点击事件
|
||||||
|
/// </summary>
|
||||||
|
private async void BtnSave_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// TODO: 根据实际API文档实现保存逻辑
|
||||||
|
// 示例:保存设置到服务器
|
||||||
|
// var settings = new { ... };
|
||||||
|
// await _apiClient.PostAsync<ApiResponse>("/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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 取消按钮点击事件
|
||||||
|
/// </summary>
|
||||||
|
private void BtnCancel_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 测试API连接
|
||||||
|
/// </summary>
|
||||||
|
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<AuthWithTokenResponse>("/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响应模型
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// auth-with-token接口响应模型
|
||||||
|
/// </summary>
|
||||||
|
public class AuthWithTokenResponse
|
||||||
|
{
|
||||||
|
[Newtonsoft.Json.JsonProperty("success")]
|
||||||
|
public bool Success { get; set; }
|
||||||
|
|
||||||
|
[Newtonsoft.Json.JsonProperty("whiteboards")]
|
||||||
|
public List<WhiteboardInfo> Whiteboards { get; set; }
|
||||||
|
|
||||||
|
[Newtonsoft.Json.JsonProperty("count")]
|
||||||
|
public int Count { get; set; }
|
||||||
|
|
||||||
|
[Newtonsoft.Json.JsonProperty("user")]
|
||||||
|
public UserInfo User { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 白板信息模型
|
||||||
|
/// </summary>
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户信息模型
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user