主要是对代码进行了优化
- 上一个版本写死了帐号跟密码 ,这一个帐本有户可以直接设置
- 对相关的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:Class="AiChat.Views.RegPage"
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";
}
}
}
}
}
桌面界面: