当前位置: 首页 > news >正文

用 .NET MAUI 10 + VS Copilot 从 0 开发一个签到 App(六)登录

用 .NET MAUI 10 + VS Copilot 从 0 开发一个签到 App(六)

一、本文背景

在前一篇中,我们已经完成了用户注册,解决了一个核心问题:

用户从哪里来?

但一个系统只有注册,没有登录,是无法真正使用的。

因此在真实项目推进中,下一步几乎是必然的:

登录页面 + 登录态建立

这一篇依然遵循同一个原则:

👉 先跑通真实业务流程,再考虑架构与抽象

需要再次强调的是:

本文中的登录页面(XAML + Code-behind)同样是由 Visual Studio Copilot 生成并整理的实现思路,对应的是一个可直接运行的工程页面。


二、登录页在这个 App 中承担的职责

在这个签到 App 中,登录页并不只是“校验用户名密码”,它实际上承担了多项职责:

  1. 选择当前租户(Tenant)
  2. 恢复上一次登录上下文(租户 / 用户名)
  3. 首次启动时触发引导流程
  4. 成功登录后,进入真实业务页面

这已经明显不是一个简单 Demo 登录页了。


三、Copilot 生成的登录页面(XAML)

下面是登录页的 UI 布局代码:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"x:Class="SignInMauiApp.LoginPage"><VerticalStackLayout Padding="30,60" Spacing="20"><Label Text="签到系统登录" FontSize="24" HorizontalOptions="Center" /><Image HeightRequest="100" WidthRequest="100" Source="logo.jpg" HorizontalOptions="Center" /><Picker x:Name="TenantPicker" Title="选择租户" MaximumWidthRequest="600" /><Entry x:Name="UsernameEntry" Placeholder="用户名" MaximumWidthRequest="600" /><Entry x:Name="PasswordEntry" Placeholder="密码" IsPassword="True" MaximumWidthRequest="600" /><Button Text="登录" Clicked="OnLoginClicked" MaximumWidthRequest="600" /><Button Text="注册新用户" Clicked="OnRegisterClicked" MaximumWidthRequest="600" /><Label x:Name="ErrorLabel" TextColor="Red" IsVisible="False" MaximumWidthRequest="600" /></VerticalStackLayout>
</ContentPage>

这个 UI 有几个明显的工程化特征:

  • 租户选择是登录的前置条件
  • 登录 / 注册入口并列存在
  • 所有控件宽度做了限制,适配桌面与移动端

它并不“炫”,但非常实用。


四、Copilot 生成的登录逻辑(Code-behind)

对应的页面逻辑如下:

public partial class LoginPage : ContentPage
{private readonly IFreeSql? _fsql;private List<Tenant> _tenants = new();public LoginPage(){InitializeComponent();_fsql = IPlatformApplication.Current?.Services.GetService<IFreeSql>();CheckAndShowOnboardingAsync();LoadTenants();}protected override void OnAppearing(){base.OnAppearing();LoadTenants();}private async void CheckAndShowOnboardingAsync(){var onboardingDone = Preferences.Get("OnboardingDone", false);if (!onboardingDone){await Navigation.PushModalAsync(new OnboardingPage(_fsql));}}private void LoadTenants(){_tenants = _fsql!.Select<Tenant>().ToList();TenantPicker.ItemsSource = _tenants.Select(t => t.Name).ToList();int lastTenantId = Preferences.Get("LastTenantId", -1);int idx = 0;if (_tenants.Count > 0){if (lastTenantId > 0){idx = _tenants.FindIndex(t => t.Id == lastTenantId);if (idx < 0) idx = 0;}TenantPicker.SelectedIndex = idx;}UsernameEntry.Text = Preferences.Get("LastUsername", "");}private async void OnLoginClicked(object sender, EventArgs e){ErrorLabel.IsVisible = false;var username = UsernameEntry.Text?.Trim();var password = PasswordEntry.Text;var tenantIdx = TenantPicker.SelectedIndex;if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password) || tenantIdx < 0){ErrorLabel.Text = "请填写完整信息";ErrorLabel.IsVisible = true;return;}var tenantId = _tenants[tenantIdx].Id;var user = _fsql!.Select<User>().Where(u => u.Username == username && u.Password == password && u.TenantId == tenantId).First();if (user == null){ErrorLabel.Text = "用户名或密码错误";ErrorLabel.IsVisible = true;return;}Preferences.Set("LastTenantId", tenantId);Preferences.Set("LastUsername", username);await Navigation.PushAsync(new SignInPage(user, _tenants[tenantIdx]));}private async void OnRegisterClicked(object sender, EventArgs e){await Navigation.PushAsync(new RegisterPage());}
}

