新增多租户开关,同时根据租户模式和管理模式,控制不同的菜单显示

This commit is contained in:
智能大石头 2023-08-22 22:44:56 +08:00
parent 73404fa854
commit bbac7f9d33
15 changed files with 196 additions and 123 deletions

View File

@ -865,6 +865,7 @@ public static class ViewHelper
ID = menu.ID,
Name = menu.Name,
DisplayName = menu.DisplayName ?? menu.Name,
FullName = menu.FullName,
Url = menu.Url,
Icon = menu.Icon,
Visible = menu.Visible,

View File

@ -82,16 +82,6 @@ public class CubeSetting : Config<CubeSetting>
[Description("机器人错误码。设置后拦截各种爬虫并返回相应错误如404/500默认0不拦截")]
[Category("通用")]
public Int32 RobotError { get; set; }
/// <summary>用户在线。是否记录用户在线信息0表示不记录1表示仅记录已登录用户2表示记录所有访客。默认2</summary>
[Description("用户在线。是否记录用户在线信息0表示不记录1表示仅记录已登录用户2表示记录所有访客。默认2")]
[Category("通用")]
public Int32 EnableUserOnline { get; set; } = 2;
/// <summary>用户统计。是否统计用户访问默认true</summary>
[Description("用户统计。是否统计用户访问默认true")]
[Category("通用")]
public Boolean EnableUserStat { get; set; } = true;
#endregion
#region
@ -278,6 +268,23 @@ public class CubeSetting : Config<CubeSetting>
public String StarWeb { get; set; }
#endregion
#region
/// <summary>多租户。是否支持多租户,租户模式禁止访问系统管理,平台管理模式禁止访问租户页面</summary>
[Description("多租户。是否支持多租户,租户模式禁止访问系统管理,平台管理模式禁止访问租户页面")]
[Category("系统功能")]
public Boolean EnableTenant { get; set; }
/// <summary>用户在线。是否记录用户在线信息0表示不记录1表示仅记录已登录用户2表示记录所有访客。默认2</summary>
[Description("用户在线。是否记录用户在线信息0表示不记录1表示仅记录已登录用户2表示记录所有访客。默认2")]
[Category("系统功能")]
public Int32 EnableUserOnline { get; set; } = 2;
/// <summary>用户统计。是否统计用户访问默认true</summary>
[Description("用户统计。是否统计用户访问默认true")]
[Category("系统功能")]
public Boolean EnableUserStat { get; set; } = true;
#endregion
#region
/// <summary>实例化</summary>
public CubeSetting() { }

View File

@ -242,6 +242,7 @@ public class IndexController : ControllerBaseX
ID = menu.ID,
Name = menu.Name,
DisplayName = menu.DisplayName ?? menu.Name,
FullName = menu.FullName,
Url = menu.Url,
Icon = menu.Icon,
Visible = menu.Visible,

View File

@ -0,0 +1,10 @@
using XCode.Membership;
namespace NewLife.Cube;
/// <summary>租户控制器</summary>
public interface ITenantController
{
///// <summary>是否租户实体类</summary>
//Boolean IsTenantSource { get; }
}

View File

@ -263,7 +263,7 @@ public class ReadOnlyEntityController<TEntity> : ControllerBaseX where TEntity :
}
/// <summary>是否租户实体类</summary>
protected Boolean IsTenantSource => typeof(TEntity).GetInterfaces().Any(e => e == typeof(ITenantSource));
public Boolean IsTenantSource => typeof(TEntity).GetInterfaces().Any(e => e == typeof(ITenantSource));
/// <summary>获取选中键</summary>
/// <returns></returns>

View File

@ -1,6 +1,7 @@
using System.Reflection;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using NewLife.Cube.ViewModels;
using NewLife.Log;
using NewLife.Reflection;
using XCode;
@ -261,4 +262,30 @@ public static class MenuHelper
return dic;
}
/// <summary>根据租户隔离菜单</summary>
/// <param name="menus"></param>
/// <param name="isTenant"></param>
/// <returns></returns>
public static IList<MenuTree> FilterByTenant(IList<MenuTree> menus, Boolean isTenant)
{
var list = new List<MenuTree>();
foreach (var item in menus)
{
var flag = false;
if (!item.FullName.IsNullOrEmpty())
{
var type = Type.GetType(item.FullName);
if (type != null && type.As<ITenantController>())
{
flag = true;
}
}
if (isTenant && flag || !isTenant && !flag) list.Add(item);
}
return list;
}
}

View File

