Compare commits

..

1 Commits

Author SHA1 Message Date
Argo Zhang 02363c764c feat: Login/Home 页面增加多语言支持 2019-06-06 10:44:58 +08:00
1478 changed files with 46231 additions and 147078 deletions

View File

@ -6,22 +6,17 @@ root=true
# All files
[*]
indent_style = space
# shell files
[*.{cmd,sh,bat}]
insert_final_newline = true
trim_trailing_whitespace = true
# Code files
[*.{cs,csx,vb,vbx}]
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8-bom
[*.{json,yml,xml,csproj,props}]
indent_size = 2
###############################
# .NET Coding Conventions #
###############################
[*.{cs,vb}]
# Unix-style newlines with a newline ending every file
end_of_line = lf
# Organize usings
dotnet_sort_system_directives_first = false
# this. preferences
@ -123,13 +118,9 @@ csharp_space_between_method_call_empty_parameter_list_parentheses = false
# Wrapping preferences
csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true
csharp_style_namespace_declarations=file_scoped:silent
###############################
# VB Coding Conventions #
###############################
[*.vb]
# Modifier preferences
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
[*.cs]
# Add file header
file_header_template = Copyright (c) Argo Zhang (argo@163.com). All rights reserved.\nLicensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.\nWebsite: https://admin.blazor.zone

1
.gitattributes vendored
View File

@ -11,6 +11,7 @@
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
*.sln text eof=crlf
###############################################################################
# Set the merge driver for project and solution files

View File

@ -1,19 +0,0 @@
name: Auto Build CI
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 5.0.103
- name: Build with dotnet
run: dotnet build src/admin/Bootstrap.Admin/ --configuration Release

View File

@ -1,15 +0,0 @@
name: Docker Image CI
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Build the Docker image
run: docker build . --file src/admin/Bootstrap.Admin/Dockerfile --tag ba:$(date +%s)

7
.gitignore vendored
View File

