引入AI支持(火山引擎)
This commit is contained in:
parent
07ab6fa3cc
commit
a6d1c966d8
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
using System.ClientModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.AI;
|
||||
using OpenAI;
|
||||
|
||||
namespace ExcelHelper.AI;
|
||||
|
||||
class AiHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 火山引擎
|
||||
/// </summary>
|
||||
private const string API_KEY = "";
|
||||
private const string API_ENDPOINT = "https://ark.cn-beijing.volces.com/api/v3/";
|
||||
|
||||
private readonly IChatClient _client;
|
||||
|
||||
private bool UseTool = false;
|
||||
public AiHelper(string modelName, bool useTools = false)
|
||||
{
|
||||
UseTool = useTools;
|
||||
if (useTools)
|
||||
{
|
||||
//TODO: add function call
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
var client = new OpenAIClient(new ApiKeyCredential(API_KEY), new OpenAIClientOptions
|
||||
{
|
||||
Endpoint = new Uri(API_ENDPOINT),
|
||||
});
|
||||
_client = client.AsChatClient(modelName);
|
||||
}
|
||||
}
|
||||
|
||||
public IChatClient Client => _client;
|
||||
|
||||
public async Task<string> GetChatResponseAsync(string question)
|
||||
{
|
||||
var response = await _client.GetResponseAsync(question);
|
||||
return response.Message.ToString();
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<string> GetStreamingResponseAsync(string question)
|
||||
{
|
||||
// 获取所有的工具函数
|
||||
//var toolsList = AgentExecutor.GetDefaultTools();
|
||||
ChatOptions chatOptions = new ChatOptions
|
||||
{
|
||||
// TODO: Models config
|
||||
TopK= 10,
|
||||
TopP = 0.85f,
|
||||
Temperature = 0.7f,
|
||||
|
||||
};
|
||||
|
||||
if (UseTool)
|
||||
{
|
||||
//TODO: add function call
|
||||
}
|
||||
await foreach (var response in _client.GetStreamingResponseAsync(question, chatOptions))
|
||||
{
|
||||
yield return response.Text;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ExcelHelper.AI
|
||||
{
|
||||
class PromptUtil
|
||||
{
|
||||
public static string UsePrompt(string msg,string excelPrompt)
|
||||
{
|
||||
return $@"
|
||||
你是一个Excel公式大师,你需要帮助用户解答Excel公式相关的问题。
|
||||
|
||||
你在回复用户的问题时,需要遵守以下规则:
|
||||
1. 优先保证答案的准确性,其次是简洁明了。
|
||||
2. 需要保证你的回答是有逻辑性的,不要让用户感到困惑。
|
||||
3. 可以例举2~3个例子来帮助用户更好地理解你的回答。
|
||||
4. 一定要保证文档格式的正确性,避免出现无法显示的内容。
|
||||
5. 尽量使用比较简短与易懂的公式来回答用户的问题,避免用户无法理解。
|
||||
|
||||
现在的时间是{DateTime.Now}。
|
||||
|
||||
当前用户提问的excel文件列信息为:{excelPrompt}
|
||||
|
||||
用户的问题是:{msg}
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
7
App.xaml
7
App.xaml
|
@ -2,13 +2,18 @@
|
|||
x:Class="ExcelHelper.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:ExcelHelper">
|
||||
xmlns:local="clr-namespace:ExcelHelper"
|
||||
xmlns:local1="clr-namespace:ExcelHelper.Converter">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml" />
|
||||
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace ExcelHelper.Converter;
|
||||
|
||||
public class BoolToColorConverter : IValueConverter
|
||||
{
|
||||
public Brush UserMessageColor { get; set; } = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#DCF8C6"));
|
||||
public Brush AIMessageColor { get; set; } = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#ECECEC"));
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is bool isUser)
|
||||
{
|
||||
return isUser ? UserMessageColor : AIMessageColor;
|
||||
}
|
||||
return AIMessageColor;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class BoolToAlignmentConverter : IValueConverter
|
||||
{
|
||||
public HorizontalAlignment TrueValue { get; set; } = HorizontalAlignment.Right;
|
||||
public HorizontalAlignment FalseValue { get; set; } = HorizontalAlignment.Left;
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is bool boolValue)
|
||||
{
|
||||
return boolValue ? TrueValue : FalseValue;
|
||||
}
|
||||
return FalseValue;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class InverseBoolConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is bool boolValue)
|
||||
{
|
||||
return !boolValue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is bool boolValue)
|
||||
{
|
||||
return !boolValue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -46,14 +46,18 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="AvalonEdit" Version="6.3.0.90" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm">
|
||||
<Version>8.3.2</Version>
|
||||
<Version>8.4.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="DuckDB.NET.Bindings.Full" Version="1.1.3" />
|
||||
<PackageReference Include="DuckDB.NET.Data.Full" Version="1.1.3" />
|
||||
<PackageReference Include="HandyControl">
|
||||
<Version>3.5.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MdXaml" Version="1.27.0" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI" Version="9.3.0-preview.1.25114.11" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.3.0-preview.1.25114.11" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3116-prerelease" />
|
||||
<PackageReference Include="MiniExcel">
|
||||
<Version>1.34.2</Version>
|
||||
</PackageReference>
|
||||
|
@ -69,4 +73,7 @@
|
|||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0-rc.2.24473.5" />
|
||||
<PackageReference Include="TinyPinyin" Version="1.1.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Web\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,131 @@
|
|||
<UserControl
|
||||
x:Class="ExcelHelper.Views.Components.AiMessageControll"
|
||||
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:local="clr-namespace:ExcelHelper.Views.Components"
|
||||
xmlns:local1="clr-namespace:ExcelHelper.Converter"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
|
||||
x:Name="userControl"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<!-- 将布尔值转换为颜色 (用户消息和AI消息使用不同颜色) -->
|
||||
<local1:BoolToColorConverter
|
||||
x:Key="BoolToColorConverter"
|
||||
AIMessageColor="#ECECEC"
|
||||
UserMessageColor="#DCF8C6" />
|
||||
|
||||
<!-- 将布尔值转换为对齐方式 (用户消息靠右,AI消息靠左) -->
|
||||
<local1:BoolToAlignmentConverter
|
||||
x:Key="BoolToAlignmentConverter"
|
||||
FalseValue="Left"
|
||||
TrueValue="Right" />
|
||||
|
||||
<!-- 布尔值取反转换器 -->
|
||||
<local1:InverseBoolConverter x:Key="InverseBoolConverter" />
|
||||
|
||||
<!-- 布尔值到可见性转换器 -->
|
||||
<BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 消息显示区 -->
|
||||
<ScrollViewer
|
||||
Grid.Row="0"
|
||||
Margin="5"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl ItemsSource="{Binding Messages, UpdateSourceTrigger=PropertyChanged, ElementName=userControl}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Margin="5"
|
||||
Padding="5"
|
||||
HorizontalAlignment="{Binding IsUser, Converter={StaticResource BoolToAlignmentConverter}}"
|
||||
Background="{Binding IsUser, Converter={StaticResource BoolToColorConverter}}"
|
||||
CornerRadius="10">
|
||||
<StackPanel>
|
||||
<mdxam:MarkdownScrollViewer
|
||||
x:Name="Markdownview"
|
||||
Width="Auto"
|
||||
FontSize="12"
|
||||
Markdown="{Binding Content}"
|
||||
PreviewMouseWheel="Markdownview_PreviewMouseWheel" />
|
||||
|
||||
<TextBlock
|
||||
Margin="0,5,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
FontSize="10"
|
||||
Foreground="#99000000"
|
||||
Text="{Binding Timestamp, StringFormat='{}{0:HH:mm}'}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- 输入区 -->
|
||||
<Grid Grid.Row="1" Margin="5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border
|
||||
Grid.Column="0"
|
||||
BorderBrush="#CCCCCC"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5">
|
||||
|
||||
<TextBox
|
||||
x:Name="MessageInput"
|
||||
MinHeight="40"
|
||||
MaxHeight="120"
|
||||
Padding="8"
|
||||
AcceptsReturn="True"
|
||||
BorderThickness="0"
|
||||
IsEnabled="{Binding IsWaiting, Converter={StaticResource InverseBoolConverter}, UpdateSourceTrigger=PropertyChanged, ElementName=userControl}"
|
||||
KeyDown="MessageInput_KeyDown"
|
||||
Text="{Binding CurrentMessage, UpdateSourceTrigger=PropertyChanged, ElementName=userControl}"
|
||||
TextWrapping="Wrap"
|
||||
VerticalScrollBarVisibility="Auto" />
|
||||
</Border>
|
||||
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Height="40"
|
||||
Margin="5,0,0,0"
|
||||
Padding="15,0"
|
||||
Click="SendButton_Click"
|
||||
Content="发送"
|
||||
IsEnabled="{Binding IsWaiting, Converter={StaticResource InverseBoolConverter}, UpdateSourceTrigger=PropertyChanged, ElementName=userControl}" />
|
||||
</Grid>
|
||||
|
||||
<!-- 等待指示器 -->
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Background="#80FFFFFF"
|
||||
Visibility="{Binding IsWaiting, Converter={StaticResource BoolToVisibilityConverter}, UpdateSourceTrigger=PropertyChanged, ElementName=userControl}">
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="AI正在思考中..." />
|
||||
<ProgressBar
|
||||
Width="100"
|
||||
Height="15"
|
||||
Margin="10,0,0,0"
|
||||
IsIndeterminate="True" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
|
@ -0,0 +1,184 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace ExcelHelper.Views.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// AiMessageControll.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class AiMessageControll : UserControl
|
||||
{
|
||||
// 消息集合
|
||||
public ObservableCollection<ChatMessage> Messages
|
||||
{
|
||||
get
|
||||
{
|
||||
return (ObservableCollection<ChatMessage>)GetValue(MessagesProperty);
|
||||
}
|
||||
set
|
||||
{
|
||||
SetValue(MessagesProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty MessagesProperty =
|
||||
DependencyProperty.Register("Messages", typeof(ObservableCollection<ChatMessage>), typeof(AiMessageControll),
|
||||
new PropertyMetadata(new ObservableCollection<ChatMessage>()));
|
||||
|
||||
|
||||
// 当前输入的消息
|
||||
public string CurrentMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return (string)GetValue(CurrentMessageProperty);
|
||||
}
|
||||
set
|
||||
{
|
||||
SetValue(CurrentMessageProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty CurrentMessageProperty =
|
||||
DependencyProperty.Register("CurrentMessage", typeof(string), typeof(AiMessageControll), new PropertyMetadata(string.Empty));
|
||||
|
||||
// 发送命令
|
||||
public ICommand SendCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
return (ICommand)GetValue(SendCommandProperty);
|
||||
}
|
||||
set
|
||||
{
|
||||
SetValue(SendCommandProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SendCommandProperty =
|
||||
DependencyProperty.Register("SendCommand", typeof(ICommand), typeof(AiMessageControll), new PropertyMetadata(null));
|
||||
|
||||
// 是否正在等待AI响应
|
||||
public bool IsWaiting
|
||||
{
|
||||
get
|
||||
{
|
||||
return (bool)GetValue(IsWaitingProperty);
|
||||
}
|
||||
set
|
||||
{
|
||||
SetValue(IsWaitingProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsWaitingProperty =
|
||||
DependencyProperty.Register("IsWaiting", typeof(bool), typeof(AiMessageControll), new PropertyMetadata(false));
|
||||
|
||||
public AiMessageControll()
|
||||
{
|
||||
InitializeComponent();
|
||||
//DataContext = this;
|
||||
}
|
||||
|
||||
private void SendButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (SendCommand != null && SendCommand.CanExecute(CurrentMessage))
|
||||
{
|
||||
SendCommand.Execute(CurrentMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void MessageInput_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Enter && !string.IsNullOrEmpty(CurrentMessage) && !IsWaiting)
|
||||
{
|
||||
if (SendCommand != null && SendCommand.CanExecute(CurrentMessage))
|
||||
{
|
||||
SendCommand.Execute(CurrentMessage);
|
||||
}
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void Markdownview_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
|
||||
{
|
||||
// 将滚动事件传递给外部的 ScrollViewer
|
||||
var scrollViewer = FindParent<ScrollViewer>((DependencyObject)sender);
|
||||
if (scrollViewer != null)
|
||||
{
|
||||
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private T FindParent<T>(DependencyObject child) where T : DependencyObject
|
||||
{
|
||||
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
|
||||
if (parentObject == null) return null;
|
||||
|
||||
if (parentObject is T parent)
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FindParent<T>(parentObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 聊天消息类
|
||||
public class ChatMessage : INotifyPropertyChanged
|
||||
{
|
||||
private string _content;
|
||||
public string Content
|
||||
{
|
||||
get
|
||||
{
|
||||
return _content;
|
||||
}
|
||||
set
|
||||
{
|
||||
_content = value;
|
||||
OnPropertyChanged(nameof(Content));
|
||||
}
|
||||
}
|
||||
private bool _isUser;
|
||||
public bool IsUser
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isUser;
|
||||
}
|
||||
set
|
||||
{
|
||||
_isUser = value;
|
||||
OnPropertyChanged(nameof(IsUser));
|
||||
}
|
||||
}
|
||||
private DateTime _timestamp = DateTime.Now;
|
||||
public DateTime Timestamp
|
||||
{
|
||||
get
|
||||
{
|
||||
return _timestamp;
|
||||
}
|
||||
set
|
||||
{
|
||||
_timestamp = value;
|
||||
OnPropertyChanged(nameof(Timestamp));
|
||||
}
|
||||
}
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
x:Class="ExcelHelper.Views.Pages.ImportExcelPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:components="clr-namespace:ExcelHelper.Views.Components"
|
||||
xmlns:converter="clr-namespace:ExcelHelper.Converter"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
|
@ -52,6 +53,10 @@
|
|||
Width="370"
|
||||
MinWidth="370"
|
||||
MaxWidth="600" />
|
||||
<ColumnDefinition
|
||||
Width="370"
|
||||
MinWidth="370"
|
||||
MaxWidth="600" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ContentPresenter
|
||||
x:Name="DropFileMask"
|
||||
|
@ -68,7 +73,7 @@
|
|||
Panel.ZIndex="10"
|
||||
Content="{Binding}"
|
||||
ContentTemplate="{StaticResource LoadingMask}"
|
||||
Visibility="{Binding IsLoading, Converter={StaticResource Boolean2VisibilityConverter}}" />
|
||||
Visibility="{Binding IsLoading, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<hc:TabControl
|
||||
Grid.Column="0"
|
||||
Margin="5,5,7,5"
|
||||
|
@ -202,6 +207,17 @@
|
|||
</hc:SimpleStackPanel>
|
||||
</hc:SimpleStackPanel>
|
||||
</hc:Card>
|
||||
|
||||
<GridSplitter
|
||||
Grid.Column="1"
|
||||
Width="2"
|
||||
Margin="0,5" />
|
||||
<hc:Card Grid.Column="2">
|
||||
<!-- Fix: Added Mode=OneWay -->
|
||||
<components:AiMessageControll
|
||||
CurrentMessage="{Binding CurrentMessage}"
|
||||
IsWaiting="{Binding IsAiLoading}"
|
||||
Messages="{Binding Messages}"
|
||||
SendCommand="{Binding SendMessageToAiCommand}" />
|
||||
</hc:Card>
|
||||
</Grid>
|
||||
</Page>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Windows.Controls;
|
||||
using System.Windows.Controls;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using ExcelHelper.Message;
|
||||
using ExcelHelper.Views.ViewModels;
|
||||
|
@ -73,4 +73,6 @@ public partial class ImportExcelPage : Page, IView, IRecipient<UpdateDataGridCol
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -7,9 +7,11 @@ using System.Windows.Controls;
|
|||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using ExcelHelper.AI;
|
||||
using ExcelHelper.Message;
|
||||
using ExcelHelper.Model;
|
||||
using ExcelHelper.Utils;
|
||||
using ExcelHelper.Views.Components;
|
||||
using MiniExcelLibs;
|
||||
using MiniExcelLibs.OpenXml;
|
||||
using SqlSugar;
|
||||
|
@ -20,6 +22,8 @@ namespace ExcelHelper.Views.ViewModels;
|
|||
public partial class ImportViewModel : ObservableRecipient, IViewModel
|
||||
{
|
||||
|
||||
private AiHelper AiHelper = new AiHelper("deepseek-v3-241226", false);
|
||||
|
||||
public Task FileDrop(string[] files)
|
||||
{
|
||||
IsLoading = true;
|
||||
|
@ -100,7 +104,7 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
|
|||
: MiniExcel.QueryRange(path, sheetName: SelectedSheetName, useHeaderRow: UseHeaderRow, startCell: StartCell, endCell: EndCell, configuration: config);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void GenColumns(IEnumerable<string> columns)
|
||||
{
|
||||
ExcelColumns.Clear();
|
||||
|
@ -250,6 +254,267 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SendMessageToAi(string message)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
Messages.Add(new ChatMessage
|
||||
{
|
||||
Content = message,
|
||||
IsUser = true,
|
||||
Timestamp = DateTime.Now
|
||||
});
|
||||
IsAiLoading = true;
|
||||
var aiChat = new ChatMessage {
|
||||
Timestamp = DateTime.Now,
|
||||
IsUser = false,
|
||||
Content = ""
|
||||
};
|
||||
Messages.Add(aiChat);
|
||||
#if DEBUG
|
||||
var results = TEST_AI_CONTENT;
|
||||
foreach(var result in results)
|
||||
{
|
||||
aiChat.Content += result;
|
||||
//await Task.Delay(1);
|
||||
}
|
||||
#else
|
||||
var promptedMsg = PromptUtil.UsePrompt(message,PromptString);
|
||||
await foreach(var result in AiHelper.GetStreamingResponseAsync(promptedMsg))
|
||||
{
|
||||
aiChat.Content += result;
|
||||
}
|
||||
#endif
|
||||
IsAiLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private const string TEST_AI_CONTENT = @"
|
||||
# Welcome to Leanote! 欢迎来到Leanote!
|
||||
|
||||
## 1. 排版
|
||||
|
||||
**粗体** *斜体*
|
||||
|
||||
~~这是一段错误的文本。~~
|
||||
|
||||
引用:
|
||||
|
||||
> 引用Leanote官方的话, 为什么要做Leanote, 原因是...
|
||||
|
||||
有充列表:
|
||||
1. 支持Vim
|
||||
2. 支持Emacs
|
||||
|
||||
无序列表:
|
||||
|
||||
- 项目1
|
||||
- 项目2
|
||||
|
||||
|
||||
## 2. 图片与链接
|
||||
|
||||
图片:
|
||||

|
||||
链接:
|
||||
|
||||
[这是去往Leanote官方博客的链接](http://leanote.leanote.com)
|
||||
|
||||
## 3. 标题
|
||||
|
||||
以下是各级标题, 最多支持5级标题
|
||||
|
||||
```
|
||||
# h1
|
||||
## h2
|
||||
### h3
|
||||
#### h4
|
||||
##### h4
|
||||
###### h5
|
||||
```
|
||||
|
||||
## 4. 代码
|
||||
|
||||
示例:
|
||||
|
||||
function get(key) {
|
||||
return m[key];
|
||||
}
|
||||
|
||||
代码高亮示例:
|
||||
|
||||
``` javascript
|
||||
/**
|
||||
* nth element in the fibonacci series.
|
||||
* @param n >= 0
|
||||
* @return the nth element, >= 0.
|
||||
*/
|
||||
function fib(n) {
|
||||
var a = 1, b = 1;
|
||||
var tmp;
|
||||
while (--n >= 0) {
|
||||
tmp = a;
|
||||
a += b;
|
||||
b = tmp;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
document.write(fib(10));
|
||||
```
|
||||
|
||||
```python
|
||||
class Employee:
|
||||
empCount = 0
|
||||
|
||||
def __init__(self, name, salary):
|
||||
self.name = name
|
||||
self.salary = salary
|
||||
Employee.empCount += 1
|
||||
```
|
||||
|
||||
# 5. Markdown 扩展
|
||||
|
||||
Markdown 扩展支持:
|
||||
|
||||
* 表格
|
||||
* 定义型列表
|
||||
* Html 标签
|
||||
* 脚注
|
||||
* 目录
|
||||
* 时序图与流程图
|
||||
* MathJax 公式
|
||||
|
||||
## 5.1 表格
|
||||
|
||||
Item | Value
|
||||
-------- | ---
|
||||
Computer | \$1600
|
||||
Phone | \$12
|
||||
Pipe | \$1
|
||||
|
||||
可以指定对齐方式, 如Item列左对齐, Value列右对齐, Qty列居中对齐
|
||||
|
||||
| Item | Value | Qty |
|
||||
| :------- | ----: | :---: |
|
||||
| Computer | \$1600 | 5 |
|
||||
| Phone | \$12 | 12 |
|
||||
| Pipe | \$1 | 234 |
|
||||
|
||||
|
||||
## 5.2 定义型列表
|
||||
|
||||
名词 1
|
||||
: 定义 1(左侧有一个可见的冒号和四个不可见的空格)
|
||||
|
||||
代码块 2
|
||||
: 这是代码块的定义(左侧有一个可见的冒号和四个不可见的空格)
|
||||
|
||||
代码块(左侧有八个不可见的空格)
|
||||
|
||||
## 5.3 Html 标签
|
||||
|
||||
支持在 Markdown 语法中嵌套 Html 标签,譬如,你可以用 Html 写一个纵跨两行的表格:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th rowspan=""2"">值班人员</th>
|
||||
<th>星期一</th>
|
||||
<th>星期二</th>
|
||||
<th>星期三</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>李强</td>
|
||||
<td>张明</td>
|
||||
<td>王平</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th rowspan=""2"">值班人员</th>
|
||||
<th>星期一</th>
|
||||
<th>星期二</th>
|
||||
<th>星期三</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>李强</td>
|
||||
<td>张明</td>
|
||||
<td>王平</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**提示**, 如果想对图片的宽度和高度进行控制, 你也可以通过img标签, 如:
|
||||
|
||||
<img src=""http://leanote.com/images/logo/leanote_icon_blue.png"" width=""50px"" />
|
||||
|
||||
## 5.4 脚注
|
||||
|
||||
Leanote[^footnote]来创建一个脚注
|
||||
[^footnote]: Leanote是一款强大的开源云笔记产品.
|
||||
|
||||
## 5.5 目录
|
||||
|
||||
通过 `[TOC]` 在文档中插入目录, 如:
|
||||
|
||||
[TOC]
|
||||
|
||||
## 5.6 时序图与流程图
|
||||
|
||||
```sequence
|
||||
Alice->Bob: Hello Bob, how are you?
|
||||
Note right of Bob: Bob thinks
|
||||
Bob-->Alice: I am good thanks!
|
||||
```
|
||||
|
||||
流程图:
|
||||
|
||||
```flow
|
||||
st=>start: Start
|
||||
e=>end
|
||||
op=>operation: My Operation
|
||||
cond=>condition: Yes or No?
|
||||
|
||||
st->op->cond
|
||||
cond(yes)->e
|
||||
cond(no)->op
|
||||
```
|
||||
|
||||
> **提示:** 更多关于时序图与流程图的语法请参考:
|
||||
|
||||
> - [时序图语法](http://bramp.github.io/js-sequence-diagrams/)
|
||||
> - [流程图语法](http://adrai.github.io/flowchart.js)
|
||||
|
||||
## 5.7 MathJax 公式
|
||||
|
||||
$ 表示行内公式:
|
||||
|
||||
质能守恒方程可以用一个很简洁的方程式 $E=mc^2$ 来表达。
|
||||
|
||||
$$ 表示整行公式:
|
||||
|
||||
$$\sum_{i=1}^n a_i=0$$
|
||||
|
||||
$$f(x_1,x_x,\ldots,x_n) = x_1^2 + x_2^2 + \cdots + x_n^2 $$
|
||||
|
||||
$$\sum^{j-1}_{k=0}{\widehat{\gamma}_{kj} z_k}$$
|
||||
|
||||
更复杂的公式:
|
||||
$$
|
||||
\begin{eqnarray}
|
||||
\vec\nabla \times (\vec\nabla f) & = & 0 \cdots\cdots梯度场必是无旋场\\
|
||||
\vec\nabla \cdot(\vec\nabla \times \vec F) & = & 0\cdots\cdots旋度场必是无散场\\
|
||||
\vec\nabla \cdot (\vec\nabla f) & = & {\vec\nabla}^2f\\
|
||||
\vec\nabla \times(\vec\nabla \times \vec F) & = & \vec\nabla(\vec\nabla \cdot \vec F) - {\vec\nabla}^2 \vec F\\
|
||||
\end{eqnarray}
|
||||
$$
|
||||
|
||||
访问 [MathJax](http://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference) 参考更多使用方法。
|
||||
";
|
||||
#region Props
|
||||
[ObservableProperty]
|
||||
private IEnumerable<dynamic> _excelData;
|
||||
|
@ -263,12 +528,20 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
|
|||
[ObservableProperty]
|
||||
private ObservableCollection<TableColumnModel> _tableColumns = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<ChatMessage> _messages = [];
|
||||
|
||||
|
||||
[ObservableProperty]
|
||||
private string _currentMessage;
|
||||
/// <summary>
|
||||
/// 是否正在加载
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool _isLoading = false;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isAiLoading = false;
|
||||
/// <summary>
|
||||
/// 使用首行作为表头
|
||||
/// </summary>
|
||||
|
|
Loading…
Reference in New Issue