-
-
Notifications
You must be signed in to change notification settings - Fork 362
doc(Template5): add transition animation #6469
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| @page "/tutorials/template5" | ||
| @layout TutorialsLayout | ||
| @inherits WebSiteModuleComponentBase | ||
| @attribute [JSModuleAutoLoader("Samples/Tutorials/LoginAndRegister/Template5.razor.js", AutoInvokeDispose = false)] | ||
|
|
||
| <HeadContent> | ||
| <style> | ||
|
|
@@ -21,8 +23,8 @@ | |
|
|
||
| <div class="background-image"> | ||
| <div class="login-container"> | ||
| <div class="login-box animate-fade-in"> | ||
| <div class="header-row"> | ||
| <div class="login-box animate-fade-in" id="@Id"> | ||
| <div class="login-header"> | ||
| @if (!isAuth) | ||
| { | ||
| if (isEmailEntered) | ||
|
|
@@ -45,45 +47,12 @@ | |
| </div> | ||
| } | ||
| </div> | ||
| @if (!isEmailEntered) | ||
| { | ||
| <h3>登录</h3> | ||
| <p class="subtitle">使用你的 BootstrapBlazor 帐户。</p> | ||
| <ValidateForm Model="_loginModel" OnValidSubmit="OnEmailSubmit"> | ||
| <BootstrapInput type="email" class="input" SkipValidate="true" IsAutoFocus="true" | ||
| placeholder="电子邮件或电话号码" @bind-Value="@_loginModel.Email"></BootstrapInput> | ||
| <div class="error" hidden="@(!showEmailError)">请输入有效的电子邮件地址或电话号码</div> | ||
| <Button class="button" Color="Color.Primary" ButtonType="ButtonType.Submit" Text="下一步"></Button> | ||
| </ValidateForm> | ||
| <div class="links"> | ||
| <a href="#" @onclick:preventDefault>忘记用户名?</a> | ||
| </div> | ||
| <div class="small"> | ||
| 不熟悉 BootstrapBlazor?<a href="/">去看文档</a> | ||
| </div> | ||
| } | ||
| else if (!isAuth) | ||
| { | ||
| <h3>输入你的密码</h3> | ||
| <p class="email-display">@_loginModel.Email</p> | ||
| <ValidateForm Model="_loginModel" OnValidSubmit="OnPasswordSubmit"> | ||
| <BootstrapInput type="password" class="input" SkipValidate="true" IsAutoFocus="true" | ||
| placeholder="密码" @bind-Value="@_loginModel.Password"></BootstrapInput> | ||
| <div class="links"> | ||
| <a href="#" @onclick:preventDefault>忘记了密码?</a> | ||
| </div> | ||
| <Button class="button" Color="Color.Primary" ButtonType="ButtonType.Submit" Text="下一步"></Button> | ||
| </ValidateForm> | ||
| <div class="links"> | ||
| <a href="#" @onclick:preventDefault>其他登录方法</a> | ||
| </div> | ||
| } | ||
| else | ||
| @if (isAuth) | ||
| { | ||
| <div class="email-display2"><span>@_loginModel.Email</span></div> | ||
| <h5 class="text-center mt-3 mb-0">欢迎,您已成功登录</h5> | ||
| <div class="login-video-wrap"> | ||
| <video class="login-video" autoplay="autoplay" playsinline="playsinline" disablepictureinpicture=""> | ||
| <video class="login-video" autoplay playsinline disablepictureinpicture> | ||
| <source src="samples/login5/loading1.mp4" type="video/mp4; codecs=av01.0.05M.08"> | ||
| <source src="samples/login5/loading.mov" type="video/quicktime; codecs=hvc1.1.6.H120.b0"> | ||
| <source src="samples/login5/loading.webm" type="video/webm; codecs=vp9"> | ||
|
|
@@ -92,55 +61,45 @@ | |
| </video> | ||
| </div> | ||
| <p class="text-center text-muted" style="font-size: 0.75rem;">此登录高仿微软登录 UI</p> | ||
| <Button class="button" Color="Color.Primary">进入</Button> | ||
| <Button class="button" Color="Color.Primary" OnClick="OnReset">进入</Button> | ||
| } | ||
| else | ||
| { | ||
| <div class="login-body"> | ||
| <div class="login-item login-item-email show"> | ||
| <h3>登录</h3> | ||
| <p class="subtitle">使用你的 BootstrapBlazor 帐户。</p> | ||
| <ValidateForm Model="_loginModel" OnValidSubmit="OnEmailSubmit"> | ||
| <BootstrapInput type="email" class="input" SkipValidate="true" IsAutoFocus="true" | ||
| placeholder="电子邮件或电话号码" @bind-Value="@_loginModel.Email"></BootstrapInput> | ||
| <div class="error" hidden="@(!showEmailError)">请输入有效的电子邮件地址或电话号码</div> | ||
| <Button class="button" Color="Color.Primary" ButtonType="ButtonType.Submit" Text="下一步"></Button> | ||
| </ValidateForm> | ||
| <div class="links"> | ||
| <a href="#" @onclick:preventDefault>忘记用户名?</a> | ||
| </div> | ||
| <div class="small"> | ||
| 不熟悉 BootstrapBlazor?<a href="/">去看文档</a> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div class="login-item login-item-password"> | ||
| <h3>输入你的密码</h3> | ||
| <p class="email-display">@_loginModel.Email</p> | ||
| <ValidateForm Model="_loginModel" OnValidSubmit="OnPasswordSubmit"> | ||
| <BootstrapInput type="password" class="input" SkipValidate="true" IsAutoFocus="true" | ||
| placeholder="密码" @bind-Value="@_loginModel.Password"></BootstrapInput> | ||
| <div class="links"> | ||
| <a href="#" @onclick:preventDefault>忘记了密码?</a> | ||
| </div> | ||
| <Button class="button" Color="Color.Primary" ButtonType="ButtonType.Submit" Text="登录"></Button> | ||
| </ValidateForm> | ||
| <div class="links"> | ||
| <a href="#" @onclick:preventDefault>其他登录方法</a> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| } | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| @code { | ||
| private bool isEmailEntered = false; | ||
| private bool isAuth = false; | ||
| private bool showEmailError = false; | ||
| private LoginModel _loginModel = new LoginModel() | ||
| { | ||
| Email = "[email protected]", | ||
| Password = "123456" | ||
| }; | ||
|
|
||
| private Task OnEmailSubmit(EditContext context) | ||
| { | ||
| if (string.IsNullOrWhiteSpace(_loginModel.Email)) | ||
| { | ||
| showEmailError = true; | ||
| } | ||
| else | ||
| { | ||
| showEmailError = false; | ||
| isEmailEntered = true; | ||
| } | ||
| StateHasChanged(); | ||
| return Task.CompletedTask; | ||
| } | ||
|
|
||
| private Task OnPasswordSubmit(EditContext context) | ||
| { | ||
| // 数据库检查密码逻辑可以在这里实现 | ||
| // 演示代码一律通过 | ||
| isAuth = true; | ||
| StateHasChanged(); | ||
| return Task.CompletedTask; | ||
| } | ||
|
|
||
| private void GoBack() | ||
| { | ||
| isEmailEntered = false; | ||
| } | ||
|
|
||
| class LoginModel | ||
| { | ||
| public string? Email { get; set; } | ||
|
|
||
| public string? Password { get; set; } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the Apache 2.0 License | ||
| // See the LICENSE file in the project root for more information. | ||
| // Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone | ||
|
|
||
| using Microsoft.AspNetCore.Components.Forms; | ||
|
|
||
| namespace BootstrapBlazor.Server.Components.Samples.Tutorials.LoginAndRegister; | ||
|
|
||
| /// <summary> | ||
| /// 高仿 Microsoft 登录界面 | ||
| /// </summary> | ||
| public partial class Template5 | ||
| { | ||
| private bool isAuth = false; | ||
| private bool showEmailError = false; | ||
| private bool isEmailEntered = false; | ||
|
|
||
| private readonly LoginModel _loginModel = new() | ||
| { | ||
| Email = "[email protected]", | ||
| Password = "123456" | ||
| }; | ||
|
|
||
| private async Task OnEmailSubmit(EditContext context) | ||
| { | ||
| if (string.IsNullOrEmpty(_loginModel.Email)) | ||
| { | ||
| showEmailError = true; | ||
| isEmailEntered = false; | ||
| } | ||
| else | ||
| { | ||
| showEmailError = false; | ||
| isEmailEntered = true; | ||
| await InvokeVoidAsync("go", Id); | ||
| } | ||
| StateHasChanged(); | ||
| } | ||
|
|
||
| private Task OnPasswordSubmit(EditContext context) | ||
| { | ||
| // 数据库检查密码逻辑可以在这里实现 | ||
| // 演示代码一律通过 | ||
| isAuth = true; | ||
| StateHasChanged(); | ||
| return Task.CompletedTask; | ||
| } | ||
|
|
||
| private async Task GoBack() | ||
| { | ||
| isEmailEntered = false; | ||
| showEmailError = false; | ||
| StateHasChanged(); | ||
| await InvokeVoidAsync("back", Id); | ||
| } | ||
|
|
||
| private void OnReset() | ||
| { | ||
| isAuth = false; | ||
| isEmailEntered = false; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// <inheritdoc/> | ||
| /// </summary> | ||
| /// <param name="firstRender"></param> | ||
| /// <returns></returns> | ||
| protected override async Task OnAfterRenderAsync(bool firstRender) | ||
| { | ||
| await base.OnAfterRenderAsync(firstRender); | ||
| } | ||
|
|
||
| private class LoginModel | ||
| { | ||
| public string? Email { get; set; } | ||
|
|
||
| public string? Password { get; set; } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,47 @@ | ||||||||||||||||||||||||||||
| export function init(id) { | ||||||||||||||||||||||||||||
| const el = document.getElementById(id); | ||||||||||||||||||||||||||||
| if (el) { | ||||||||||||||||||||||||||||
| const email = el.querySelector('.login-item-email'); | ||||||||||||||||||||||||||||
| if (email) { | ||||||||||||||||||||||||||||
| email.classList.add('show'); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (bug_risk): The 'init' function is defined but not invoked from C#. Call 'init' from the C# side (e.g., in OnAfterRenderAsync) to ensure the login form displays the email step correctly on first render. |
||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| export function go(id) { | ||||||||||||||||||||||||||||
| const el = document.getElementById(id); | ||||||||||||||||||||||||||||
| if (el) { | ||||||||||||||||||||||||||||
| const email = el.querySelector('.login-item-email'); | ||||||||||||||||||||||||||||
| if (email) { | ||||||||||||||||||||||||||||
| email.classList.remove('show'); | ||||||||||||||||||||||||||||
| email.classList.remove('animate-fade-in') | ||||||||||||||||||||||||||||
| email.classList.add('animate-fade-out'); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const password = el.querySelector('.login-item-password'); | ||||||||||||||||||||||||||||
| if (password) { | ||||||||||||||||||||||||||||
| password.classList.add('show'); | ||||||||||||||||||||||||||||
| password.classList.add('animate-fade-in') | ||||||||||||||||||||||||||||
| password.classList.remove('animate-fade-out') | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| export function back(id) { | ||||||||||||||||||||||||||||
| const el = document.getElementById(id); | ||||||||||||||||||||||||||||
| if (el) { | ||||||||||||||||||||||||||||
| const email = el.querySelector('.login-item-email'); | ||||||||||||||||||||||||||||
| if (email) { | ||||||||||||||||||||||||||||
| email.classList.add('show'); | ||||||||||||||||||||||||||||
| email.classList.remove('animate-fade-out'); | ||||||||||||||||||||||||||||
| email.classList.add('animate-fade-in'); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+33
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Consider removing all animation classes before adding new ones to prevent animation conflicts. Residual animation classes can cause unintended effects. Remove both 'animate-fade-in' and 'animate-fade-out' before adding the new class.
Suggested change
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const password = el.querySelector('.login-item-password'); | ||||||||||||||||||||||||||||
| if (password) { | ||||||||||||||||||||||||||||
| password.classList.remove('show'); | ||||||||||||||||||||||||||||
| password.classList.remove('animate-fade-in') | ||||||||||||||||||||||||||||
| password.classList.add('animate-fade-out') | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (complexity): Consider replacing multiple boolean flags with a single state enum, removing the unused lifecycle method, and extracting the model class to its own file for clarity.
Here are a few focused tweaks that preserve all functionality while collapsing the three bool flags into a single state enum, removing the no-op lifecycle hook, and pulling your model out of the component:
Stagefield:StateHasChanged()calls (Blazor auto-re-renders on events):Remove the empty
OnAfterRenderAsyncoverride completely.Extract
LoginModelinto its own file (e.g.LoginModel.cs) under the same namespace:_stageinstead of multiple booleans:This collapses your three flags (
isAuth,showEmailError,isEmailEntered) into one, removes redundant lifecycle overrides, and pulls the model to its own file.