@ -85,6 +85,7 @@ StyleCopReport.xml
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
@ -346,11 +347,11 @@ ASALocalRun/
**/[tT]humbs.db
# Net Core Keys
**/[Kk]eys/*.xml
Bootstrap*.xml
[Kk]eys/
Bootstrap.Admin.xml
###### -- Custom Ignore Section, Make sure all files you add to the git repo are below this line -- ######
# Coverage
coverage*.xml
tools/
tools/

View File

@ -3,7 +3,7 @@
# (note that '\' need to be escaped).
[issuetracker "Gitee-Issue"]
regex = "#((?!.*Issue|issue|Comme|comme)[A-Za-z0-9]+)"
regex = "#((?!.*Issue|issue|Comme|comme).{5})"
url = "https://gitee.com/LongbowEnterprise/BootstrapAdmin/issues/$1?from=project-issue"
[issuetracker "Gitee-Url"]

View File

@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\Keys\Longbow.Utility.snk</AssemblyOriginatorKeyFile>
<MvcRazorCompileOnPublish>true</MvcRazorCompileOnPublish>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
</PropertyGroup>
<PropertyGroup>
<DocumentationFile>..\Bootstrap.Admin\Bootstrap.Admin.xml</DocumentationFile>
<DockerDefaultTargetOS>Windows</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bootstrap.Security.Mvc" Version="2.2.6" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="3.1.2" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.7.9" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.3" PrivateAssets="All" />
<PackageReference Include="Sentry.AspNetCore" Version="1.2.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" />
</ItemGroup>
<ItemGroup>
<None Include="..\Keys\Longbow.Utility.snk" Link="Longbow.Utility.snk" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Bootstrap.DataAccess\Bootstrap.DataAccess.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,97 @@
using Bootstrap.Admin.Models;
using Bootstrap.DataAccess;
using Longbow.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Linq;
using System.Net;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Bootstrap.Admin.Controllers
{
/// <summary>
/// Account controller.
/// </summary>
[AllowAnonymous]
[AutoValidateAntiforgeryToken]
public class AccountController : Controller
{
/// <summary>
///
/// </summary>
/// <returns></returns>
[HttpGet]
public ActionResult Login()
{
if (DictHelper.RetrieveSystemModel())
{
ViewBag.UserName = "Admin";
ViewBag.Password = "123789";
}
return User.Identity.IsAuthenticated ? (ActionResult)Redirect("~/Home/Index") : View("Login", new LoginModel());
}
/// <summary>
/// Login the specified userName, password and remember.
/// </summary>
/// <returns>The login.</returns>
/// <param name="onlineUserSvr"></param>
/// <param name="ipLocator"></param>
/// <param name="userName">User name.</param>
/// <param name="password">Password.</param>
/// <param name="remember">Remember.</param>
[HttpPost]
public async Task<IActionResult> Login([FromServices]IOnlineUsers onlineUserSvr, [FromServices]IIPLocatorProvider ipLocator, string userName, string password, string remember)
{
if (UserHelper.Authenticate(userName, password, loginUser => CreateLoginUser(onlineUserSvr, ipLocator, HttpContext, loginUser)))
{
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.Name, userName));
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity), new AuthenticationProperties { ExpiresUtc = DateTimeOffset.Now.AddDays(DictHelper.RetrieveCookieExpiresPeriod()), IsPersistent = remember == "true" });
// redirect origin url
var originUrl = Request.Query[CookieAuthenticationDefaults.ReturnUrlParameter].FirstOrDefault() ?? "~/Home/Index";
return Redirect(originUrl);
}
return View("Login", new LoginModel() { AuthFailed = true });
}
/// <summary>
///
/// </summary>
/// <param name="onlineUserSvr"></param>
/// <param name="ipLocator"></param>
/// <param name="context"></param>
/// <param name="loginUser"></param>
internal static void CreateLoginUser(IOnlineUsers onlineUserSvr, IIPLocatorProvider ipLocator, HttpContext context, LoginUser loginUser)
{
loginUser.UserAgent = context.Request.Headers["User-Agent"];
var agent = new UserAgent(loginUser.UserAgent);
loginUser.Ip = (context.Connection.RemoteIpAddress ?? IPAddress.IPv6Loopback).ToString();
loginUser.City = ipLocator.Locate(loginUser.Ip);
loginUser.Browser = $"{agent.Browser?.Name} {agent.Browser?.Version}";
loginUser.OS = $"{agent.OS?.Name} {agent.OS?.Version}";
}
/// <summary>
/// Logout this instance.
/// </summary>
/// <returns>The logout.</returns>
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Redirect("~" + CookieAuthenticationDefaults.LoginPath);
}
/// <summary>
/// Accesses the denied.
/// </summary>
/// <returns>The denied.</returns>
[ResponseCache(Duration = 600)]
public ActionResult AccessDenied() => View("Error", ErrorModel.CreateById(403));
}
}

View File

@ -1,8 +1,4 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.Admin.Models;
using Bootstrap.Admin.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
@ -11,85 +7,72 @@ using System;
namespace Bootstrap.Admin.Controllers
{
/// <summary>
/// 后台管理控制器
///
/// </summary>
[Authorize]
public class AdminController : Controller
{
/// <summary>
/// 后台管理首页
///
/// </summary>
/// <returns></returns>
public ActionResult Index() => View(new NavigatorBarModel(this));
/// <summary>
/// 用户维护
///
/// </summary>
/// <returns></returns>
public ActionResult Users() => View(new NavigatorBarModel(this));
/// <summary>
/// 部门维护
///
/// </summary>
/// <returns></returns>
public ActionResult Groups() => View(new NavigatorBarModel(this));
/// <summary>
/// 字典表维护
///
/// </summary>
/// <returns></returns>
public ActionResult Dicts() => View(new NavigatorBarModel(this));
/// <summary>
/// 角色维护
///
/// </summary>
/// <returns></returns>
public ActionResult Roles() => View(new NavigatorBarModel(this));
/// <summary>
/// 菜单维护
///
/// </summary>
/// <returns></returns>
public ActionResult Menus() => View(new NavigatorBarModel(this));
/// <summary>
/// 操作日志
///
/// </summary>
/// <returns></returns>
public ActionResult Logs() => View(new NavigatorBarModel(this));
/// <summary>
/// 脚本日志
/// </summary>
/// <returns></returns>
public ActionResult SQL() => View(new NavigatorBarModel(this));
/// <summary>
/// 访问日志
///
/// </summary>
/// <returns></returns>
public ActionResult Traces() => View(new NavigatorBarModel(this));
/// <summary>
/// 登录日志
///
/// </summary>
/// <returns></returns>
public ActionResult Logins() => View(new NavigatorBarModel(this));
/// <summary>
/// FA 图标页面
///
/// </summary>
/// <returns></returns>
public ActionResult FAIcon() => View(new NavigatorBarModel(this));
/// <summary>
/// 健康检查
/// </summary>
/// <returns></returns>
public ActionResult Healths() => View(new NavigatorBarModel(this));
/// <summary>
/// 图标视图
///
/// </summary>
/// <returns></returns>
[AllowAnonymous]
@ -97,51 +80,44 @@ namespace Bootstrap.Admin.Controllers
public PartialViewResult IconView() => PartialView("IconView");
/// <summary>
/// 侧边栏局部视图
///
/// </summary>
/// <returns></returns>
/// <remark>菜单维护页面增删菜单时局部刷新时调用</remark>
public PartialViewResult Sidebar() => PartialView("Sidebar", new NavigatorBarModel(this));
public ActionResult Settings() => View(new ThemeModel(this));
/// <summary>
/// 网站设置
/// </summary>
/// <returns></returns>
public ActionResult Settings() => View(new SettingsModel(this));
/// <summary>
/// 通知管理
///
/// </summary>
/// <returns></returns>
public ActionResult Notifications() => View(new NavigatorBarModel(this));
/// <summary>
/// 个人中心
///
/// </summary>
/// <param name="host"></param>
/// <returns></returns>
public ActionResult Profiles([FromServices]IWebHostEnvironment host) => View(new ProfilesModel(this, host));
public ActionResult Profiles([FromServices]IHostingEnvironment host) => View(new ProfilesModel(this, host));
/// <summary>
/// 程序异常
///
/// </summary>
/// <returns></returns>
public ActionResult Exceptions() => View(new NavigatorBarModel(this));
/// <summary>
/// 消息通知
///
/// </summary>
/// <returns></returns>
public ActionResult Messages() => View(new NavigatorBarModel(this));
/// <summary>
/// 任务管理
///
/// </summary>
/// <returns></returns>
public ActionResult Tasks() => View(new TaskModel(this));
public ActionResult Tasks() => View(new NavigatorBarModel(this));
/// <summary>
/// 客户端测试页面
///
/// </summary>
/// <returns></returns>
public ActionResult Mobile() => View(new NavigatorBarModel(this));

View File

@ -1,9 +1,4 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.DataAccess;
using Microsoft.AspNetCore.Authorization;
using Bootstrap.DataAccess;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
@ -12,19 +7,18 @@ using System.Linq;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
/// 网站分析控制器
///
/// </summary>
[Route("api/[controller]")]
[Authorize]
[ApiController]
public class AnalyseController : ControllerBase
{
/// <summary>
/// 通过 logType 查询分析数据接口
///
/// </summary>
/// <returns></returns>
[HttpGet()]
public ActionResult<AnalyseData> Get([FromQuery]string logType)
public ActionResult<AnalyseData> Get([FromQuery]string logType = "")
{
var ret = new AnalyseData();
if (logType.Equals("LoginUsers", StringComparison.OrdinalIgnoreCase))
@ -64,19 +58,19 @@ namespace Bootstrap.Admin.Controllers.Api
}
/// <summary>
/// 分析数据实体类
///
/// </summary>
public class AnalyseData
{
/// <summary>
/// 获得/设置 折线数据集合
///
/// </summary>
public IEnumerable<string> Polylines { get; set; } = new string[0];
public IEnumerable<string> Polylines { get; set; }
/// <summary>
/// 获得 数据集合
///
/// </summary>
public List<KeyValuePair<string, string>> Datas { get; } = new List<KeyValuePair<string, string>>();
public List<KeyValuePair<string, string>> Datas { get; set; } = new List<KeyValuePair<string, string>>();
}
}
}

View File

@ -0,0 +1,22 @@
using Bootstrap.DataAccess;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
///
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class AppsController : ControllerBase
{
/// <summary>
///
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("{id}")]
public IEnumerable<App> Get(string id) => AppHelper.RetrievesByRoleId(id);
}
}

View File

@ -0,0 +1,47 @@
using Bootstrap.DataAccess;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
/// 数据字典分类
/// </summary>
[Route("api/[controller]/[action]")]
[ApiController]
public class CategoryController : ControllerBase
{
/// <summary>
/// 获取字典表中所有Category数据
/// </summary>
/// <returns></returns>
[HttpGet]
[AllowAnonymous]
public IEnumerable<string> RetrieveDictCategorys()
{
return DictHelper.RetrieveCategories();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
[HttpGet]
public IEnumerable<string> RetrieveMenus()
{
return MenuHelper.RetrieveAllMenus(User.Identity.Name).OrderBy(m => m.Name).Select(m => m.Name);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
[HttpGet]
public IEnumerable<string> RetrieveParentMenus()
{
return MenuHelper.RetrieveMenus(User.Identity.Name).Where(m => m.Menus.Count() > 0).OrderBy(m => m.Name).Select(m => m.Name);
}
}
}

View File

@ -1,28 +1,22 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.Admin.Query;
using Bootstrap.DataAccess;
using Bootstrap.Security;
using Longbow.Web.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Collections.Generic;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
/// 字典表维护控制器
///
/// </summary>
[Route("api/[controller]")]
[Authorize]
[ApiController]
public class DictsController : ControllerBase
{
/// <summary>
/// 获取所有字典表数据方法
///
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
@ -32,7 +26,7 @@ namespace Bootstrap.Admin.Controllers.Api
return value.RetrieveData();
}
/// <summary>
/// 保存字典方法
///
/// </summary>
/// <param name="value"></param>
[HttpPost]
@ -42,7 +36,7 @@ namespace Bootstrap.Admin.Controllers.Api
return DictHelper.Save(value);
}
/// <summary>
/// 删除字典项方法
///
/// </summary>
/// <param name="value"></param>
[HttpDelete]
@ -53,4 +47,4 @@ namespace Bootstrap.Admin.Controllers.Api
return DictHelper.Delete(value);
}
}
}
}

View File

@ -0,0 +1,87 @@
using Bootstrap.Admin.Query;
using Longbow.Web.Mvc;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
///
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class ExceptionsController : ControllerBase
{
/// <summary>
/// 显示所有异常
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[HttpGet]
public QueryData<object> Get([FromQuery]QueryExceptionOption value)
{
return value.Retrieves();
}
/// <summary>
/// 异常程序页面点击服务器日志按钮获取所有物理日志文件列表方法
/// </summary>
/// <returns></returns>
[HttpPost]
[ButtonAuthorize(Url = "~/Admin/Exceptions", Auth = "log")]
public IEnumerable<string> Post()
{
var filePath = Path.Combine(AppContext.BaseDirectory, "Error");
return Directory.Exists(filePath)
? Directory.GetFiles(filePath)
.Where(f => Path.GetExtension(f).Equals(".log", StringComparison.OrdinalIgnoreCase))
.Select(f => Path.GetFileNameWithoutExtension(f)).OrderByDescending(s => s)
: Enumerable.Empty<string>();
}
/// <summary>
/// 选中指定文件查看其内容方法
/// </summary>
/// <returns></returns>
[HttpPut]
[ButtonAuthorize(Url = "~/Admin/Exceptions", Auth = "log")]
public JsonResult Put([FromBody]ExceptionFileQuery exceptionFile)
{
var filePath = Path.Combine(AppContext.BaseDirectory, "Error");
var logName = $"{Path.Combine(filePath, exceptionFile.FileName)}.log";
if (!System.IO.File.Exists(logName)) return new JsonResult("无此日志文件");
StringBuilder sb = new StringBuilder();
using (StreamReader reader = new StreamReader(logName))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine().Replace("<", "&lt;").Replace(">", "&gt;");
if (line == "General Information ") sb.AppendFormat("<h4><b>{0}</b></h4>", line);
else if (line.StartsWith("TimeStamp:")) sb.AppendFormat("<div class='logTs'>{0}</div>", line);
else if (line.EndsWith("Exception Information")) sb.AppendFormat("<div class='logExcep'>{0}</div>", line);
else if (line.StartsWith("Message:")) sb.AppendFormat("<div class='logMsg'>{0}</div>", line);
else if (line.StartsWith("ErrorSql:")) sb.AppendFormat("<div class='logSql'>{0}</div>", line);
else if (line.StartsWith("Exception Type: Longbow.Data.DBAccessException")) sb.AppendFormat("<div class='logDbExcep'>{0}</div>", line);
else if (line.StartsWith("StackTrace Information")) sb.AppendFormat("<b>{0}</b><br>", line);
else sb.AppendFormat("{0}<br>", line);
}
}
return new JsonResult(sb.ToString());
}
/// <summary>
///
/// </summary>
public class ExceptionFileQuery
{
/// <summary>
///
/// </summary>
public string FileName { get; set; }
}
}
}

View File

@ -0,0 +1,136 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
///
/// </summary>
[Route("api/[controller]/[action]")]
[ApiController]
[AllowAnonymous]
public class GiteeController : ControllerBase
{
/// <summary>
///
/// </summary>
/// <param name="httpClientFactory"></param>
/// <param name="userName"></param>
/// <param name="repoName"></param>
/// <param name="label"></param>
/// <param name="color"></param>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult> Issues([FromServices]IHttpClientFactory httpClientFactory, [FromQuery]string userName = "LongbowEnterprise", [FromQuery]string repoName = "BootstrapAdmin", [FromQuery]string label = "custom badge", [FromQuery]string color = "orange")
{
var client = httpClientFactory.CreateClient();
var content = await GetJsonAsync(() => client.GetStringAsync($"https://gitee.com/{userName}/{repoName}/issues"));
var regex = Regex.Matches(content, "<div class='ui mini circular label'>([\\d]+)</div>", RegexOptions.IgnoreCase);
var labels = new string[] { "open", "closed", "rejected" };
var result = string.IsNullOrEmpty(content) ? new string[] { "unknown" } : regex.Select((m, i) => $"{labels[i]} {m.Groups[1].Value}");
return new JsonResult(new { schemaVersion = 1, label, message = string.Join(" ", result), color });
}
/// <summary>
///
/// </summary>
/// <param name="httpClientFactory"></param>
/// <param name="userName"></param>
/// <param name="repoName"></param>
/// <param name="label"></param>
/// <param name="color"></param>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult> Pulls([FromServices]IHttpClientFactory httpClientFactory, [FromQuery]string userName = "LongbowEnterprise", [FromQuery]string repoName = "BootstrapAdmin", [FromQuery]string label = "custom badge", [FromQuery]string color = "orange")
{
var client = httpClientFactory.CreateClient();
var content = await GetJsonAsync(() => client.GetStringAsync($"https://gitee.com/{userName}/{repoName}/pulls"));
var regex = Regex.Matches(content, "<div class='ui mini circular label'>([\\d]+)</div>", RegexOptions.IgnoreCase);
var labels = new string[] { "open", "merged", "closed" };
var result = string.IsNullOrEmpty(content) ? new string[] { "unknown" } : regex.Select((m, i) => $"{labels[i]} {m.Groups[1].Value}");
return new JsonResult(new { schemaVersion = 1, label, message = string.Join(" ", result), color });
}
/// <summary>
///
/// </summary>
/// <param name="httpClientFactory"></param>
/// <param name="userName"></param>
/// <param name="repoName"></param>
/// <param name="label"></param>
/// <param name="color"></param>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult> Releases([FromServices]IHttpClientFactory httpClientFactory, [FromQuery]string userName = "LongbowEnterprise", [FromQuery]string repoName = "BootstrapAdmin", [FromQuery]string label = "custom badge", [FromQuery]string color = "orange")
{
var client = httpClientFactory.CreateClient();
var content = await GetJsonAsync(() => client.GetStringAsync($"https://gitee.com/{userName}/{repoName}/releases"));
var regex = Regex.Match(content, $"<a href=\"/{userName}/{repoName}/releases/([^\\s]+)\" target=\"_blank\">", RegexOptions.IgnoreCase);
var result = string.IsNullOrEmpty(content) ? "unknown" : regex.Groups[1].Value;
return new JsonResult(new { schemaVersion = 1, label, message = result, color });
}
/// <summary>
///
/// </summary>
/// <param name="httpClientFactory"></param>
/// <param name="userName"></param>
/// <param name="projName"></param>
/// <param name="branchName"></param>
/// <param name="label"></param>
/// <param name="color"></param>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult> Builds([FromServices]IHttpClientFactory httpClientFactory, [FromQuery]string userName = "ArgoZhang", [FromQuery]string projName = "bootstrapadmin", [FromQuery]string branchName = "master", [FromQuery]string label = "custom badge", [FromQuery]string color = "orange")
{
var client = httpClientFactory.CreateClient();
var content = await GetJsonAsync(() => client.GetAsJsonAsync<AppveyorBuildResult>($"https://ci.appveyor.com/api/projects/{userName}/{projName}/branch/{branchName}"));
return new JsonResult(new { schemaVersion = 1, label, message = content == null ? "unknown" : content.Build.Version, color });
}
private async static Task<T> GetJsonAsync<T>(Func<Task<T>> callback)
{
var ret = default(T);
try
{
ret = await callback();
}
catch (TaskCanceledException)
{
}
catch (Exception ex)
{
ex.Log();
}
return ret;
}
/// <summary>
///
/// </summary>
private class AppveyorBuildResult
{
/// <summary>
///
/// </summary>
public Build Build { get; set; }
}
/// <summary>
///
/// </summary>
private class Build
{
/// <summary>
///
/// </summary>
public string Version { get; set; }
}
}
}

View File

@ -1,27 +1,21 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.Admin.Query;
using Bootstrap.DataAccess;
using Longbow.Web.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Collections.Generic;
using System.Linq;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
/// 部门维护控制器
///
/// </summary>
[Route("api/[controller]")]
[Authorize]
[ApiController]
public class GroupsController : ControllerBase
{
/// <summary>
/// 部门数据查询方法
///
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
@ -32,7 +26,18 @@ namespace Bootstrap.Admin.Controllers.Api
}
/// <summary>
/// 保存部门方法
///
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("{id}")]
public Group Get(string id)
{
return GroupHelper.Retrieves().FirstOrDefault(t => t.Id == id);
}
/// <summary>
///
/// </summary>
/// <param name="value"></param>
[HttpPost]
@ -43,7 +48,7 @@ namespace Bootstrap.Admin.Controllers.Api
}
/// <summary>
/// 删除部门方法
///
/// </summary>
/// <param name="value"></param>
[HttpDelete]
@ -56,7 +61,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// <summary>
/// 获取部门授权
/// </summary>
/// <param name="id">用户ID或者角色ID</param>
/// <param name="id"></param>
/// <param name="type"></param>
/// <returns></returns>
[HttpPost("{id}")]

View File

@ -1,10 +1,5 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.DataAccess;
using Bootstrap.DataAccess;
using Bootstrap.Security;
using Bootstrap.Security.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
@ -12,7 +7,7 @@ using System.Collections.Generic;
namespace Bootstrap.Admin.Controllers
{
/// <summary>
/// 接口控制器
///
/// </summary>
[Route("api/[controller]/[action]")]
[AllowAnonymous]
@ -20,7 +15,7 @@ namespace Bootstrap.Admin.Controllers
public class InterfaceController : ControllerBase
{
/// <summary>
/// 获取所有字典表数据
///
/// </summary>
/// <returns></returns>
[HttpPost]
@ -28,43 +23,41 @@ namespace Bootstrap.Admin.Controllers
{
return DictHelper.RetrieveDicts();
}
/// <summary>
/// 通过请求地址获取相对应角色集合
///
/// </summary>
/// <returns></returns>
[HttpPost]
public IEnumerable<string> RetrieveRolesByUrl([FromBody]string url)
{
return RoleHelper.RetrievesByUrl(url, BootstrapAppContext.AppId);
return RoleHelper.RetrieveRolesByUrl(url);
}
/// <summary>
/// 通过用户名获得分配所有角色
///
/// </summary>
/// <returns></returns>
[HttpPost]
public IEnumerable<string> RetrieveRolesByUserName([FromBody]string userName)
{
return RoleHelper.RetrievesByUserName(userName);
return RoleHelper.RetrieveRolesByUserName(userName);
}
/// <summary>
/// 通过用户名获得 User 实例
///
/// </summary>
/// <returns></returns>
[HttpPost]
public BootstrapUser? RetrieveUserByUserName([FromBody]string userName)
public BootstrapUser RetrieveUserByUserName([FromBody]string userName)
{
return UserHelper.RetrieveUserByUserName(userName);
}
/// <summary>
/// 通过指定条件获得应用程序菜单
///
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
[HttpPost]
public IEnumerable<BootstrapMenu> RetrieveAppMenus([FromBody]AppMenuOption args) => MenuHelper.RetrieveAppMenus(args.AppId, args.UserName, args.Url);
public IEnumerable<BootstrapMenu> RetrieveAppMenus([FromBody]AppMenuOption args)
{
return MenuHelper.RetrieveAppMenus(args.AppId, args.UserName, args.Url);
}
}
}

View File

@ -0,0 +1,63 @@
using Bootstrap.Admin.Query;
using Bootstrap.DataAccess;
using Bootstrap.Security;
using Longbow.Web;
using Longbow.Web.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
///
/// </summary>
/// <summary>
///
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class LoginController : ControllerBase
{
/// <summary>
/// 获得登录历史记录
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[HttpGet]
public QueryData<LoginUser> Get([FromQuery]QueryLoginOption value) => value.RetrieveData();
/// <summary>
///
/// </summary>
/// <param name="onlineUserSvr"></param>
/// <param name="ipLocator"></param>
/// <param name="value"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpPost]
public string Post([FromServices]IOnlineUsers onlineUserSvr, [FromServices]IIPLocatorProvider ipLocator, [FromBody]JObject value)
{
string token = null;
dynamic user = value;
string userName = user.userName;
string password = user.password;
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password) && UserHelper.Authenticate(userName, password, loginUser => AccountController.CreateLoginUser(onlineUserSvr, ipLocator, HttpContext, loginUser)))
{
token = BootstrapAdminJwtTokenHandler.CreateToken(userName);
}
return token;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
[AllowAnonymous]
[HttpOptions]
public string Options()
{
return null;
}
}
}

View File

@ -1,54 +1,48 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.Admin.Query;
using Bootstrap.Admin.Query;
using Bootstrap.DataAccess;
using Longbow.Web;
using Longbow.Web.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Net;
using System.Threading.Tasks;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
/// 操作日志控制器
///
/// </summary>
[Route("api/[controller]")]
[Authorize]
[ApiController]
public class LogsController : ControllerBase
{
/// <summary>
/// 前台获取操作日志数据调用
///
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[HttpGet]
public QueryData<Log> Get([FromQuery] QueryLogOption value)
public QueryData<Log> Get([FromQuery]QueryLogOption value)
{
return value.RetrieveData();
}
/// <summary>
/// 操作日志记录方法
///
/// </summary>
/// <param name="onlineUserSvr"></param>
/// <param name="ipLocator"></param>
/// <param name="value"></param>
/// <returns></returns>
[HttpPost]
public async Task<bool> Post([FromServices] IIPLocatorProvider ipLocator, [FromBody] Log value)
public bool Post([FromServices]IOnlineUsers onlineUserSvr, [FromServices]IIPLocatorProvider ipLocator, [FromBody]Log value)
{
value.UserAgent = Request.Headers["User-Agent"];
var agent = new UserAgent(value.UserAgent);
value.Ip = HttpContext.Connection.RemoteIpAddress?.ToIPv4String() ?? "";
value.Ip = (HttpContext.Connection.RemoteIpAddress ?? IPAddress.IPv6Loopback).ToString();
value.Browser = $"{agent.Browser?.Name} {agent.Browser?.Version}";
value.OS = $"{agent.OS?.Name} {agent.OS?.Version}";
value.City = await ipLocator.Locate(value.Ip);
value.UserName = User.Identity?.Name ?? string.Empty;
value.City = ipLocator.Locate(value.Ip);
value.UserName = User.Identity.Name;
return LogHelper.Save(value);
}
}
}
}

View File

@ -1,14 +1,8 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.Admin.Query;
using Bootstrap.DataAccess;
using Bootstrap.Security;
using Longbow.Web.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Collections.Generic;
namespace Bootstrap.Admin.Controllers.Api
@ -17,7 +11,6 @@ namespace Bootstrap.Admin.Controllers.Api
///
/// </summary>
[Route("api/[controller]")]
[Authorize]
[ApiController]
public class MenusController : ControllerBase
{
@ -29,7 +22,7 @@ namespace Bootstrap.Admin.Controllers.Api
[HttpGet]
public QueryData<object> Get([FromQuery]QueryMenuOption value)
{
return value.RetrieveData(User.Identity!.Name);
return value.RetrieveData(User.Identity.Name);
}
/// <summary>
@ -70,7 +63,7 @@ namespace Bootstrap.Admin.Controllers.Api
ret = MenuHelper.RetrieveMenusByRoleId(id);
break;
case "user":
ret = MenuHelper.RetrieveMenus(User.Identity!.Name);
ret = MenuHelper.RetrieveMenus(User.Identity.Name);
break;
}
return ret;
@ -89,4 +82,4 @@ namespace Bootstrap.Admin.Controllers.Api
return RoleHelper.SavaByMenuId(id, roleIds);
}
}
}
}

View File

@ -1,10 +1,5 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.Admin.Models;
using Bootstrap.Admin.Models;
using Bootstrap.DataAccess;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
@ -16,7 +11,6 @@ namespace Bootstrap.Admin.Controllers.Api
///
/// </summary>
[Route("api/[controller]")]
[Authorize]
[ApiController]
public class MessagesController : ControllerBase
{
@ -32,16 +26,16 @@ namespace Bootstrap.Admin.Controllers.Api
switch (id)
{
case "inbox":
ret = MessageHelper.Inbox(User.Identity!.Name).ToList();
ret = MessageHelper.Inbox(User.Identity.Name).ToList();
break;
case "sendmail":
ret = MessageHelper.SendMail(User.Identity!.Name).ToList();
ret = MessageHelper.SendMail(User.Identity.Name).ToList();
break;
case "mark":
ret = MessageHelper.Mark(User.Identity!.Name).ToList();
ret = MessageHelper.Mark(User.Identity.Name).ToList();
break;
case "trash":
ret = MessageHelper.Trash(User.Identity!.Name).ToList();
ret = MessageHelper.Trash(User.Identity.Name).ToList();
break;
}
return ret;
@ -56,12 +50,12 @@ namespace Bootstrap.Admin.Controllers.Api
{
var mcm = new MessageCountModel
{
InboxCount = MessageHelper.Inbox(User.Identity!.Name).Count(),
SendmailCount = MessageHelper.SendMail(User.Identity!.Name).Count(),
MarkCount = MessageHelper.Mark(User.Identity!.Name).Count(),
TrashCount = MessageHelper.Trash(User.Identity!.Name).Count()
InboxCount = MessageHelper.Inbox(User.Identity.Name).Count(),
SendmailCount = MessageHelper.SendMail(User.Identity.Name).Count(),
MarkCount = MessageHelper.Mark(User.Identity.Name).Count(),
TrashCount = MessageHelper.Trash(User.Identity.Name).Count()
};
return mcm;
}
}
}
}

View File

@ -1,9 +1,4 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.DataAccess;
using Microsoft.AspNetCore.Authorization;
using Bootstrap.DataAccess;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
@ -11,10 +6,9 @@ using System.Linq;
namespace Bootstrap.Admin.Controllers
{
/// <summary>
/// 新用户注册控制器
///
/// </summary>
[Route("api/[controller]")]
[Authorize]
[ApiController]
public class NewController : ControllerBase
{
@ -42,19 +36,15 @@ namespace Bootstrap.Admin.Controllers
public bool Put([FromBody]User value)
{
var ret = false;
var userName = User.Identity!.Name;
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(value.Id))
if (value.UserStatus == UserStates.ApproveUser)
{
if (value.UserStatus == UserStates.ApproveUser)
{
ret = UserHelper.Approve(value.Id, userName);
}
else if (value.UserStatus == UserStates.RejectUser)
{
ret = UserHelper.Reject(value.Id, userName);
}
ret = UserHelper.Approve(value.Id, User.Identity.Name);
}
else if (value.UserStatus == UserStates.RejectUser)
{
ret = UserHelper.Reject(value.Id, User.Identity.Name);
}
return ret;
}
}
}
}

View File

@ -1,26 +1,19 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.DataAccess;
using Bootstrap.DataAccess;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Linq;
using Longbow.Tasks;
using Microsoft.AspNetCore.Authorization;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
/// 系统通知控制器
///
/// </summary>
[Route("api/[controller]")]
[Authorize]
[ApiController]
public class NotificationsController : ControllerBase
{
/// <summary>
/// 后台 Header 状态条调用
///
/// </summary>
/// <returns></returns>
[HttpGet]
@ -43,15 +36,15 @@ namespace Bootstrap.Admin.Controllers.Api
});
// Tasks
var task = TaskServicesManager.ToList().Where(s => s.NextRuntime != null).Select(s => new { s.Name, s.LastRuntime, s.LastRunResult });
var task = TaskHelper.Retrieves();
var tasksCount = task.Count();
//Message
var message = MessageHelper.Retrieves(User.Identity!.Name);
var message = MessageHelper.Retrieves(User.Identity.Name);
var messagesCount = message.Count();
message = message.Take(6);
message.AsParallel().ForAll(m => m.FromIcon = Url.Content(m.FromIcon) ?? string.Empty);
message.AsParallel().ForAll(m => m.FromIcon = Url.Content(m.FromIcon));
//Apps
var apps = ExceptionsHelper.Retrieves().Where(n => n.Category != "DB");
@ -60,7 +53,7 @@ namespace Bootstrap.Admin.Controllers.Api
apps = apps.Take(6);
apps.AsParallel().ForAll(n =>
{
n.ExceptionType = n.ExceptionType?.Split('.').Last();
n.ExceptionType = n.ExceptionType.Split('.').Last();
var ts = DateTime.Now - n.LogTime;
if (ts.TotalMinutes < 5) n.Period = "刚刚";
else if (ts.Days > 0) n.Period = string.Format("{0}天", ts.Days);
@ -97,4 +90,4 @@ namespace Bootstrap.Admin.Controllers.Api
};
}
}
}
}

View File

@ -1,8 +1,4 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Longbow.Web;
using Longbow.Web;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
@ -18,7 +14,6 @@ namespace Bootstrap.Admin.Controllers.Api
/// 在线用户接口
/// </summary>
[Route("api/[controller]")]
[Authorize]
[ApiController]
public class OnlineUsersController : ControllerBase
{
@ -27,7 +22,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// </summary>
/// <returns></returns>
[HttpGet()]
public IEnumerable<OnlineUser> Get([FromServices] IOnlineUsers onlineUSers)
public IEnumerable<OnlineUser> Get([FromServices]IOnlineUsers onlineUSers)
{
return onlineUSers.OnlineUsers.OrderByDescending(u => u.LastAccessTime);
}
@ -39,7 +34,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// <param name="onlineUSers"></param>
/// <returns></returns>
[HttpGet("{id}")]
public IEnumerable<KeyValuePair<DateTime, string>> Get(string id, [FromServices] IOnlineUsers onlineUSers)
public IEnumerable<KeyValuePair<DateTime, string>> Get(string id, [FromServices]IOnlineUsers onlineUSers)
{
var user = onlineUSers.OnlineUsers.FirstOrDefault(u => u.ConnectionId == id);
return user?.RequestUrls ?? new KeyValuePair<DateTime, string>[0];
@ -53,7 +48,7 @@ namespace Bootstrap.Admin.Controllers.Api
[AllowAnonymous]
public bool Put()
{
var ip = Request.HttpContext.Connection.RemoteIpAddress?.ToIPv4String() ?? "";
var ip = (Request.HttpContext.Connection.RemoteIpAddress ?? IPAddress.IPv6Loopback).ToString();
if (_loginUsers.TryGetValue(ip, out var user))
{
user.Reset();
@ -76,7 +71,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// <summary>
///
/// </summary>
public string? Ip { get; set; }
public string Ip { get; set; }
/// <summary>
///
@ -112,7 +107,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// </summary>
public void Reset()
{
dispatcher.Change(TimeSpan.FromSeconds(30), Timeout.InfiniteTimeSpan);
if (dispatcher != null) dispatcher.Change(TimeSpan.FromSeconds(30), Timeout.InfiniteTimeSpan);
}
#region Impletement IDispose
@ -127,6 +122,7 @@ namespace Bootstrap.Admin.Controllers.Api
if (dispatcher != null)
{
dispatcher.Dispose();
dispatcher = null;
}
}
}

View File

@ -1,13 +1,7 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.DataAccess;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.IO;
using System.Threading.Tasks;
@ -15,10 +9,9 @@ using System.Threading.Tasks;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
/// 个人中心控制器
///
/// </summary>
[Route("api/[controller]")]
[Authorize]
[ApiController]
public class ProfilesController : ControllerBase
{
@ -31,10 +24,13 @@ namespace Bootstrap.Admin.Controllers.Api
/// <returns></returns>
[HttpPost("{id}")]
[ButtonAuthorize(Url = "~/Admin/Profiles", Auth = "saveIcon")]
public JsonResult Post(string id, [FromServices] IWebHostEnvironment env, [FromForm] DeleteFileCollection files)
public JsonResult Post(string id, [FromServices]IHostingEnvironment env, [FromForm]DeleteFileCollection files)
{
if (!id.Equals("Delete", StringComparison.OrdinalIgnoreCase)) return new JsonResult(new object());
var userName = User.Identity!.Name;
var previewUrl = string.Empty;
long fileSize = 0;
var userName = User.Identity.Name;
var fileName = files.Key;
fileName = Path.Combine(env.WebRootPath, $"images{Path.DirectorySeparatorChar}uploader{Path.DirectorySeparatorChar}{fileName}");
@ -42,10 +38,10 @@ namespace Bootstrap.Admin.Controllers.Api
fileName = "default.jpg";
var webSiteUrl = DictHelper.RetrieveIconFolderPath();
var filePath = Path.Combine(env.WebRootPath, webSiteUrl.Replace("~", string.Empty).Replace('/', Path.DirectorySeparatorChar).TrimStart(Path.DirectorySeparatorChar) + fileName);
var fileSize = new FileInfo(filePath).Length;
fileSize = new FileInfo(filePath).Length;
var iconName = $"{fileName}?v={DateTime.Now.Ticks}";
var previewUrl = Url.Content($"{webSiteUrl}{iconName}") ?? string.Empty;
if (!string.IsNullOrEmpty(userName)) UserHelper.SaveUserIconByName(userName, iconName);
previewUrl = Url.Content($"{webSiteUrl}{iconName}");
UserHelper.SaveUserIconByName(userName, iconName);
return new JsonResult(new
{
@ -58,14 +54,14 @@ namespace Bootstrap.Admin.Controllers.Api
}
/// <summary>
/// 待删除文件集合类
///
/// </summary>
public class DeleteFileCollection
{
/// <summary>
/// 获得/设置 文件名称
///
/// </summary>
public string Key { get; set; } = "";
public string Key { get; set; }
}
/// <summary>
@ -76,11 +72,11 @@ namespace Bootstrap.Admin.Controllers.Api
/// <returns></returns>
[HttpPost]
[ButtonAuthorize(Url = "~/Admin/Profiles", Auth = "saveIcon")]
public async Task<JsonResult> Post([FromServices] IWebHostEnvironment env, IFormCollection files)
public async Task<JsonResult> Post([FromServices]IHostingEnvironment env, IFormCollection files)
{
string? previewUrl = null;
var previewUrl = string.Empty;
long fileSize = 0;
var userName = User.Identity!.Name;
var userName = User.Identity.Name;
var fileName = string.Empty;
if (files.Files.Count > 0)
{
@ -88,18 +84,20 @@ namespace Bootstrap.Admin.Controllers.Api
var webSiteUrl = DictHelper.RetrieveIconFolderPath();
fileName = $"{userName}{Path.GetExtension(uploadFile.FileName)}";
var filePath = Path.Combine(env.WebRootPath, webSiteUrl.Replace("~", string.Empty).Replace('/', Path.DirectorySeparatorChar).TrimStart(Path.DirectorySeparatorChar) + fileName);
var fileFolder = Path.GetDirectoryName(filePath);
fileSize = uploadFile.Length;
if (!Directory.Exists(fileFolder)) Directory.CreateDirectory(fileFolder);
using (var fs = new FileStream(filePath, FileMode.Create))
{
await uploadFile.CopyToAsync(fs);
}
var iconName = $"{fileName}?v={DateTime.Now.Ticks}";
previewUrl = Url.Content($"{webSiteUrl}{iconName}");
if (!string.IsNullOrEmpty(userName)) UserHelper.SaveUserIconByName(userName, iconName);
UserHelper.SaveUserIconByName(userName, iconName);
}
return new JsonResult(new
{
initialPreview = new string[] { previewUrl ?? string.Empty },
initialPreview = new string[] { previewUrl },
initialPreviewConfig = new object[] {
new { caption = "新头像", size = fileSize, showZoom = true, key = fileName }
},
@ -108,26 +106,26 @@ namespace Bootstrap.Admin.Controllers.Api
}
/// <summary>
/// 个人中心操作方法 更改样式 更改显示名称 更改默认应用
///
/// </summary>
/// <returns></returns>
[HttpPut]
[ButtonAuthorize(Url = "~/Admin/Profiles", Auth = "saveDisplayName,savePassword,saveApp,saveTheme")]
public bool Put([FromBody] User value)
public bool Put([FromBody]User value)
{
var ret = false;
if (value.UserName.Equals(User.Identity!.Name, StringComparison.OrdinalIgnoreCase))
if (value.UserName.Equals(User.Identity.Name, StringComparison.OrdinalIgnoreCase))
{
ret = value.UserStatus switch
{
UserStates.ChangeTheme => UserHelper.SaveUserCssByName(value.UserName, value.Css),
UserStates.ChangeDisplayName => UserHelper.SaveDisplayName(value.UserName, value.DisplayName),
UserStates.ChangePassword => UserHelper.ChangePassword(value.UserName, value.Password, value.NewPassword),
UserStates.SaveApp => UserHelper.SaveApp(value.UserName, value.App),
_ => false
};
if (value.UserStatus == UserStates.ChangeTheme)
ret = UserHelper.SaveUserCssByName(value.UserName, value.Css);
else if (value.UserStatus == UserStates.ChangeDisplayName)
ret = UserHelper.SaveDisplayName(value.UserName, value.DisplayName);
else if (value.UserStatus == UserStates.ChangePassword)
ret = UserHelper.ChangePassword(value.UserName, value.Password, value.NewPassword);
else if (value.UserStatus == UserStates.SaveApp)
ret = UserHelper.SaveApp(value.UserName, value.App);
}
return ret;
}
}
}
}

View File

@ -1,7 +1,3 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.DataAccess;
using Longbow.Web.SignalR;
using Microsoft.AspNetCore.Authorization;
@ -15,8 +11,8 @@ namespace Bootstrap.Admin.Controllers.Api
/// <summary>
/// 注册用户操作类
/// </summary>
[Route("api/[controller]")]
[AllowAnonymous]
[Route("api/[controller]")]
[ApiController]
public class RegisterController : ControllerBase
{
@ -41,7 +37,7 @@ namespace Bootstrap.Admin.Controllers.Api
public async Task<bool> Post([FromServices]IHubContext<SignalRHub> hub, [FromBody]User user)
{
var ret = UserHelper.Save(user);
if (ret) await hub.SendMessageBody(new MessageBody() { Category = "Users", Message = string.Format("{0}-{1}", user.UserName, user.Description) }, HttpContext.RequestAborted);
if (ret) await SignalRManager.Send(hub.Clients.All, new MessageBody() { Category = "Users", Message = string.Format("{0}-{1}", user.UserName, user.Description) });
return ret;
}

View File

@ -1,13 +1,7 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.Admin.Query;
using Bootstrap.DataAccess;
using Longbow.Web.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Collections.Generic;
using System.Linq;
@ -15,15 +9,14 @@ using System.Linq;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
/// 角色维护控制器
///
/// </summary>
[Route("api/[controller]")]
[Authorize]
[ApiController]
public class RolesController : ControllerBase
{
/// <summary>
/// 获取所有角色数据
///
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
@ -41,17 +34,23 @@ namespace Bootstrap.Admin.Controllers.Api
[HttpPost("{id}")]
public IEnumerable<object> Post(string id, [FromQuery]string type)
{
var ret = type switch
IEnumerable<Role> ret = new List<Role>();
switch (type)
{
"user" => RoleHelper.RetrievesByUserId(id),
"group" => RoleHelper.RetrievesByGroupId(id),
"menu" => RoleHelper.RetrievesByMenuId(id),
_ => new Role[0]
};
case "user":
ret = RoleHelper.RetrievesByUserId(id);
break;
case "group":
ret = RoleHelper.RetrievesByGroupId(id);
break;
case "menu":
ret = RoleHelper.RetrievesByMenuId(id);
break;
}
return ret.Select(m => new { m.Id, m.Checked, m.RoleName, m.Description });
}
/// <summary>
/// 保存角色授权方法
/// 保存角色
/// </summary>
/// <param name="id">角色ID</param>
/// <param name="values">选中的ID集合</param>
@ -59,16 +58,28 @@ namespace Bootstrap.Admin.Controllers.Api
/// <returns></returns>
[HttpPut("{id}")]
[ButtonAuthorize(Url = "~/Admin/Roles", Auth = "assignUser,assignGroup,assignMenu,assignApp")]
public bool Put(string id, [FromBody]IEnumerable<string> values, [FromQuery]string type) => type switch
public bool Put(string id, [FromBody]IEnumerable<string> values, [FromQuery]string type)
{
"user" => UserHelper.SaveByRoleId(id, values),
"group" => GroupHelper.SaveByRoleId(id, values),
"menu" => MenuHelper.SaveMenusByRoleId(id, values),
"app" => AppHelper.SaveByRoleId(id, values),
_ => false
};
var ret = false;
switch (type)
{
case "user":
ret = UserHelper.SaveByRoleId(id, values);
break;
case "group":
ret = GroupHelper.SaveByRoleId(id, values);
break;
case "menu":
ret = MenuHelper.SaveMenusByRoleId(id, values);
break;
case "app":
ret = AppHelper.SaveByRoleId(id, values);
break;
}
return ret;
}
/// <summary>
/// 保存角色方法
///
/// </summary>
/// <param name="value"></param>
[HttpPost]
@ -78,7 +89,7 @@ namespace Bootstrap.Admin.Controllers.Api
return RoleHelper.Save(value);
}
/// <summary>
/// 删除角色方法
///
/// </summary>
/// <param name="value"></param>
[HttpDelete]

View File

@ -0,0 +1,31 @@
using Bootstrap.DataAccess;
using Bootstrap.Security;
using Longbow.Cache;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
///
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class SettingsController : ControllerBase
{
/// <summary>
///
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[HttpPost]
[ButtonAuthorize(Url = "~/Admin/Settings", Auth = "saveTitle,saveFooter,saveTheme")]
public bool Post([FromBody]BootstrapDict value) => DictHelper.SaveSettings(value);
/// <summary>
///
/// </summary>
[HttpGet]
public IEnumerable<ICacheCorsItem> Get() => CacheManager.CorsSites;
}
}

View File

@ -0,0 +1,24 @@
using Bootstrap.DataAccess;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
///
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class TasksController : ControllerBase
{
/// <summary>
///
/// </summary>
/// <returns></returns>
[HttpGet]
public IEnumerable<Task> Get()
{
return TaskHelper.Retrieves();
}
}
}

View File

@ -0,0 +1,26 @@
using Bootstrap.Admin.Query;
using Bootstrap.DataAccess;
using Longbow.Web.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
///
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class TracesController : ControllerBase
{
/// <summary>
///
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[HttpGet]
public QueryData<Trace> Get([FromQuery]QueryTraceOptions value)
{
return value.RetrieveData();
}
}
}

View File

@ -1,13 +1,8 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.Admin.Query;
using Bootstrap.DataAccess;
using Longbow.Web.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
@ -15,15 +10,14 @@ using System.Linq;
namespace Bootstrap.Admin.Controllers.Api
{
/// <summary>
/// 用户控制器
///
/// </summary>
[Route("api/[controller]")]
[Authorize]
[ApiController]
public class UsersController : ControllerBase
{
/// <summary>
/// 调用获取所有用户信息 用户管理查询按钮
///
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
@ -34,25 +28,35 @@ namespace Bootstrap.Admin.Controllers.Api
}
/// <summary>
/// 用户相关授权操作
///
/// </summary>
/// <param name="id">主键</param>
/// <param name="type">类型 如角色、部门</param>
/// <param name="id"></param>
/// <param name="type"></param>
/// <returns></returns>
[HttpPost("{id}")]
public IEnumerable<object> Post(string id, [FromQuery]string type) => type switch
public IEnumerable<object> Post(string id, [FromQuery]string type)
{
"role" => UserHelper.RetrievesByRoleId(id).Select(p => new
IEnumerable<object> ret = null;
switch (type)
{
p.Id,
p.DisplayName,
p.UserName,
p.Checked
}).OrderBy(u => u.DisplayName),
"group" => UserHelper.RetrievesByGroupId(id),
"reset" => UserHelper.RetrieveResetReasonsByUserName(id).Select(u => new { u.Key, u.Value }),
_ => new string[0]
};
case "role":
ret = UserHelper.RetrievesByRoleId(id).Select(p => new
{
p.Id,
p.DisplayName,
p.UserName,
p.Checked
});
break;
case "group":
ret = UserHelper.RetrievesByGroupId(id);
break;
case "reset":
ret = UserHelper.RetrieveResetReasonsByUserName(id).Select(u => new { u.Key, u.Value });
break;
}
return ret;
}
/// <summary>
/// 前台User View调用新建/更新用户
@ -62,10 +66,10 @@ namespace Bootstrap.Admin.Controllers.Api
[ButtonAuthorize(Url = "~/Admin/Users", Auth = "add,edit")]
public bool Post([FromBody]User value)
{
bool ret;
var ret = false;
if (string.IsNullOrEmpty(value.Id))
{
value.Description = string.Format("管理员{0}创建用户", User.Identity!.Name);
value.Description = string.Format("管理员{0}创建用户", User.Identity.Name);
value.ApprovedBy = User.Identity.Name;
value.ApprovedTime = DateTime.Now;
ret = UserHelper.Save(value);
@ -78,7 +82,7 @@ namespace Bootstrap.Admin.Controllers.Api
}
/// <summary>
/// 保存授权操作
///
/// </summary>
/// <param name="id"></param>
/// <param name="values"></param>
@ -86,15 +90,23 @@ namespace Bootstrap.Admin.Controllers.Api
/// <returns></returns>
[HttpPut("{id}")]
[ButtonAuthorize(Url = "~/Admin/Users", Auth = "assignRole,assignGroup")]
public bool Put(string id, [FromBody]IEnumerable<string> values, [FromQuery]string type) => type switch
public bool Put(string id, [FromBody]IEnumerable<string> values, [FromQuery]string type)
{
"role" => RoleHelper.SaveByUserId(id, values),
"group" => GroupHelper.SaveByUserId(id, values),
_ => false
};
var ret = false;
switch (type)
{
case "role":
ret = RoleHelper.SaveByUserId(id, values);
break;
case "group":
ret = GroupHelper.SaveByUserId(id, values);
break;
}
return ret;
}
/// <summary>
/// 删除用户操作
///
/// </summary>
/// <param name="value"></param>
[HttpDelete]
@ -105,14 +117,14 @@ namespace Bootstrap.Admin.Controllers.Api
}
/// <summary>
/// api 握手协议
///
/// </summary>
/// <returns></returns>
[AllowAnonymous]
[HttpOptions]
public string? Options()
public string Options()
{
return null;
}
}
}
}

View File

@ -1,8 +1,4 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.Admin.Models;
using Bootstrap.Admin.Models;
using Bootstrap.DataAccess;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
@ -11,32 +7,30 @@ using Microsoft.AspNetCore.Mvc;
namespace Bootstrap.Admin.Controllers
{
/// <summary>
/// Home Controller
///
/// </summary>
[Authorize]
public class HomeController : Controller
{
/// <summary>
/// Index View
///
/// </summary>
/// <returns></returns>
public IActionResult Index()
{
var model = new HeaderBarModel(User.Identity!.Name);
var homeUrl = DictHelper.RetrieveHomeUrl(User.Identity.Name, model.AppId);
var useBlazor = DictHelper.RetrieveEnableBlazor();
return homeUrl.Equals("~/Home/Index", System.StringComparison.OrdinalIgnoreCase) ? (useBlazor ? Redirect("~/Pages") : (IActionResult)View(model)) : Redirect(homeUrl);
var model = new HeaderBarModel(User.Identity);
var url = DictHelper.RetrieveHomeUrl(model.AppCode);
return url.Equals("~/Home/Index", System.StringComparison.OrdinalIgnoreCase) ? (IActionResult)View(model) : Redirect(url);
}
/// <summary>
/// Error View
///
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[AllowAnonymous]
public IActionResult Error(int? id = 0)
public IActionResult Error(int id)
{
var model = ErrorModel.CreateById(id ?? 0);
var model = ErrorModel.CreateById(id);
if (id != 403)
{
var returnUrl = Request.Query[CookieAuthenticationDefaults.ReturnUrlParameter].ToString();
@ -45,4 +39,4 @@ namespace Bootstrap.Admin.Controllers
return View(model);
}
}
}
}

View File

@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bootstrap.Admin.Controllers
{
/// <summary>
///
/// </summary>
[AllowAnonymous]
public class ToolsController : Controller
{
/// <summary>
///
/// </summary>
/// <returns></returns>
public IActionResult Index()
{
var returnUrl = Request.Query[CookieAuthenticationDefaults.ReturnUrlParameter].ToString();
return Redirect(returnUrl);
}
}
}

View File

@ -0,0 +1,25 @@
#Depending on the operating system of the host machines(s) that will build or run the containers, the image specified in the FROM statement may need to be changed.
#For more information, please see https://aka.ms/containercompat
FROM microsoft/dotnet:2.2-aspnetcore-runtime-nanoserver-1803 AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/dotnet:2.2-sdk-nanoserver-1803 AS build
WORKDIR /src
COPY ["Bootstrap.Admin/Bootstrap.Admin.csproj", "Bootstrap.Admin/"]
COPY ["Bootstrap.DataAccess/Bootstrap.DataAccess.csproj", "Bootstrap.DataAccess/"]
RUN dotnet restore "Bootstrap.Admin/Bootstrap.Admin.csproj"
COPY . .
WORKDIR "/src/Bootstrap.Admin"
RUN dotnet build "Bootstrap.Admin.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "Bootstrap.Admin.csproj" -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
COPY --from=build ["/src/Bootstrap.Admin/BootstrapAdmin.db", "./BootstrapAdmin.db"]
COPY --from=build ["/src/Scripts/Longbow.lic", "./Longbow.lic"]
ENTRYPOINT ["dotnet", "Bootstrap.Admin.dll"]

View File

@ -0,0 +1,35 @@
using Microsoft.AspNetCore.Authorization;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Collections.Generic;
using Operation = Swashbuckle.AspNetCore.Swagger.Operation;
namespace Bootstrap.Admin
{
/// <summary>
///
/// </summary>
public class HttpHeaderOperation : IOperationFilter
{
/// <summary>
///
/// </summary>
/// <param name="operation"></param>
/// <param name="context"></param>
public void Apply(Operation operation, OperationFilterContext context)
{
if (operation.Parameters == null) operation.Parameters = new List<IParameter>();
if (context.MethodInfo.GetCustomAttributes(typeof(AllowAnonymousAttribute), true).Length == 0)
{
operation.Parameters.Add(new NonBodyParameter()
{
Name = "Authorization", //添加Authorization头部参数
In = "header",
Type = "string",
Required = false
});
}
}
}
}

View File

@ -0,0 +1,22 @@
FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/dotnet:2.2-sdk AS build
WORKDIR /src
COPY ["Bootstrap.Admin/Bootstrap.Admin.csproj", "Bootstrap.Admin/"]
COPY ["Bootstrap.DataAccess/Bootstrap.DataAccess.csproj", "Bootstrap.DataAccess/"]
RUN dotnet restore "Bootstrap.Admin/Bootstrap.Admin.csproj"
COPY . .
WORKDIR "/src/Bootstrap.Admin"
RUN dotnet build "Bootstrap.Admin.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "Bootstrap.Admin.csproj" -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
COPY --from=publish ["/src/Bootstrap.Admin/BootstrapAdmin.db", "./BootstrapAdmin.db"]
COPY --from=publish ["/src/Scripts/Longbow.lic", "./Longbow.lic"]
ENTRYPOINT ["dotnet", "Bootstrap.Admin.dll"]

View File

@ -1,13 +1,9 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
namespace Bootstrap.Admin.Models
namespace Bootstrap.Admin.Models
{
/// <summary>
///
/// </summary>
public class ErrorModel : ModelBase
public class ErrorModel
{
/// <summary>
///
@ -17,28 +13,28 @@ namespace Bootstrap.Admin.Models
/// <summary>
///
/// </summary>
public string Title { get; set; } = "";
public string Title { get; set; }
/// <summary>
///
/// </summary>
public string Content { get; set; } = "";
public string Content { get; set; }
/// <summary>
///
/// </summary>
public string Image { get; set; } = "";
public string Image { get; set; }
/// <summary>
///
/// </summary>
public string Detail { get; set; } = "";
public string Detail { get; set; }
/// <summary>
///
/// </summary>
public string ReturnUrl { get; set; } = "";
public string ReturnUrl { get; set; }
/// <summary>
///
@ -53,7 +49,7 @@ namespace Bootstrap.Admin.Models
Title = "服务器内部错误",
Content = "服务器内部错误",
Detail = "相关错误信息已经记录到日志中,请登录服务器或后台管理中查看",
Image = "~/images/error_icon.png",
Image = "~/Images/error_icon.png",
ReturnUrl = "~/Admin/Index"
};
@ -65,7 +61,7 @@ namespace Bootstrap.Admin.Models
case 404:
model.Title = "资源未找到";
model.Content = "请求资源未找到";
model.Image = "~/images/404_icon.png";
model.Image = "~/Images/404_icon.png";
break;
case 403:
model.Title = "未授权请求";

View File

@ -0,0 +1,56 @@
using Bootstrap.DataAccess;
using System.Security.Principal;
namespace Bootstrap.Admin.Models
{
/// <summary>
///
/// </summary>
public class HeaderBarModel : ModelBase
{
/// <summary>
///
/// </summary>
/// <param name="identity"></param>
public HeaderBarModel(IIdentity identity)
{
var user = UserHelper.RetrieveUserByUserName(identity.Name);
Icon = string.Format("{0}{1}", DictHelper.RetrieveIconFolderPath(), user.Icon);
DisplayName = user.DisplayName;
UserName = user.UserName;
AppCode = user.App;
Css = user.Css;
ActiveCss = string.IsNullOrEmpty(Css) ? Theme : Css;
}
/// <summary>
///
/// </summary>
public string UserName { get; }
/// <summary>
///
/// </summary>
public string DisplayName { get; }
/// <summary>
/// 获得/设置 用户头像地址
/// </summary>
public string Icon { get; }
/// <summary>
/// 获取/设置 个人网站样式
/// </summary>
public string Css { get; }
/// <summary>
/// 获得 当前设置的默认应用
/// </summary>
public string AppCode { get; }
/// <summary>
/// 获得 当前样式
/// </summary>
public string ActiveCss { get; }
}
}

View File

@ -0,0 +1,28 @@
using Bootstrap.DataAccess;
namespace Bootstrap.Admin.Models
{
/// <summary>
///
/// </summary>
public class LoginModel : ModelBase
{
/// <summary>
///
/// </summary>
public LoginModel()
{
ImageLibUrl = DictHelper.RetrieveImagesLibUrl();
}
/// <summary>
/// 验证码图床地址
/// </summary>
public string ImageLibUrl { get; protected set; }
/// <summary>
/// 是否登录认证失败
/// </summary>
public bool AuthFailed { get; set; }
}
}

View File

@ -1,8 +1,4 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
namespace Bootstrap.Admin.Models
namespace Bootstrap.Admin.Models
{
/// <summary>
///

View File

@ -0,0 +1,41 @@
using Bootstrap.DataAccess;
namespace Bootstrap.Admin.Models
{
/// <summary>
///
/// </summary>
public class ModelBase
{
/// <summary>
///
/// </summary>
public ModelBase()
{
Title = DictHelper.RetrieveWebTitle();
Footer = DictHelper.RetrieveWebFooter();
Theme = DictHelper.RetrieveActiveTheme();
IsDemo = DictHelper.RetrieveSystemModel();
}
/// <summary>
///
/// </summary>
public string Title { get; private set; }
/// <summary>
///
/// </summary>
public string Footer { get; private set; }
/// <summary>
/// 网站样式全局设置
/// </summary>
public string Theme { get; protected set; }
/// <summary>
/// 是否为演示系统
/// </summary>
public bool IsDemo { get; protected set; }
}
}

View File

@ -0,0 +1,36 @@
using Bootstrap.DataAccess;
using Bootstrap.Security;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Bootstrap.Admin.Models
{
/// <summary>
///
/// </summary>
public class NavigatorBarModel : HeaderBarModel
{
/// <summary>
///
/// </summary>
/// <param name="controller"></param>
public NavigatorBarModel(ControllerBase controller) : base(controller.User.Identity)
{
Navigations = MenuHelper.RetrieveSystemMenus(UserName, $"~{controller.HttpContext.Request.Path}");
var authApps = AppHelper.RetrievesByUserName(controller.User.Identity.Name);
Applications = DictHelper.RetrieveApps().Where(app => app.Key == "0" || authApps.Any(key => key.Equals(app.Key, StringComparison.OrdinalIgnoreCase)));
}
/// <summary>
///
/// </summary>
public IEnumerable<BootstrapMenu> Navigations { get; private set; }
/// <summary>
///
/// </summary>
public IEnumerable<KeyValuePair<string, string>> Applications { get; private set; }
}
}

View File

@ -0,0 +1,44 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using System.IO;
using System.Linq;
namespace Bootstrap.Admin.Models
{
/// <summary>
///
/// </summary>
public class ProfilesModel : ThemeModel
{
/// <summary>
/// 获得 头像文件大小
/// </summary>
public long Size { get; }
/// <summary>
/// 获得 头像文件名称
/// </summary>
public string FileName { get; }
/// <summary>
///
/// </summary>
/// <param name="host"></param>
/// <param name="controller"></param>
public ProfilesModel(ControllerBase controller, IHostingEnvironment host) : base(controller)
{
if (host != null)
{
var fileName = Path.Combine(host.WebRootPath, Icon.TrimStart('~', '/').Replace('/', Path.DirectorySeparatorChar));
// 数据库存储的个人图片有后缀 default.jpg?v=1234567
fileName = fileName.Split('?').FirstOrDefault();
if (File.Exists(fileName))
{
Size = new FileInfo(fileName).Length;
FileName = Path.GetFileName(fileName);
}
}
}
}
}

View File

@ -0,0 +1,26 @@
using Bootstrap.DataAccess;
using Bootstrap.Security;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace Bootstrap.Admin.Models
{
/// <summary>
///
/// </summary>
public class ThemeModel : NavigatorBarModel
{
/// <summary>
///
/// </summary>
/// <param name="controller"></param>
public ThemeModel(ControllerBase controller) : base(controller)
{
Themes = DictHelper.RetrieveThemes();
}
/// <summary>
/// 获得 系统配置的所有样式表
/// </summary>
public IEnumerable<BootstrapDict> Themes { get; }
}
}

View File

@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace Bootstrap.Client
namespace Bootstrap.Admin
{
/// <summary>
///
@ -14,15 +14,14 @@ namespace Bootstrap.Client
/// <param name="args"></param>
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
CreateWebHostBuilder(args).Build().Run();
}
/// <summary>
///
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(builder => builder.UseStartup<Startup>());
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args).UseSentry().UseStartup<Startup>();
}
}

View File

@ -16,11 +16,6 @@
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}"
},
"Bootstrap.Admin": {
"commandName": "Project",
"launchBrowser": true,
@ -28,6 +23,11 @@
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:50852/"
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}"
}
}
}

View File

@ -1,61 +1,46 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.DataAccess;
using Bootstrap.DataAccess;
using Bootstrap.Security;
using Longbow.Web.Mvc;
using System;
using System.Linq;
namespace Bootstrap.Admin.Query
{
/// <summary>
/// 字典表查询类
///
/// </summary>
public class QueryDictOption : PaginationOption
{
/// <summary>
/// 字典分项
/// </summary>
public string? Category { get; set; }
public string Category { get; set; }
/// <summary>
/// 字典名称
/// </summary>
public string? Name { get; set; }
public string Name { get; set; }
/// <summary>
/// 字典种类
/// </summary>
public string? Define { get; set; }
public string Define { get; set; }
/// <summary>
/// 字典表查询
/// </summary>
/// <returns></returns>
public QueryData<BootstrapDict> RetrieveData()
{
if (string.IsNullOrEmpty(Order)) Order = "asc";
if (string.IsNullOrEmpty(Sort)) Sort = "Category";
var data = DictHelper.RetrieveDicts();
if (!string.IsNullOrEmpty(Category))
{
data = data.Where(t => t.Category.Contains(Category, StringComparison.OrdinalIgnoreCase));
data = data.Where(t => t.Category.Contains(Category));
}
if (!string.IsNullOrEmpty(Name))
{
data = data.Where(t => t.Name.Contains(Name, StringComparison.OrdinalIgnoreCase));
data = data.Where(t => t.Name.Contains(Name));
}
if (!string.IsNullOrEmpty(Define))
{
data = data.Where(t => t.Define.ToString() == Define);
}
if (!string.IsNullOrEmpty(Search))
{
data = data.Where(t => t.Category.Contains(Search, StringComparison.OrdinalIgnoreCase) || t.Name.Contains(Search, StringComparison.OrdinalIgnoreCase) || t.Code.Contains(Search, StringComparison.OrdinalIgnoreCase));
}
var ret = new QueryData<BootstrapDict>();
ret.total = data.Count();
// 通过option.Sort属性判断对那列进行排序
@ -78,4 +63,4 @@ namespace Bootstrap.Admin.Query
return ret;
}
}
}
}

View File

@ -1,8 +1,4 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.DataAccess;
using Bootstrap.DataAccess;
using Longbow.Web.Mvc;
using System;
using System.Linq;
@ -10,22 +6,20 @@ using System.Linq;
namespace Bootstrap.Admin.Query
{
/// <summary>
/// 程序异常查询条件类
///
/// </summary>
public class QueryExceptionOption : PaginationOption
{
/// <summary>
/// 获得/设置 开始时间
///
/// </summary>
public DateTime? StartTime { get; set; }
/// <summary>
/// 获得/设置 结束时间
///
/// </summary>
public DateTime? EndTime { get; set; }
/// <summary>
/// 查询方法
///
/// </summary>
/// <returns></returns>
public QueryData<object> Retrieves()

View File

@ -1,31 +1,24 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.DataAccess;
using Bootstrap.DataAccess;
using Longbow.Web.Mvc;
using System;
using System.Linq;
namespace Bootstrap.Admin.Query
{
/// <summary>
/// 部门查询条件类
///
/// </summary>
public class QueryGroupOption : PaginationOption
{
/// <summary>
/// 获得/设置 部门名称
///
/// </summary>
public string? GroupName { get; set; }
public string GroupName { get; set; }
/// <summary>
/// 获得/设置 部门描述
///
/// </summary>
public string? Description { get; set; }
public string Description { get; set; }
/// <summary>
/// 部门查询数据方法
///
/// </summary>
/// <returns></returns>
public QueryData<object> RetrieveData()
@ -34,21 +27,17 @@ namespace Bootstrap.Admin.Query
var data = GroupHelper.Retrieves();
if (!string.IsNullOrEmpty(GroupName))
{
data = data.Where(t => t.GroupName.Contains(GroupName, StringComparison.OrdinalIgnoreCase));
data = data.Where(t => t.GroupName.Contains(GroupName));
}
if (!string.IsNullOrEmpty(Description))
{
data = data.Where(t => t.Description?.Contains(Description, StringComparison.OrdinalIgnoreCase) ?? false);
}
if (!string.IsNullOrEmpty(Search))
{
data = data.Where(t => t.GroupName.Contains(Search, StringComparison.OrdinalIgnoreCase) || (t.Description?.Contains(Search, StringComparison.OrdinalIgnoreCase) ?? false));
data = data.Where(t => t.Description.Contains(Description));
}
var ret = new QueryData<object>();
ret.total = data.Count();
data = Order == "asc" ? data.OrderBy(t => t.GroupName) : data.OrderByDescending(t => t.GroupName);
ret.rows = data.Skip(Offset).Take(Limit).Select(g => new { g.Id, g.GroupCode, g.GroupName, g.Description });
ret.rows = data.Skip(Offset).Take(Limit).Select(g => new { g.Id, g.GroupName, g.Description });
return ret;
}
}
}
}

View File

@ -1,35 +1,31 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.DataAccess;
using Bootstrap.DataAccess;
using Longbow.Web.Mvc;
using System;
namespace Bootstrap.Admin.Query
{
/// <summary>
/// 操作日志查询条件类
///
/// </summary>
public class QueryLogOption : PaginationOption
{
/// <summary>
/// 获得/设置 操作类型
///
/// </summary>
public string? OperateType { get; set; }
public string OperateType { get; set; }
/// <summary>
/// 获得/设置 开始时间
///
/// </summary>
public DateTime? OperateTimeStart { get; set; }
/// <summary>
/// 获得/设置 结束时间
///
/// </summary>
public DateTime? OperateTimeEnd { get; set; }
/// <summary>
/// 获得/设置 获取查询分页数据
///
/// </summary>
/// <returns></returns>
public QueryData<Log> RetrieveData()
@ -41,4 +37,4 @@ namespace Bootstrap.Admin.Query
return ret;
}
}
}
}

View File

@ -1,8 +1,4 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.DataAccess;
using Bootstrap.DataAccess;
using Longbow.Web.Mvc;
using System;
@ -26,7 +22,7 @@ namespace Bootstrap.Admin.Query
/// <summary>
/// 登录IP地址
/// </summary>
public string? LoginIP { get; set; }
public string LoginIP { get; set; }
/// <summary>
///
@ -34,6 +30,7 @@ namespace Bootstrap.Admin.Query
/// <returns></returns>
public QueryData<LoginUser> RetrieveData()
{
if (string.IsNullOrEmpty(Order)) Order = "LoginTime desc";
var data = LoginHelper.RetrievePages(this, StartTime, EndTime, LoginIP);
return new QueryData<LoginUser>
{

View File

@ -1,7 +1,3 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.DataAccess;
using Longbow.Web.Mvc;
using System;
@ -10,66 +6,62 @@ using System.Linq;
namespace Bootstrap.Admin.Query
{
/// <summary>
///
///
/// </summary>
public class QueryMenuOption : PaginationOption
{
/// <summary>
///
///
/// </summary>
public string? Name { get; set; }
public string Name { get; set; }
/// <summary>
///
///
/// </summary>
public string? ParentName { get; set; }
public string ParentName { get; set; }
/// <summary>
///
///
/// </summary>
public string? Category { get; set; }
public string Category { get; set; }
/// <summary>
///
///
/// </summary>
public string? IsResource { get; set; }
public string IsResource { get; set; }
/// <summary>
///
///
/// </summary>
public string? AppId { get; set; }
public string AppCode { get; set; }
/// <summary>
///
///
/// </summary>
/// <param name="userName"></param>
/// <returns></returns>
public QueryData<object> RetrieveData(string? userName)
public QueryData<object> RetrieveData(string userName)
{
var data = MenuHelper.RetrieveMenusByUserName(userName);
if (!string.IsNullOrEmpty(ParentName))
{
data = data.Where(t => t.Name.Contains(ParentName, StringComparison.OrdinalIgnoreCase) || (t.ParentName != null && t.ParentName.Contains(ParentName, StringComparison.OrdinalIgnoreCase)));
data = data.Where(t => t.ParentName != null && t.ParentName.Contains(ParentName));
}
if (!string.IsNullOrEmpty(Name))
{
data = data.Where(t => t.Name.Contains(Name, StringComparison.OrdinalIgnoreCase));
data = data.Where(t => t.Name.Contains(Name));
}
if (!string.IsNullOrEmpty(Category))
{
data = data.Where(t => t.Category.Contains(Category, StringComparison.OrdinalIgnoreCase));
data = data.Where(t => t.Category.Contains(Category));
}
if (!string.IsNullOrEmpty(IsResource))
{
data = data.Where(t => t.IsResource.ToString() == IsResource);
}
if (!string.IsNullOrEmpty(AppId))
if (!string.IsNullOrEmpty(AppCode))
{
data = data.Where(t => t.Application.Equals(AppId, StringComparison.OrdinalIgnoreCase));
}
if (!string.IsNullOrEmpty(Search))
{
data = data.Where(t => t.Name.Contains(Search, StringComparison.OrdinalIgnoreCase) || t.ParentName.Contains(Search, StringComparison.OrdinalIgnoreCase));
data = data.Where(t => t.Application.Equals(AppCode, StringComparison.OrdinalIgnoreCase));
}
var ret = new QueryData<object>();
ret.total = data.Count();
@ -115,4 +107,4 @@ namespace Bootstrap.Admin.Query
return ret;
}
}
}
}

View File

@ -1,31 +1,24 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.DataAccess;
using Bootstrap.DataAccess;
using Longbow.Web.Mvc;
using System;
using System.Linq;
namespace Bootstrap.Admin.Query
{
/// <summary>
/// 角色查询条件类
///
/// </summary>
public class QueryRoleOption : PaginationOption
{
/// <summary>
/// 角色名称
///
/// </summary>
public string? RoleName { get; set; }
public string RoleName { get; set; }
/// <summary>
/// 角色描述
///
/// </summary>
public string? Description { get; set; }
public string Description { get; set; }
/// <summary>
/// 角色数据
///
/// </summary>
/// <returns></returns>
public QueryData<object> RetrieveData()
@ -34,15 +27,11 @@ namespace Bootstrap.Admin.Query
var data = RoleHelper.Retrieves();
if (!string.IsNullOrEmpty(RoleName))
{
data = data.Where(t => t.RoleName.Contains(RoleName, StringComparison.OrdinalIgnoreCase));
data = data.Where(t => t.RoleName.Contains(RoleName));
}
if (!string.IsNullOrEmpty(Description))
{
data = data.Where(t => t.Description.Contains(Description, StringComparison.OrdinalIgnoreCase));
}
if (!string.IsNullOrEmpty(Search))
{
data = data.Where(t => t.RoleName.Contains(Search, StringComparison.OrdinalIgnoreCase) || t.Description.Contains(Search, StringComparison.OrdinalIgnoreCase));
data = data.Where(t => t.Description.Contains(Description));
}
var ret = new QueryData<object>();
ret.total = data.Count();
@ -51,4 +40,4 @@ namespace Bootstrap.Admin.Query
return ret;
}
}
}
}

View File

@ -1,8 +1,4 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.DataAccess;
using Bootstrap.DataAccess;
using Longbow.Web.Mvc;
using System;
@ -26,7 +22,7 @@ namespace Bootstrap.Admin.Query
/// <summary>
/// 请求IP地址
/// </summary>
public string? AccessIP { get; set; }
public string AccessIP { get; set; }
/// <summary>
///

View File

@ -1,31 +1,24 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone
using Bootstrap.DataAccess;
using Bootstrap.DataAccess;
using Longbow.Web.Mvc;
using System;
using System.Linq;
namespace Bootstrap.Admin.Query
{
/// <summary>
/// 用户维护查询条件类
///
/// </summary>
public class QueryUserOption : PaginationOption
{
/// <summary>
/// 获得/设置 用户登录名称
///
/// </summary>
public string? Name { get; set; }
public string Name { get; set; }
/// <summary>
/// 获得/设置 用户显示名称
///
/// </summary>
public string? DisplayName { get; set; }
public string DisplayName { get; set; }
/// <summary>
/// 获取用户分页数据
///
/// </summary>
/// <returns></returns>
public QueryData<object> RetrieveData()
@ -34,15 +27,11 @@ namespace Bootstrap.Admin.Query
var data = UserHelper.Retrieves();
if (!string.IsNullOrEmpty(Name))
{
data = data.Where(t => t.UserName.Contains(Name, StringComparison.OrdinalIgnoreCase));
data = data.Where(t => t.UserName.Contains(Name));
}
if (!string.IsNullOrEmpty(DisplayName))
{
data = data.Where(t => t.DisplayName.Contains(DisplayName, StringComparison.OrdinalIgnoreCase));
}
if (!string.IsNullOrEmpty(Search))
{
data = data.Where(t => t.DisplayName.Contains(Search, StringComparison.OrdinalIgnoreCase) || t.Description.Contains(Search, StringComparison.OrdinalIgnoreCase) || t.UserName.Contains(Search, StringComparison.OrdinalIgnoreCase));
data = data.Where(t => t.DisplayName.Contains(DisplayName));
}
var ret = new QueryData<object>();
ret.total = data.Count();
@ -78,4 +67,4 @@ namespace Bootstrap.Admin.Query
return ret;
}
}
}
}

View File

@ -0,0 +1,147 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Forgot password?" xml:space="preserve">
<value>忘记密码</value>
</data>
<data name="Invalid email or password." xml:space="preserve">
<value>用户名或密码错误!</value>
</data>
<data name="Login" xml:space="preserve">
<value>登 陆</value>
</data>
<data name="Password" xml:space="preserve">
<value>密码</value>
</data>
<data name="Please input the password" xml:space="preserve">
<value>请输入密码</value>
</data>
<data name="Please input the user name" xml:space="preserve">
<value>请输入用户名</value>
</data>
<data name="Remember me" xml:space="preserve">
<value>记住密码自动登录</value>
</data>
<data name="Sign up" xml:space="preserve">
<value>申请账号</value>
</data>
<data name="UserName" xml:space="preserve">
<value>用户名</value>
</data>
</root>

View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Home" xml:space="preserve">
<value>首页</value>
</data>
</root>

View File

@ -0,0 +1,34 @@
using Bootstrap.DataAccess;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Data.Common;
namespace Bootstrap.Admin
{
/// <summary>
///
/// </summary>
public static class SignalRManager
{
/// <summary>
///
/// </summary>
/// <param name="client"></param>
/// <param name="args"></param>
/// <returns></returns>
public static async System.Threading.Tasks.Task Send(IClientProxy client, MessageBody args) => await client.SendAsync("rev", args);
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="ex"></param>
/// <returns></returns>
public static async System.Threading.Tasks.Task Send<T>(IHubContext<T> context, Exception ex) where T : Hub
{
var category = "App";
if (ex.GetType().IsSubclassOf(typeof(DbException))) category = "DB";
var message = new MessageBody() { Category = category, Message = ex.Message };
await Send(context.Clients.All, message);
}
}
}

166
Bootstrap.Admin/Startup.cs Normal file
View File

@ -0,0 +1,166 @@
using Bootstrap.DataAccess;
using Bootstrap.Security.Filter;
using Longbow.Web;
using Longbow.Web.SignalR;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Swashbuckle.AspNetCore.Swagger;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text.Encodings.Web;
using System.Text.Unicode;
namespace Bootstrap.Admin
{
/// <summary>
///
/// </summary>
public class Startup
{
/// <summary>
///
/// </summary>
/// <param name="configuration"></param>
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
/// <summary>
///
/// </summary>
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
/// <summary>
///
/// </summary>
/// <param name="services"></param>
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All));
services.AddLocalization(op => op.ResourcesPath = "Resources");
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddCors();
services.AddLogging(builder => builder.AddFileLogger().AddDBLogger(ExceptionsHelper.Log));
services.AddConfigurationManager(Configuration);
services.AddCacheManager(Configuration);
services.AddDbAdapter();
services.AddIPLocator(DictHelper.ConfigIPLocator);
services.AddOnlineUsers();
var dataProtectionBuilder = services.AddDataProtection(op => op.ApplicationDiscriminator = Configuration["ApplicationDiscriminator"])
.SetApplicationName(Configuration["ApplicationName"])
.PersistKeysToFileSystem(new DirectoryInfo(Configuration["KeyPath"]));
if (Configuration["DisableAutomaticKeyGeneration"] == "True") dataProtectionBuilder.DisableAutomaticKeyGeneration();
services.AddSignalR().AddJsonProtocalDefault();
services.AddSignalRExceptionFilterHandler<SignalRHub>(async (client, ex) => await SignalRManager.Send(client, ex));
services.AddResponseCompression();
services.AddMvc(options =>
{
options.Filters.Add<BootstrapAdminAuthorizeFilter>();
options.Filters.Add<ExceptionFilter>();
options.Filters.Add<SignalRExceptionFilter<SignalRHub>>();
}).AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
JsonConvert.DefaultSettings = () => options.SerializerSettings;
}).AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
options.Cookie.Path = "/";
if (!string.IsNullOrEmpty(Configuration["Domain"])) options.Cookie.Domain = Configuration["Domain"];
});
services.AddApiVersioning(option =>
{
option.DefaultApiVersion = new ApiVersion(1, 0);
option.ReportApiVersions = true;
option.AssumeDefaultVersionWhenUnspecified = true;
option.ApiVersionReader = ApiVersionReader.Combine(new HeaderApiVersionReader("api-version"), new QueryStringApiVersionReader("api-version"));
});
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Info
{
Version = "v1",
Title = "BootstrapAdmin API"
});
//Set the comments path for the swagger json and ui.
var xmlPath = Path.Combine(AppContext.BaseDirectory, "Bootstrap.Admin.xml");
options.IncludeXmlComments(xmlPath);
options.OperationFilter<HttpHeaderOperation>(); // 添加httpHeader参数
});
services.AddButtonAuthorization(MenuHelper.AuthorizateButtons);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
/// <summary>
///
/// </summary>
/// <param name="app"></param>
/// <param name="env"></param>
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseForwardedHeaders(new ForwardedHeadersOptions() { ForwardedHeaders = ForwardedHeaders.All });
app.UseRequestLocalization(options => options.AddSupportedCultures("en-us", "zh-CN").AddSupportedUICultures("en-us", "zh-CN"));
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/Home/Error/{0}");
app.UseCors(builder => builder.WithOrigins(Configuration["AllowOrigins"].Split(',', StringSplitOptions.RemoveEmptyEntries)).AllowAnyHeader().AllowAnyMethod().AllowCredentials());
app.UseHttpsRedirection();
app.UseResponseCompression();
app.UseStaticFiles();
app.UseAuthentication();
app.UseBootstrapAdminAuthorization(RoleHelper.RetrieveRolesByUserName, RoleHelper.RetrieveRolesByUrl, AppHelper.RetrievesByUserName);
app.UseOnlineUsers(callback: TraceHelper.Save);
app.UseCacheManagerCorsHandler();
app.UseSignalR(routes => { routes.MapHub<SignalRHub>("/NotiHub"); });
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.UseWhen(context => context.Request.Path.StartsWithSegments("/swagger"), builder =>
{
builder.Use(async (context, next) =>
{
if (!context.User.Identity.IsAuthenticated) await context.ChallengeAsync();
else await next();
});
});
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{Configuration["SwaggerPathBase"]}/swagger/v1/swagger.json", "BootstrapAdmin API V1");
});
}
}
}

View File

@ -1,4 +1,5 @@
@model LoginModel
@model LoginModel
@inject IViewLocalizer Localizer
@{
ViewBag.Title = Model.Title;
Layout = "_Layout";
@ -7,31 +8,34 @@
<environment include="Development">
<link href="~/lib/twitter-bootstrap/css/bootstrap.css" rel="stylesheet" />
<link href="~/lib/font-awesome/css/font-awesome.css" rel="stylesheet" />
<link href="~/lib/sweetalert/sweetalert2.css" rel="stylesheet" />
<link href="~/lib/bootstrap-sweetalert/sweetalert.css" rel="stylesheet" />
</environment>
<environment exclude="Development">
<link href="~/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<link href="~/lib/font-awesome/css/font-awesome.min.css" rel="stylesheet" />
<link href="~/lib/sweetalert/sweetalert2.min.css" rel="stylesheet" />
<link href="~/lib/bootstrap-sweetalert/sweetalert.min.css" rel="stylesheet" />
</environment>
<link href="~/lib/captcha/slidercaptcha.css" rel="stylesheet" />
<link href="~/css/theme.css" rel="stylesheet" asp-append-version="true" />
<link href="~/css/login-footer.css" rel="stylesheet" asp-append-version="true" />
<link href="~/css/login.css" rel="stylesheet" asp-append-version="true" />
<link href="~/css/login-responsive.css" rel="stylesheet" asp-append-version="true" />
@if (!string.IsNullOrEmpty(Model.Theme))
{
<link href="~/css/@Model.Theme" rel="stylesheet" asp-append-version="true" />
}
}
@section javascript {
<environment include="Development">
<script src="~/lib/twitter-bootstrap/js/bootstrap.bundle.js"></script>
<script src="~/lib/validate/jquery.validate.js"></script>
<script src="~/lib/validate/localization/messages_zh.js"></script>
<script src="~/lib/sweetalert/sweetalert2.js" rel="stylesheet"></script>
<script src="~/lib/bootstrap-sweetalert/sweetalert.js"></script>
</environment>
<environment exclude="Development">
<script src="~/lib/twitter-bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="~/lib/validate/jquery.validate.min.js"></script>
<script src="~/lib/validate/localization/messages_zh.min.js"></script>
<script src="~/lib/sweetalert/sweetalert2.min.js" rel="stylesheet"></script>
<script src="~/lib/bootstrap-sweetalert/sweetalert.min.js"></script>
</environment>
<script src="~/lib/captcha/longbow.slidercaptcha.js"></script>
<script src="~/lib/longbow/longbow.common.js"></script>
@ -40,105 +44,41 @@
}
<div class="container">
<input id="imgUrl" type="hidden" value="@Model.ImageLibUrl" />
<form id="login" method="post" class="form-signin" data-demo="@Model.IsDemo">
<form id="login" method="post" class="form-signin">
<h2 class="form-signin-heading">@Model.Title</h2>
<div class="login-wrap" data-auth="@Model.AuthFailed" data-toggle="LgbValidate" data-valid-button="button[type='submit']">
<div class="alert alert-danger d-none" asp-condition="@Model.AuthFailed">用户名或密码错误!</div>
<div id="loginUser" class="form-group">
<div class="alert alert-danger" asp-condition="@Model.AuthFailed">@Localizer["Invalid email or password."] </div>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<span class="fa fa-user"></span>
</div>
</div>
<input type="text" name="userName" class="form-control" data-toggle="tooltip" placeholder="用户名" maxlength="16" data-required-msg="请输入用户名" value="" autofocus data-valid="true" />
<input type="text" name="userName" class="form-control" placeholder="@Localizer["UserName"]" maxlength="16" data-required-msg="@Localizer["Please input the user name"]" value="@ViewBag.UserName" autofocus data-valid="true" />
</div>
</div>
<div id="loginPwd" class="form-group">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<span class="fa fa-lock"></span>
</div>
</div>
<input type="password" name="password" class="form-control" value="" data-toggle="tooltip" placeholder="密码" maxlength="16" autocomplete="off" data-required-msg="请输入密码" data-valid="true" />
<input type="password" name="password" class="form-control" value="@ViewBag.Password" placeholder="@Localizer["Password"]" maxlength="16" data-required-msg="@Localizer["Please input the password"]" data-valid="true" />
</div>
</div>
<div asp-condition="@Model.AllowMobile">
<div id="loginMobile" class="form-group d-none">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<span class="fa fa-user"></span>
</div>
</div>
<input type="tel" id="phone" name="phone" class="form-control digits" data-toggle="tooltip" placeholder="手机号码" minlength="11" maxlength="11" data-required-msg="请输入手机号码" value="" data-valid="true" />
</div>
</div>
<div id="loginSMS" class="form-group d-none">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<span class="fa fa-lock"></span>
</div>
</div>
<input type="number" id="code" name="code" class="form-control digits" data-toggle="tooltip" disabled value="" placeholder="验证码" maxlength="4" data-required-msg="请输入验证码" data-valid="true" />
<div class="input-group-append">
<button type="button" id="btnSendCode" class="btn btn-sms" data-toggle="tooltip" title="点击发送验证码">发送验证码</button>
</div>
</div>
</div>
<div class="form-group rememberPwd" onselectstart="return false">
<i class="fa fa-square-o"></i>
<span>@Localizer["Remember me"]</span>
<input id="remember" name="remember" type="hidden" value="false" />
</div>
<div class="d-flex justify-content-between">
<div class="form-group rememberPwd" onselectstart="return false">
<i class="fa fa-square-o"></i>
<span>记住密码自动登录</span>
<input id="remember" name="remember" type="hidden" value="false" />
</div>
<div asp-condition="@Model.AllowMobile">
<a id="loginType" data-value="username" href="#" class="">短信验证登陆</a>
</div>
<button class="btn btn-lg btn-login btn-block" type="submit">@Localizer["Login"]</button>
<div class="login-footer">
<a href="#" data-method="register">@Localizer["Sign up"]</a>
<a href="#" data-method="forgot">@Localizer["Forgot password?"]</a>
</div>
<button class="btn btn-lg btn-login btn-block" data-oauth="@Model.AllowOAuth" data-toggle="tooltip" title="不填写密码默认使用 Gitee 认证" type="submit">登 录</button>
<div class="d-flex justify-content-between">
<a href="#" data-method="register">申请账号</a>
<a href="#" data-method="forgot">忘记密码</a>
</div>
<div asp-condition="@Model.AllowOAuth">
<div class="login-other">
<span class="text-muted">
其他方式登录
</span>
</div>
<div class="login-list">
<div class="item">
<a href="~/Account/Gitee" data-toggle="tooltip" title="使用 Gitee 帐号登录">
<img class="item" src="~/images/gitee.svg" />
</a>
</div>
<div class="item">
<a href="~/Account/GitHub" data-toggle="tooltip" title="使用 GitHub 帐号登录">
<img class="item" src="~/images/git.svg" />
</a>
</div>
<div class="item">
<a href="#" data-toggle="tooltip" title="微信-暂未实现">
<img class="item" src="~/images/weixin-2.svg" />
</a>
</div>
<div class="item">
<a href="~/Account/Tencent" data-toggle="tooltip" title="使用 QQ 账号登录">
<img class="item" src="~/images/qq.svg" />
</a>
</div>
<div class="item">
<a href="~/Account/Alipay" data-toggle="tooltip" title="使用支付宝账号登录">
<img class="item" src="~/images/zhifubao.svg" />
</a>
</div>
</div>
</div>
<div class="slidercaptcha @(Model.ShowOAuth ? "card oauth" : "card")">
<div class="slidercaptcha card">
<div class="card-header">
<span>请完成安全验证</span>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
@ -148,7 +88,6 @@
</div>
</form>
</div>
@await Html.PartialAsync("LoginFooter", Model)
<div class="modal fade" id="dialogNew" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content" data-toggle="LgbValidate" data-valid-button="#btnSubmit" data-valid-modal="#dialogNew">
@ -158,14 +97,14 @@
</div>
<div class="modal-body">
<div class="form-group">
<label for="userName">登名称:</label>
<label for="userName">登名称:</label>
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<span class="fa fa-user-plus"></span>
</div>
</div>
<input type="text" id="userName" autocomplete="off" class="form-control" placeholder="登账号不可为空" userName="true" minlength="4" maxlength="16" remote="api/Register" data-remote-msg="此用户已存在" data-valid="true" />
<input type="text" id="userName" autocomplete="off" class="form-control" placeholder="登账号不可为空" userName="true" minlength="4" maxlength="16" remote="api/Register" data-remote-msg="此用户已存在" data-valid="true" />
</div>
</div>
<div class="form-group">
@ -187,7 +126,7 @@
<span class="fa fa-lock"></span>
</div>
</div>
<input type="password" id="password" class="form-control" value="" placeholder="密码不可为空" maxlength="16" data-valid="true" autocomplete="off" />
<input type="password" id="password" class="form-control" value="" placeholder="密码不可为空" maxlength="16" data-valid="true" />
</div>
</div>
<div class="form-group">
@ -198,7 +137,7 @@
<span class="fa fa-lock"></span>
</div>
</div>
<input type="password" id="assurePassword" class="form-control" value="" placeholder="确认密码" maxlength="16" equalTo="#password" data-valid="true" autocomplete="off" />
<input type="password" id="assurePassword" class="form-control" value="" placeholder="确认密码" maxlength="16" equalTo="#password" data-valid="true" />
</div>
</div>
<div class="form-group">
@ -235,14 +174,14 @@
</div>
<div class="modal-body">
<div class="form-group">
<label for="f_userName">登账号:</label>
<label for="f_userName">登账号:</label>
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<span class="fa fa-user-plus"></span>
</div>
</div>
<input type="text" id="f_userName" autocomplete="off" class="form-control" placeholder="登账号不可为空" minlength="4" maxlength="16" data-valid="true" />
<input type="text" id="f_userName" autocomplete="off" class="form-control" placeholder="登账号不可为空" minlength="4" maxlength="16" data-valid="true" />
</div>
</div>
<div class="form-group">

View File

@ -12,6 +12,7 @@
<link href="~/lib/bootstrap-table/bootstrap-table.min.css" rel="stylesheet" />
</environment>
<style>
.card .card-body {
padding-bottom: 15px;
}
@ -19,19 +20,6 @@
.spark {
position: absolute;
}
.loading {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
height: 178px;
width: 100%;
}
.loading span {
margin-left: 10px;
}
</style>
}
@section javascript {
@ -96,9 +84,7 @@
</div>
<div class="card-body">
<div id="trace" class="spark"></div>
<div id="traceLoading" class="loading"><i class="fa fa-spinner fa-pulse"></i><span>正在加载...请稍等</span></div>
<div id="traceChart" style="height: 178px">
</div>
<div id="traceChart" style="height: 178px"></div>
</div>
</div>
<div class="card">
@ -107,9 +93,7 @@
</div>
<div class="card-body">
<div id="login" class="spark"></div>
<div id="loginLoading" class="loading"><i class="fa fa-spinner fa-pulse"></i><span>正在加载...请稍等</span></div>
<div id="loginChart" style="height: 178px">
</div>
<div id="loginChart" style="height: 178px"></div>
</div>
</div>
<div class="card">
@ -118,8 +102,6 @@
</div>
<div class="card-body">
<div id="log" class="spark"></div>
<div id="logLoading" class="loading"><i class="fa fa-spinner fa-pulse"></i><span>正在加载...请稍等</span></div>
<div id="logChart" style="height: 178px">
</div>
<div id="logChart" style="height: 178px"></div>
</div>
</div>

View File

@ -0,0 +1,83 @@
@model NavigatorBarModel
@{
ViewBag.Title = "字典表维护";
Layout = "_Default";
}
@section javascript {
<environment include="Development">
<script src="~/lib/bootstrap-3-typeahead/bootstrap3-typeahead.js"></script>
</environment>
<environment exclude="Development">
<script src="~/lib/bootstrap-3-typeahead/bootstrap3-typeahead.min.js"></script>
</environment>
<script src="~/js/dicts.js" asp-append-version="true"></script>
}
@section query {
<form class="form-inline">
<div class="row">
<div class="form-group col-sm-6 col-md-auto">
<label class="control-label" for="txt_dict_cate">字典标签</label>
<input type="text" class="form-control" id="txt_dict_cate" data-provide="typeahead" />
</div>
<div class="form-group col-sm-6 col-md-auto">
<label class="control-label" for="txt_dict_name">字典名称</label>
<input type="text" class="form-control" id="txt_dict_name" />
</div>
<div class="form-group form-group-dropdown col-sm-6 col-md-auto">
<label class="control-label" for="txt_dict_define">字典类别</label>
<div class="dropdown">
<button id="txt_dict_define" class="btn btn-success dropdown-select dropdown-toggle" data-toggle="dropdown">全部</button>
<div class="dropdown-menu">
<a href="#" data-val="">全部</a>
<div class="dropdown-divider"></div>
<a href="#" data-val="0">系统使用</a>
<a href="#" data-val="1">自定义</a>
</div>
</div>
</div>
<div class="form-group col-sm-6 col-md-auto flex-md-fill justify-content-md-end">
<button type="button" id="btn_query" class="btn btn-primary btn-fill"><i class="fa fa-search" aria-hidden="true"></i><span>查询</span></button>
</div>
</div>
</form>
}
@section body {
<div class="alert alert-danger" role="alert" asp-condition="@Model.IsDemo">
<span>演示系统禁止修改系统使用字典配置项</span>
</div>
}
@section modal {
<div class="modal-header">
<h5 class="modal-title" id="myModalLabel">字典编辑窗口</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<div class="modal-body">
<form class="form-inline">
<div class="row">
<input type="hidden" id="dictID" />
<div class="form-group col-12">
<label class="control-label" for="dictCate">字典标签</label>
<input type="text" class="form-control flex-sm-fill" id="dictCate" placeholder="不可为空50字以内" maxlength="50" data-valid="true" />
</div>
<div class="form-group col-12">
<label class="control-label" for="dictName">字典名称</label>
<input type="text" class="form-control flex-sm-fill" id="dictName" placeholder="不可为空50字以内" maxlength="50" data-valid="true" />
</div>
<div class="form-group col-12">
<label class="control-label" for="dictCode">字典代码</label>
<input type="text" class="form-control flex-sm-fill" id="dictCode" placeholder="不可为空2000字以内" maxlength="2000" data-valid="true" />
</div>
<div class="form-group form-group-dropdown col-12">
<label class="control-label" for="dictDefine">字典类型</label>
<div class="dropdown">
<button id="dictDefine" class="btn btn-success dropdown-select dropdown-toggle" data-toggle="dropdown" data-default-val="1">全部</button>
<div class="dropdown-menu">
<a href="#" data-val="0">系统使用</a>
<a href="#" data-val="1">自定义</a>
</div>
</div>
</div>
</div>
</form>
</div>
}

View File

@ -0,0 +1,135 @@
@model NavigatorBarModel
@{
ViewBag.Title = "程序异常";
}
@section css {
<environment include="Development">
<link href="~/lib/bootstrap-table/bootstrap-table.css" rel="stylesheet" />
<link href="~/lib/datetimepicker/css/bootstrap-datetimepicker.css" rel="stylesheet" />
</environment>
<environment exclude="Development">
<link href="~/lib/bootstrap-table/bootstrap-table.min.css" rel="stylesheet" />
<link href="~/lib/datetimepicker/css/bootstrap-datetimepicker.min.css" rel="stylesheet" />
</environment>
<link href="~/css/fa.css" rel="stylesheet" asp-append-version="true" />
}
@section javascript {
<environment include="Development">
<script src="~/lib/bootstrap-table/bootstrap-table.js"></script>
<script src="~/lib/bootstrap-table/extensions/export/bootstrap-table-export.js"></script>
<script src="~/lib/bootstrap-table/locale/bootstrap-table-zh-CN.js"></script>
<script src="~/lib/tablexport/tableExport.js"></script>
<script src="~/lib/datetimepicker/js/bootstrap-datetimepicker.js"></script>
</environment>
<environment exclude="Development">
<script src="~/lib/bootstrap-table/bootstrap-table.min.js"></script>
<script src="~/lib/bootstrap-table/extensions/export/bootstrap-table-export.min.js"></script>
<script src="~/lib/bootstrap-table/locale/bootstrap-table-zh-CN.min.js"></script>
<script src="~/lib/tablexport/tableExport.min.js"></script>
<script src="~/lib/datetimepicker/js/bootstrap-datetimepicker.min.js"></script>
</environment>
<script src="~/lib/datetimepicker/js/locales/bootstrap-datetimepicker.zh-CN.js"></script>
<script src="~/js/exceptions.js" asp-append-version="true"></script>
}
<div class="card">
<div class="card-header">查询条件</div>
<div class="card-body">
<form class="form-inline">
<div class="row">
<div class="form-group col-sm-auto">
<label class="control-label" for="txt_operate_start">起始时间</label>
<div class="input-group date">
<input id="txt_operate_start" class="form-control" size="16" type="text" value="" readonly>
<div class="input-group-append input-group-addon">
<div class="input-group-text"><span class="fa fa-times"></span></div>
</div>
<div class="input-group-append input-group-addon">
<div class="input-group-text"><span class="fa fa-calendar"></span></div>
</div>
</div>
</div>
<div class="form-group col-sm-auto">
<label class="control-label" for="txt_operate_end">终止时间</label>
<div class="input-group date">
<input id="txt_operate_end" class="form-control" size="16" type="text" value="" readonly>
<div class="input-group-append input-group-addon">
<div class="input-group-text"><span class="fa fa-times"></span></div>
</div>
<div class="input-group-append input-group-addon">
<div class="input-group-text"><span class="fa fa-calendar"></span></div>
</div>
</div>
</div>
<div class="form-group col-sm-auto flex-sm-fill justify-content-sm-end">
<label class="sr-only"></label>
<button type="button" id="btn_query" class="btn btn-primary btn-fill"><i class="fa fa-search" aria-hidden="true"></i><span>查询</span></button>
</div>
</div>
</form>
</div>
</div>
<div id="toolbar" class="d-none">
<div class="toolbar btn-group">
<button id="btn_view" type="button" class="btn btn-danger" asp-auth="log">
<i class="fa fa-file-text-o" aria-hidden="true"></i><span>服务器日志</span>
</button>
</div>
<div class="gear btn-group dropdown">
<button class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" type="button"><i class="fa fa-gear"></i></button>
<div class="dropdown-menu">
<a id="tb_view" href="#" title="查看明细" asp-auth="log"><i class="fa fa-file-text-o"></i></a>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
查询结果
</div>
<div class="card-body">
<table></table>
</div>
</div>
@section modal {
<div class="modal fade" id="dialogNew" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="myModalLabel" aria-hidden="true">
<div id="errorList" class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="myModalLabel">程序异常日志窗口</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<div class="modal-body">
<form class="form-inline" id="dataForm" name="dataForm"><div class="form-row"></div></form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
<i class="fa fa-times"></i>
<span>关闭</span>
</button>
</div>
</div>
</div>
</div>
<div id="errorDetail" class="modal-content icon-content">
<div class="modal-header">
<h5 class="modal-title" id="myDetailModalLabel">程序异常日志窗口</h5>
<button type="button" class="close" aria-hidden="true">&times;</button>
</div>
<div class="modal-body">
<div class="iconview">
<i id="fa-top" class="fa-target"></i>
<div class="affix">
<div><a href="#fa-top" class="fa fa-arrow-circle-up"></a></div>
<div><a href="#fa-bottom" class="fa fa-arrow-circle-down"></a></div>
</div>
<div id="dataFormDetail" class="ex-content"><div class="text-center"><i class="fa fa-spinner fa-pulse fa-3x fa-fw"></i></div></div>
<i id="fa-bottom" class="fa-target"></i>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary">
<i class="fa fa-times"></i>
<span>关闭</span>
</button>
</div>
</div>
}

View File

@ -9,13 +9,16 @@
@section query {
<form class="form-inline">
<div class="row">
<div class="form-group col-12">
<div class="form-group col-sm-5 col-md-auto">
<label class="control-label" for="txt_search_name">部门名称</label>
<input type="text" class="form-control" id="txt_search_name" data-default-val="" />
<input type="text" class="form-control" id="txt_search_name" />
</div>
<div class="form-group col-12">
<div class="form-group col-sm-5 col-md-auto">
<label class="control-label" for="txt_group_desc">部门描述</label>
<input type="text" class="form-control" id="txt_group_desc" data-default-val="" />
<input type="text" class="form-control" id="txt_group_desc" />
</div>
<div class="form-group col-sm-2 col-md-auto flex-md-fill justify-content-md-end">
<button type="button" id="btn_query" class="btn btn-primary btn-fill align-self-sm-end align-self-md-auto"><i class="fa fa-search" aria-hidden="true"></i><span>查询</span></button>
</div>
</div>
</form>
@ -44,23 +47,16 @@
<label class="control-label" for="groupName">部门名称</label>
<input type="text" class="form-control" id="groupName" placeholder="不可为空50字以内" maxlength="50" data-valid="true" />
</div>
<div class="form-group col-sm-6">
<label class="control-label" for="groupCode">部门编码</label>
<input type="text" class="form-control" id="groupCode" placeholder="不可为空50字以内" maxlength="50" data-valid="true" />
</div>
<div class="form-group col-sm-6">
<input type="hidden" id="groupID" />
<label class="control-label" for="groupDesc">部门描述</label>
<input type="text" class="form-control" id="groupDesc" placeholder="描述信息(可为空)500字以内" maxlength="500" />
<input type="text" class="form-control" id="groupDesc" placeholder="描述信息(可为空)50字以内" maxlength="50" />
</div>
</div>
</form>
</div>
}
@section cardbody {
<div class="alert alert-info" role="alert" asp-condition="@Model.IsDemo">
<span>部门与组织结构中的带有层次关系的部门不同,用于资源权限的分配与管理 <a href="https://gitee.com/LongbowEnterprise/BootstrapAdmin/wikis/%E6%95%B0%E6%8D%AE%E6%9D%83%E9%99%90?sort_id=1680495" target="_blank">参考文档</a></span>
</div>
}
@await Html.PartialAsync("RoleConfig")
@await Html.PartialAsync("UserConfig")
@section customModal {
@await Html.PartialAsync("RoleConfig")
@await Html.PartialAsync("UserConfig")
}

View File

@ -12,10 +12,6 @@
color: #fff;
margin-top: 0;
}
.main-content .main-header {
display: none;
}
</style>
}
@section javascript {

View File

@ -31,10 +31,10 @@
<div class="card-body">
<form class="form-inline">
<div class="row">
<div class="form-group col-sm-6 col-lg-auto">
<div class="form-group col-sm-auto">
<label class="control-label" for="txt_operate_start">起始时间</label>
<div class="input-group date">
<input id="txt_operate_start" class="form-control" size="16" type="text" value="@DateTime.Today.ToString("yyyy-MM-dd")" readonly>
<input id="txt_operate_start" class="form-control" size="16" type="text" value="" readonly>
<div class="input-group-append input-group-addon">
<div class="input-group-text"><span class="fa fa-times"></span></div>
</div>
@ -43,7 +43,7 @@
</div>
</div>
</div>
<div class="form-group col-sm-6 col-lg-auto">
<div class="form-group col-sm-auto">
<label class="control-label" for="txt_operate_end">终止时间</label>
<div class="input-group date">
<input id="txt_operate_end" class="form-control" size="16" type="text" value="" readonly>
@ -55,11 +55,11 @@
</div>
</div>
</div>
<div class="form-group col-sm-6 col-lg-auto">
<div class="form-group col-sm-auto">
<label class="control-label" for="txt_ip">请求IP</label>
<input type="text" class="form-control" id="txt_ip" />
</div>
<div class="form-group col-sm-6 col-lg-auto flex-sm-fill justify-content-sm-end align-self-sm-end">
<div class="form-group col-sm-auto flex-md-fill justify-content-md-end">
<button type="button" id="btn_query" class="btn btn-primary btn-fill"><i class="fa fa-search" aria-hidden="true"></i><span>查询</span></button>
</div>
</div>

View File

@ -35,10 +35,10 @@
<div class="card-body">
<form class="form-inline">
<div class="row">
<div class="form-group col-sm-6 col-lg-auto">
<div class="form-group col-sm-auto">
<label class="control-label" for="txt_operate_start">起始时间</label>
<div class="input-group date">
<input id="txt_operate_start" class="form-control" size="16" type="text" value="@DateTime.Today.ToString("yyyy-MM-dd")" readonly>
<input id="txt_operate_start" class="form-control" size="16" type="text" value="" readonly>
<div class="input-group-append input-group-addon">
<div class="input-group-text"><span class="fa fa-times"></span></div>
</div>
@ -47,7 +47,7 @@
</div>
</div>
</div>
<div class="form-group col-sm-6 col-lg-auto">
<div class="form-group col-sm-auto">
<label class="control-label" for="txt_operate_end">终止时间</label>
<div class="input-group date">
<input id="txt_operate_end" class="form-control" size="16" type="text" value="" readonly>
@ -59,11 +59,11 @@
</div>
</div>
</div>
<div class="form-group col-sm-6 col-lg-auto">
<div class="form-group col-sm-auto">
<label class="control-label" for="txt_operate_type">操作类型</label>
<input type="text" class="form-control" id="txt_operate_type" />
</div>
<div class="form-group col-sm-6 col-lg-auto flex-sm-fill justify-content-sm-end align-self-sm-end">
<div class="form-group col-sm-auto flex-sm-fill justify-content-sm-end">
<button type="button" id="btn_query" class="btn btn-primary btn-fill"><i class="fa fa-search" aria-hidden="true"></i><span>查询</span></button>
</div>
</div>
@ -78,28 +78,30 @@
<table></table>
</div>
</div>
<div class="modal fade" id="dialogRequestData" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="myModalLabel">请求数据明细窗口</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<div class="modal-body">
<form class="form-inline">
<div class="form-row">
<div class="form-group col-12">
<pre id="requestData"></pre>
@section modal {
<div class="modal fade" id="dialogRequestData" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="myModalLabel">请求数据明细窗口</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<div class="modal-body">
<form class="form-inline">
<div class="form-row">
<div class="form-group col-12">
<pre id="requestData"></pre>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
<i class="fa fa-times"></i>
<span>关闭</span>
</button>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
<i class="fa fa-times"></i>
<span>关闭</span>
</button>
</div>
</div>
</div>
</div>
</div>
}

View File

@ -11,7 +11,6 @@
<link href="~/lib/nestable2/jquery.nestable.min.css" rel="stylesheet" />
</environment>
<link href="~/lib/treegrid/css/jquery.treegrid.css" rel="stylesheet" />
<link href="~/lib/longbow-select/longbow-select.css" rel="stylesheet" />
<link href="~/css/fa.css" rel="stylesheet" asp-append-version="true" />
}
@section javascript {
@ -27,49 +26,58 @@
<script src="~/lib/treegrid/js/jquery.treegrid.min.js"></script>
<script src="~/lib/bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.min.js"></script>
</environment>
<script src="~/lib/longbow-select/longbow-select.js"></script>
<script src="~/js/menus.js" asp-append-version="true"></script>
}
@section query {
<form class="form-inline">
<div class="row">
<div class="form-group col-12">
<div class="form-group col-sm-6 col-lg-auto">
<label class="control-label" for="txt_menus_name">菜单名称</label>
<input type="text" class="form-control" id="txt_menus_name" data-default-val="" />
<input type="text" class="form-control" id="txt_menus_name" />
</div>
<div class="form-group col-12">
<div class="form-group col-sm-6 col-lg-auto">
<label class="control-label" for="txt_parent_menus_name">父级菜单</label>
<input type="text" class="form-control" id="txt_parent_menus_name" data-default-val="" />
<input type="text" class="form-control" id="txt_parent_menus_name" />
</div>
<div class="form-group col-12">
<div class="form-group form-group-dropdown col-sm-6 col-lg-auto">
<label class="control-label" for="sel_menus_category">菜单类别</label>
<input class="form-control" data-toggle="lgbSelect" data-default-val="" />
<select data-toggle="lgbSelect" class="d-none" id="sel_menus_category">
<option value="">全部</option>
<option value="0">系统菜单</option>
<option value="1">外部菜单</option>
</select>
<div class="dropdown">
<button id="sel_menus_category" class="btn btn-success dropdown-select dropdown-toggle" data-toggle="dropdown">全部</button>
<div class="dropdown-menu">
<a href="#" data-val="">全部</a>
<div class="dropdown-divider"></div>
<a href="#" data-val="0">系统菜单</a>
<a href="#" data-val="1">外部菜单</a>
</div>
</div>
</div>
<div class="form-group col-12">
<div class="form-group form-group-dropdown col-sm-6 col-lg-auto">
<label class="control-label" for="sel_menus_res">菜单类型</label>
<input class="form-control" data-toggle="lgbSelect" data-default-val="" />
<select data-toggle="lgbSelect" class="d-none" id="sel_menus_res">
<option value="">全部</option>
<option value="0">菜单</option>
<option value="1">资源</option>
<option value="2">按钮</option>
</select>
<div class="dropdown">
<button id="sel_menus_res" class="btn btn-success dropdown-select dropdown-toggle" data-toggle="dropdown">全部</button>
<div class="dropdown-menu">
<a href="#" data-val="">全部</a>
<div class="dropdown-divider"></div>
<a href="#" data-val="0">菜单</a>
<a href="#" data-val="1">资源</a>
<a href="#" data-val="2">按钮</a>
</div>
</div>
</div>
<div class="form-group col-12">
<div class="form-group form-group-dropdown col-sm-6 col-lg-auto">
<label class="control-label" for="sel_app">所属应用</label>
<input class="form-control" data-toggle="lgbSelect" data-default-val="" />
<select data-toggle="lgbSelect" class="d-none" id="sel_app">
<option value="">全部</option>
@foreach (var kv in Model.Applications)
{
<option value="@kv.Key">@kv.Value</option>
}
</select>
<div class="dropdown">
<button id="sel_app" class="btn btn-info dropdown-select dropdown-toggle" data-toggle="dropdown" data-default-val="0">未设置</button>
<div class="dropdown-menu">
@foreach (var kv in Model.Applications)
{
<a href="#" data-val="@kv.Key">@kv.Value</a>
}
</div>
</div>
</div>
<div class="form-group col-lg-auto flex-lg-fill justify-content-lg-end">
<button type="button" id="btn_query" class="btn btn-primary btn-fill"><i class="fa fa-search" aria-hidden="true"></i><span>查询</span></button>
</div>
</div>
</form>
@ -82,7 +90,7 @@
@section gear {
<a id="tb_assignRole" href="#" title="分配角色" asp-auth="assignRole"><i class="fa fa-sitemap"></i></a>
}
@section cardbody {
@section body {
<div class="alert alert-danger" role="alert" asp-condition="@Model.IsDemo">
<span>演示系统禁止修改系统菜单,可修改外部菜单</span>
</div>
@ -100,7 +108,7 @@
<div class="form-group col-sm-6">
<input type="hidden" id="menuID" />
<label class="control-label" for="name">菜单名称</label>
<div class="input-group flex-sm-fill">
<div class="input-group">
<input type="text" class="form-control" id="name" name="name" placeholder="不可为空50字以内" maxlength="50" data-valid="true" />
<div class="input-group-append">
<button data-method="clear" class="btn" type="button"><i class="fa fa-remove"></i></button>
@ -109,9 +117,9 @@
</div>
</div>
<div class="form-group col-sm-6">
<input type="hidden" class="form-control" id="parentId" data-default-val="0" />
<input type="text" class="form-control d-none" id="parentId" data-default-val="0" />
<label class="control-label" for="parentName">父级菜单</label>
<div class="input-group flex-sm-fill">
<div class="input-group">
<input type="text" class="form-control" readonly id="parentName" name="parentName" placeholder="请选择...(可为空)50字以内" maxlength="50" />
<div class="input-group-append">
<button data-method="clear" class="btn" type="button"><i class="fa fa-remove"></i></button>
@ -121,7 +129,7 @@
</div>
<div class="form-group col-sm-6">
<label class="control-label" for="order">菜单序号</label>
<div class="input-group flex-sm-fill">
<div class="input-group">
<input type="text" class="form-control digits" id="order" name="order" data-default-val="10" placeholder="默认为10" maxlength="8" data-valid="true" />
<div class="input-group-append">
<button data-method="clear" class="btn" type="button"><i class="fa fa-remove"></i></button>
@ -131,7 +139,7 @@
</div>
<div class="form-group col-sm-6">
<label class="control-label" for="icon">菜单图标</label>
<div class="input-group flex-sm-fill">
<div class="input-group">
<input type="text" class="form-control" readonly id="icon" data-default-val="fa fa-fa" placeholder="请选择...(可为空)50字以内" maxlength="50" />
<div class="input-group-append">
<button data-method="clear" class="btn" type="button"><i class="fa fa-remove"></i></button>
@ -143,62 +151,76 @@
<label class="control-label" for="url">路径</label>
<input type="text" class="form-control flex-sm-fill" id="url" placeholder="不可为空4000字以内" maxlength="4000" data-valid="true" />
</div>
<div class="form-group col-sm-6">
<label class="control-label" for="isRes">菜单类型</label>
<select data-toggle="lgbSelect" class="d-none menuChild" data-default-val="0" id="isRes" data-valid="true">
<option value="0">菜单</option>
<option value="1">资源</option>
<option value="2">按钮</option>
</select>
</div>
<div class="form-group col-sm-6">
<label class="control-label" for="target">目标</label>
<select data-toggle="lgbSelect" class="d-none" data-default-val="_self" id="target">
<option value="_self">本窗口</option>
<option value="_blank">新窗口</option>
<option value="_parent">父级窗口</option>
<option value="_top">顶级窗口</option>
</select>
</div>
<div class="form-group col-sm-6">
<label class="control-label" for="app">所属应用</label>
<select data-toggle="lgbSelect" class="d-none" data-default-val="@BootstrapAppContext.AppId" id="app">
@foreach (var kv in Model.Applications)
{
<option value="@kv.Key">@kv.Value</option>
}
</select>
</div>
<div class="form-group col-sm-6">
<div class="form-group form-group-dropdown col-sm-6">
<label class="control-label" for="category">菜单类别</label>
<select data-toggle="lgbSelect" class="d-none" disabled data-default-val="1" id="category">
<option value="0">系统菜单</option>
<option value="1">外部菜单</option>
</select>
<div class="dropup">
<button id="category" class="btn btn-success dropdown-select dropdown-toggle" data-toggle="dropdown" data-default-val="1">外部菜单</button>
<div class="dropdown-menu">
<a href="#" data-val="0">系统菜单</a>
<a href="#" data-val="1">外部菜单</a>
</div>
</div>
</div>
<div class="form-group form-group-dropdown col-sm-6">
<label class="control-label" for="target">目标</label>
<div class="dropup">
<button id="target" class="btn btn-info dropdown-select dropdown-toggle" data-toggle="dropdown" data-default-val="_self">本窗口</button>
<div class="dropdown-menu">
<a href="#" data-val="_self">本窗口</a>
<a href="#" data-val="_blank">新窗口</a>
<a href="#" data-val="_parent">父级窗口</a>
<a href="#" data-val="_top">顶级窗口</a>
</div>
</div>
</div>
<div class="form-group form-group-dropdown col-sm-6">
<label class="control-label" for="isRes">菜单类型</label>
<div class="dropup">
<button id="isRes" class="btn btn-success dropdown-select dropdown-toggle" data-toggle="dropdown" data-default-val="0">菜单</button>
<div class="dropdown-menu">
<a href="#" data-val="0">菜单</a>
<a href="#" data-val="1">资源</a>
<a href="#" data-val="2">按钮</a>
</div>
</div>
</div>
<div class="form-group form-group-dropdown col-sm-6">
<label class="control-label" for="app">所属应用</label>
<div class="dropup">
<button id="app" class="btn btn-info dropdown-select dropdown-toggle" data-toggle="dropdown" data-default-val="0">未设置</button>
<div class="dropdown-menu">
@foreach (var kv in Model.Applications)
{
<a href="#" data-val="@kv.Key">@kv.Value</a>
}
</div>
</div>
</div>
</div>
</form>
</div>
}
<div id="dialogIcon" class="modal-content icon-content">
<div class="modal-header">
<h5 class="modal-title" id="myIconModalLabel">请选择图标</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
@section customModal {
@await Html.PartialAsync("RoleConfig")
<div id="dialogIcon" class="modal-content icon-content">
<div class="modal-header">
<h5 class="modal-title" id="myIconModalLabel">请选择图标</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<div class="modal-body">
<div class="loadIconView"><i class="fa fa-spinner fa-pulse"></i><span>正在加载图标...请稍等</span></div>
</div>
<div class="modal-footer">
<span>被选择的图标</span><i class="fa fa-fa" id="pickIcon"></i>
<button type="button" class="btn btn-secondary" data-dismiss="modal">
<i class="fa fa-times"></i>
<span>关闭</span>
</button>
<button type="button" class="btn btn-primary" id="btnSubmitIcon">
<i class="fa fa-save"></i>
<span>确定</span>
</button>
</div>
</div>
<div class="modal-body">
<div class="loadIconView"><i class="fa fa-spinner fa-pulse"></i><span>正在加载图标...请稍等</span></div>
</div>
<div class="modal-footer">
<span>被选择的图标</span><i class="fa fa-fa" id="pickIcon"></i>
<button type="button" class="btn btn-secondary" data-dismiss="modal">
<i class="fa fa-times"></i>
<span>关闭</span>
</button>
<button type="button" class="btn btn-primary" id="btnSubmitIcon">
<i class="fa fa-save"></i>
<span>确定</span>
</button>
</div>
</div>
@await Html.PartialAsync("RoleConfig")
@await Html.PartialAsync("NavigatorConfig")
@await Html.PartialAsync("NavigatorConfig")
}

View File

@ -22,8 +22,8 @@
<script src="~/js/noti.js" asp-append-version="true"></script>
}
<div class="card" asp-condition="@User.IsInRole("Administrators")">
<div class="card-header">用户注册<span class="pull-right"><a id="refreshUsers" href="#" class="fa fa-refresh" title="点击刷新" data-toggle="tooltip" data-placement="left"></a></span></div>
<div class="card-body">
<div class="card-header">用户注册<span class="pull-right"><a id="refreshUsers" href="javascript:;" class="fa fa-refresh" title="点击刷新" data-toggle="tooltip" data-placement="left"></a></span></div>
<div class="card-body" style="padding-top: 25px;">
<table></table>
</div>
</div>

View File

@ -23,7 +23,7 @@
}
<div class="card">
<div class="card-header">在线用户<span class="pull-right"><a id="refreshUsers" href="javascript:;" class="fa fa-refresh" title="点击刷新" data-toggle="tooltip" data-placement="left"></a></span></div>
<div class="card-body">
<div class="card-body" style="padding-top: 25px;">
<table></table>
</div>
</div>

View File

@ -30,8 +30,7 @@
<script src="~/lib/longbow/longbow.validate.js" asp-append-version="true"></script>
<script src="~/js/profiles.js" asp-append-version="true"></script>
}
<input type="hidden" value="@Model.UserName" id="userName"/>
<div class="card" asp-auth="saveDisplayName">
<div class="card">
<div class="card-header">基本资料</div>
<div class="card-body" data-toggle="LgbValidate" data-valid-button="#btnSaveDisplayName">
<div class="alert alert-danger" role="alert" asp-condition="@Model.IsDemo">
@ -40,21 +39,21 @@
<form class="form-inline">
<div class="row">
<div class="form-group col-sm-6 col-md-auto">
<label class="control-label" for="userName1">登录名称</label>
<input type="text" class="form-control ignore" id="userName1" value="@Model.UserName" readonly />
<label class="control-label" for="userName">登陆名称</label>
<input type="text" class="form-control ignore" id="userName" name="userName" value="@Model.UserName" readonly />
</div>
<div class="form-group col-sm-6 col-md-auto">
<label class="control-label" for="DisplayName">显示名称</label>
<input type="text" class="form-control" id="displayName" value="@Model.DisplayName" placeholder="不可为空20字以内" maxlength="20" data-valid="true" />
<input type="text" class="form-control" id="displayName" name="displayName" value="@Model.DisplayName" placeholder="不可为空20字以内" maxlength="20" data-valid="true" />
</div>
</div>
</form>
<div class="modal-footer" asp-condition="!@Model.IsDemo">
<div class="modal-footer" asp-auth="saveDisplayName" asp-condition="!@Model.IsDemo">
<button id="btnSaveDisplayName" data-method="user" class="btn btn-secondary" type="button"><i class="fa fa-save"></i><span>保存</span></button>
</div>
</div>
</div>
<div class="card" asp-auth="savePassword" asp-condition="!@Model.External">
<div class="card">
<div class="card-header">修改密码</div>
<div class="card-body" data-toggle="LgbValidate" data-valid-button="#btnSavePassword">
<div class="alert alert-danger" role="alert" asp-condition="@Model.IsDemo">
@ -78,17 +77,17 @@
</div>
</div>
</form>
<div class="modal-footer" asp-condition="!@Model.IsDemo">
<div class="modal-footer" asp-auth="savePassword" asp-condition="!@Model.IsDemo">
<button id="btnSavePassword" data-method="password" class="btn btn-secondary" type="button"><i class="fa fa-save"></i><span>保存</span></button>
</div>
</div>
</div>
<div class="card" asp-auth="saveApp">
<div class="card">
<div class="card-header">默认应用</div>
<div class="card-body">
<div class="form-group">
<div class="btn-group" role="group">
<button id="app" class="btn btn-success dropdown-select dropdown-toggle" data-toggle="dropdown" value="@Model.AppId">@Model.AppName</button>
<button id="app" class="btn btn-success dropdown-select dropdown-toggle" data-toggle="dropdown" data-default-val="" value="@Model.AppCode">未设置</button>
<div class="dropdown-menu">
@foreach (var app in Model.Applications)
{
@ -97,12 +96,12 @@
</div>
</div>
</div>
<div class="modal-footer">
<div class="modal-footer" asp-auth="saveApp">
<button id="btnSaveApp" data-method="app" class="btn btn-secondary" type="button"><i class="fa fa-save"></i><span>保存</span></button>
</div>
</div>
</div>
<div class="card" asp-auth="saveTheme">
<div class="card">
<div class="card-header">网站样式</div>
<div class="card-body">
<div class="alert alert-info" role="alert">
@ -120,15 +119,15 @@
</div>
</div>
</div>
<div class="modal-footer">
<div class="modal-footer" asp-auth="saveTheme">
<button id="btnSaveCss" data-method="profileCss" class="btn btn-secondary" type="button"><i class="fa fa-save"></i><span>保存</span></button>
</div>
</div>
</div>
<div class="card" asp-auth="saveIcon">
<div class="card">
<div class="card-header">修改头像</div>
<div class="card-body">
<form enctype="multipart/form-data">
<form enctype="multipart/form-data" asp-auth="saveIcon">
<div class="form-group">
<input id="fileIcon" type="file" data-init="@Model.Size" data-file="@Model.FileName">
</div>

View File

@ -24,13 +24,16 @@
@section query {
<form class="form-inline">
<div class="row">
<div class="form-group col-12">
<div class="form-group col-sm-5 col-md-auto">
<label class="control-label" for="txt_search_name">角色名称</label>
<input type="text" class="form-control" id="txt_search_name" data-default-val="" />
<input type="text" class="form-control" id="txt_search_name" />
</div>
<div class="form-group col-12">
<div class="form-group col-sm-5 col-md-auto">
<label class="control-label" for="txt_role_desc">角色描述</label>
<input type="text" class="form-control" id="txt_role_desc" data-default-val="" />
<input type="text" class="form-control" id="txt_role_desc" />
</div>
<div class="form-group col-sm-2 col-md-auto flex-md-fill justify-content-md-end">
<button type="button" id="btn_query" class="btn btn-primary btn-fill align-self-sm-end align-self-md-auto"><i class="fa fa-search" aria-hidden="true"></i><span>查询</span></button>
</div>
</div>
</form>
@ -55,7 +58,7 @@
<a id="tb_assignMenu" href="#" title="分配菜单" asp-auth="assignMenu"><i class="fa fa-dashboard"></i></a>
<a id="tb_assignApp" href="#" title="分配应用" asp-auth="assignApp"><i class="fa fa-cubes"></i></a>
}
@section cardbody {
@section body {
<div class="alert alert-danger" role="alert" asp-condition="@Model.IsDemo">
<span>演示系统禁止修改内置角色Administrators、Default</span>
</div>
@ -75,13 +78,15 @@
<div class="form-group col-sm-6">
<input type="hidden" id="roleID" />
<label class="control-label" for="roleDesc">角色描述</label>
<input type="text" class="form-control" id="roleDesc" placeholder="描述信息(可为空)500字以内" maxlength="500" />
<input type="text" class="form-control" id="roleDesc" placeholder="描述信息(可为空)50字以内" maxlength="50" />
</div>
</div>
</form>
</div>
}
@await Html.PartialAsync("UserConfig")
@await Html.PartialAsync("GroupConfig")
@await Html.PartialAsync("NavigatorConfig")
@await Html.PartialAsync("AppConfig")
@section customModal{
@await Html.PartialAsync("UserConfig")
@await Html.PartialAsync("GroupConfig")
@await Html.PartialAsync("NavigatorConfig")
@await Html.PartialAsync("AppConfig")
}

View File

@ -0,0 +1,83 @@
@model ThemeModel
@{
ViewBag.Title = "网站设置";
}
@section javascript {
<environment include="Development">
<script src="~/lib/validate/jquery.validate.js"></script>
<script src="~/lib/validate/localization/messages_zh.js"></script>
</environment>
<environment exclude="Development">
<script src="~/lib/validate/jquery.validate.min.js"></script>
<script src="~/lib/validate/localization/messages_zh.min.js"></script>
</environment>
<script src="~/lib/longbow/longbow.dataentity.js"></script>
<script src="~/lib/longbow/longbow.validate.js"></script>
<script src="~/js/settings.js" asp-append-version="true"></script>
}
<div class="card">
<div class="card-header">系统名称设置</div>
<div class="card-body" data-toggle="LgbValidate" data-valid-button="#sysSave">
<div class="alert alert-danger" role="alert" asp-condition="@Model.IsDemo">
<span>演示系统禁止更改系统名称</span>
</div>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" id="sysName" placeholder="请输入网站标题50字以内" value="@Model.Title" maxlength="50" data-valid="true" />
<div class="input-group-append" asp-condition="!@Model.IsDemo">
<button class="btn btn-secondary" type="button" data-method="title" asp-auth="saveTitle">保存</button>
</div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">页脚设置</div>
<div class="card-body" data-toggle="LgbValidate" data-valid-button="#footSave">
<div class="alert alert-danger" role="alert" asp-condition="@Model.IsDemo">
<span>演示系统禁止更改页脚</span>
</div>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" id="sysFoot" placeholder="请输入网站页脚50字以内" value="@Model.Footer" maxlength="50" data-valid="true" />
<div class="input-group-append" asp-condition="!@Model.IsDemo">
<button class="btn btn-secondary" type="button" data-method="footer" asp-auth="saveFooter">保存</button>
</div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">网站样式</div>
<div class="card-body">
<div class="alert alert-info" role="alert">
<span>注意:<b><a class="badge-pill" href="./Profiles">个人中心</a></b>中设置的网站样式覆盖本设置</span>
</div>
<div class="form-group text-right">
<div class="btn-group" role="group">
<div class="btn-group" role="group">
<button id="dictCssDefine" class="btn btn-success dropdown-select dropdown-toggle" data-toggle="dropdown" data-default-val="" value="@Model.Theme">默认样式</button>
<div class="dropdown-menu">
<a href="#" data-val="">默认样式</a>
@foreach (var css in Model.Themes)
{
<a href="#" data-val="@css.Code">@css.Name</a>
}
</div>
</div>
<button class="btn btn-secondary" type="button" data-method="css" asp-auth="saveTheme">保存</button>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<div class="d-flex align-items-center">
<span class="flex-fill">网站缓存</span>
<a data-method="clear" href="#" class="fa fa-times-circle-o" title="全部清除" data-toggle="tooltip" data-placement="left" asp-auth="clearAllCache"></a>
<a data-method="refresh" href="#" class="fa fa-refresh ml-3" title="点击刷新" data-toggle="tooltip"></a>
</div>
</div>
<div class="card-body" id="sortable">
</div>
</div>

View File

@ -0,0 +1,18 @@
@model NavigatorBarModel
@{
ViewBag.Title = "任务管理";
}
@section css {
<link href="~/css/tasks.css" rel="stylesheet" asp-append-version="true" />
}
@section javascript {
<script src="~/js/tasks.js" asp-append-version="true"></script>
}
<div class="card">
<div class="card-header">任务消息<span class="pull-right"><a id="refreshTask" href="javascript:;" class="fa fa-refresh"></a></span></div>
<div class="card-body">
<div class="tasks-widget">
<ul id="list-task" class="task-list ui-sortable"></ul>
</div>
</div>
</div>

View File

@ -35,10 +35,10 @@
<div class="card-body">
<form class="form-inline">
<div class="row">
<div class="form-group col-sm-6 col-lg-auto">
<div class="form-group col-sm-auto">
<label class="control-label" for="txt_operate_start">起始时间</label>
<div class="input-group date">
<input id="txt_operate_start" class="form-control" size="16" type="text" value="@DateTime.Today.ToString("yyyy-MM-dd")" readonly>
<input id="txt_operate_start" class="form-control" size="16" type="text" value="" readonly>
<div class="input-group-append input-group-addon">
<div class="input-group-text"><span class="fa fa-times"></span></div>
</div>
@ -47,7 +47,7 @@
</div>
</div>
</div>
<div class="form-group col-sm-6 col-lg-auto">
<div class="form-group col-sm-auto">
<label class="control-label" for="txt_operate_end">终止时间</label>
<div class="input-group date">
<input id="txt_operate_end" class="form-control" size="16" type="text" value="" readonly>
@ -59,11 +59,11 @@
</div>
</div>
</div>
<div class="form-group col-sm-6 col-lg-auto">
<div class="form-group col-sm-auto">
<label class="control-label" for="txt_ip">请求IP</label>
<input type="text" class="form-control" id="txt_ip" />
</div>
<div class="form-group col-sm-6 col-lg-auto flex-sm-fill justify-content-sm-end align-self-sm-end">
<div class="form-group col-sm-auto flex-sm-fill justify-content-sm-end">
<button type="button" id="btn_query" class="btn btn-primary btn-fill"><i class="fa fa-search" aria-hidden="true"></i><span>查询</span></button>
</div>
</div>
@ -78,28 +78,30 @@
<table></table>
</div>
</div>
<div class="modal fade" id="dialogRequestData" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="myModalLabel">请求数据明细窗口</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<div class="modal-body">
<form class="form-inline">
<div class="form-row">
<div class="form-group col-12">
<pre id="requestData"></pre>
@section modal {
<div class="modal fade" id="dialogRequestData" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="myModalLabel">请求数据明细窗口</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<div class="modal-body">
<form class="form-inline">
<div class="form-row">
<div class="form-group col-12">
<pre id="requestData"></pre>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
<i class="fa fa-times"></i>
<span>关闭</span>
</button>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
<i class="fa fa-times"></i>
<span>关闭</span>
</button>
</div>
</div>
</div>
</div>
</div>
}

View File

@ -0,0 +1,116 @@
@model NavigatorBarModel
@{
ViewBag.Title = "用户管理";
Layout = "_Default";
}
@section javascript {
<script src="~/js/users.js" asp-append-version="true"></script>
}
@section query {
<form class="form-inline">
<div class="row">
<div class="form-group col-sm-5 col-md-auto">
<label class="control-label" for="txt_search_name">登陆名称</label>
<input type="text" class="form-control" id="txt_search_name" maxlength="16" />
</div>
<div class="form-group col-sm-5 col-md-auto">
<label class="control-label" for="txt_display_name">显示名称</label>
<input type="text" class="form-control" id="txt_display_name" maxlength="20" />
</div>
<div class="form-group col-sm-2 col-md-auto flex-md-fill justify-content-md-end">
<button type="button" id="btn_query" class="btn btn-primary btn-fill align-self-sm-end align-self-md-auto"><i class="fa fa-search" aria-hidden="true"></i><span>查询</span></button>
</div>
</div>
</form>
}
@section toolbar {
<button id="btn_assignGroup" type="button" class="btn btn-info" asp-auth="assignGroup">
<i class="fa fa-bank" aria-hidden="true"></i><span>分配部门</span>
</button>
<button id="btn_assignRole" type="button" class="btn btn-warning" asp-auth="assignRole">
<i class="fa fa-sitemap" aria-hidden="true"></i><span>分配角色</span>
</button>
}
@section gear {
<a id="tb_assignRole" href="#" title="分配角色"><i class="fa fa-sitemap" asp-auth="assignGroup"></i></a>
<a id="tb_assignGroup" href="#" title="分配部门"><i class="fa fa-bank" asp-auth="assignRole"></i></a>
}
@section tableButtons {
<button class="reset btn btn-warning" asp-auth="resetPassword"><i class="fa fa-remove"></i><span>重置</span></button>
}
@section body {
<div class="alert alert-danger" role="alert" asp-condition="@Model.IsDemo">
<span>演示系统禁止修改Admin账户</span>
</div>
}
@section modal {
<div class="modal-header">
<h5 class="modal-title" id="myModalLabel">用户编辑窗口</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<div class="modal-body">
<form class="form-inline">
<input type="hidden" id="userID" />
<div class="row">
<div class="form-group col-sm-6">
<label class="control-label" for="userName">登陆名称</label>
<input type="text" class="form-control" id="userName" placeholder="不可为空16字以内" minlength="4" maxlength="16" userName="true" remote="api/Register" data-remote-msg="此用户已存在" data-valid="true" />
</div>
<div class="form-group col-sm-6">
<label class="control-label" for="displayName">显示名称</label>
<input type="text" class="form-control" id="displayName" placeholder="不可为空20字以内" maxlength="20" data-valid="true" />
</div>
<div class="form-group col-sm-6">
<label class="control-label" for="password">登录密码</label>
<input type="password" class="form-control" id="password" placeholder="不可为空16字以内" maxlength="16" data-valid="true" />
</div>
<div class="form-group col-sm-6">
<label class="control-label" for="confirm">确认密码</label>
<input type="password" class="form-control" id="confirm" placeholder="与登陆密码一致" maxlength="16" equalTo="#password" data-valid="true" />
</div>
</div>
</form>
</div>
}
@section customModal {
@await Html.PartialAsync("RoleConfig")
@await Html.PartialAsync("GroupConfig")
<div class="modal fade" id="dialogReset" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="myResetModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content" data-toggle="LgbValidate" data-valid-button="#btnReset" data-valid-modal="#dialogReset">
<div class="modal-header">
<h5 class="modal-title" id="myResetModalLabel">重置密码窗口</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<div class="modal-body">
<form class="form-inline">
<div class="form-row" id="resetForm">
<div class="form-group col-sm-6">
<label class="control-label" for="password">登录密码</label>
<input type="password" class="form-control" id="resetPassword" placeholder="不可为空16字以内" maxlength="16" data-valid="true" />
</div>
<div class="form-group col-sm-6">
<label class="control-label" for="confirm">确认密码</label>
<input type="password" class="form-control" id="resetConfirm" placeholder="与登陆密码一致" maxlength="16" equalTo="#resetPassword" data-valid="true" />
</div>
<div class="form-group col-sm-12">
<label class="control-label" for="resetReason">重置原因</label>
<textarea class="form-control flex-sm-fill" id="resetReason" rows="3" readonly="readonly" maxlength="200"></textarea>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
<i class="fa fa-times"></i>
<span>关闭</span>
</button>
<button type="button" class="btn btn-primary" id="btnReset">
<i class="fa fa-save"></i>
<span>保存</span>
</button>
</div>
</div>
</div>
</div>
}

View File

@ -0,0 +1,20 @@
@inject IViewLocalizer Localizer
@{
ViewBag.Title = @Localizer["Home"];
Layout = "_Bootstrap";
}
@section css {
<style type="text/css">
.content-body {
position: fixed;
left: 0;
bottom: 40px;
right: 0;
top: 96px;
overflow: hidden;
}
</style>
}
<div class="content-body welcome-bg">
</div>

View File

@ -13,7 +13,7 @@
<img src="@Url.Content(Model.Image)" />
<h1>@Model.Content</h1>
<h3>@Model.Detail</h3>
<p><a href="~/Account/Logout">登录</a> <span>或者</span> <a href="@Url.Content(Model.ReturnUrl)">返回首页</a> <span>或者</span> <a href="~/healths-ui">系统自检</a></p>
<p><a href="~/Account/Logout">登录</a> <span>或者</span> <a href="@Url.Content(Model.ReturnUrl)">返回首页</a></p>
@if (Model.Id == 403)
{
<div>

View File

@ -84,17 +84,7 @@
</div>
</div>
<!-- db dropdown end -->
<div class="dropdown">
<a class="shadow-success" href="~/Account/Lock" data-toggle="tooltip" title="系统锁屏">
<i class="fa fa-tv"></i>
</a>
</div>
}
<div class="@(Model.EnableBlazor ? "dropdown" : "dropdown d-none")">
<a class="shadow-success dropdown-blazor" data-method="blazor" href="#" data-toggle="tooltip" title="Blazor 多 Tabs 模式">
<img src="~/images/blazor.svg" />
</a>
</div>
</div>
<div class="dropdown userinfo">
<a data-toggle="dropdown" class="dropdown-toggle shadow-default" href="#">
@ -102,15 +92,6 @@
<span id="userDisplayName" data-userName="@Model.UserName" class="username text-truncate d-inline-block">@Model.DisplayName</span>
</a>
<div class="dropdown-menu dropdown-menu-right">
<div class="dropdown-item">
<div class="d-flex flex-fill align-items-center">
<img src="@Url.Content(Model.Icon)">
<div class="flex-fill">
<div class="username text-truncate">@Model.DisplayName</div>
<div>登录名:@Model.UserName</div>
</div>
</div>
</div>
<div class="dropdown-item">
<a href="~/Admin/Profiles"><i class=" fa fa-suitcase"></i>个人中心</a>
<a href="~/Admin/Index"><i class="fa fa-cog"></i>设置</a>
@ -124,7 +105,7 @@
</div>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="~/Admin/Index"><i class="fa fa-home"></i>首页</a></li>
<li class="breadcrumb-item"><a href="@Url.Content("~/Admin/Index")"><i class="fa fa-home"></i>首页</a></li>
<li class="breadcrumb-item d-none" id="breadNav"></li>
</ol>
</nav>

View File

@ -0,0 +1,11 @@
@model NavigatorBarModel
<aside>
<!-- sidebar menu start-->
<ul class="sidebar nav nav-pills flex-column flex-nowrap">
@foreach (var menu in Model.Navigations)
{
@await Html.PartialAsync("SubNavItem", menu)
}
</ul>
<!-- sidebar menu end-->
</aside>

View File

@ -0,0 +1,25 @@
<div class="modal fade" id="dialogMenu" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="myMenuModalLabel" aria-hidden="true">
<div id="dialogSubMenu" class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="myMenuModalLabel">请选择菜单</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<div class="modal-body">
<div class="dd" id="nestable_menu">
<ol class="dd-list"></ol>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
<i class="fa fa-times"></i>
<span>关闭</span>
</button>
<button type="button" class="btn btn-primary" data-dismiss="modal" id="btnSubmitMenu">
<i class="fa fa-save"></i>
<span>确认</span>
</button>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,8 @@
@model Bootstrap.Security.BootstrapMenu
<li class="nav-item @Model.Active">
<a href="@Url.Content(Model.Url)" class="nav-link @Model.Active" target="@Model.Target"><i class="@Model.Icon"></i>@Model.Name</a>
@if (Model.Menus.Any())
{
@await Html.PartialAsync("SubNavigation", Model.Menus)
}
</li>

View File

@ -1,5 +1,5 @@
@model IEnumerable<Bootstrap.Security.BootstrapMenu>
<ul class="sub nav flex-column">
<ul class="sub nav nav-pills flex-column flex-nowrap" style="display: none;">
@foreach (var menu in Model)
{
@await Html.PartialAsync("SubNavItem", menu)

View File

@ -7,19 +7,6 @@
</div>
<div class="modal-body">
<form class="form-inline">
<div class="row" id="useFilter">
<div class="form-group col-12">
<div class="input-group flex-fill">
<div class="input-group-prepend d-none d-sm-flex">
<span class="input-group-text">过滤条件</span>
</div>
<input class="form-control" type="text" />
<div class="input-group-append">
<button class="btn btn-primary" type="button">查询</button>
</div>
</div>
</div>
</div>
<div class="row" id="userForm"></div>
</form>
</div>

View File

@ -1,4 +1,3 @@
@model AdminModel
@{
Layout = "_Bootstrap";
}
@ -6,43 +5,44 @@
<environment include="Development">
<link href="~/lib/toastr.js/toastr.css" rel="stylesheet" />
<link href="~/lib/nprogress/nprogress.css" rel="stylesheet" />
<link href="~/lib/sweetalert/sweetalert2.css" rel="stylesheet" />
<link href="~/lib/overlayscrollbars/OverlayScrollbars.css" rel="stylesheet" />
<link href="~/lib/bootstrap-sweetalert/sweetalert.css" rel="stylesheet" />
<link href="~/lib/scrollbar/jquery.mCustomScrollbar.css" rel="stylesheet" />
</environment>
<environment exclude="Development">
<link href="~/lib/toastr.js/toastr.min.css" rel="stylesheet" />
<link href="~/lib/nprogress/nprogress.min.css" rel="stylesheet" />
<link href="~/lib/sweetalert/sweetalert2.min.css" rel="stylesheet" />
<link href="~/lib/overlayscrollbars/OverlayScrollbars.min.css" rel="stylesheet" />
<link href="~/lib/bootstrap-sweetalert/sweetalert.min.css" rel="stylesheet" />
<link href="~/lib/scrollbar/jquery.mCustomScrollbar.min.css" rel="stylesheet" />
</environment>
@RenderSection("css", false)
}
@section javascript {
<environment include="Development">
<script src="~/lib/overlayscrollbars/jquery.overlayScrollbars.js"></script>
<script src="~/lib/scrollbar/jquery.mousewheel.js"></script>
<script src="~/lib/scrollbar/jquery.mCustomScrollbar.js"></script>
<script src="~/lib/signalr/dist/browser/signalr.js"></script>
<script src="~/lib/sweetalert/sweetalert2.js"></script>
<script src="~/lib/dcjqaccordion/js/jquery.dcjqaccordion.2.7.js"></script>
<script src="~/lib/bootstrap-sweetalert/sweetalert.js"></script>
<script src="~/lib/nprogress/nprogress.js"></script>
</environment>
<environment exclude="Development">
<script src="~/lib/overlayscrollbars/jquery.overlayScrollbars.min.js"></script>
<script src="~/lib/scrollbar/jquery.mousewheel.min.js"></script>
<script src="~/lib/scrollbar/jquery.mCustomScrollbar.concat.min.js"></script>
<script src="~/lib/signalr/dist/browser/signalr.min.js"></script>
<script src="~/lib/sweetalert/sweetalert2.min.js"></script>
<script src="~/lib/dcjqaccordion/js/jquery.dcjqaccordion.2.7.min.js"></script>
<script src="~/lib/bootstrap-sweetalert/sweetalert.min.js"></script>
<script src="~/lib/nprogress/nprogress.min.js"></script>
</environment>
<script src="~/lib/toastr.js/toastr.min.js"></script>
<script src="~/lib/dcjqaccordion/js/jquery.cookie.js"></script>
<script src="~/js/common-scripts.js" asp-append-version="true"></script>
@RenderSection("javascript", false)
<script src="~/js/log.js" asp-append-version="true"></script>
}
@await Html.PartialAsync("Navigator")
<section id="main-content" class="main-content @(Model.ShowCardTitle ? "" : "no-card-header")">
<input id="lockScreenPeriod" type="hidden" asp-condition="@Model.EnableAutoLockScreen" value="@Model.LockScreenPeriod" />
<div class="main-header">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="~/Admin/Index"><i class="fa fa-home"></i>首页</a></li>
<li class="breadcrumb-item d-none"></li>
</ol>
</div>
<section id="main-content" class="main-content">
@RenderBody()
</section>
@section modal {
@RenderSection("modal", false)
}

View File

@ -34,3 +34,4 @@
@await Html.PartialAsync("Header")
@RenderBody()
@await Html.PartialAsync("Footer")
@RenderSection("modal", false)

View File

@ -9,7 +9,6 @@
<environment exclude="Development">
<link href="~/lib/bootstrap-table/bootstrap-table.min.css" rel="stylesheet" />
</environment>
<link href="~/lib/longbow-checkbox/longbow-checkbox.css" rel="stylesheet" />
@RenderSection("css", false)
}
@section javascript {
@ -29,11 +28,36 @@
<script src="~/lib/validate/jquery.validate.min.js"></script>
<script src="~/lib/validate/localization/messages_zh.min.js"></script>
</environment>
<script src="~/lib/longbow-checkbox/longbow-checkbox.js"></script>
<script src="~/lib/longbow/longbow.dataentity.js" asp-append-version="true"></script>
<script src="~/lib/longbow/longbow.validate.js" asp-append-version="true"></script>
@RenderSection("javascript", false)
}
@section modal {
<div class="modal fade" id="dialogNew" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content" data-toggle="LgbValidate" data-valid-button="#btnSubmit" data-valid-modal="#dialogNew">
@RenderSection("modal", false)
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
<i class="fa fa-times"></i>
<span>关闭</span>
</button>
<button type="button" class="btn btn-primary" id="btnSubmit">
<i class="fa fa-save"></i>
<span>保存</span>
</button>
</div>
</div>
</div>
</div>
@RenderSection("customModal", false)
}
<div class="card">
<div class="card-header">查询条件</div>
<div class="card-body">
@RenderSection("query", false)
</div>
</div>
<div id="toolbar" class="d-none">
<div class="gear btn-group">
<button class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" type="button"><i class="fa fa-gear"></i></button>
@ -56,8 +80,8 @@
查询结果
</div>
<div class="card-body">
@await RenderSectionAsync("cardbody", false)
<table data-Header="@(Model.FixedTableHeader ? "fixed" : "scroll")"></table>
@await RenderSectionAsync("body", false)
<table></table>
</div>
</div>
<div id="tableButtons" class="d-none">
@ -66,39 +90,4 @@
<button class='del btn btn-sm btn-danger' asp-auth="del"><i class='fa fa-remove'></i><span>删除</span></button>
@RenderSection("tableButtons", false)
</div>
</div>
<div class="modal fade" id="dialogNew" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content" data-toggle="LgbValidate" data-valid-button="#btnSubmit" data-valid-modal="#dialogNew">
@RenderSection("modal", false)
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
<i class="fa fa-times"></i>
<span>关闭</span>
</button>
<button type="button" class="btn btn-primary" id="btnSubmit">
<i class="fa fa-save"></i>
<span>保存</span>
</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="dialogAdvancedSearch" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="mySearchModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="mySearchModalLabel">查询条件</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<div class="modal-body modal-query">
@RenderSection("query", false)
</div>
<div class="modal-footer">
<button type="button" id="btn_reset" class="btn btn-info btn-fill"><i class="fa fa-trash-o" aria-hidden="true"></i><span>重置</span></button>
<button type="button" id="btn_query" class="btn btn-primary btn-fill"><i class="fa fa-search" aria-hidden="true"></i><span>查询</span></button>
</div>
</div>
</div>
</div>
@RenderBody()
</div>

View File

@ -1,31 +1,19 @@
@model ModelBase
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="@Url.Content(Model.WebSiteIcon)" type="image/x-icon" />
<link rel="shortcut icon" href="@Url.Content(Model.WebSiteIcon)" type="image/x-icon" />
<link rel="apple-touch-icon" href="~/favicon.png" />
<link rel="icon" href="~/favicon.ico" type="image/x-icon" />
<link rel="shortcut icon" href="~/favicon.ico" type="image/x-icon" />
<link rel="apple-touch-icon" href="~/favicon.png"/>
<title>@ViewBag.Title</title>
@RenderSection("css", false)
<!--[if lt IE 10 ]>
<link href="../css/IE8.css" rel="stylesheet" />
<![endif]-->
<environment include="Staging,Production">
<script>
var _hmt = _hmt || [];
(function () {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?ce3c9cfb729226f39dd330899489086d";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
</environment>
</head>
<body class="trans-mute">
<body>
<!--[if lt IE 10 ]>
<div id="ieAlert" class="alert alert-danger alert-dismissible">
<div>当前浏览器版本太低不支持本系统请升级到至少IE10 <a href="../browser/IE10.exe" target="_blank">本地下载</a> <a href="https://support.microsoft.com/zh-cn/help/17621/internet-explorer-downloads" target="_blank">微软下载</a>或者使用Chrome浏览器 <a href="../browser/ChromeSetup.exe" target="_blank">本地下载</a></div>

View File

@ -0,0 +1,5 @@
@using Bootstrap.Admin.Models
@using Microsoft.AspNetCore.Mvc.Localization
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Bootstrap.Security.Mvc
@addTagHelper *, Longbow.Web

Some files were not shown because too many files have changed in this diff Show More