@ -79,16 +79,6 @@ public class CubeSetting : Config<CubeSetting>
[Description("机器人错误码。设置后拦截各种爬虫并返回相应错误如404/500默认0不拦截")]
[Category("通用")]
public Int32 RobotError { get; set; }
/// <summary>用户在线。是否记录用户在线信息0表示不记录1表示仅记录已登录用户2表示记录所有访客。默认2</summary>
[Description("用户在线。是否记录用户在线信息0表示不记录1表示仅记录已登录用户2表示记录所有访客。默认2")]
[Category("通用")]
public Int32 EnableUserOnline { get; set; } = 2;
/// <summary>用户统计。是否统计用户访问默认true</summary>
[Description("用户统计。是否统计用户访问默认true")]
[Category("通用")]
public Boolean EnableUserStat { get; set; } = true;
#endregion
#region
@ -275,6 +265,23 @@ public class CubeSetting : Config<CubeSetting>
public String StarWeb { get; set; }
#endregion
#region
/// <summary>多租户。是否支持多租户,租户模式禁止访问系统管理,平台管理模式禁止访问租户页面</summary>
[Description("多租户。是否支持多租户,租户模式禁止访问系统管理,平台管理模式禁止访问租户页面")]
[Category("系统功能")]
public Boolean EnableTenant { get; set; }
/// <summary>用户在线。是否记录用户在线信息0表示不记录1表示仅记录已登录用户2表示记录所有访客。默认2</summary>
[Description("用户在线。是否记录用户在线信息0表示不记录1表示仅记录已登录用户2表示记录所有访客。默认2")]
[Category("系统功能")]
public Int32 EnableUserOnline { get; set; } = 2;
/// <summary>用户统计。是否统计用户访问默认true</summary>
[Description("用户统计。是否统计用户访问默认true")]
[Category("系统功能")]
public Boolean EnableUserStat { get; set; } = true;
#endregion
#region
/// <summary>实例化</summary>
public CubeSetting() { }

View File

@ -22,6 +22,8 @@ namespace NewLife.Cube.ViewModels
/// 显示名
/// </summary>
public String DisplayName { get; set; }
/// <summary>全名</summary>
public String FullName { get; set; }
/// <summary>
/// 父级id
/// </summary>

View File

