maui 调用文心一言开发的聊天APP 3

主要是对代码进行了优化

  1. 上一个版本写死了帐号跟密码 ,这一个帐本有户可以直接设置
  2. 对相关的key以及secret如果设置错时,在聊天中也会返回提示。
  3. 注册帐号时同时也设置了key及secrete
  4. 升级到了net.8.0
  5. 导出APK,上一个版本是导出abb.
  6. 解决了变型问题,现在生成桌面系统也能正常显示。

注册界面

xml 复制代码
<?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>
csharp 复制代码
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。

聊天代码

csharp 复制代码
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";
                }
            }
        }
    }
}

桌面界面:


相关推荐
深海呐7 分钟前
Android 从本地选择视频,用APP播放或进行其他处理
android·音视频·从本地选择视频,用app播放·从本地选择视频,并拿到信息·跳转到本地视频列表
深海呐7 分钟前
Android Google登录接入
android·google登录接入·android 谷歌登录接入·google登录·android google
daiyang123...27 分钟前
MySQL【知识改变命运】11
android·数据库·mysql
时光追逐者34 分钟前
.NET 9 中 LINQ 新增功能实操
开发语言·开源·c#·.net·.netcore·linq·微软技术
huaqianzkh35 分钟前
学习C#中的Parallel类
windows·microsoft·c#
8931519601 小时前
Android开发教程案例源码分享-匹配动画多个头像飘动效果
android·android开发·android教程·kotlin教程·android头像飘动动画·android匹配动画·android多个头像飘动动画
sky_smile_Allen1 小时前
[C#] 关于数组的详细解释以及使用注意点
开发语言·算法·c#
老码沉思录2 小时前
Android开发实战班 - 网络编程 - WebSocket 实时通信
android·网络·websocket
江上清风山间明月2 小时前
Android 14 screenrecord录制视频失败的原因分析
android·视频·大小·失败·录制·screenrecord·0kb
keeng20082 小时前
Compose学习记录(3): ViewModel数据驱动更新组件
android