Maui Blazor 中文社区 QQ群:645660665

用 .NET MAUI 10 + VS Copilot 从 0 开发一个签到 App(三)Copilot 生成第一个签到页面(一次没有人工干预的尝试)

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

一、本文目标

从这一篇开始,我们正式进入业务功能开发

本文将完成一个最重要的里程碑:

使用 Visual Studio Copilot,从 0 生成一个“可用但不幼稚”的签到页面

读完本文,你将看到:

  • Copilot 在 MAUI + XAML 中能做到什么程度
  • 哪些代码“看起来对,但实际不该直接用”
  • 一个真实项目中,如何控制 Copilot 的参与边界

二、签到页面的设计目标

在写任何代码之前,先明确目标。

这个签到页面需要做到:

  • 一个明确的页面标题
  • 一个「签到」按钮
  • 当前用户最近一次签到时间
  • 不引入复杂 UI 动效

这是一个典型的业务页面,而不是 Demo 展示页。


三、使用 Copilot 生成第一步

1️⃣ Copilot Prompt(原始输入)

我在 MainPage.xaml 中,直接向 Copilot Chat 输入了下面的提示:

制作一个签到app, 要求有身份验证, 多租户, 使用freesql orm

这一步非常关键

Prompt 写得越清晰,后面需要人工修改的成本就越低。

Copilot回复如下

你的需求是将当前的 .NET MAUI 工程改造成一个支持身份验证、多租户,并使用 FreeSql ORM 的签到应用。
我会分步实现基础结构,优先完成以下内容:
1.	添加 FreeSql ORM 支持。
2.	设计用户、租户、签到相关的数据模型。
3.	实现基础身份验证(如简单登录界面和逻辑)。
4.	多租户支持(用户与租户关联)。
5.	基础签到功能界面和逻辑。

首先,我将获取项目文件列表,确认需要修改和添加的文件。接下来会逐步实现上述功能。

下一步将进行如下操作:
1.	在 MauiProgram.cs 中集成 FreeSql ORM(注册为单例)。
2.	创建数据模型(用户、租户、签到记录)。
3.	添加登录页面和基础身份验证逻辑。
4.	设计多租户结构(用户与租户关联)。
5.	初步实现签到功能界面。

2️⃣ Copilot 生成

Copilot 很快给出了一个完整的文件结构,大致包括:

  • MauiProgram.cs
  • Entities.cs

• 在 MauiProgram.cs 注册了 FreeSql ORM,使用 SQLite 数据库。
• 新建了 Entities.cs,包含租户、用户、签到记录的数据模型。


四、添加登录页面和基础身份验证逻辑,并设计多租户结构与签到功能界面。

继续实现以下内容:

  1. 登录页面(用户名、密码、租户选择)。
  2. 登录逻辑(身份验证,用户与租户关联)。
  3. 登录成功后进入签到页面。
    我将先创建登录页面的 XAML 和后台代码,并实现租户列表加载与登录校验。稍后会添加签到页面。

image

已完成以下步骤:

  1. 创建了登录页面(LoginPage.xaml 和后台代码),支持租户选择、用户名、密码输入。
  2. 登录逻辑实现,校验用户与租户信息,登录成功后跳转签到页面。
  3. 创建了签到页面(SignInPage.xaml 和后台代码),用户可进行签到操作。
  4. 设置应用入口为登录页面。
    你可以直接运行项目,体验多租户身份验证和签到功能。

五、首次运行出错

❌ 问题 1:freesql页面获取实例没弄好, 因为maui10改了一些特性,手工调整了一下ok了

Copilot 有时会:

  • 没有最新SDK的训练数据,写出旧版方法
  • 默认加入不必要的写法

这会让程序运行不起来,需要手工介入。

❌ 问题 2:当前的 App.xaml.cs 文件中,MainPage 和 CreateWindow(IActivationState?) 都设置了主页面,可能导致 MAUI 导航和窗口初始化冲突。

image

Copilot 完美的修复了问题

六、登录页面中文乱码

生成的页面编码不对

  1. 用支持 UTF-8 的编辑器(如 VS、VS Code)打开 LoginPage.xaml。
  2. 点击右下角编码, 用GBK重新打开, 选择“另存为”,编码选 UTF-8。

image


七、这一页完成后的状态

到这一步为止,我们已经拥有:

  • 一个可运行的登录和签到页面
  • 一个结构清晰的 ViewModel
  • 没有任何“Demo 味”的临时代码

这也是整个签到 App 的业务起点


八、Copilot 在这一阶段的真实评价

客观来说:

  • Copilot 可以完成 80%~90% 的样板代码
  • 但剩下的 10%,决定了项目的质量

尤其是在:

  • 新SDK方法
  • 可扩展性

这些地方,人类工程师不可替代


九、Copilot 全生成的签到页面(一次没有人工干预的尝试)

在这一篇中,有一个地方我需要特别说明