五、几个非常“真实工程”的细节

1️⃣ 登录不是全局的,而是租户内的

.Where(u => u.Username == username && u.Password == password && u.TenantId == tenantId)

这一行明确体现了:

同一个用户名,在不同租户中是完全独立的身份


2️⃣ 登录体验被认真对待

  • 记住上一次租户
  • 自动填充用户名

这些并不是必须功能,但它们让 App 更像“真的会被每天使用”。


3️⃣ 引导页不是写在文档里的,而是写在代码里的

CheckAndShowOnboardingAsync();

通过本地标记控制首次引导流程,这是一个非常典型的业务 App 设计。


六、Copilot 在“登录态”问题上的边界

需要特别说明的是:

  • Copilot 能写出登录页面
  • 但它并不会自动帮你设计完整的登录态生命周期

例如:

  • 登录后是否清空返回栈
  • 退出登录如何回到初始状态
  • 多页面共享用户上下文如何处理

这些问题,已经开始进入应用结构层面


七、为什么现在依然没有引入认证框架?

和注册页一样,这里依然没有引入:

  • Identity
  • Token
  • Session

原因很简单:

在当前阶段,这个 App 的复杂度还不值得引入这些重量级组件。

而 Copilot 在“简单明确”的业务逻辑下,产出质量反而更高。


八、下一步:初始化数据与租户管理

现在,这个签到 App 已经具备:

  • 注册
  • 登录
  • 多租户隔离
  • 基础业务页面

下一步不可避免地会遇到:

系统第一次启动时怎么办?

也就是:

  • 初始化租户
  • 默认管理员
  • 数据兜底策略

九、下一篇预告

下一篇将进入:

第 7 篇:初始化数据与租户管理 —— Copilot 能写 CRUD,但系统规则必须由你决定

从这一篇开始,这个项目将正式进入“可长期维护”的阶段。

http://www.jsqmd.com/news/120314/

相关文章:

  • 信息学奥赛一本通 1616:A 的 B 次方
  • 微信开发者secret和appid获取方法
  • 解锁大模型“能干活“的秘诀:RAG×MoE技术组合深度解析
  • Java毕设选题推荐:基于Java+springboot招投标管理系统设计与实现基于springboot的在线招标系统的设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 2025 --【J+S 二十连测】-- 第十二套 总结+题解
  • Java计算机毕设之基于springboot的在线招标系统的设计与实现基于springboot招投标管理系统设计与实现(完整前后端代码+说明文档+LW,调试定制等)
  • 深入解析MySQL事务与锁:构建高并发数据系统的基石
  • android kotlinx.serialization用法和封装全解
  • 系统架构设计师教程资源合集
  • 什么是八股文?Java程序员春招如何提前储备?拿高薪offer?
  • 创新点解读:基于贝叶斯优化PatchTST的时间序列预测算法(附代码实现)
  • 【毕业设计】基于springboot的幼儿园管理系统的设计与实现(源码+文档+远程调试,全bao定制等)
  • AI伦理风险防控与治理体系构建 守护技术向善之路
  • AI应用架构师如何实现高效的上下文理解增强方案?
  • 吐血整理!儿童鞋服宝藏品牌大盘点 - 品牌测评鉴赏家
  • 创新点解读:基于非线性二次分解的Ridge-RF-XGBoost时间序列预测(附代码实现)
  • 大模型微调资源合集
  • 【毕业设计】基于springboot的在线招标系统的设计与实现(源码+文档+远程调试,全bao定制等)
  • I/O多路复用
  • 基于CNN(卷积神经网路)-BiLSTM(双向长短期记忆网络)-Attention(注意力机制)的时间序列预测python代码
  • Vue.js:轻量高效的渐进式前端框架,为何成为开发者首选?
  • EI顶刊复现:基于氨储能技术的电转氨耦合风–光–火综合能源系统双层优化调度附Matlab代码
  • 生成式AI重构内容生态 人机协同定义创作新范式
  • 【课程设计/毕业设计】基于springboot+vue的在线招标系统的设计与实现基于springboot电子招投标系统【附源码、数据库、万字文档】
  • 2025.12.21博客
  • 实用指南:【threejs】材质共享导致的典型问题
  • 2025年儿童鞋服品牌前十名揭晓!哪些品牌靠科技与口碑征服家长? - 品牌测评鉴赏家
  • Vue.js从入门到实战:一站式学习指南
  • 深入解析:NewStar CTF 2025公开赛道-web题目-week4-writeup
  • centos7.9上面卸载中文语言包和中文字体重新安装