根据您要实现的功能,可能需要在 Limyee 电商平台的系统管理 UI 中公开一些管理选项。管理 UI 完全使用插件定义,因此易于添加。
系统管理 UI 扩展的类型
管理员使用 Limyee 电商平台中的系统管理 UI 来配置全局选项并启用特定功能。
它使用三种类型的插件构建:
- 类别:类别是管理 UI 中的顶级导航项,用于对实现特定功能的面板进行分类。
- 类别面板:面板位于一个类别中,当选中类别后,类别将会隐藏,当前类别显示在面板的标题处。它们的管理 UI(如上图的第二列) 由可用的插件类型编辑器或显式面板定义。
- 插件类型面板:插件类型面板是类别中特定类型的插件分组。
- 插件类型编辑器:平台使用插件类型编辑器来扩展插件类型面板的插件编辑。插件类型编辑器当与插件定义的条件匹配时,插件类型编辑器将显示到 UI。
- 显式面板:显式面板是只能通过直接链接访问的面板。例如,在管理 UI 中有几个不同的面板和类别可编辑会员,但通过链接到编辑会员的显式面板只需要实现一次。当需要从多个位置访问相同的功能时,显式面板非常有用。
管理类别
要将顶级类别添加到管理 UI,必须定义并启用实现 IAdministrationPanelCategory 接口的插件。IAdministrationPanelCategory 接口在 Limyee.Core.dll 的 Limyee.Extensibility.Administration.Version1 命名空间中定义。下面的示例创建一个名为“类别示例”的类别:
using System; using Limyee.Extensibility.Administration.Version1; namespace Samples { public class SampleAdministrationCategory : IAdministrationPanelCategory { private readonly Guid _id = new Guid("07108CA8-9D4E-4D98-BE87-69F19742E0D2"); #region IPlugin // ... #endregion #region IAdministrationPanelCategory public Guid AdministrationPanelCategoryId { get { return _id; } } public string AvatarUrl { get { return null; } } public string CategoryName { get { return "类别示例"; } } public int? DisplayOrder { get { return null; } } #endregion } }
AdministrationPanelCategoryId 应该是用于唯一引用此类别的唯一标识符。请注意,没有任何面板的管理类别不会在站点中呈现。当它有用户可访问的面板时,该面板将显示在管理 UI 中:
管理面板
要将新的管理面板添加到管理 UI,必须定义并启用实现 IAdministrationPanel 接口的插件。IAdministrationPanel 接口在 Limyee.Core.dll 中定义。面板的一个非常基本的示例(添加到上面创建的类别,使用相同的 AdministrationPanelCategoryId)如下所示:
using System; using Limyee.Extensibility.Administration.Version1; namespace Samples { public class SampleAdministrationPanel : IAdministrationPanel { private readonly Guid _id = new Guid("8C41AD4F-EBBF-4B03-8CAD-F7A50A66C90D"); #region IPlugin // ... #endregion #region IAdministrationPanel public Guid PanelId { get { return _id; } } public string PanelName { get { return "面板示例 1"; } } public string PanelDescription { get { return "面板示例描述 1。"; } } public bool VaryCacheByUser { get { return false; } } public string GetViewHtml() { return "<p>面板内容 1</p>"; } public bool HasAccess(int userId) { return true; } public Guid AdministrationPanelCategoryId { get { return new Guid("07108CA8-9D4E-4D98-BE87-69F19742E0D2"); } } public string CssClass { get { return string.Empty; } } public int? DisplayOrder { get { return null; } } public bool IsCacheable { get { return true; } } #endregion } }
PanelId 应该是此面板的唯一标识符。AdministrationPanelCategoryId 应引用管理类别的标识符(示例引用我们上面创建的类别)。PanelName 和 PanelDescription 属性标识 UI 中的面板,当此面板处于活动状态时,GetViewHtml() 方法返回填充管理 UI 的大内容区域的 HTML。
面板的完整内容可以在代码中定义,也可以使用插件定义的小组件定义管理面板。
当一个类别中只有一个面板可用时,将删除插件类型面板,导航到该类别或面板将呈现单个可用面板。当某个类别中的用户可以使用多个面板时,将显示第插件类型面板,并在导航到类别中时选择第一个面板。当多个面板可用且选择了类别时,此示例面板将呈现为:
插件类型面板
要按类型将插件列表添加到管理类别,应定义并启用实现 IPluginTypesAdministrationPanel 接口的插件。IPluginTypesAdministrationPanel 接口在 Limyee.Core.dll 的 Limyee.Extensibility.Administration.Version1 命名空间中定义。下面是一个实现示例,其中包括“插件类型”的内容类型插件列表:
using System; using System.Collections.Generic; using Limyee.Extensibility.Administration.Version1; namespace Samples { public class SamplePluginTypesAdministrationPanel : IPluginTypesAdministrationPanel { #region IPlugin // ... #endregion public Guid AdministrationPanelCategoryId { get { return new Guid("07108CA8-9D4E-4D98-BE87-69F19742E0D2"); } } public string PluginTypesName { get { return "Plugin Types"; } } public IEnumerable<Type> PluginTypes { get { return new Type[] { typeof(Limyee.Extensibility.Content.Version1.IContentType) }; } } public int? DisplayOrder { get { return null; } } } }
AdministrationPanelCategoryId 属性引用会将此插件列表添加到其中的类别。PluginTypesName是插件列表上方显示的标签。PluginTypes 属性列出了应列出的插件类型,这些插件可以是接口、基类或实质类。平台呈现匹配插件的列表,这些插件链接到平台定义的插件编辑器。
该平台包括优化过滤选项,以便在列表中有一定数量插件时能提供匹配插件,并且当只有一个匹配的插件时,还会折叠列表使它像其他面板一样显示。
该示例呈现为:
插件类型编辑器
该平台定义了用于在管理 UI 中编辑插件的 UI。插件管理体验可以通过实现 IPluginTypesEditor 接口插件来扩展特定类型。IPluginTypesEditor 接口是在 Limyee.Components.dll 的 Limyee.Extensibility.Version1 命名空间中定义的。下面的示例实现将“示例类型编辑器”添加到用于编辑内容类型的 UI 中:
using System; using Limyee.Extensibility.Version1; namespace Samples { public class SamplePluginTypesEditor : IPluginTypesEditor { #region IPlugin // ... #endregion #region IPluginTypesEditor public IEnumerable<Type> PluginTypes { get { return new Type[] { typeof(Limyee.Extensibility.Content.Version1.IContentType) }; } } public string GetViewHtml(IPlugin plugin, string apiJson) { return string.Format(@" <div id=""sample-plugin-type-editor""> '{0}'的插件类型编辑器内容 </div> <script type=""text/javascript""> (function() {{ var api = {1}; api.registerContent({{ name: '类型编辑器示例', orderNumber: 0, selected: function() {{ $('#sample-plugin-type-editor').show(); }}, unselected: function() {{ $('#sample-plugin-type-editor').hide(); }} }}); }})(); </script> ", plugin.Name, apiJson); } #endregion } }
PluginTypes 属性标识应应用此编辑器的插件类型,并且可以是接口或实体类的列表。当在管理 UI 中呈现与定义类型匹配的插件时,将调用插件类型编辑器的 GetViewHtml() 方法,并提供正在编辑的插件的当前实例以及用于与平台定义的插件编辑 UI 交互的 JSON API。GetViewHtml() 方法应返回实现编辑器行为所需的 HTML 和脚本。在上面的示例中,它添加了一组命名的内容“类型编辑器示例”,内容包含文本“正在编辑的插件的名称”和“插件类型编辑器内容”。
该示例呈现为:
GetViewHtml() 呈现的标记和脚本应与提供的 JSON API 交互。API 是一个 JSON 对象,其中包含以下成员,用于定义编辑器的 UI 并与平台插件编辑器集成:
{ registerSave: function(saveFunction) { /* ... */ }, registerContent: function(contentDetails) { /* ... */ }, validate: function() { /* forces all validation on the plugin process, returns true (valid) or false (invalid) */ } }
注册内容
应调用 registerContent(contentDetails) 函数来注册条件的渲染内容(如:tabs)。contentDetails 参数是以下格式的对象:
{ name: 'Name of the tab', orderNumber: 1, /* optional order for this UI compared to other UIs */ selected: function() { /* called when the tab is selected */ }, unselected: function() { /* called when the tab is unselected */ }, validate: function() { /* called when tab should be validated, should return true (valid) or false (invalid) */ }, actions: [ { label: 'Label of the action', messageName: 'name of the message to publish when this action is invoked', messageData: 'data to provide when the message is published', show: /* either 'always' or 'contextually', default: 'contextually' */ } ] }
注册保存
registerSave(saveFunction) 函数应通过引用自定义 UI 的函数来调用,该函数可以用于保存配置表单。当平台调用 saveFunction 参数时,会提供一个有以下属性的对象参数:
{ success: function() { ... }, error: function() { ... } }
强制验证
要强制验证所有插件类型编辑器,可以调用 validate() 方法。
显式管理面板
要创建可从多个其他面板引用的管理面板,可以定义显式管理面板。显式管理面板不存在于管理 UI 的可浏览层次结构中,而是通过 URL 从任何面板打开。要添加显式管理面板,可以定义并启用实现 IAdministrationExplicitPanel 接口的插件。IAdministrationExplicitPanel 接口是在 Limyee.Core.dll 的 Limyee.Extensibility.Administration.Version1 命名空间中定义。示例实现如下:
using System; using Limyee.Extensibility.Administration.Version1; using System.Collections.Specialized; using System.Linq; namespace Samples { public class SampleExplicitAdministrationPanel : IAdministrationExplicitPanel { private readonly Guid _id = new Guid("cb7864ca-395e-49e0-b7b3-4bfece0f9db9"); private IAdministrationExplicitPanelController _explicitPanelController; #region IPlugin // ... #endregion public void SetController(IAdministrationExplicitPanelController controller) { _explicitPanelController = controller; } public bool HasAccess(int userId, NameValueCollection parameters) { return true; } public string CssClass { get { return string.Empty; } } public int? DisplayOrder { get { return null; } } public bool IsCacheable { get { return true; } } public Guid PanelId { get { return _id; } } public bool VaryCacheByUser { get { return false; } } public string GetPanelName(NameValueCollection parameters) { return "Sample Explicit Panel"; } public string GetPanelDescription(NameValueCollection parameters) { return "This is a sample explicit panel."; } public string GetViewHtml(NameValueCollection parameters) { if (parameters != null && parameters.Count > 0) return "<p>Sample explicit panel. Parameters: " + string.Join(", ", parameters.AllKeys.Select(k => k + " = " + parameters[k])) + "</p>"; else return "<p>Sample explicit panel.</p>"; } } }
显式面板通过 SetController() 方法随控制器一起提供。此控制器提供了一种方法来生成此面板的 URL(带或不带参数)。然后,可以将此 URL 提供给其他面板,以使它们能够生成指向此显式面板的链接并共享其功能。大多数成员与上面的面板示例类似,但有一些例外:
- 显式面板没有分类。显式面板可以从任何管理面板引用,也可以直接引用。从另一个管理面板引用时,将保留选定的类别和非显式面板导航,并且显式面板显示为现有非显式面板的扩展。直接引用时,不会选择任何类别,显式面板将直接加载到类别面板旁边。
- 提供的参数有面板名称、描述和视图 HTML。GetPanelName()、GetPanelDescription() 和 GetViewHtml() 都提供了从控制器生成的 URL 传递的可选参数列表。这些参数可用于影响面板的命名和呈现。
如果我们从之前创建的示例面板 1(以及名为“示例面板 2”)添加了指向此示例显式面板的链接,
using System; using Limyee.Extensibility.Administration.Version1; namespace Samples { public class SampleAdministrationPanel : IAdministrationPanel { private readonly Guid _id = new Guid("8C41AD4F-EBBF-4B03-8CAD-F7A50A66C90D"); #region IPlugin // ... #endregion #region IAdministrationPanel public Guid PanelId { get { return _id; } } public string PanelName { get { return "面板示例 1"; } } public string GetViewHtml() { return string.Format("<p>面板内容 1</p><div><a href=\"{0}\">显式面板</a></div>", SiteUrls.Instance().AdministrationExplicitPanel(new Guid("cb7864ca-395e-49e0-b7b3-4bfece0f9db9"))); } public bool VaryCacheByUser { get { return false; } } public string GetViewHtml() { return "<p>面板内容 1</p>"; } public bool HasAccess(int userId) { return true; } public Guid AdministrationPanelCategoryId { get { return new Guid("07108CA8-9D4E-4D98-BE87-69F19742E0D2"); } } public string CssClass { get { return string.Empty; } } public int? DisplayOrder { get { return null; } } public bool IsCacheable { get { return true; } } #endregion } }
则单击面板中的链接将呈现:
请注意,示例类别和面板仍突出显示,我们可以从显式面板导航回示例面板。
如果我们从“示例面板 2”面板链接到同一显式面板,它将如下所示:
如果我们在生成URL时传递参数,
using System; using Limyee.Extensibility.Administration.Version1; namespace Samples { public class SampleAdministrationPanel : IAdministrationPanel { private readonly Guid _id = new Guid("8C41AD4F-EBBF-4B03-8CAD-F7A50A66C90D"); #region IPlugin // ... #endregion #region IAdministrationPanel public Guid PanelId { get { return _id; } } public string PanelName { get { return "面板示例 1"; } } public string GetViewHtml() { return string.Format("<p>面板内容 1</p><div><a href=\"{0}\">显式面板</a></div>", SiteUrls.Instance().AdministrationExplicitPanel(new Guid("cb7864ca-395e-49e0-b7b3-4bfece0f9db9"), new NameValueCollection { { "Key1", "Value1" }, { "Key2", "Value2" } })); } public bool VaryCacheByUser { get { return false; } } public string GetViewHtml() { return "<p>面板内容 1</p>"; } public bool HasAccess(int userId) { return true; } public Guid AdministrationPanelCategoryId { get { return new Guid("07108CA8-9D4E-4D98-BE87-69F19742E0D2"); } } public string CssClass { get { return string.Empty; } } public int? DisplayOrder { get { return null; } } public bool IsCacheable { get { return true; } } #endregion } }
显式面板可以检索并使用这些值: