extjs+MVC4+PetaPoco+AutoFac+AutoMapper后台管理系统(附源码)
前言
本项目使用的开发环境及技术列举如下:
1、开发环境
IDE:VS2010+MVC4
数据库:SQLServer2008
2、技术
前端:Extjs
后端:
(1)、数据持久层:轻量级ORM框架PetaPoco
(2)、依赖注入:AutoFac
(3)、对象关系映射:AutoMapper
(4)、数据验证(MVC自带的验证封装使用)
(5)、SQL翻译机
(6)、缓存
以上使用都参考或直接借鉴使用了园子内牛人们的代码,只是学习交流使用而已,还请勿怪,我为了简便,没有分多个类库,而是以文
件夹的形式分的,大家可以根据文件夹分成类库也是一样的。好了,废话不多说,还是先上几张图大家看看吧,如果有兴趣再往下看
项目截图




要点一:Extjs
本项目虽然功能不多,但是基本已经涵盖了extjs的所有基本用法了,对于一般的extjs初学者或是简单应用的开发应该是足够了,后
台开发主要在tab、grid、treegrid的用法比较多,也是比较麻烦的地方,下面直接上代码,大家看看
首页布局:
/**
* 程序主入口
*/
Ext.onReady(function () {
/**
* 上,panel.Panel
*/
this.topPanel = Ext.create('Ext.panel.Panel', {
region: 'north',
height: 55
});
/**
* 左,panel.Panel
*/
this.leftPanel = Ext.create('Ext.panel.Panel', {
region: 'west',
title: '主菜单',
iconCls: 'House',
width: 200,
layout: 'accordion',
collapsible: true
});
/**
* 右,tab.Panel
*/
this.rightPanel = Ext.create('Ext.tab.Panel', {
region: 'center',
layout: 'fit',
id: 'mainContent',
collapisble: true,
tabWidth: 120,
items: [{ title: '首页', html: ' <iframe scrolling="auto" frameborder="0" width="100%" height="100%" src="' + DefaultUrl + '"> </iframe>'}]
});
/**
* 下,panel.Panel
*/
this.bottomPanel = Ext.create('Ext.panel.Panel', {
region: 'south',
layout: 'fit',
id: 'foot',
collapisble: true,
height: 30,
html:'<div>欢迎您光临!</div>'
});
/**
* 组建树
*/
Ext.define('TreeModelExtension', {
extend: 'Ext.data.Model',
//当Model实体类模型被用在某个TreeStore上,并且第一次实例化的时候 ,这些个属性会添加到Model实体类的的原型(prototype )上 (至于上述代码,则是通过把他设置为根节点的时候触发实例化处理的)
fields: [
{name: 'text', type: 'string'},
{name: 'url', type: 'string'}
],
});
var buildTree = function (json) {
return Ext.create('Ext.tree.Panel', {
rootVisible: false,
border: false,
store: Ext.create('Ext.data.TreeStore', {
model:'TreeModelExtension',
root: {
expanded: true,
children: json.children
}
}),
listeners: {
'itemdblclick': function (view, record, item,
index, e) {
var id = record.get('id');
var text = record.get('text');
var iconCls = record.get('iconCls');
var leaf = record.get('leaf');
var url = record.get('url');
if (leaf) { //没有子节点时才创建新的tab显示
var tabs = Ext.getCmp('mainContent'); //获取布局页的Tab组件
var Loadtab = Ext.getCmp(id); //判断Tab是否已经加载
if(Loadtab==undefined){ //未加载则加载
tabs.add({
id:id,
closable: true,
//这种方式采取了加载多个iframe的方式,优化看如何采取一个iframe的方式
html: ' <iframe scrolling="auto" frameborder="0" width="100%" height="100%" src="' + url + '"> </iframe>',
iconCls: iconCls,
title: text
}).show()
}else{ //已加载则设置为活动页
//tabs.setActiveTab(id); //适合于数据量较大,但是不需要实时改变得情况下,直接将已打开的tab设置为活动tab //适合于需要实时展示最新数据的情况,先移除已打开的此tab,然后再重新加载
tabs.remove(id);
tabs.add({
id: id,
closable: true,
//这种方式采取了加载多个iframe的方式,优化看如何采取一个iframe的方式
html: ' <iframe scrolling="auto" frameborder="0" width="100%" height="100%" src="' + url + '"> </iframe>',
iconCls: iconCls,
title: text
}).show()
}
}
},
scope: this
}
});
};
/**
* 加载菜单树
*/
Ext.Ajax.request({
url: AjaxPath, success: function (response) {
var json = Ext.JSON.decode(response.responseText)
Ext.each(json.data, function (el) {
var panel = Ext.create(
'Ext.panel.Panel', {
id: el.id,
title: el.text,
iconCls:el.iconCls,
layout: 'fit'
});
panel.add(buildTree(el));
leftPanel.add(panel);
});
},
failure: function (request) {
Ext.MessageBox.show({
title: '操作提示',
msg: "连接服务器失败",
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.ERROR
});
},
method: 'post'
});
/**
* Viewport
*/
Ext.create('Ext.container.Viewport', {
layout: 'border',
renderTo: Ext.getBody(),
items: [this.topPanel, this.leftPanel, this.rightPanel, this.bottomPanel]
});
});
Grid行内增删改查:
Ext.onReady(function () {
// ExtJS组件自适应浏览器大小改变,看还有没有其他实现方式
Ext.EventManager.onWindowResize(function () {
Ext.ComponentManager.each(function (cmpId, cmp, length) {
if (cmp.hasOwnProperty("renderTo")) {
cmp.doLayout();
}
});
});
var toolbar = Ext.create('Ext.toolbar.Toolbar', {
renderTo: document.body,
items: [
// 使用右对齐容器
'->', // 等同 { xtype: 'tbfill' }
{
xtype: 'textfield',
name: 'roleName',
id: 'roleName',
emptyText: '输入角色名关键字',
listeners: {
specialkey: function (field, e) {
if (e.getKey() == Ext.EventObject.ENTER) {
store.load({ //传递查询条件参数
params: {
roleName: Ext.getCmp('roleName').getValue()
}
});
}
}
}
},
{
// xtype: 'button', // 默认的工具栏类型
text: '查询',
tooltip: '根据数据条件查询数据',
iconCls: "Zoom",
listeners: {
click: function () {
store.load({ //传递查询条件参数
params: {
roleName: Ext.getCmp('roleName').getValue()
}
});
}
}
},
// 添加工具栏项之间的垂直分隔条
'-', // 等同 {xtype: 'tbseparator'} 创建 Ext.toolbar.Separator
{
// xtype: 'button', // 默认的工具栏类型
text: '重置',
tooltip: '清空当前查询条件',
iconCls: "Arrowrotateanticlockwise",
handler: function () { //此事件可以代替click事件
Ext.getCmp('roleName').setValue("");
}
},
]
});
//1.定义Model
Ext.define("BeiDream.model.BeiDream_Role", {
extend: "Ext.data.Model",
fields: [
{ name: 'ID', type: 'int' },
{ name: 'Name', type: 'string' },
{ name: 'Description', type: 'string' },
{ name: 'IsUsed', type: 'boolean', defaultValue: true }
]
});
//2.创建store
var store = Ext.create("Ext.data.Store", {
model: "BeiDream.model.BeiDream_Role",
autoLoad: true,
pageSize: 10,
proxy: {
type: 'ajax',
api: {
read: RoleListUrl, //查询
create: AddUrl, //创建
update: UpdateUrl, //更新,必须真正修改了才会触发
destroy: RemoveUrl //删除
},
reader: {
type: 'json',
root: 'data'
},
writer: {
type: 'json', //默认格式 //MVC下后台使用模型自动进行转换,如果是普通webform,则配置root:'data',encode:'true',这样之后就可以使用request【data】获取
writeAllFields: true, //false只提交修改过的字段
allowSingle: false //默认为true,为true时,一条数据不以数组形式提交,为false时,都以数组形式提交,这样避免了提交了一条数据时,后台是list模型无法接收到数据问题
},
listeners: {
exception: function (proxy, response, operation) {
grid.store.load(); //删除失败,数据重新加载
var resText = Ext.decode(response.responseText);
Ext.MessageBox.show({
title: '服务器端异常',
msg: resText.msg,
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
}
}
}
// sorters: [{
// //排序字段。
// property: 'id'
// }]
});
store.on('beforeload', function (store, options) {
var params = { roleName: Ext.getCmp('roleName').getValue() };
Ext.apply(store.proxy.extraParams, params);
});
var Gridtoolbar = Ext.create('Ext.toolbar.Toolbar', {
renderTo: document.body,
items: [{
text: '新增',
tooltip: '新增一条数据',
iconCls: 'Add',
handler: function () {
RowEditing.cancelEdit();
// Create a model instance
var r = new BeiDream.model.BeiDream_Role();
Ext.getCmp('RoleGrid').getStore().insert(0, r);
RowEditing.startEdit(0, 0);
}
}, '-', {
text: '编辑',
tooltip: '编辑当前选择行数据',
iconCls: 'Pencil',
handler: function () {
RowEditing.cancelEdit();
var data = Ext.getCmp("RoleGrid").getSelectionModel().getSelection();
RowEditing.startEdit(data[0].index, 0);
},
disabled: true
}, '-', {
itemId: 'removeUser',
text: '删除',
tooltip: '可以多选删除多条数据',
iconCls: 'Delete',
handler: function () {
Ext.MessageBox.confirm('提示', '确定删除该记录?', function (btn) {
if (btn != 'yes') {
return;
}
var sm = Ext.getCmp('RoleGrid').getSelectionModel();
RowEditing.cancelEdit();
var store = Ext.getCmp('RoleGrid').getStore();
store.remove(sm.getSelection());
store.sync(); //根据状态执行对应的服务器方法,delete,放在remove后才能成功执行服务器方法
if (store.getCount() > 0) {
sm.select(0);
}
});
},
disabled: true
}, '-', {
itemId: 'gridSync',
text: '保存',
tooltip: '保存到服务器',
iconCls: 'Disk',
handler: function () {
grid.store.sync();
grid.store.commitChanges(); //执行commitChanges()提交数据修改。
}
}, '-', {
itemId: 'gridCancel',
text: '取消',
tooltip: '取消所有的已编辑数据',
iconCls: 'Decline',
handler: function () {
Ext.MessageBox.confirm('提示', '确定取消已编辑数据吗?', function (btn) {
if (btn != 'yes') {
return;
}
grid.store.rejectChanges(); //执行rejectChanges()撤销所有修改,将修改过的record恢复到原来的状态
});
}
}, '-', {
itemId: 'gridrefresh',
text: '刷新',
tooltip: '重新加载数据',
iconCls: 'Arrowrefresh',
handler: function () {
grid.store.load();
}
}, '->', {
itemId: 'ImportExcel',
text: '导入Excel',
tooltip: '导入角色数据',
iconCls: 'Pageexcel',
handler: function () {
Ext.MessageBox.show({
title: '暂未开放',
msg: '即将开放',
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
}
}, '-', {
itemId: 'ExportExcel',
text: '导出Ecxel',
tooltip: '角色数据导出Excel',
iconCls: 'Pageexcel',
handler: function () {
Ext.MessageBox.show({
title: '暂未开放',
msg: '即将开放',
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
}
}
]
});
var RowEditing = Ext.create('Ext.grid.plugin.RowEditing', { // 行编辑模式
clicksToEdit: 2, //双击进行修改 1-单击 2-双击
autoCancel: false,
saveBtnText: '确定',
cancelBtnText: '取消',
errorsText: '错误',
dirtyText: '你要确认或取消更改',
listeners: {
cancelEdit: function (rowEditing, context) {
// Canceling editing of a locally added, unsaved record: remove it
if (context.record.phantom) { //服务器上是否有此条记录的标志,true为没有
store.remove(context.record);
}
},
Edit: function (rowEditing, context) {
//store.sync(); //根据状态执行对应的服务器方法,Add/Edit
var IsValidate = ValidateInput(context.record.data, context.record.phantom);
if (!IsValidate) {
grid.store.rejectChanges();
}
},
validateedit: function (rowEditing, context) {
}
}
});
function ValidateInput(data, IsAdd) {
var IsValidate;
Ext.Ajax.request({
url: ValidateInputUrl,
method: 'POST',
jsonData: data,
params: { IsAdd: IsAdd },
async: false,
success: function (response) {
var resText = Ext.decode(response.responseText);
if (resText.success) {
Ext.MessageBox.alert('警告', resText.msg);
IsValidate = false;
} else {
IsValidate = true;
}
},
failure: function (response, options) {
Ext.MessageBox.alert('服务器异常', response.status);
}
});
return IsValidate;
}
//多选框变化
function selectchange() {
var count = this.getCount();
//删除
if (count == 0) {
Gridtoolbar.items.items[2].disable();
Gridtoolbar.items.items[4].disable();
}
else {
Gridtoolbar.items.items[2].enable();
Gridtoolbar.items.items[4].enable();
}
}
//3.创建grid
var grid = Ext.create("Ext.grid.Panel", {
id: "RoleGrid",
xtype: "grid",
store: store,
columnLines: true,
renderTo: Ext.getBody(),
selModel: {
injectCheckbox: 0,
listeners: {
'selectionchange': selectchange
},
mode: "MULTI", //"SINGLE"/"SIMPLE"/"MULTI"
checkOnly: false //只能通过checkbox选择
},
selType: "checkboxmodel",
columns: [
{ xtype: "rownumberer", text: "序号", width: 40, align: 'center' },
{ id: "id", text: "ID", width: 40, dataIndex: 'ID', sortable: true, hidden: true },
{ text: '角色名称', dataIndex: 'Name', flex: 1, editor: "textfield" },
{ text: '角色描述', dataIndex: 'Description', flex: 1, editor: "textfield" },
{ text: '是否启用', dataIndex: 'IsUsed', flex: 1, xtype: 'checkcolumn', editor: { xtype: 'checkbox', cls: 'x-grid-checkheader-editor'} }
],
plugins: [RowEditing],
listeners: {
itemdblclick: function (me, record, item, index, e, eOpts) {
//双击事件的操作
}
},
tbar: Gridtoolbar,
bbar: { xtype: "pagingtoolbar", store: store, displayInfo: true, emptyMsg: "没有记录" }
});
});
TreeGrid展示:前台代码和后台模型结合
Ext.onReady(function () {
// ExtJS组件自适应浏览器大小改变,看还有没有其他实现方式
Ext.EventManager.onWindowResize(function () {
Ext.ComponentManager.each(function (cmpId, cmp, length) {
if (cmp.hasOwnProperty("renderTo")) {
cmp.doLayout();
}
});
});
Ext.create('Ext.container.Viewport', {
layout: 'border',
renderTo: Ext.getBody(),
items: [{
title: '主菜单模块',
region: 'west',
xtype: 'panel',
margins: '5 0 0 5',
width: 200,
collapsible: true, // 可折叠/展开
id: 'NavigationMenucontainer',
layout: 'fit'
}, {
title: '子菜单列表',
region: 'center', // 必须指定中间区域
xtype: 'panel',
layout: 'fit',
id: 'Gridcontainer',
margins: '5 5 0 0'
}]
});
var NavigationMenu=Ext.getCmp('NavigationMenucontainer');
/**
* 加载菜单树
*/
Ext.Ajax.request({
url: AjaxPath,
success: function (response) {
var json = Ext.JSON.decode(response.responseText)
Ext.each(json.data, function (el) {
var panel = Ext.create(
'Ext.panel.Panel', {
id: el.id,
layout: 'fit'
});
var ShowGrid=CreateGrid(el.id);
Gridcontainer.add(ShowGrid); //初始化,加载主菜单下的菜单
panel.add(buildTree(el));
NavigationMenu.add(panel);
});
},
failure: function (request) {
Ext.MessageBox.show({
title: '操作提示',
msg: "连接服务器失败",
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.ERROR
});
},
method: 'post'
});
var Gridcontainer=Ext.getCmp('Gridcontainer');
/**
* 组建树
*/
Ext.define('TreeModelExtension', {
extend: 'Ext.data.Model',
//当Model实体类模型被用在某个TreeStore上,并且第一次实例化的时候 ,这些个属性会添加到Model实体类的的原型(prototype )上 (至于上述代码,则是通过把他设置为根节点的时候触发实例化处理的)
fields: [
{name: 'text', type: 'string'},
{name: 'url', type: 'string'}
],
});
var buildTree = function (json) {
return Ext.create('Ext.tree.Panel', {
id:'MenuTree',
rootVisible: true,
border: false,
store: Ext.create('Ext.data.TreeStore', {
model:'TreeModelExtension',
root: {
id:json.id,
text:json.text,
iconCls: json.iconCls,
expanded: json.expanded,
children: json.children
}
}),
listeners: {
'itemclick': function (view, record, item,
index, e) {
var ParentID = record.get('id');
var ShowGrid=CreateGrid(ParentID);
Gridcontainer.add(ShowGrid);
},
scope: this
}
});
};
function CreateGrid(ParentID) {
var Gridtoolbar = Ext.create('Ext.toolbar.Toolbar', {
renderTo: document.body,
items: [{
text: '新增',
tooltip: '新增一条数据',
iconCls: 'Add',
handler: function () {
RowEditing.cancelEdit();
// Create a model instance
var r = new BeiDream.model.BeiDream_NavigationMenu();
Ext.getCmp('NavigationMenuGrid').getStore().insert(0, r);
RowEditing.startEdit(0, 0);
}
}, '-', {
text: '编辑',
tooltip: '编辑当前选择行数据',
iconCls: 'Pencil',
handler: function () {
RowEditing.cancelEdit();
var data = Ext.getCmp("NavigationMenuGrid").getSelectionModel().getSelection();
RowEditing.startEdit(data[0].index, 0);
},
disabled: true
}, '-', {
itemId: 'removeUser',
text: '删除',
tooltip: '可以多选删除多条数据',
iconCls: 'Delete',
handler: function () {
Ext.MessageBox.confirm('提示', '确定删除该记录?', function (btn) {
if (btn != 'yes') {
return;
}
var sm = Ext.getCmp('NavigationMenuGrid').getSelectionModel();
RowEditing.cancelEdit();
var store = Ext.getCmp('NavigationMenuGrid').getStore();
store.remove(sm.getSelection());
store.sync(); //根据状态执行对应的服务器方法,delete,放在remove后才能成功执行服务器方法
if (store.getCount() > 0) {
sm.select(0);
}
});
},
disabled: true
}, '-', {
itemId: 'gridSync',
text: '保存',
tooltip: '保存到服务器',
iconCls: 'Disk',
handler: function () {
var grid=Ext.getCmp('NavigationMenuGrid');
grid.store.sync();
grid.store.commitChanges(); //执行commitChanges()提交数据修改。
}
}, '-', {
itemId: 'gridCancel',
text: '取消',
tooltip: '取消所有的已编辑数据',
iconCls: 'Decline',
handler: function () {
Ext.MessageBox.confirm('提示', '确定取消已编辑数据吗?', function (btn) {
if (btn != 'yes') {
return;
}
var grid=Ext.getCmp('NavigationMenuGrid');
grid.store.rejectChanges(); //执行rejectChanges()撤销所有修改,将修改过的record恢复到原来的状态
});
}
}, '-', {
itemId: 'gridrefresh',
text: '刷新',
tooltip: '重新加载数据',
iconCls: 'Arrowrefresh',
handler: function () {
var grid=Ext.getCmp('NavigationMenuGrid');
grid.store.load();
}
}
]
});
var RowEditing = Ext.create('Ext.grid.plugin.RowEditing', { // 行编辑模式
clicksToEdit: 2, //双击进行修改 1-单击 2-双击
autoCancel: false,
saveBtnText: '确定',
cancelBtnText: '取消',
errorsText: '错误',
dirtyText: '你要确认或取消更改',
listeners: {
// beforeedit: function (rowEditing,e,context) {
// if(e.colldx==2 && e.record.data.IsLeaf==false){
// return false;
// }else{
// return true;
// }
// },
cancelEdit: function (rowEditing, context) {
// Canceling editing of a locally added, unsaved record: remove it
if (context.record.phantom) { //服务器上是否有此条记录的标志,true为没有
store.remove(context.record);
}
},
Edit: function (rowEditing, context) {
//store.sync(); //根据状态执行对应的服务器方法,Add/Edit
//var IsValidate = ValidateInput(context.record.data, context.record.phantom);
// if (!IsValidate) {
// grid.store.rejectChanges();
// }
}
}
});
function ValidateInput(data, IsAdd) {
var IsValidate;
Ext.Ajax.request({
url: ValidateInputUrl,
method: 'POST',
jsonData: data,
params: { IsAdd: IsAdd },
async: false,
success: function (response) {
var resText = Ext.decode(response.responseText);
if (resText.success) {
Ext.MessageBox.alert('警告', resText.msg);
IsValidate = false;
} else {
IsValidate = true;
}
},
failure: function (response, options) {
Ext.MessageBox.alert('服务器异常', response.status);
}
});
return IsValidate;
}
//多选框变化
function selectchange() {
var count = this.getCount();
//删除
if (count == 0) {
Gridtoolbar.items.items[2].disable();
Gridtoolbar.items.items[4].disable();
}
else {
Gridtoolbar.items.items[2].enable();
Gridtoolbar.items.items[4].enable();
}
}
//1.定义Model
Ext.define("BeiDream.model.BeiDream_NavigationMenu", {
extend: "Ext.data.Model",
fields: [
{ name: 'ID', type: 'int' },
{ name: 'ParentID', type: 'int' },
{ name: 'ShowName', type: 'string', defaultValue: '名称......' },
{ name: 'IsLeaf', type: 'boolean', defaultValue: true },
{ name: 'url', type: 'string' },
{ name: 'OrderNo', type: 'int', defaultValue: 1 },
{ name: 'iconCls', type: 'string' },
{ name: 'Expanded', type: 'boolean', defaultValue: false }
]
});
//2.创建store
var store = Ext.create("Ext.data.Store", {
model: "BeiDream.model.BeiDream_NavigationMenu",
autoLoad: true,
pageSize: 15,
proxy: {
type: 'ajax',
api: {
read: MenuListUrl, //查询
create: AddUrl, //创建
update: UpdateUrl, //更新,必须真正修改了才会触发
destroy: RemoveUrl //删除
},
reader: {
type: 'json',
root: 'data'
},
writer: {
type: 'json', //默认格式 //MVC下后台使用模型自动进行转换,如果是普通webform,则配置root:'data',encode:'true',这样之后就可以使用request【data】获取
writeAllFields: true, //false只提交修改过的字段
allowSingle: false //默认为true,为true时,一条数据不以数组形式提交,为false时,都以数组形式提交,这样避免了提交了一条数据时,后台是list模型无法接收到数据问题
},
listeners: {
exception: function (proxy, response, operation) {
// var grid=Ext.getCmp('NavigationMenuGrid');
// grid.store.load(); //删除失败,数据重新加载
var resText = Ext.decode(response.responseText);
Ext.MessageBox.show({
title: '服务器端异常',
msg: resText.msg,
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
}
}
}
});
store.on('beforeload', function (store, options) {
var params = { ParentID: ParentID };
Ext.apply(store.proxy.extraParams, params);
});
return Ext.create("Ext.grid.Panel", {
id: "NavigationMenuGrid",
xtype: "grid",
store: store,
columnLines: true,
selModel: {
injectCheckbox: 0,
listeners: {
'selectionchange': selectchange
},
mode: "SINGLE", //"SINGLE"/"SIMPLE"/"MULTI"
checkOnly: false //只能通过checkbox选择
},
selType: "checkboxmodel",
columns: [
{ xtype: "rownumberer", text: "序号", width: 40, align: 'center' },
{ id: "id", text: "ID", width: 40, dataIndex: 'ID', sortable: true, hidden: true },
{ id: "id", text: "ParentID", width: 40, dataIndex: 'ParentID', sortable: true, hidden: true },
{ text: '名称', dataIndex: 'ShowName', flex: 1, editor: {
xtype: 'textfield',
allowBlank: false
} },
{ text: '是否为模块', dataIndex: 'IsLeaf', flex: 1, xtype: 'checkcolumn', editor: { xtype: 'checkbox', cls: 'x-grid-checkheader-editor'} },
{ text: '控制器路径', dataIndex: 'url', flex: 1, editor : {
xtype: 'combobox',
editable:false,
listeners: {
//点击下拉列表事件
expand: function (me, event, eOpts) {
var grid=Ext.getCmp('NavigationMenuGrid');
var record = grid.getSelectionModel().getLastSelected();
if(record!=null){
if(record.data.IsLeaf==true){
currentComboBox = me;
f_openSelectControllerWin();
}else{
Ext.MessageBox.alert('警告', '只有模块才拥有控制器!');
}
}
}
}
} },
{ text: '排序号', dataIndex: 'OrderNo',align:"center", width: 48, flex: 1,editor: {
xtype: 'numberfield',
allowBlank: false,
minValue: 1,
maxValue: 150000
} },
{ text: '图标', dataIndex: 'iconCls',align:"center", width: 48,renderer : function(value) {
return "<div Align='center' style='height:16px;width:16px' class="+value+"></div>";
} ,editor : {
xtype: 'combobox',
editable:false,
listeners: {
//点击下拉列表事件
expand: function (me, event, eOpts) {
currentComboBox = me;
f_openIconsWin();
}
}
} },
{ text: '是否展开', dataIndex: 'Expanded', flex: 1, xtype: 'checkcolumn', editor: { xtype: 'checkbox', cls: 'x-grid-checkheader-editor'} }
],
plugins: [RowEditing],
tbar: Gridtoolbar,
bbar: { xtype: "pagingtoolbar", store: store, displayInfo: true, emptyMsg: "没有记录" }
});
};
});
要点二:后台MVC的传参绑定,返回值自定义
MVC方便了Ajax的异步实现,并且方便的模型传参,代码如下
/// <summary>
/// 返回数据库新增后的实体,供前台的extjs的 grid的store更新数据,这样就不需要进行重新加载store了,删,改类似
/// </summary>
/// <param name="Roles"></param>
/// <returns></returns>
[Anonymous]
[HttpPost]
public ActionResult Add(List<BeiDream_Role> Roles)
{
List<BeiDream_Role> AddRoles = new List<BeiDream_Role>();
List<Object> ListObj = RoleService.Add(Roles);
if (ListObj.Count == )
{
List<string> msg = new List<string>();
msg.Add("添加角色失败!");
return this.ExtjsJsonResult(false, msg);
}
else
{
foreach (var item in ListObj)
{
AddRoles.Add(RoleService.GetModelByID(item));
}
List<string> msg = new List<string>();
msg.Add("添加角色成功!");
return this.ExtjsJsonResult(true, AddRoles, msg);
} }
可以看到我直接通过后台模型来接收前台传递过来的参数,而不需要去一一解析参数值
返回值自定义:重写了ActionResult,实现了extjs需要的返回值
/// <summary>
/// 扩展的jsonResult模型,适用于extjs需要的json数据类型
/// </summary>
public class JsonResultExtension:ActionResult
{
public bool success { get; set; }
public string msg { get; set; }
public object data { get; set; }
public long? total { get; set; }
public Dictionary<string, string> errors { get; set; }
/// <summary>
/// 是否序列化为extjs需要的json格式,否则进行普通序列化
/// </summary>
public bool ExtjsUISerialize { get; set; }
public override void ExecuteResult(ControllerContext context)
{ if (context == null)
{
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = "application/json"; StringWriter sw = new StringWriter();
//IsoDateTimeConverter timeFormat = new IsoDateTimeConverter();
//timeFormat.DateTimeFormat = "yyyy-MM-dd HH:mm:ss";
IsoDateTimeConverter timeFormat = new IsoDateTimeConverter();
timeFormat.DateTimeFormat = "yyyy-MM-dd";
JsonSerializer serializer = JsonSerializer.Create(
new JsonSerializerSettings
{
Converters = new JsonConverter[] { timeFormat },
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore //忽略为null的值序列化 }
); using (JsonWriter jsonWriter = new JsonTextWriter(sw))
{
jsonWriter.Formatting = Formatting.Indented; if (ExtjsUISerialize)
serializer.Serialize(jsonWriter, this);
else
serializer.Serialize(jsonWriter, data);
}
response.Write(sw.ToString()); }
}
特性标注及权限验证,代码如下
/// <summary>
/// 权限拦截
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class PermissionFilterAttribute : ActionFilterAttribute
{
/// <summary>
/// 权限拦截
/// </summary>
/// <param name="filterContext"></param>
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!this.CheckAnonymous(filterContext))
{
//未登录验证
if (SessionHelper.Get("UserID") == null)
{
//跳转到登录页面
filterContext.RequestContext.HttpContext.Response.Redirect("~/Admin/User/Login");
}
}
}
/// <summary>
/// [Anonymous标记]验证是否匿名访问
/// </summary>
/// <param name="filterContext"></param>
/// <returns></returns>
public bool CheckAnonymous(ActionExecutingContext filterContext)
{
//验证是否是匿名访问的Action
object[] attrsAnonymous = filterContext.ActionDescriptor.GetCustomAttributes(typeof(AnonymousAttribute), true);
//是否是Anonymous
var Anonymous = attrsAnonymous.Length == ;
return Anonymous;
}
}
通过写一个BaseController来进行权限的验证,这样就不需要所有需要验证的Controller加标注了,当然BaseController还可以增加其他通用的处理
/// <summary>
/// Admin后台系统公共控制器(需要验证的模块)
/// </summary>
[PermissionFilter]
public class BaseController:Controller
{ }
要点三:轻量级ORM框架PetaPoco
非侵入性ORM框架,只需要一个PetaPoco.cs文件就OK了,不过我对其进行了再次封装,实现了工作单元,还是上代码吧
一:封装
public interface IDataRepository<TEntity> : IDependency where TEntity : class
{
#region 属性 /// <summary>
/// 获取 当前实体的查询数据集
/// </summary>
Database PetaPocoDB { get; } #endregion #region 公共方法 /// <summary>
/// 插入实体记录
/// </summary>
/// <param name="entity"> 实体对象 </param>
/// <returns> 操作影响的行数 </returns>
bool Add(TEntity entity); /// <summary>
/// 批量插入实体记录集合
/// </summary>
/// <param name="entities"> 实体记录集合 </param>
/// <returns> 操作影响的行数 </returns>
List<object> Add(IEnumerable<TEntity> entities); /// <summary>
/// 删除实体记录
/// </summary>
/// <param name="entity"> 实体对象 </param>
/// <returns> 操作影响的行数 </returns>
int Delete(TEntity entity); /// <summary>
/// 删除实体记录集合
/// </summary>
/// <param name="entities"> 实体记录集合 </param>
/// <returns> 操作影响的行数 </returns>
bool Delete(IEnumerable<TEntity> entities); /// <summary>
/// 更新实体记录
/// </summary>
/// <param name="entity"> 实体对象 </param>
/// <returns> 操作影响的行数 </returns>
int Update(TEntity entity); /// <summary>
/// 更新实体记录
/// </summary>
/// <param name="entity"> 实体对象 </param>
/// <returns> 操作影响的行数 </returns>
bool Update(IEnumerable<TEntity> entities);
/// <summary>
/// 根据主键ID获取实体
/// </summary>
/// <param name="KeyID">主键ID</param>
/// <returns>实体</returns>
TEntity GetModelByID(object KeyID); /// <summary>
/// 动态查询,返回dynamic类型的列表
/// 请使用标准SQL语句进行查询(SELECT ... FROM ...)
/// </summary>
/// <returns></returns>
PagedList<dynamic> DynamicPagedList(int pageIndex, int pageSize, Sql sql); PagedList<TEntity> PagedList(int pageIndex, int pageSize, string sql, params object[] args); PagedList<TEntity> PagedList(int pageIndex, int pageSize, Sql sql); PagedList<TDto> PagedList<TDto>(int pageIndex, int pageSize, string sql, params object[] args); PagedList<TDto> PagedList<TDto>(int pageIndex, int pageSize, Sql sql);
#endregion
}
二:使用,具体封装和使用,大家还是去下载源码看吧
public class NavigationMenuService : DbContextBase<BeiDream_NavigationMenu>, INavigationMenuService, IDependency
{
public NavigationMenuService(IUnitOfWork unitOfWork)
: base(unitOfWork)
{ }
public List<NavigationMenu> GetNavigationMenu(int id)
{
var sql = Sql.Builder.Where("ParentID=@0",id);
sql.OrderBy("OrderNo ASC"); //默认ASC升序,降序为DESC
List<BeiDream_NavigationMenu> List = this.PetaPocoDB.Fetch<BeiDream_NavigationMenu>(sql);
return AutoMapperHelper.GetMapper(List);
}
public List<NavigationMenu> GetNavigationMenuNoLeaf(int id)
{
var sql = Sql.Builder.Where("ParentID=@0", id);
sql.Where("IsLeaf=@0", false);
sql.OrderBy("OrderNo ASC"); //默认ASC升序,降序为DESC
List<BeiDream_NavigationMenu> List = this.PetaPocoDB.Fetch<BeiDream_NavigationMenu>(sql);
return AutoMapperHelper.GetMapper(List);
}
/// <summary>
/// 递归查询产品分类列表
/// </summary>
/// <param name="list">父级产品分类列表</param>
public void GetNavigationMenus(ref List<NavigationMenu> list)
{
foreach (NavigationMenu season in list)
{
List<NavigationMenu> lstSeason = GetNavigationMenu(season.id);
season.children = lstSeason;
if (list.Count > )
{
GetNavigationMenus(ref lstSeason);
}
}
}
/// <summary>
/// 递归查询产品分类列表
/// </summary>
/// <param name="list">父级产品分类列表</param>
public void GetNavigationMenusNoLeaf(ref List<NavigationMenu> list)
{
foreach (NavigationMenu season in list)
{
List<NavigationMenu> lstSeason = GetNavigationMenuNoLeaf(season.id);
season.children = lstSeason;
if (list.Count > )
{
GetNavigationMenusNoLeaf(ref lstSeason);
}
}
}
}
要点四:依赖注入框架Autofac
目前使用心得最大的好处就是不需要配置即实现了面向接口编程,特别是和MVC结合,实现构造函数注入就更加方便了,当然它还有其他
功能,比如生命周期唯一实例,单例啊等等,暂时还研究不深,只是简单应用,大家看看具体实现吧
private static void AutofacMvcRegister()
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(DbContextBase<>)).As(typeof(IDataRepository<>));
Type baseType = typeof(IDependency);
Assembly[] assemblies = Assembly.GetExecutingAssembly().GetReferencedAssemblies()
.Select(Assembly.Load).ToArray();
assemblies = assemblies.Union(new[] { Assembly.GetExecutingAssembly() }).ToArray();
builder.RegisterAssemblyTypes(assemblies)
.Where(type => baseType.IsAssignableFrom(type) && !type.IsAbstract)
.AsImplementedInterfaces().InstancePerLifetimeScope();//InstancePerLifetimeScope 保证生命周期基于请求 //无效
//builder.RegisterType<DefaultCacheAdapter>().PropertiesAutowired().As<ICacheStorage>(); builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterFilterProvider();
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
要点五;对象关系映射AutoMapper
目前也只是简单应用,先看代码,它是如何简化我们的工作量的
public static List<NavigationMenu> GetMapper(List<BeiDream_NavigationMenu> List)
{
List<NavigationMenu> NavigationMenuList = new List<NavigationMenu>();
foreach (var item in List)
{
//NavigationMenu DaoModel = new NavigationMenu();
//DaoModel.id = item.ID;
//DaoModel.text = item.ShowName;
//DaoModel.leaf = item.IsLeaf;
//DaoModel.url = item.url;
//DaoModel.Expanded = item.Expanded;
//DaoModel.children = null;
NavigationMenu DaoModel = item.ToDestination<BeiDream_NavigationMenu, NavigationMenu>();
NavigationMenuList.Add(DaoModel);
}
return NavigationMenuList;
}
注释掉的是不使用automapper之前的代码,没注释掉的是使用automapper,扩展了方法直接一句代码实现转化,是不是很easy,当然实
现这些之前,我们需要给他定义规则,然后还要注册,代码如下,具体的请看源码
public class NavigationMenuProfile : Profile
{
protected override void Configure()
{
CreateMap<BeiDream_NavigationMenu, NavigationMenu>()
.ForMember(dest => dest.id, opt => opt.MapFrom(src => src.ID))
.ForMember(dest => dest.text, opt => opt.MapFrom(src => src.ShowName))
.ForMember(dest => dest.leaf, opt => opt.MapFrom(src => src.IsLeaf));
}
}
要点六:数据验证
extjs前台验证我们已经做了,但是客户端传来的东西我们不能完全相信,后台需要再次验证,我们看到mvc的官方demo。一句话就实现
了验证,我们是不是可以自己做验证呢,看代码
[Anonymous]
public ActionResult SaveUser(BeiDream_User model, List<int> Roles)
{
var ValidateResult = Validation.Validate(model);//服务器端的验证
if (ValidateResult.IsValid) //验证成功
{
bool IsExitUser = UserService.PetaPocoDB.Exists<BeiDream_User>(model.ID);
if (!IsExitUser)
{
FilterGroup userRoleGroup = new FilterGroup();
FilterHelper.CreateFilterGroup(userRoleGroup, null, "UserName", model.UserName, GroupOperatorQueryEnum.and, RuleOperatorQueryEnum.equal);
bool IsExist = UserService.IsExist(userRoleGroup);
if (IsExist)
{
List<string> errorName=new List<string>();
errorName.Add("UserName");
ValidationResult error = new ValidationResult("已存在相同的用户名", errorName);
ValidateResult.Add(error);
return this.ExtjsFromJsonResult(false,ValidateResult);
}
else
{
bool IsSaveSuccess = TransactionService.AddUserAndUserRole(model, Roles);
List<string> msg = new List<string>();
msg.Add(IsSaveSuccess ? "用户信息保存成功!" : "用户信息保存失败!");
return this.ExtjsFromJsonResult(true, null, msg);
}
}
else
{
FilterGroup userRoleGroup = new FilterGroup();
FilterHelper.CreateFilterGroup(userRoleGroup, null, "UserName", model.UserName, GroupOperatorQueryEnum.and, RuleOperatorQueryEnum.equal);
FilterHelper.CreateFilterGroup(userRoleGroup, null, "ID", model.ID, GroupOperatorQueryEnum.and, RuleOperatorQueryEnum.notequal);
bool IsExist = UserService.IsExist(userRoleGroup);
if (IsExist)
{
List<string> errorName = new List<string>();
errorName.Add("UserName");
ValidationResult error = new ValidationResult("已存在相同的用户名", errorName);
ValidateResult.Add(error);
return this.ExtjsFromJsonResult(false, ValidateResult);
}
else
{
bool IsSaveSuccess = TransactionService.UpdateUserAndUserRole(model, Roles);
List<string> msg = new List<string>();
msg.Add(IsSaveSuccess ? "用户信息保存成功!" : "用户信息保存失败!");
return this.ExtjsFromJsonResult(true, null, msg);
}
}
}
else
{
return this.ExtjsFromJsonResult(false,ValidateResult); //验证失败,返回失败的验证结果,给出前台提示信息
}
}
大家可以看到前台传进来的参数,我们先进行验证 var ValidateResult = Validation.Validate(model),验证的条件我们是在模型上定义好的,然后判断验证是否通过,通过进行下一步动作,不通过,把验证的结果信息返回前台,提示给用户
要点七:SQL翻译机
这个只能算是一个简单的东西吧,并且感觉用起来麻烦,但是我觉得用的熟练了,还是很不错的,只是省了手拼SQL的问题嘛,减少了出
错几率,具体使用还是看代码吧
[Anonymous]
public ActionResult GetUserList(int page, int start, int limit, string UserKeyName, string RoleID)
{
RemoveSelectId();
PagedList<BeiDream_User> PageList = null;
FilterGroup userGroup = GetQueryConditions(UserKeyName, RoleID);
PageList = UserService.GetPagedList(page, limit, userGroup);
return this.ExtjsGridJsonResult(PageList, PageList.TotalItemCount);
}
private FilterGroup GetQueryConditions(string UserKeyName, string RoleID)
{
FilterGroup userGroup = new FilterGroup();
if (!string.IsNullOrEmpty(RoleID)) //用户角色不为空时
{
FilterGroup userRoleGroup = new FilterGroup();
FilterHelper.CreateFilterGroup(userRoleGroup, null, "RoleID", RoleID, GroupOperatorQueryEnum.and, RuleOperatorQueryEnum.equal);
//先根据用户角色查出对应的用户ID
List<BeiDream_User_Role> List = UserRoleService.GetList(userRoleGroup);
if (List.Count != ) //todo,此角色信息为空情况下,查到的用户也应该为空,目前未处理
{
if (string.IsNullOrEmpty(UserKeyName))
{
//再根据此用户角色下的用户ID,因为查出所以用户ID,查询条件是或的关系GroupOperatorQueryEnum.or,翻译出对应用户的查询条件,最后查出对应用户
foreach (var item in List)
{
FilterHelper.CreateFilterGroup(userGroup, null, "ID", item.UserID, GroupOperatorQueryEnum.or, RuleOperatorQueryEnum.equal);
}
}
else
{
//先翻译出用户名查询条件,与其他查询条件是与的关系GroupOperatorQueryEnum.and
FilterHelper.CreateFilterGroup(userGroup, null, "UserName", UserKeyName, GroupOperatorQueryEnum.and, RuleOperatorQueryEnum.like);
//因为第二个查询条件是多个查询条件的结合组成再与第一个查询条件结合,故放到子FilterGroup中
List<FilterGroup> filterGroups = new List<FilterGroup>();
FilterGroup userIDGroup = new FilterGroup();
//再根据此用户角色下的用户ID,因为查出所以用户ID,查询条件是或的关系GroupOperatorQueryEnum.or,翻译出对应用户的查询条件,最后查出对应用户
foreach (var item in List)
{
FilterHelper.CreateFilterGroup(userIDGroup, null, "ID", item.UserID, GroupOperatorQueryEnum.or, RuleOperatorQueryEnum.equal);
}
filterGroups.Add(userIDGroup);
userGroup.groups = filterGroups;
}
}
}
else
{
if (!string.IsNullOrEmpty(UserKeyName))
{
//先翻译出用户名查询条件,与其他查询条件是与的关系GroupOperatorQueryEnum.and
FilterHelper.CreateFilterGroup(userGroup, null, "UserName", UserKeyName, GroupOperatorQueryEnum.and, RuleOperatorQueryEnum.like);
}
}
return userGroup;
}
要点八:缓存
缓存也就简单应用Helper级别,主要用了.net自带缓存和分布式Memcached缓存,一个接口,两个实现
/// <summary>
/// 缓存接口
/// </summary>
public interface ICacheStorage
{
#region 缓存操作
/// <summary>
/// 添加缓存
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
void Insert(string key, object value);
/// <summary>
/// 添加缓存(默认滑动时间为20分钟)
/// </summary>
/// <param name="key">key</param>
/// <param name="value">value</param>
/// <param name="expiration">绝对过期时间</param>
void Insert(string key, object value, DateTime expiration);
/// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">key</param>
/// <param name="value">value</param>
/// <param name="expiration">过期时间</param>
void Insert(string key, object value, TimeSpan expiration);
/// <summary>
/// 获得key对应的value
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
object Get(string key);
/// <summary>
/// 根据key删除缓存
/// </summary>
/// <param name="key"></param>
void Remove(string key);
/// <summary>
/// 缓存是否存在key的value
/// </summary>
/// <param name="key">key</param>
/// <returns></returns>
bool Exist(string key);
/// <summary>
/// 获取所有的缓存key
/// </summary>
/// <returns></returns>
List<string> GetCacheKeys();
/// <summary>
/// 清空缓存
/// </summary>
void Flush(); #endregion
}
写在最后
写博客真的是很累人的事,很敬佩那些能写连载博客的牛人们,虽然自己做的项目很小,但是觉得写成博客,要写的要点还是很多的
,上面我讲的很粗略,但是主要的知识点都讲出来了,这个项目其实没有做完,不打算再继续了,打算换了,接下来打算使用easyui
+knockout+ef来写一个完整的权限管理系统,涉及菜单权限、按钮权限、字段权限等等吧,路很长.....任重而道远
最后,大家如果觉得有帮助,请点推荐哦!源码下载地址:
extjs+MVC4+PetaPoco+AutoFac+AutoMapper后台管理系统(附源码)的更多相关文章
- springboot+mybatis+bootstrap开发员工oa后台管理系统项目源码
java项目源码详情描述:S020<springboot+mybatis+bootstrap开发员工oa后台管理系统项目源码>jboa项目有请假以及报销单的申请和审核session共享加登 ...
- 基于S2SH开发学生考勤管理系统 附源码
开发环境: Windows操作系统开发工具:Eclipse+Jdk+Tomcat+mysql数据库 运行效果图 源码及原文链接:http://javadao.xyz/forum.php?mod=vie ...
- JavaSwing开发简单的银行管理系统 附源码
开发环境: Windows操作系统开发工具: MyEclipse/Eclipse+Jdk+mysql数据库 运行效果图:
- 基于SSH开发银行个人业务管理系统 附源码
开发环境: Windows操作系统开发工具: MyEclipse+Jdk+Tomcat+MySql数据库 运行效果图
- 基于Struts2+Hibernate开发小区物业管理系统 附源码
开发环境: Windows操作系统开发工具: MyEclipse+Jdk+Tomcat+MySql数据库 运行效果图: 源码及原文链接:https://javadao.xyz/forum.php?mo ...
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(32)-swfupload多文件上传[附源码]
原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(32)-swfupload多文件上传[附源码] 文件上传这东西说到底有时候很痛,原来的asp.net服务器 ...
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(6)-Unity 2.x依赖注入by运行时注入[附源码]
原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(6)-Unity 2.x依赖注入by运行时注入[附源码] Unity 2.x依赖注入(控制反转)IOC,对 ...
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(2)-easyui构建前端页面框架[附源码]
原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(2)-easyui构建前端页面框架[附源码] 开始,我们有了一系列的解决方案,我们将动手搭建新系统吧. 用 ...
- Ext.NET 4.1 系统框架的搭建(后台) 附源码
Ext.NET 4.1 系统框架的搭建(后台) 附源码 代码运行环境:.net 4.5 VS2013 (代码可直接编译运行) 预览图: 分析图: 上面系统的构建包括三块区域:North.West和C ...
随机推荐
- LINQPad 编译调试C#代码的工具推荐
LinqPad介绍 学习C#代码的好帮手,很容易调试C#代码片段. LINQPad 4 支持.NET Framework 4.0 / 4.5 ,专业调试LINQ,lambda等特性,完全取代Snipp ...
- IM
一.IM技术概念 IM技术全称Instant Messaging,中文翻译"即时通讯",它是一种使人们能在网上识别在线用户并与他们实时交换消息的技术,是电子邮件发明以来迅速崛起的在 ...
- luogu P2016 战略游戏
嘟嘟嘟 树形dp水题啦. 刚开始以为和[SDOI2006]保安站岗这道题一样,然后交上去WA了. 仔细想想还是有区别的,一个是能看到相邻点,一个是能看到相邻边.对于第一个,可以(u, v)两个点都不放 ...
- 安装最新版的wampserver,可以兼容php5和php7
本文介绍的wamp是Windows+Apache+MySQL+PHP+phpMyAdmin,主要应用于开发环境[一键安装包,简单好用]. 这是运行在Windows系统下的官方安装包,可以快速的搭建属于 ...
- mybatis查询缓存
一级缓存针对每个sqlSession进行缓存,sqlSession销毁,一级缓存就不存在. ,使用Map存储了sql执行查询结果集(java对象) 二级缓存针对每个map的namespace进行缓存. ...
- Word中高效输入公式:内嵌公式和Mathtype
Word中高效输入公式:内嵌公式和Mathtype 前言:对于理工科学生而言,公式输入必不可缺.LaTeX相比Word,在公式输入及排版方面更强大.但是对于轻量级的任务,用Word而言更加轻便(起码不 ...
- IP黑白名单
防攻击可以增加IP白名单/etc/hosts.allow和黑名单/etc/hosts.deny /etc/hosts.allow IP白名单 /etc/hosts.deny IP黑名单 /etc ...
- 给windows添加路由
route add 10.0.0.0 mask 255.0.0.0 172.16.1.253 -p
- 十七、IntelliJ IDEA 中的 Maven 项目初体验及搭建 Spring MVC 框架
我们已经将 IntelliJ IDEA 中的 Maven 项目的框架搭建完成.接着上文,在本文中,我们更近一步,利用 Tomcat 运行我们的 Web 项目. 如上图所示,我们进一步扩展了项目的结构, ...
- 使用jQuery实现option的上移和下移
基本思路: 上移:(1)获取当前选中的元素的索引值 (2)判断当前元素是否为第一个元素 (3)如果是,则不执行上移操作,如果不是,则则调用ins ...