@ -117,53 +117,53 @@
<script src="@res/artDialog/dist/dialog.new.life.js"></script>
<script type="text/javascript">
$(function () {
var $frmContent = $('#main');
function fixFrmContentSize() {
var offset = $frmContent.offset();
var top = offset.top;
var height = document.documentElement.clientHeight;
$frmContent.height(height - top);
$(function () {
var $frmContent = $('#main');
function fixFrmContentSize() {
var offset = $frmContent.offset();
var top = offset.top;
var height = document.documentElement.clientHeight;
$frmContent.height(height - top);
}
fixFrmContentSize();
$(window).resize(fixFrmContentSize);
//增加iframe被嵌套的检测2015-6-4 景裔增加
if (window != top) {
top.location.href = location.href;
}
//菜单active样式
$(".nav-list").children("li").click(function () {
var $this = $(this);
$this.siblings("li").each(function () {
var $item = $(this);
if ($item.attr("class") == "open") {
$item.removeClass("open");
$item.children(".submenu").hide();
}
fixFrmContentSize();
$(window).resize(fixFrmContentSize);
//增加iframe被嵌套的检测2015-6-4 景裔增加
if (window != top) {
top.location.href = location.href;
}
//菜单active样式
$(".nav-list").children("li").click(function () {
var $this = $(this);
$this.siblings("li").each(function () {
var $item = $(this);
if ($item.attr("class") == "open") {
$item.removeClass("open");
$item.children(".submenu").hide();
}
});
$this.addClass('open');
});
$(".submenu li").click(function () {
var $this = $(this);
//兄弟节点去焦
$this.siblings("li").removeClass("active");
$this.addClass('active');
//父节点获取焦点
var $parent = $this.parent().parent();
$parent.addClass("active");
$parent.siblings("li").each(function () {
var $item = $(this);
$item.removeClass("open").removeClass("active");
$item.children(".submenu").hide();
if ($item.children(".submenu").length != 0) {
$item.children(".submenu").children("li").removeClass("active");
}
});
});
});
$this.addClass('open');
});
$(".submenu li").click(function () {
var $this = $(this);
//兄弟节点去焦
$this.siblings("li").removeClass("active");
$this.addClass('active');
//父节点获取焦点
var $parent = $this.parent().parent();
$parent.addClass("active");
$parent.siblings("li").each(function () {
var $item = $(this);
$item.removeClass("open").removeClass("active");
$item.children(".submenu").hide();
if ($item.children(".submenu").length != 0) {
$item.children(".submenu").children("li").removeClass("active");
}
});
});
});
</script>
</body>

View File

@ -1,12 +1,18 @@
@using NewLife.Model;
@using NewLife.Cube.Membership;
@using NewLife.Model;
@using NewLife.Cube.ViewModels;
@{
String[] icos = new String[] { "fa-tachometer", "fa-desktop", "fa-list", "fa-pencil-square-o", "fa-list-alt", "fa-calendar", "fa-picture-o", "fa-tag", "fa-file-o" };
Int32 _idx = 0;
var menus = ViewBag.Menus as IList<MenuTree> ?? new List<MenuTree>();
// 根据租户模式过滤菜单
var ctx = TenantContext.Current;
var set = CubeSetting.Current;
if (set.EnableTenant) menus = MenuHelper.FilterByTenant(menus, ctx != null && ctx.TenantId > 0);
}
<ul class="nav nav-list">
@foreach (var menu in menus.Where(w=>w.Visible))
@foreach (var menu in menus.Where(w => w.Visible))
{
var childs = menu.Children ?? new List<MenuTree>();
if (_idx >= icos.Length) { _idx = 0; }
@ -23,18 +29,11 @@
<b class="arrow"></b>
<ul @Html.Raw(menu == menus[0] ? "class=\"submenu nav-show\" style=\"display:block;\"" : "class=\"submenu nav-hide\" style=\"display:none;\"")>
@foreach (var menu2 in childs.Where(w=>w.Visible))
@foreach (var menu2 in childs.Where(w => w.Visible))
{
@await Html.PartialAsync("_Left_Item", menu2);
@await Html.PartialAsync("_Left_Item", menu2)
}
</ul>
@*<ul class="submenu">
@foreach (IMenu menu2 in childs)
{
@await Html.PartialAsync("_Left_Item", menu2);
}
</ul>*@
}
</li>
}

View File

@ -1,4 +1,5 @@
@using NewLife.Model;
@using NewLife.Cube.Membership;
@using NewLife.Model;
@using XCode.Membership;
@using NewLife.Cube.ViewModels;
@{
@ -6,6 +7,11 @@
var childs = item.Children ?? new List<MenuTree>();
var url = item.Url.IsNullOrEmpty() ? "" : Url.Content(item.Url);
var ico = !item.Icon.IsNullOrEmpty() ? item.Icon : "fa-caret-right";
// 根据租户模式过滤菜单
var ctx = TenantContext.Current;
var set = CubeSetting.Current;
if (set.EnableTenant) childs = MenuHelper.FilterByTenant(childs, ctx != null && ctx.TenantId > 0);
}
<li>
@if (childs.Any())

View File

@ -12,14 +12,7 @@
var cfg = ViewBag.Config as SysConfig;
var name = cfg == null ? "" : cfg.DisplayName;
var tList = TenantUser.FindAllByUserId(user.ID);
if (roleNames.Contains("管理员")) tList.Add(new TenantUser() { TenantId = 0 });
var tenant = XCode.Membership.Tenant.FindById(TenantContext.Current?.TenantId ?? 0);
var lastTenant = tList.LastOrDefault();
var tenantName = tenant != null ? tList.Select(e => e.TenantId).Contains(tenant.Id) ? tenant.Name : lastTenant?.TenantName : lastTenant?.TenantName;
var flag = false;
var set = CubeSetting.Current;
}
<!-- #section:basics/navbar.layout -->
<div id="navbar" class="navbar navbar-default">
@ -67,7 +60,7 @@
<li>
<a href="~/" target="_blank">
<i class="ace-icon fa fa-cog"></i>
网站首页
系统首页
</a>
</li>
<li class="divider"></li>
@ -91,35 +84,42 @@
</div>
<!--tenant-->
@if (tList.Count > 0)
@if (set.EnableTenant && user != null)
{
var tList = TenantUser.FindAllByUserId(user.ID);
var tenant = XCode.Membership.Tenant.FindById(TenantContext.Current?.TenantId ?? 0);
var tenantName = tenant?.Name;
<div class="navbar-buttons navbar-header pull-right" role="navigation">
<ul class="nav ace-nav">
<li class="light-blue">
<a data-toggle="dropdown" href="#" class="dropdown-toggle" target="main">
<span class="hidden-xs" title="@tenantName" style="line-height:45px;top:0px;">
@(tenantName ?? "管理员")
<span class="hidden-xs" title="租户模式禁止访问系统管理,平台管理模式禁止访问租户页面" style="line-height:45px;top:0px;">
@(tenantName ?? "系统后台")
</span>
<i class="ace-icon fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu-right dropdown-menu dropdown-caret">
@if (user.Roles.Any(e => e.IsSystem))
{
<li>
<a href="~/Admin/Index?tenantid=0">
<span class="hidden-xs" title="系统管理后台,用于管理租户,但不能访问租户页面" style="line-height:45px;top:0px;">
系统管理后台
</span>
</a>
</li>
<li class="divider"></li>
}
@foreach (var item in tList)
{
if (flag)
{
<li class="divider"></li>
}
else
{
flag = true;
}
<li>
<a href="~/Admin/Index?tenantid=@item.TenantId" target="_blank">
<a href="~/Admin/Index?tenantid=@item.TenantId">
<i class="ace-icon fa fa-cog"></i>
@(item.TenantName ?? "管理员")
@item.TenantName
</a>
</li>
}

View File

@ -1,8 +1,14 @@
@using NewLife.Model;
@using NewLife.Cube.Membership;
@using NewLife.Model;
@using NewLife.Cube.ViewModels;
@{
var menus = ViewBag.Menus as IList<MenuTree> ?? new List<MenuTree>();
var flag = true;
// 根据租户模式过滤菜单
var ctx = TenantContext.Current;
var set = CubeSetting.Current;
if (set.EnableTenant) menus = MenuHelper.FilterByTenant(menus, ctx != null && ctx.TenantId > 0);
}
<div class="layui-side layui-bg-black">

View File

@ -1,10 +1,16 @@
@using NewLife.Model;
@using NewLife.Cube.Membership;
@using NewLife.Model;
@using XCode.Membership;
@using NewLife.Cube.ViewModels;
@{
var item = Model as MenuTree;
var childs = item.Children ?? new List<MenuTree>();
var url = item.Url.IsNullOrEmpty() ? "" : Url.Content(item.Url);
// 根据租户模式过滤菜单
var ctx = TenantContext.Current;
var set = CubeSetting.Current;
if (set.EnableTenant) childs = MenuHelper.FilterByTenant(childs, ctx != null && ctx.TenantId > 0);
}
@if (item.Visible)

View File

@ -12,14 +12,7 @@
var cfg = ViewBag.Config as SysConfig;
var name = cfg == null ? "" : cfg.DisplayName;
var tList = TenantUser.FindAllByUserId(user.ID);
if (roleNames.Contains("管理员")) tList.Add(new TenantUser() { TenantId = 0 });
var tenant = XCode.Membership.Tenant.FindById(TenantContext.Current?.TenantId ?? 0);
var lastTenant = tList.LastOrDefault();
var tenantName = tenant != null ? tList.Select(e => e.TenantId).Contains(tenant.Id) ? tenant.Name : lastTenant?.TenantName : lastTenant?.TenantName;
var flag = false;
var set = CubeSetting.Current;
}
<div class="layui-header">
@ -50,26 +43,34 @@
</li>
</ul>
@if (tList.Count > 0)
@if (set.EnableTenant && user != null)
{
var tList = TenantUser.FindAllByUserId(user.ID);
var tenant = XCode.Membership.Tenant.FindById(TenantContext.Current?.TenantId ?? 0);
var tenantName = tenant?.Name;
<ul class="layui-nav layui-layout-right" style="right:140px;">
<li class="layui-nav-item layui-show-md-inline-block">
<a href="javascript:;">
@(tenantName ?? "管理员")
@(tenantName ?? "系统后台")
</a>
<dl class="layui-nav-child">
@if (user.Roles.Any(e => e.IsSystem))
{
<dd>
<a href="~/Admin/Index?tenantid=0">
<span class="hidden-xs" title="系统管理后台,用于管理租户,但不能访问租户页面" style="line-height:45px;top:0px;">
系统管理后台
</span>
</a>
</dd>
<dd class="divider"></dd>
}
@foreach (var item in tList)
{
if (flag)
{
<hr>
}
else
{
flag = true;
}
<dd><a href="~/Admin/Index?tenantid=@item.TenantId" lay-header-event="menuSetting" data-title="@(item.TenantName??"管理员")">@(item.TenantName ?? "管理员")</a></dd>
<dd><a href="~/Admin/Index?tenantid=@item.TenantId" lay-header-event="menuSetting">@item.TenantName</a></dd>
}
</dl>
</li>