Skip to content

Commit e5b57f6

Browse files
committed
feat(openiddict): Add the implementation of SelectAccount
- 增加 `SelectAccount` 页面,实现select_account模式
1 parent afef5f7 commit e5b57f6

9 files changed

Lines changed: 728 additions & 3 deletions

File tree

apps/vben5/packages/@abp/account/src/utils/auth.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ const oidcSettings: UserManagerSettings = {
157157
silent_redirect_uri: `${window.location.origin}/silent-renew.html`,
158158
automaticSilentRenew: true,
159159
loadUserInfo: true,
160+
prompt: 'select_account',
160161
userStore: new WebStorageStateStore({
161162
store: import.meta.env.DEV
162163
? localStorage

aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/AbpAccountWebOpenIddictModule.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
using LINGYUN.Abp.Account.Web.OpenIddict.ViewModels.Authorize;
1+
using LINGYUN.Abp.Account.Web.OpenIddict.Pages.Account;
22
using Microsoft.AspNetCore.Mvc.Razor;
33
using Microsoft.AspNetCore.Mvc.RazorPages;
44
using Microsoft.Extensions.DependencyInjection;
5+
using Volo.Abp.AspNetCore.Mvc.Localization;
56
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
67
using Volo.Abp.Localization;
78
using Volo.Abp.Modularity;
@@ -18,6 +19,11 @@ public class AbpAccountWebOpenIddictModule : AbpModule
1819
{
1920
public override void PreConfigureServices(ServiceConfigurationContext context)
2021
{
22+
context.Services.PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options =>
23+
{
24+
options.AddAssemblyResource(typeof(AbpOpenIddictResource), typeof(AbpAccountWebOpenIddictModule).Assembly);
25+
});
26+
2127
PreConfigure<IMvcBuilder>(mvcBuilder =>
2228
{
2329
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAccountWebOpenIddictModule).Assembly);
@@ -39,6 +45,7 @@ public override void ConfigureServices(ServiceConfigurationContext context)
3945
Configure<RazorPagesOptions>(options =>
4046
{
4147
options.Conventions.AuthorizePage("/Authorize");
48+
options.Conventions.AuthorizePage("/Account/SelectAccount");
4249
});
4350

4451
Configure<AbpLocalizationOptions>(options =>
@@ -47,5 +54,19 @@ public override void ConfigureServices(ServiceConfigurationContext context)
4754
.Get<AbpOpenIddictResource>()
4855
.AddVirtualJson("/Localization/Resources");
4956
});
57+
58+
Configure<AbpBundlingOptions>(options =>
59+
{
60+
options.ScriptBundles
61+
.Add(typeof(SelectAccountModel).FullName, bundle =>
62+
{
63+
bundle.AddFiles("/Pages/Account/SelectAccount.js");
64+
});
65+
options.StyleBundles
66+
.Add(typeof(SelectAccountModel).FullName, bundle =>
67+
{
68+
bundle.AddFiles("/css/select-account.css");
69+
});
70+
});
5071
}
5172
}

aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/LINGYUN.Abp.Account.Web.OpenIddict.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@
1818

1919
<ItemGroup>
2020
<EmbeddedResource Include="Localization\**\*.json" />
21+
<EmbeddedResource Include="Pages\**\*.js" />
2122
<EmbeddedResource Include="Views\**\*.js" />
23+
<EmbeddedResource Include="wwwroot\**\*.css" />
2224
<Content Remove="Localization\**\*.json" />
25+
<Content Remove="Pages\**\*.js" />
2326
<Content Remove="Views\**\*.js" />
27+
<Content Remove="wwwroot\**\*.css" />
2428
</ItemGroup>
2529

2630
<ItemGroup>
Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
{
22
"culture": "en",
33
"texts": {
4-
"Required": "Required"
4+
"Required": "Required",
5+
"InvalidSelectAccountRequest": "Invalid request for a selected account!",
6+
"NoAvailableAccounts": "No Available Accounts!",
7+
"InvalidSelectAccount": "Invalid Select Account!",
8+
"SelectAccount": "Select Account",
9+
"CurrentAccount": "Current",
10+
"SignedInAs": "Select to login to the account of {0}",
11+
"LastLoginTime": "Last Login",
12+
"Continue": "Continue",
13+
"SwitchToAnotherAccount": "Switch to another account",
14+
"CreateNewAccount": "Create a new account",
15+
"SelectedAccountId": "Selected a cccount",
16+
"RememberSelection": "Remember selection",
17+
"RememberMe": "Remember me"
518
}
619
}
Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
{
22
"culture": "zh-Hans",
33
"texts": {
4-
"Required": "必须"
4+
"Required": "必须",
5+
"InvalidSelectAccountRequest": "无效的选择账户请求!",
6+
"NoAvailableAccounts": "未找到可用账户!",
7+
"InvalidSelectAccount": "选择的账户无效!",
8+
"SelectAccount": "选择账户",
9+
"CurrentAccount": "当前账户",
10+
"SignedInAs": "选择登录到 {0} 的账户",
11+
"LastLoginTime": "上次登录",
12+
"Continue": "继续",
13+
"SwitchToAnotherAccount": "使用其他账户",
14+
"CreateNewAccount": "创建新账户",
15+
"SelectedAccountId": "请选择一个账户",
16+
"RememberSelection": "记住我的选择",
17+
"RememberMe": "保持登录状态"
518
}
619
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
@page
2+
@using Microsoft.AspNetCore.Mvc.Localization
3+
@using Volo.Abp.AspNetCore.Mvc.UI.Layout
4+
@using Volo.Abp.AspNetCore.Mvc.UI.Theming;
5+
@using Volo.Abp.OpenIddict.Localization
6+
@using LINGYUN.Abp.Account.Web.OpenIddict.Pages.Account
7+
@inject IThemeManager ThemeManager
8+
@inject IPageLayout PageLayout
9+
@inject IHtmlLocalizer<AbpOpenIddictResource> L
10+
@model SelectAccountModel
11+
12+
@{
13+
Layout = ThemeManager.CurrentTheme.GetAccountLayout();
14+
PageLayout.Content.Title = L["SelectAccount"].Value;
15+
}
16+
17+
@section styles
18+
{
19+
<abp-style-bundle name="@typeof(SelectAccountModel).FullName" />
20+
}
21+
22+
@section scripts
23+
{
24+
<abp-script-bundle name="@typeof(SelectAccountModel).FullName" />
25+
}
26+
27+
<abp-card>
28+
<abp-card-body>
29+
@if (Model.AvailableAccounts.Any())
30+
{
31+
<form method="post" id="selectAccountForm">
32+
@Html.AntiForgeryToken()
33+
34+
<input type="hidden" asp-for="Input.ClientId" value="@Model.ClientName" />
35+
<input type="hidden" asp-for="Input.RedirectUri" value="@Model.RedirectUri" />
36+
<input type="hidden" asp-for="Input.RememberSelection" value="true" />
37+
<input type="hidden" asp-for="Input.RememberMe" value="true" />
38+
39+
<div class="alert alert-info d-flex align-items-center mb-4">
40+
<i class="fas fa-user-check me-2"></i>
41+
<span>@string.Format(@L["SignedInAs"].Value, @Model.UserName)</span>
42+
</div>
43+
44+
<div class="list-group list-group-flush rounded-3">
45+
@for (int i = 0; i < Model.AvailableAccounts.Count; i++)
46+
{
47+
var account = Model.AvailableAccounts[i];
48+
var accountId = $"{account.UserId}@{account.TenantId}";
49+
var accountName = $"{account.UserName}";
50+
if (!string.IsNullOrWhiteSpace(account.TenantName))
51+
{
52+
accountName += "/" + account.TenantName;
53+
}
54+
// TODO: Date format
55+
var lastLoginTime = account.LastLoginTime.HasValue ? account.LastLoginTime.Value.ToString("yyyy-MM:dd HH:mm:ss") : "";
56+
var isCurrent = account.IsCurrentAccount;
57+
// TODO: 实现用户头像
58+
var avatar = !string.IsNullOrWhiteSpace(account.UserName) ? account.UserName[0].ToString().ToUpper() : "U";
59+
60+
<label class="list-group-item list-group-item-action border-0 cursor-pointer @(isCurrent ? "active" : "") account-item"
61+
for="account-@i">
62+
<div class="d-flex align-items-center">
63+
<!-- 头像 -->
64+
<div class="rounded-circle bg-primary text-white d-flex align-items-center justify-content-center me-3"
65+
style="width: 40px; height: 40px; font-size: 16px; font-weight: 500;">
66+
@avatar
67+
</div>
68+
69+
<!-- 账户信息 -->
70+
<div class="flex-grow-1 me-3">
71+
<div class="d-flex align-items-center">
72+
<span class="fw-semibold">@accountName</span>
73+
@if (isCurrent)
74+
{
75+
<span class="badge bg-success ms-2">@L["CurrentAccount"].Value</span>
76+
}
77+
</div>
78+
<div class="text-muted small">
79+
@account.Email
80+
</div>
81+
@if (!string.IsNullOrWhiteSpace(lastLoginTime))
82+
{
83+
<div class="text-muted small">
84+
@L["LastLoginTime"]: @lastLoginTime
85+
</div>
86+
}
87+
</div>
88+
89+
<!-- 选择账户 -->
90+
<input type="radio"
91+
asp-for="Input.SelectedAccountId"
92+
value="@accountId"
93+
id="account-@i"
94+
class="form-check-input"
95+
style="width: 1.2em; height: 1.2em;"
96+
checked="@isCurrent" />
97+
</div>
98+
</label>
99+
}
100+
</div>
101+
102+
<!-- 操作按钮 -->
103+
<div class="d-grid gap-2 mt-2">
104+
<button type="submit" class="btn btn-primary" id="continueButton">
105+
@L["Continue"].Value
106+
<i class="fas fa-arrow-right"></i>
107+
</button>
108+
</div>
109+
</form>
110+
}
111+
else
112+
{
113+
<div class="alert alert-warning mb-4">
114+
<i class="fas fa-info-circle me-2"></i>
115+
@L["NoAccountsAvailable"].Value
116+
</div>
117+
}
118+
119+
<!-- 其他选项 -->
120+
<div class="d-grid gap-2 mt-2">
121+
<a href="@Url.Page("/Account/Login", new { ReturnUrl = Model.RedirectUri, prompt = "login" })"
122+
class="btn btn-outline-primary">
123+
<i class="fas fa-exchange-alt me-2"></i>
124+
@L["SwitchToAnotherAccount"].Value
125+
</a>
126+
<a href="@Url.Page("/Account/Register", new { ReturnUrl = Model.RedirectUri })"
127+
class="btn btn-outline-primary">
128+
<i class="fas fa-user-plus me-2"></i>
129+
@L["CreateNewAccount"].Value
130+
</a>
131+
</div>
132+
</abp-card-body>
133+
</abp-card>

0 commit comments

Comments
 (0)