后端首页是管理员登陆后进入的第一个页面,主要是显示当前登陆用户信息、在线人数、菜单树列表、相关功能按键和系统介绍。让管理员能更方便的找到息想要的内容。
根据不同系统的需要,首页会显示不同的内容,比如显示公司公告、公司新闻、内部短消息、个人事务、各种业务提醒......等各种内容,这些大家可以需要去进行呈现。
先上代码
Main.aspx
using System;
using System.Collections.Generic;
using System.Data;
using System.Web;
using DotNet.Utilities;
using FineUI;
using Solution.DataAccess.DataModel;
using Solution.DataAccess.DbHelper;
using Solution.Logic.Managers;
using Solution.Web.Managers.WebManage.Application;
using SubSonic.Query;
namespace Solution.Web.Managers
{
public partial class Main : PageBase
{
//用户页面操作权限
string _pagePower = "";
#region Page_Load
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
//添加万年历按键事件,在主窗口中添加新选项卡
btnCalendar.OnClientClick = mainTabStrip.GetAddTabReference("calendar_tab", "/WebManage/Help/wannianli.htm", "万年历", IconHelper.GetIconUrl(Icon.Calendar), true);
//加载信息
LoadData();
}
}
#endregion
#region 接口函数,用于UI页面初始化,给逻辑层对象、列表等对象赋值
public override void Init()
{
}
#endregion
#region 加载数据
/// <summary>读取数据</summary>
public override void LoadData()
{
#region 展示用户信息
//在线人数
txtOnlineUserCount.Text = OnlineUsersBll.GetInstence().GetUserOnlineCount() + "";
//当前用户信息
var userHashKey = OnlineUsersBll.GetInstence().GetUserHashKey();
var model = OnlineUsersBll.GetInstence().GetModelForCache(x => x.UserHashKey == userHashKey);
if (model == null)
return;
//用户名称
txtUser.Text = model.Manager_CName + " [" + IpHelper.GetUserIp() + "]";
//部门
txtBranchName.Text = model.Branch_Name;
//职位
txtPositionInfoName.Text = model.Position_Name;
#endregion
#region 菜单栏数据绑定
//获取用户页面操作权限
_pagePower = OnlineUsersBll.GetInstence().GetPagePower();
//创建查询条件
var wheres = new List<ConditionFun.SqlqueryCondition>();
//条件:只查询出需要显示的菜单
wheres.Add(new ConditionFun.SqlqueryCondition(ConstraintType.And, MenuInfoTable.IsDisplay, Comparison.Equals, 1));
//进行查询,获取DataTable
var dt = MenuInfoBll.GetInstence().GetDataTable(false, 0, null, 0, 0, wheres);
//绑定树列表
BandingTree(dt);
#endregion
#region 开启时钟检测
Timer1.Enabled = true;
#endregion
}
#endregion
#region 页面按键
#region 清空缓存并重新加载
/// <summary>
/// 清空缓存并重新加载
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnClearCache_Click(object sender, EventArgs e)
{
//清空全部后端缓存HttpRuntime.Cache(在线列表缓存除外)
CacheHelper.RemoveManagersAllCache();
}
#endregion
#region 退出系统
/// <summary>
/// 退出系统
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnExit_Click(object sender, EventArgs e)
{
LoginLogBll.GetInstence().UserExit();
FineUI.Alert.ShowInTop("成功退出系统!", "安全退出", MessageBoxIcon.Information, "top.location='Login.aspx'");
}
#endregion
#endregion
#region 定时器
/// <summary>
/// 定时执行方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Timer1_Tick(object sender, EventArgs e)
{
Timer1.Enabled = false;
#region 检测当前用户是否退出
OnlineUsersBll.GetInstence().IsTimeOut();
#endregion
#region 检测用户登录的有效性(是否被系统踢下线或管理员踢下线)
if (OnlineUsersBll.GetInstence().IsOffline(this))
return;
#endregion
#region 更新信息(在线人数,未读取的短消息)
if (HttpRuntime.Cache == null)
{
txtOnlineUserCount.Text = "--";
}
else
{
//更新当前在线用户数量
txtOnlineUserCount.Text = OnlineUsersBll.GetInstence().GetUserOnlineCount() + "";
}
#endregion
#region 修改用户最后在线时间
//修改用户最后在线时间
OnlineUsersBll.GetInstence().UpdateTime();
#endregion
Timer1.Enabled = true;
}
#endregion
#region FineUI控件之--树控件(Tree)
#region 绑定树控件
/// <summary>树控件(Tree)
/// </summary>
/// <param name="dataTable">DataTable数据源</param>
/// <returns>树控件(Tree)</returns>
public void BandingTree(DataTable dataTable)
{
try
{
//检查指定的列是否在数据源中能否找到
if (dataTable.Rows.Count == 0)
{
return;
}
//筛选出全部一级节点
DataTable dtRoot = DataTableHelper.GetFilterData(dataTable, MenuInfoTable.ParentId, "0", MenuInfoTable.Sort, "Asc");
//判断是否有节点存在
if (dtRoot.Rows.Count != 0)
{
//循环读取节点
foreach (DataRow dr in dtRoot.Rows)
{
//判断当前节点是否有权限访问,没有则跳过本次循环
//暂时先注释掉权限判断,等添加相关权限后再开启
//if (_pagePower.IndexOf("," + dr[MenuInfoTable.Id].ToString() + ",") < 0)
//{
// continue;
//}
//创建树节点
var treenode = new FineUI.TreeNode();
//设置节点ID
treenode.NodeID = dr[MenuInfoTable.Id].ToString();
//设置节点名称
treenode.Text = dr[MenuInfoTable.Name].ToString();
treenode.Target = "mainRegion";
//判断当前节点是否为最终节点
if (int.Parse(dr[MenuInfoTable.IsMenu].ToString()) != 0)
{
//设置节点链接地址,并在Url后面添加页面加密参数
treenode.NavigateUrl = dr[MenuInfoTable.Url].ToString() + "?" + MenuInfoBll.GetInstence().PageUrlEncryptString();
treenode.Leaf = true;
}
else
{
treenode.NavigateUrl = "";
treenode.Leaf = false;
//设置树节点收缩起来
treenode.Expanded = false;
}
//添加子节点
AddChildrenNode(dataTable, treenode, dr[MenuInfoTable.Id].ToString());
//将节点加入树列表中
leftMenuTree.Nodes.Add(treenode);
}
}
}
catch (Exception ex)
{
CommonBll.WriteLog("", ex);
}
}
#endregion
#region 添加子节点
/// <summary>
/// 添加子节点
/// </summary>
/// <param name="dt">数据表</param>
/// <param name="treenode">当前树节点</param>
/// <param name="parentID">父节点ID值</param>
private void AddChildrenNode(DataTable dt, FineUI.TreeNode treenode, string parentID)
{
//筛选出当前节点下面的子节点
DataTable Childdt = DataTableHelper.GetFilterData(dt, MenuInfoTable.ParentId, parentID, MenuInfoTable.Sort, "Asc");
//判断是否有节点存在
if (Childdt.Rows.Count > 0)
{
//循环读取节点
foreach (DataRow dr in Childdt.Rows)
{
//判断当前节点是否有权限访问,没有则跳过本次循环
//if (_pagePower.IndexOf("," + dr[MenuInfoTable.Id].ToString() + ",") < 0)
//{
// continue;
//}
//创建子节点
var TreeChildNode = new FineUI.TreeNode();
//设置节点ID
TreeChildNode.NodeID = dr[MenuInfoTable.Id].ToString();
//设置节点名称
TreeChildNode.Text = dr[MenuInfoTable.Name].ToString();
TreeChildNode.Target = "mainRegion";
//判断当前节点是否为最终节点
if (int.Parse(dr[MenuInfoTable.IsMenu].ToString()) != 0)
{
//设置节点链接地址
if (dr[MenuInfoTable.Url].ToString().IndexOf("?") > 0)
{
TreeChildNode.NavigateUrl = dr[MenuInfoTable.Url].ToString() + "&" + MenuInfoBll.GetInstence().PageUrlEncryptString();
}
else
{
TreeChildNode.NavigateUrl = dr[MenuInfoTable.Url].ToString() + "?" + MenuInfoBll.GetInstence().PageUrlEncryptString();
}
//TreeChildNode.NavigateUrl = dr[MenuInfoTable.Url].ToString() + "?" + MenuInfoBll.PageURLEncryptString();
TreeChildNode.Leaf = true;
}
else
{
TreeChildNode.NavigateUrl = "";
TreeChildNode.Leaf = false;
//设置树节点扩张
TreeChildNode.Expanded = true;
}
//将节点添加进树列表中
treenode.Nodes.Add(TreeChildNode);
//递归添加子节点
AddChildrenNode(dt, TreeChildNode, dr[MenuInfoTable.Id].ToString());
}
}
}
#endregion
#endregion
}
}
1、Main.aspx页面说明
Main.aspx页面主要使用的是FineUI发布的空项目改造而来的,在这个基础上,顶部添加了当前用户的相关信息、在线人数,以及清除缓存、万年历和退出等按键。
添加了<f:Timer ID="Timer1" Interval="60" Enabled="false" OnTick="Timer1_Tick" runat="server">定时器功能,它每60秒会运行一次,执行Timer1_Tick函数,检查当前用户是否已退出,检查当前用户帐号是否已在其他电脑或浏览器上登陆或给管理员踢出系统,更新顶部当前在线人数,更新用户最后在线时间(用于检查用户是否超时离线退出)
#region 定时器
/// <summary>
/// 定时执行方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Timer1_Tick(object sender, EventArgs e)
{
Timer1.Enabled = false;
#region 检测当前用户是否退出
OnlineUsersBll.GetInstence().IsTimeOut();
#endregion
#region 检测用户登录的有效性(是否被系统踢下线或管理员踢下线)
if (OnlineUsersBll.GetInstence().IsOffline(this))
return;
#endregion
#region 更新信息(在线人数,未读取的短消息)
if (HttpRuntime.Cache == null)
{
txtOnlineUserCount.Text = "--";
}
else
{
//更新当前在线用户数量
txtOnlineUserCount.Text = OnlineUsersBll.GetInstence().GetUserOnlineCount() + "";
}
#endregion
#region 修改用户最后在线时间
//修改用户最后在线时间
OnlineUsersBll.GetInstence().UpdateTime();
#endregion
Timer1.Enabled = true;
}
#endregion
2、Main.aspx.cs文件父类
它继承PageBase类,页面在加载的时间,就会自动调用父类的OnInit初始化函数,运行里面的程序,来检查当前用户是否退出,是否有当前页面的操作权限,设置放置在页面控件Id为toolBar中的各个按键是否有权限使用。并记录用户当前所在位置。
3、添加页面按键事件
对于FineUI的页面按键事件,共一两种,一种是服务器端事件OnClick,一种是客户端事件OnClientClick。
添加了服务器端事件OnClick事件后,必须在cs文件中添加对应的事件函数,比如清空缓存按键(btnClearCache_Click)与安全退出按键(btnExit_Click)
#region 页面按键
#region 清空缓存并重新加载
/// <summary>
/// 清空缓存并重新加载
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnClearCache_Click(object sender, EventArgs e)
{
//清空全部后端缓存HttpRuntime.Cache(在线列表缓存除外)
CacheHelper.RemoveManagersAllCache();
}
#endregion
#region 退出系统
/// <summary>
/// 退出系统
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnExit_Click(object sender, EventArgs e)
{
LoginLogBll.GetInstence().UserExit();
FineUI.Alert.ShowInTop("成功退出系统!", "安全退出", MessageBoxIcon.Information, "top.location='Login.aspx'");
}
#endregion
清空缓存按键主要功能:清除当前IIS应用程序池中除在线缓存以外的所有HttpRuntime.Cache缓存
安全退出按键主要功能:删除当前登陆用户的所有缓存、Session与Cookies记录,并添加用户退出日志。
点击退出后日志记录信息(LoginLog表):
而客户端事件OnClientClick,只需要在页面中添加JS代码,它就会直接执行对应的JS函数,不与服务器端进行直接交互。
当前除了以上添加按键事件外,还可以直接在代码中绑定控件事件方法(添加新选项卡还有其他一些方法,不过以这一种最为方便)
#region Page_Load
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
//添加万年历按键事件,在主窗口中添加新选项卡
btnCalendar.OnClientClick = mainTabStrip.GetAddTabReference("calendar_tab", "/WebManage/Help/wannianli.htm", "万年历", IconHelper.GetIconUrl(Icon.Calendar), true);
//加载信息
LoadData();
}
}
#endregion
以上代码主要是为万年历按键添加一个事件,点击按键后,在首页标签旁边新增一个选项卡,打开万年历页面。这些FineUI的范例官网上有,大家自行学习就可以了。
4、展示用户信息
这个比较简单,在LoadData函数中,直接从在线缓存中读取当前用户实体后,为页面顶部控件赋值就可以了。
在这里简单的讲讲LoadData函数,这是父类的抽像函数(因为每个子类都必须将它实现,所以将它从虚函数改为抽像函数),在每个子类中都必须要重写它,它的主要功能是加载数据,列表页面主要加载的是列表中需要显示的记录,而编辑页面加载的是各个页面的实体,将它们与页面控件进行绑定并显示出来。实现了该函数后,它将会被父类中的其他各种函数引用,比如刷新、查询、翻页、保存排序、自动排序、删除、关闭子窗口......等函数执行完后会调用它来重新加载数据,刷新页面显示内容,具体大家可以查看PageBase.cs类,看看有那些函数引用了LoadData函数就知道了。
5、绑定菜单栏树列表
首先将所有可以显示的菜单全部读取出来
下面是LoadData函数中的一段代码
#region 菜单栏数据绑定
//获取用户页面操作权限
_pagePower = OnlineUsersBll.GetInstence().GetPagePower();
//创建查询条件
var wheres = new List<ConditionFun.SqlqueryCondition>();
//条件:只查询出需要显示的菜单
wheres.Add(new ConditionFun.SqlqueryCondition(ConstraintType.And, MenuInfoTable.IsDisplay, Comparison.Equals, 1));
//进行查询,获取DataTable
var dt = MenuInfoBll.GetInstence().GetDataTable(false, 0, null, 0, 0, wheres);
//绑定树列表
BandingTree(dt);
#endregion
ConditionFun.SqlqueryCondition是封装好的查询条件类。
第一个参数为查询表达式,即当前条件与前一条件使用的表达式是And还是Or;
第二个参数是想要查询的列表名称,这里我们使用的是表名+Table.列名的方式调用,这样操作的话可以使我们的代码去除硬编码,好处是当我们万一修改了数据库字段名称后,重新生成相关的模板并按F6重新编译代码时,就会直接报出所有未同步修改的代码错误位置出来,让我们能快速定位并修改掉它,而硬编码(指的是直接使用类似"IsDisplay"这种字段书写格式的代码)则不会报错,代码量多时我们将很难找出问题,直接程序执行到对应位置时才会抛出异常。
第三个参数是条件值表达式,表示当前列与值的关系,比如:等于、大于、小于、in、not in、like......等;
第四个参数是条件值
在这里要同大家说明的是,SubSonic3.0对于括号支持的不是很多,只支持单层括号,嵌套括号时将会出错,为了避免这种问题,建议多括号做为条件值查询时,请使用Linq表达式来查询(Lambd表达式),或者存储过程。具体加括号后的使用方法在后面章节会详细的进行说明。
MenuInfoBll.GetInstence().GetDataTable()函数是我们逻辑层生成好的模板函数,我们直接调用就可以了。(我们后端开发对于数据的增、删、查、改等操作函数几乎都已经生成好了,直接调用就可以)
通过执行BandingTree函数与AddChildrenNode函数,将菜单树列表创建后绑定<f:Tree runat="server" ShowBorder="false" ShowHeader="false" EnableArrows="true" EnableLines="true" ID="leftMenuTree"></f:Tree>控件
下面通过向MenuInfo表添加一些菜单记录来演示我们首页中的树列表显示效果
TRUNCATE TABLE MenuInfo
GO
set IDENTITY_INSERT MenuInfo on
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (1, '系统管理', '/WebManage/Systems/SysAuthority/', 0, 6, 0, 1, 0)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (2, '基本设置', '/WebManage/Systems/SysSet/', 1, 1, 1, 1, 0)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (3, '权限管理', '/WebManage/Systems/SysAuthority/', 1, 2, 1, 1, 0)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (5, '安全管理', '/WebManage/Systems/SysSecurity/', 1, 3, 1, 1, 0)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (6, '网站参数设置', '/WebManage/Systems/SysSet/WebConfigSet.aspx', 2, 1, 2, 1, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (11, '菜单管理', '/WebManage/Systems/SysAuthority/MenuPage.aspx', 3, 1, 2, 1, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (12, '菜单编辑', '/WebManage/Systems/SysAuthority/MenuPageEdit.aspx', 3, 2, 2, 0, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (13, '页面权限设置', '/WebManage/Systems/SysAuthority/PagePowerSignList.aspx', 3, 3, 2, 1, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (14, '部门管理', '/WebManage/Systems/SysAuthority/BranchList.aspx', 3, 7, 2, 1, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (15, '部门编辑', '/WebManage/Systems/SysAuthority/BranchEdit.aspx', 3, 8, 2, 0, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (16, '职位管理', '/WebManage/Systems/SysAuthority/PositionList.aspx', 3, 9, 2, 1, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (17, '职位编辑', '/WebManage/Systems/SysAuthority/PositionEdit.aspx', 3, 10, 2, 0, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (18, '管理员管理', '/WebManage/Systems/SysAuthority/ManagerList.aspx', 3, 12, 2, 1, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (19, '管理员编辑', '/WebManage/Systems/SysAuthority/ManagerEdit.aspx', 3, 13, 2, 0, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (20, '职位权限树状图', '/WebManage/Systems/SysAuthority/MenuPageTree.aspx', 3, 11, 2, 0, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (21, '登陆日志', '/WebManage/Systems/SysSecurity/LoginLogList.aspx', 5, 1, 2, 1, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (22, '操作日志', '/WebManage/Systems/SysSecurity/UseLogList.aspx', 5, 2, 2, 1, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (23, '在线用户', '/WebManage/Systems/SysSecurity/OnlineUsers.aspx', 5, 3, 2, 1, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (36, '信息发布', '/WebManage/Information/', 0, 1, 0, 1, 0)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (37, '文章管理', '/WebManage/Information/Info/', 36, 1, 1, 1, 0)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (38, '文章栏目管理', '/WebManage/Information/Info/InfoClassList.aspx', 37, 1, 2, 1, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (39, '文章栏目编辑', '/WebManage/Information/Info/InfoClassAdd.aspx', 37, 2, 2, 0, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (40, '文章内容管理', '/WebManage/Information/Info/InfoList.aspx', 37, 3, 2, 1, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (41, '文章内容编辑', '/WebManage/Information/Info/InfoAdd.aspx', 37, 4, 2, 0, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (127, '页面权限编辑', '/WebManage/Systems/SysAuthority/PagePowerSignEdit.aspx', 3, 4, 2, 0, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (129, '公共页面权限设置', '/WebManage/Systems/SysAuthority/PagePowerSignPublicList.aspx', 3, 5, 2, 1, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (130, '公共页面权限编辑', '/WebManage/Systems/SysAuthority/PagePowerSignPublicEdit.aspx', 3, 6, 2, 0, 1)
GO
INSERT INTO MenuInfo (Id, Name, Url, ParentId, Sort, Depth, IsDisplay, IsMenu) VALUES (203, '错误日志', '/WebManage/Systems/SysSecurity/ErrorLog.aspx', 5, 4, 2, 1, 1)
GO
set IDENTITY_INSERT MenuInfo off
6、小结
登陆页、父类与后端首页,是本系统三个最基本的组成零部件,完成了这三个部件后,接下来就是体验如何来进行快速开发的成果了。后面章节发表的内容,主要是针对框架中已生成函数或一些特殊功能(函数)的调用方法讲解,用实例来讲述这些功能的使用方法。
点击下载:
本框架不内置 ExtJS 库,请自行下载ExtJS 库后手工添加到Solution.Web.Managers项目的根目录中(如下图):http://fineui.com/bbs/forum.php?mod=viewthread&tid=3218
版权声明:
本文由AllEmpty原创并发布于博客园,欢迎转载,未经本人同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。如有问题,可以通过1654937@qq.com 联系我,非常感谢。
发表本编内容,只要主为了和大家共同学习共同进步,有兴趣的朋友可以加加Q群:327360708 ,大家一起探讨。
更多内容,敬请观注博客:http://www.cnblogs.com/EmptyFS/
https://www.cnblogs.com/EmptyFS/p/3791945.html