下面这个「签到页面」,包括:

  • XAML UI 布局
  • Code-behind 页面逻辑(数据写入、导航、权限判断)

全部由 Visual Studio Copilot 一次性生成完成,并非事后整理或人工重构。

我本人只做了三件事:

  1. 确认代码可以正常编译
  2. 运行应用验证功能是否正确
  3. 没有对结构和实现方式做人工调整

这也是我在这个项目中,第一次有意识地“放手”让 Copilot 独立完成一个完整业务页面。


1️⃣ Copilot 生成的 XAML 页面

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="SignInMauiApp.SignInPage">
    <VerticalStackLayout Padding="30,60" Spacing="20">
        <Image HeightRequest="100" WidthRequest="100" Source="logo.jpg" HorizontalOptions="Center" />
        <Label x:Name="WelcomeLabel" FontSize="20" HorizontalOptions="Center" />
        <Button Text="签到" Clicked="OnSignInClicked" MaximumWidthRequest="600" />
        <Label x:Name="SignInResultLabel" FontSize="16" TextColor="Green" IsVisible="False" />
    </VerticalStackLayout>
</ContentPage>

这个 UI 非常克制,但已经完全满足一个内部业务 App 的实际需求:

  • 页面结构清晰
  • 控件命名明确,直接服务于后端逻辑
  • 没有过度样式或复杂布局

更重要的是:它是一个“为业务而生”的页面,而不是 Demo 页面


2️⃣ Copilot 生成的页面逻辑(Code-behind)

public partial class SignInPage : ContentPage
{
    private readonly IFreeSql? _fsql;
    private readonly User _user;
    private readonly Tenant _tenant;

    public SignInPage(User user, Tenant tenant)
    {
        InitializeComponent();
        _fsql = IPlatformApplication.Current?.Services.GetService<IFreeSql>();
        _user = user;
        _tenant = tenant;
        WelcomeLabel.Text = $"欢迎 {_user.Username},公司:{_tenant.Name}";
    }

    private async void OnSignInClicked(object sender, EventArgs e)
    {
        var record = new SignInRecord
        {
            UserId = _user.Id,
            TenantId = _tenant.Id,
            SignInTime = DateTime.Now
        };
        await _fsql!.Insert(record).ExecuteAffrowsAsync();
        SignInResultLabel.Text = $"签到成功:{record.SignInTime:yyyy-MM-dd HH:mm:ss}";
        SignInResultLabel.IsVisible = true;
        await Navigation.PushAsync(new SignInHistoryPage(_user, _tenant));
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();

        if (ToolbarItems.All(t => t.Text != "退出登录"))
        {
            ToolbarItems.Add(new ToolbarItem("退出登录", null, async () => {
                await Navigation.PopToRootAsync();
            }));
        }

        if (ToolbarItems.All(t => t.Text != "公司管理"))
        {
            ToolbarItems.Add(new ToolbarItem("公司管理", null, async () => {
                await Navigation.PushAsync(new TenantManagementPage());
            }));
        }

        if (_user.Username == "admin" && ToolbarItems.All(t => t.Text != "签到报表"))
        {
            ToolbarItems.Add(new ToolbarItem("签到报表", null, async () => {
                await Navigation.PushAsync(new SignInReportPage());
            }));
        }
    }
}

这段代码里,有几个非常值得注意的点:

  • 自动识别并使用了 多租户字段(TenantId)
  • 正确使用了 依赖注入获取 IFreeSql
  • 根据用户身份动态添加 Toolbar 操作
  • 页面职责清晰,没有引入不必要的抽象

在当下的 MAUI 项目实践中,这已经是一份完全可以直接投入使用的页面实现


3️⃣ Copilot 为什么能一次写对?

事后复盘我认为原因并不神秘:

  • 项目模型命名清晰(User / Tenant / SignInRecord)
  • 数据访问层边界明确(FreeSql)
  • 页面职责单一,没有复杂交互

Copilot 并不是“凭空理解业务”,而是在清晰工程上下文中做出了合理推断


4️⃣ 一个刻意的选择:暂不重构为 MVVM

从严格架构角度看,这一页当然可以:

  • 拆成 View + ViewModel
  • 使用 Command + Binding

但在这个阶段,我选择先验证业务闭环,而不是过早重构

在真实项目中,这是一个非常常见、也非常合理的决策。


十、下一篇预告

在验证了「Copilot 可生成完整业务页面」之后,下一步就非常自然了。

下一篇将进入:

第 4 篇:第一个页面跑起来之后,我是如何逐步引入 MVVM 的

我们会在真实代码基础上讨论:

  • 哪些地方值得重构
  • 哪些地方保持简单反而更好
  • Copilot 在重构阶段还能帮上什么忙

到这里,这个签到 App 已经不再是 Demo,而是一个真正开始生长的项目。

posted @ 2025-12-21 20:39  AlexChow  阅读(70)  评论(0)    收藏  举报