主要是对代码进行了优化
上一个版本写死了帐号跟密码 ,这一个帐本有户可以直接设置 对相关的key以及secret如果设置错时,在聊天中也会返回提示。 注册帐号时同时也设置了key及secrete 升级到了net.8.0 导出APK,上一个版本是导出abb. 解决了变型问题,现在生成桌面系统也能正常显示。注册界面
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x: Shell.NavBarIsVisible="True" xmlns:mct="clr-namespace:CommunityToolkit.Maui.Behaviors;assembly=CommunityToolkit.Maui" xmlns:local="clr-namespace:AiChat.Views;assembly=AiChat" Title="注册"> <Grid RowDefinitions="Auto,*" Margin="0,10,0,0"> <VerticalStackLayout Padding="10" VerticalOptions="Center" HorizontalOptions="FillAndExpand"> <Frame BorderColor="White" CornerRadius="10" HasShadow="True" Margin="0,20,0,0" ZIndex="0" Padding="8"> <Frame.Shadow> <Shadow Brush="Black" Offset="20,20" Radius="10" Opacity="0.9" /> </Frame.Shadow> <StackLayout Padding="10"> <VerticalStackLayout Padding="10" BackgroundColor="{StaticResource White}"> <Label Text="AI CHAT" FontSize="30" FontAttributes="Bold" TextColor="{StaticResource Cyan100Accent}" FontFamily="Consolas" Padding="5"/> <Label Text="to continue!" TextColor="{StaticResource Cyan100Accent}" FontSize="14" Padding="5" FontAttributes="Bold" /> </VerticalStackLayout> <VerticalStackLayout Padding="10"> <Label FontFamily="Consolas" Text="手机号" TextColor="{StaticResource Cyan100Accent}" /> <Frame CornerRadius="10" Padding="3" Margin="0,10,0,0"> <VerticalStackLayout> <Entry x:Name="Phone" Text="{Binding Phone,Mode=TwoWay}" Margin="5,0,0,0" Placeholder="手机" FontSize="14"> <Entry.Behaviors> <local:PhoneNumberValidatorBehavior /> </Entry.Behaviors> </Entry> </VerticalStackLayout> </Frame> <VerticalStackLayout Padding="0" Margin="0,5,0,0"> <Label FontFamily="Consolas" Text="密码" TextColor="{StaticResource Cyan100Accent}" /> <Frame CornerRadius="10" Padding="3" Margin="0,10,0,0"> <Entry x:Name="Password" Text="{Binding Password,Mode=TwoWay}" Margin="5,0,0,0" Placeholder="密码6位数字" IsPassword="True" FontSize="14"> <Entry.Behaviors> <local:PasswordValidatorBehavior /> </Entry.Behaviors> </Entry> </Frame> </VerticalStackLayout> <Label FontFamily="Consolas" Text="文心一言API_KEY" TextColor="{StaticResource Cyan100Accent}" /> <Frame CornerRadius="10" Padding="3" Margin="0,10,0,0"> <VerticalStackLayout> <Entry x:Name="API_KEY" Text="{Binding API_KEY,Mode=TwoWay}" Margin="5,0,0,0" Placeholder="API_KEY" FontSize="14" /> </VerticalStackLayout> </Frame> <Label FontFamily="Consolas" Text="文心一言SECRET_KEY" TextColor="{StaticResource Cyan100Accent}" /> <Frame CornerRadius="10" Padding="3" Margin="0,10,0,0"> <VerticalStackLayout> <Entry x:Name="SECRET_KEY" Text="{Binding SECRET_KEY,Mode=TwoWay}" Margin="5,0,0,0" Placeholder="SECRET_KEY" FontSize="14" /> </VerticalStackLayout> </Frame> <Button Margin="0,20,0,0" x:Name="RegButton" Clicked="RegButton_Clicked" Text="确定注册" VerticalOptions="CenterAndExpand" BackgroundColor="{StaticResource Cyan100Accent}" HorizontalOptions="FillAndExpand"/> <BoxView Color="{StaticResource Cyan100Accent}" Margin="0,20,0,0" HeightRequest="2" HorizontalOptions="Fill" /> <Grid Padding="10" Margin="0,10,0,0" InputTransparent="False"> <Label FontFamily="Consolas" InputTransparent="False"> <Label.FormattedText> <FormattedString> <Span Text="返回 " TextColor="{StaticResource Cyan100Accent}" /> <Span Text="登陆" TextColor="{StaticResource Cyan100Accent}" /> </FormattedString> </Label.FormattedText> <Label.GestureRecognizers> <TapGestureRecognizer Tapped="OnLoginLabelTapped" /> </Label.GestureRecognizers> </Label> </Grid> <Grid Padding="10" Margin="0,10,0,0" InputTransparent="False"> <Label FontFamily="Consolas" InputTransparent="False"> <Label.FormattedText> <FormattedString> <Span Text="百度文言一心登陆获取 API_KEY SECRET_KEY" TextColor="Red" /> </FormattedString> </Label.FormattedText> <Label.GestureRecognizers> <TapGestureRecognizer Tapped="OnBaiduLabelTapped" /> </Label.GestureRecognizers> </Label> </Grid> </VerticalStackLayout> </StackLayout> </Frame> </VerticalStackLayout> </Grid> </ContentPage>
using System.Text.RegularExpressions; using System.Windows.Input; using static Microsoft.Maui.ApplicationModel.Permissions; namespace AiChat.Views { public class PhoneNumberValidatorBehavior : Behavior<Entry> { protected override void OnAttachedTo(Entry entry) { entry.TextChanged += OnEntryTextChanged; base.OnAttachedTo(entry); } protected override void OnDetachingFrom(Entry entry) { entry.TextChanged -= OnEntryTextChanged; base.OnDetachingFrom(entry); } private void OnEntryTextChanged(object sender, TextChangedEventArgs args) { if (!(sender is Entry entry)) return; string phoneNumber = args.NewTextValue; bool isValid = Regex.IsMatch(phoneNumber, @"^\d{11}$"); // Set IsValid property on the associated entry entry.SetValue(IsValidProperty, isValid); } public static readonly BindableProperty IsValidProperty = BindableProperty.CreateAttached("IsValid", typeof(bool), typeof(PhoneNumberValidatorBehavior), false); } public class PasswordValidatorBehavior : Behavior<Entry> { protected override void OnAttachedTo(Entry entry) { entry.TextChanged += OnEntryTextChanged; base.OnAttachedTo(entry); } protected override void OnDetachingFrom(Entry entry) { entry.TextChanged -= OnEntryTextChanged; base.OnDetachingFrom(entry); } private void OnEntryTextChanged(object sender, TextChangedEventArgs args) { if (!(sender is Entry entry)) return; string password = args.NewTextValue; bool isValid = Regex.IsMatch(password, @"^\d{6}$"); // Set IsValid property on the associated entry entry.SetValue(IsValidProperty, isValid); } public static readonly BindableProperty IsValidProperty = BindableProperty.CreateAttached("IsValid", typeof(bool), typeof(PasswordValidatorBehavior), false); } public partial class RegPage : ContentPage { public RegPage() { InitializeComponent(); BindingContext = this; } private async void OnLoginLabelTapped(object sender, EventArgs e) { var nextPage = new LoginPage(); var navigation = Application.Current.MainPage.Navigation; await navigation.PushAsync(nextPage); } private async void OnBaiduLabelTapped(object sender, EventArgs e) { await Launcher.TryOpenAsync(new Uri("https://login.bce.baidu.com/")); } protected override bool OnBackButtonPressed() { Application.Current.Quit(); return true; } private async void RegButton_Clicked(object sender, EventArgs e) { bool isPhoneValid = (bool)Phone.GetValue(PhoneNumberValidatorBehavior.IsValidProperty); bool isPasswordValid = (bool)Password.GetValue(PasswordValidatorBehavior.IsValidProperty); if (isPhoneValid && isPasswordValid) { if (string.IsNullOrEmpty(API_KEY.Text) || string.IsNullOrEmpty(SECRET_KEY.Text)) { await DisplayAlert("确定", "API_KEY SECRET_KEY 不能为空?", "确定"); // 修改按钮标签为 "确定" return; } if (await DisplayAlert("确定", "确定增加吗?", "确定", "取消")) // 修改按钮标签为 "确定" 和 "取消" { await SecureStorage.SetAsync("PHONE", Phone.Text); await SecureStorage.SetAsync("PASSWORD", Password.Text); await SecureStorage.SetAsync("API_KEY", API_KEY.Text); await SecureStorage.SetAsync("SECRET_KEY", SECRET_KEY.Text); await DisplayAlert("成功", "注册成功", "OK"); } } else { await DisplayAlert("验证失改", "手机号密码错", "OK"); } } } }
加入了手机号密码的验证,同时要求加入API_KEY,SECRET_KEY。
聊天代码
using System; using System.Collections.ObjectModel; using System.IO; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; namespace AiChat.Views { // 用于将文本颜色转换为视图颜色的转换器 public class MessageColorConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { // 根据“IsUser”的值确定文本颜色的逻辑 bool isUser = (bool)value; if (isUser) { // 返回用户的文本颜色 return Color.FromHex("#0000FF"); // 更改为所需的颜色 } else { // 返回其他情况的文本颜色 return Color.FromHex("#000000");// 更改为所需的颜色 } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { // 如果需要双向绑定,请实现此方法进行转换 throw new NotImplementedException(); } } // 聊天页面类 public partial class Chat : ContentPage { // 定义表示聊天消息的类 public class ChatMessage { public string Text { get; set; } public bool IsUser { get; set; } public DateTime Timestamp { get; set; } } static string sAPI_KEY = ""; static string sSECRET_KEY = ""; // 用于存储聊天消息的集合 private ObservableCollection<ChatMessage> chatMessages = new ObservableCollection<ChatMessage>(); // 构造函数 public Chat() { InitializeComponent(); // 将chatMessages集合绑定到CollectionView的ItemsSource collectionView.ItemsSource = chatMessages; SetStoredValues(); } // 获取 API_KEY SECRET_KEY private async void SetStoredValues() { var storedKey = await SecureStorage.GetAsync("API_KEY"); if (!string.IsNullOrEmpty(storedKey)) { sAPI_KEY = storedKey; } var storedSecret = await SecureStorage.GetAsync("SECRET_KEY"); if (!string.IsNullOrEmpty(storedSecret)) { sSECRET_KEY = storedSecret; } entryUserMessage.Text = ""; } // 发送消息按钮点击事件处理程序 private async void SendMessage_Clicked(object sender, EventArgs e) { try { sendmessageButton.IsEnabled = false; loadingIndicator.IsVisible = true; // 从Entry中获取用户的消息 string userMessage = entryUserMessage.Text; if (string.IsNullOrEmpty(userMessage) ) { return; } // 将用户的消息添加到chatMessages集合,并添加时间戳 chatMessages.Add(new ChatMessage { Text = $"您:{userMessage}", IsUser = true, Timestamp = DateTime.Now }); // 模拟对方的响应 string response = await getAnswer(userMessage); response = response != null ? response : "请配置好文心一言的API_KEY SECRET_KEY"; // 将对方的响应添加到chatMessages集合,并添加时间戳 chatMessages.Add(new ChatMessage { Text = $"AI:{response}", IsUser = false, Timestamp = DateTime.Now }); // 可选:滚动到底部以显示最新的消息 collectionView.ScrollTo(chatMessages[chatMessages.Count - 1], ScrollToPosition.End); // 发送后清除用户的输入 entryUserMessage.Text = string.Empty; } finally { // Hide the loading indicator when the operation is complete loadingIndicator.IsVisible = false; sendmessageButton.IsEnabled = true; } } public static async Task<string> getAnswer(string question) { var url = $"https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/aquilachat_7b?access_token={await GetAccessToken()}"; var payload = JsonConvert.SerializeObject(new { messages = new[] { new { role = "user", content = question } } }); using (var client = new HttpClient()) { var content = new StringContent(payload, Encoding.UTF8, "application/json"); var response = await client.PostAsync(url, content); if (response.IsSuccessStatusCode) { var responseContent = await response.Content.ReadAsStringAsync(); var dictObj = JsonConvert.DeserializeObject<dynamic>(responseContent); return dictObj.result; } else { Console.WriteLine($"HTTP请求失败: {response.StatusCode}"); return "请配置好文心一言的API_KEY SECRET_KEY"; } } } private static async Task<string> GetAccessToken() { var url = "https://aip.baidubce.com/oauth/2.0/token"; using (var client = new HttpClient()) { var parameters = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("grant_type", "client_credentials"), new KeyValuePair<string, string>("client_id", sAPI_KEY), new KeyValuePair<string, string>("client_secret", sSECRET_KEY) }); var response = await client.PostAsync(url, parameters); if (response.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync(); var result = JsonConvert.DeserializeObject<dynamic>(content); return result.access_token; } else { Console.WriteLine($"HTTP请求失败: {response.StatusCode}"); return "wrong"; } } } } }