转自:http://www.it165.net/pro/html/201311/7616.html

使用JsPlumb绘制拓扑图的通用方法

一、 实现目标

绘制拓扑图, 实际上是个数据结构和算法的问题。 需要设计一个合适的数据结构来表达拓扑结构,设计一个算法来计算拓扑节点的位置及连接。


 

二、 实现思想

1. 数据结构

首先, 从节点开始。 显然, 需要一个字段 type 表示节点类型, 一个字段 data 表示节点数据(详情), 对于连接, 则采用一个 rel 字段, 表示有哪些节点与之关联, 相当于C 里面的指针。 为了唯一标识该节点, 还需要一个字段 key 。 通过 type-key 组合来唯一标识该节点。 这样, 初步定下数据结构如下:

a. 节点数据结构: node = { type: 'typeName', key: 'key', rel: [], data: {'More Info'}}
b. rel, data 可选 , type-key 唯一标识该节点, rel 为空标识该节点为叶子节点
c. 关联关系: rel: [node1, node2, ..., nodeN]
d. 更多详情: 关于节点的更多信息可放置于此属性中

2. 算法

在算法上, 要预先规划好各个节点类型如何布局以及如何连接。 连接方向很容易定: 根据起始节点及终止节点的类型组合, 可以规定不同的连接方向。 位置确定稍有点麻烦。 这里采用的方法是: 采用深度遍历方法, 下一个的节点位置通过上一个节点位置确定, 不同类型的节点位置计算不一样, 但是相同类型的节点位置是重合的, 需要在后面进行调整。实际上, 这个节点位置的算法是不够高明的, 如果有更好的算法, 请告知。

3. JsPlumb

jsPlumb 有几个基本概念。 首先, 拓扑节点实际上是 DIV 区域,每个DIV 都必须有一个ID,用于唯一标识该节点。 连接拓扑节点的一个重要概念是EndPoint . EndPoint 是附着于节点上的连接线的端点, 简称“附着点”。 将附着点 attach 到指定拓扑节点上的方法如下:

jsPlumb.addEndpoint(toId, this.sourceEndpoint, { anchor: sourceAnchor, uuid:sourceUUID });

toId 是 拓扑节点的 DIV 区域的 ID 值, sourceEndpoint 是附着点的样式设置, 可以复用 , sourceAnchor 是附着点位置, 共有八种:

Top (also aliased as TopCenter) - TopRight - Right (also aliased as RightMiddle) - BottomRight - Bottom (also aliased asBottomCenter) -BottomLeft - Left (also aliased as LeftMiddle) - TopLeft

sourceUUID 是拓扑节点与附着位置的结合, 也就是说, 要将一个 附着点附着到拓扑节点为 toId 的 sourceAnchor 指定的位置上。 每个拓扑节点都可以定义多个源附着点和目标附着点。 源附着点是连接线的起始端, 目标附着点是连接线的终止端。

两个 uuid 即可定义一条连接线:

jsPlumb.connect({uuids:[startPoint, endPoint], editable: false});

startPoint 和 endPoint 分别是连接线的起始端 Endpoint uuid 和 终止段 Endpoint uuid. 它定义了从起始拓扑节点的指定附着点连接到终止拓扑节点的指定附着点。

三、 实现代码

drawTopo.js 提供绘制拓扑图的基本方法, 只要按照数据结构扔进去, 就可以自动绘制出拓扑图来。

 /**
* 使用 jsPlumb 根据指定的拓扑数据结构绘制拓扑图
* 使用 drawTopo(topoData, nodeTypeArray) 方法
*
*/ /**
* 初始化拓扑图实例及外观设置
*/
(function() { jsPlumb.importDefaults({ DragOptions : { cursor: 'pointer', zIndex:2000 }, EndpointStyles : [{ fillStyle:'#225588' }, { fillStyle:'#558822' }], Endpoints : [ [ "Dot", { radius:2 } ], [ "Dot", { radius: 2 } ]], ConnectionOverlays : [
[ "Arrow", { location:1 } ],
[ "Label", {
location:0.1,
id:"label",
cssClass:"aLabel"
}]
]
}); var connectorPaintStyle = {
lineWidth: 1,
strokeStyle: "#096EBB",
joinstyle:"round",
outlineColor: "#096EBB",
outlineWidth: 1
}; var connectorHoverStyle = {
lineWidth: 2,
strokeStyle: "#5C96BC",
outlineWidth: 2,
outlineColor:"white"
}; var endpointHoverStyle = {
fillStyle:"#5C96BC"
}; window.topoDrawUtil = { sourceEndpoint: {
endpoint:"Dot",
paintStyle:{
strokeStyle:"#1e8151",
fillStyle:"transparent",
radius: 2,
lineWidth:2
},
isSource:true,
maxConnections:-1,
connector:[ "Flowchart", { stub:[40, 60], gap:10, cornerRadius:5, alwaysRespectStubs:true } ],
connectorStyle: connectorPaintStyle,
hoverPaintStyle: endpointHoverStyle,
connectorHoverStyle: connectorHoverStyle,
dragOptions:{},
overlays:[
[ "Label", {
location:[0.5, 1.5],
label:"",
cssClass:"endpointSourceLabel"
} ]
]
}, targetEndpoint: {
endpoint: "Dot",
paintStyle: { fillStyle:"#1e8151",radius: 2 },
hoverPaintStyle: endpointHoverStyle,
maxConnections:-1,
dropOptions:{ hoverClass:"hover", activeClass:"active" },
isTarget:true,
overlays:[
[ "Label", { location:[0.5, -0.5], label:"", cssClass:"endpointTargetLabel" } ]
]
}, initConnection: function(connection) {
connection.getOverlay("label").setLabel(connection.sourceId + "-" + connection.targetId);
connection.bind("editCompleted", function(o) {
if (typeof console != "undefined")
console.log("connection edited. path is now ", o.path);
});
}, addEndpoints: function(toId, sourceAnchors, targetAnchors) {
for (var i = 0; i < sourceAnchors.length; i++) {
var sourceUUID = toId + sourceAnchors[i];
jsPlumb.addEndpoint(toId, this.sourceEndpoint, { anchor:sourceAnchors[i], uuid:sourceUUID });
}
for (var j = 0; j < targetAnchors.length; j++) {
var targetUUID = toId + targetAnchors[j];
jsPlumb.addEndpoint(toId, this.targetEndpoint, { anchor:targetAnchors[j], uuid:targetUUID });
}
}
}; })(); /**
* drawTopo 根据给定拓扑数据绘制拓扑图
* @param topoData 拓扑数据
* @param rootPosition 拓扑图根节点的位置
* @param nodeTypeArray 节点类型数组
*
* 拓扑图的所有节点是自动生成的, DIV class = "node" , id= nodeType.toUpperCase + "-" + key
* 拓扑图的所有节点连接也是自动生成的, 可以进行算法改善与优化, 但使用者不需要关心此问题
* 需要定义节点类型数组 nodeTypeArray
*
* 拓扑数据结构:
* 1. 节点数据结构: node = { type: 'typeName', key: 'key', rel: [], data: {'More Info'}}
* rel, data 可选 , type-key 唯一标识该节点
* 2. 关联关系: rel: [node1, node2, ..., nodeN]
* 3. 更多详情: 关于节点的更多信息可放置于此属性中
* 4. 示例:
* var topoData = {
* type: 'VM', key: '110.75.188.35',
* rel: [
* { type: 'DEVICE', key: '3-120343' },
* { type: 'DEVICE', key: '3-120344' },
* { type: 'VIP', key: '223.6.250.2',
* rel: [
* { type: 'VM', key: '110.75.189.12' },
* { type: 'VM', key: '110.75.189.12' }
* ]
* },
* { type: 'NC', key: '10.242.192.2',
* rel: [
* { type: 'VM', key: '110.75.188.132' },
* { type: 'VM', key: '110.75.188.135' },
* { type: 'VM', key: '110.75.188.140' }
* ]
*
* }
* ]
* };
*
*/
function drawTopo(topoData, rootPosition, nodeTypeArray) { // 创建所有拓扑节点及连接并确定其位置
createNodes(topoData, rootPosition, nodeTypeArray); // 调整重合节点的位置, 添加节点的附着点, 即连接线的端点
adjust(topoData, nodeTypeArray); // 使所有拓扑节点均为可拉拽的
jsPlumb.draggable(jsPlumb.getSelector(".node"), { grid: [5, 5] }); // 创建所有节点连接
createConnections(topoData, nodeTypeArray); } /**
* 根据给定拓扑数据绘制拓扑节点并确定其位置, 使用深度优先遍历
* @param topoData 拓扑数据
* @param rootPosition 根节点的位置设定
* @param nodeTypeArray 拓扑节点类型
*/
function createNodes(rootData, rootPosition, nodeTypeArray) { if (rootData == null) {
return ;
} var topoRegion = $('#topoRegion');
var relData = rootData.rel;
var i=0, relLen = relLength(relData);;
var VM_TYPE = nodeTypeArray[0];
var DEVICE_TYPE = nodeTypeArray[1];
var NC_TYPE = nodeTypeArray[2];
var VIP_TYPE = nodeTypeArray[3]; // 根节点的位置, 单位: px
var rootTop = rootPosition[0];
var rootLeft = rootPosition[1]; var nextRootData = {};
var nextRootPosition = []; // 自动生成并插入根节点的 DIV
var divStr = createDiv(rootData);
var nodeDivId = obtainNodeDivId(rootData);
topoRegion.append(divStr);
//console.log(divStr); // 设置节点位置
$('#'+nodeDivId).css('top', rootTop + 'px');
$('#'+nodeDivId).css('left', rootLeft + 'px'); for (i=0; i < relLen; i++) {
nextRootData = relData[i];
nextRootPosition = obtainNextRootPosition(rootData, nextRootData, rootPosition, nodeTypeArray);
createNodes(nextRootData, nextRootPosition, nodeTypeArray);
} } /**
* 调整重合节点的位置, 并添加节点的附着点, 即连接线的端点
*/
function adjust(topoData, nodeTypeArray) { var vm_deviceOffset = 0; // 起始节点为 vm , 终止节点为 device, device div 的偏移量
var vm_vipOffset = 0; // 起始节点为 vm , 终止节点为 vip, vip div 的偏移量
var vm_ncOffset = 0; // 起始节点为 vm , 终止节点为 nc, nc div 的偏移量
var vip_vmOffset = 0; // 起始节点为 vip , 终止节点为 vm, vm div 的偏移量
var nc_vmOffset = 0; // 起始节点为nc , 终止节点为 vm, vm div 的偏移量
var verticalDistance = 120;
var horizontalDistance = 150; var VM_TYPE = nodeTypeArray[0];
var DEVICE_TYPE = nodeTypeArray[1];
var NC_TYPE = nodeTypeArray[2];
var VIP_TYPE = nodeTypeArray[3]; $('.node').each(function(index, element) {
var nodeDivId = $(element).attr('id');
var nodeType = nodeDivId.split('-')[0];
var offset = $(element).offset();
var originalTop = offset.top;
var originalLeft = offset.left;
var parentNode = $(element).parent();
var parentNodeType = parentNode.attr('id').split('-')[0];
switch (nodeType) {
case VM_TYPE:
// VM 位置水平偏移
$(element).css('left', (originalLeft + vip_vmOffset*horizontalDistance) + 'px');
vip_vmOffset++;
topoDrawUtil.addEndpoints(nodeDivId, ['Top', 'Bottom', 'Right'], []);
break;
case DEVICE_TYPE:
// DEVICE 位置垂直偏移
$(element).css('top', (originalTop + (vm_deviceOffset-1)*verticalDistance) + 'px');
vm_deviceOffset++;
topoDrawUtil.addEndpoints(nodeDivId, [], ['Left']);
break;
case VIP_TYPE:
// VIP 位置水平偏移
$(element).css('left', (originalLeft + vm_vipOffset*horizontalDistance) + 'px');
vm_vipOffset++;
topoDrawUtil.addEndpoints(nodeDivId, ['Top'], ['Bottom']);
break;
case NC_TYPE:
// NC 位置水平偏移
$(element).css('left', (originalLeft + vm_ncOffset*verticalDistance) + 'px');
vm_ncOffset++;
topoDrawUtil.addEndpoints(nodeDivId, ['Bottom'], ['Top']);
break;
default:
break;
}
});
} /**
* 获取下一个根节点的位置, 若节点类型相同, 则位置会重合, 需要后续调整一次
* @root 当前根节点
* @nextRoot 下一个根节点
* @rootPosition 当前根节点的位置
* @nodeTypeArray 节点类型数组
*/
function obtainNextRootPosition(root, nextRoot, rootPosition, nodeTypeArray) { var VM_TYPE = nodeTypeArray[0];
var DEVICE_TYPE = nodeTypeArray[1];
var NC_TYPE = nodeTypeArray[2];
var VIP_TYPE = nodeTypeArray[3]; var startNodeType = root.type;
var endNodeType = nextRoot.type;
var nextRootPosition = [];
var rootTop = rootPosition[0];
var rootLeft = rootPosition[1]; var verticalDistance = 120;
var horizontalDistance = 250;
var shortVerticalDistance = 80; switch (startNodeType) {
case VM_TYPE:
if (endNodeType == VIP_TYPE) {
nextRootPosition = [rootTop-verticalDistance, rootLeft];
}
else if (endNodeType == DEVICE_TYPE) {
nextRootPosition = [rootTop, rootLeft+horizontalDistance];
}
else if (endNodeType == NC_TYPE) {
nextRootPosition = [rootTop+verticalDistance, rootLeft];
}
break;
case VIP_TYPE:
if (endNodeType == VM_TYPE) {
nextRootPosition = [rootTop-shortVerticalDistance, rootLeft];
}
break;
case NC_TYPE:
if (endNodeType == VM_TYPE) {
nextRootPosition = [rootTop+shortVerticalDistance, rootLeft];
}
break;
default:
break;
}
return nextRootPosition;
} /**
* 根据给定拓扑数据, 绘制节点之间的连接关系, 使用深度优先遍历
* @param topoData 拓扑数据
* @param nodeTypeArray 节点类型数组
*/
function createConnections(topoData, nodeTypeArray) { if (topoData == null) {
return ;
}
var rootData = topoData;
var relData = topoData.rel;
var i=0, len = relLength(relData);;
for (i=0; i < len; i++) {
connectionNodes(rootData, relData[i], nodeTypeArray);
createConnections(relData[i], nodeTypeArray);
}
} /**
* 连接起始节点和终止节点
* @beginNode 起始节点
* @endNode 终止节点
* NOTE: 根据是起始节点与终止节点的类型
*/
function connectionNodes(beginNode, endNode, nodeTypeArray)
{
var startNodeType = beginNode.type;
var endNodeType = endNode.type;
var startDirection = '';
var endDirection = ''; var VM_TYPE = nodeTypeArray[0];
var DEVICE_TYPE = nodeTypeArray[1];
var NC_TYPE = nodeTypeArray[2];
var VIP_TYPE = nodeTypeArray[3]; switch (startNodeType) {
case VM_TYPE:
if (endNodeType == VIP_TYPE) {
// VIP 绘制于 VM 上方
startDirection = 'Top';
endDirection = 'Bottom';
}
else if (endNodeType == DEVICE_TYPE) {
// DEVICE 绘制于 VM 右方
startDirection = 'Right';
endDirection = 'Left';
}
else if (endNodeType == NC_TYPE) {
// NC 绘制于 VM 下方
startDirection = 'Bottom';
endDirection = 'Top';
}
break;
case VIP_TYPE:
if (endNodeType == VM_TYPE) {
// VM 绘制于 VIP 上方
startDirection = 'Top';
endDirection = 'Top';
}
break;
case NC_TYPE:
if (endNodeType == VM_TYPE) {
// VM 绘制于 NC 下方
startDirection = 'Bottom';
endDirection = 'Bottom';
}
break;
default:
break;
}
var startPoint = obtainNodeDivId(beginNode) + startDirection;
var endPoint = obtainNodeDivId(endNode) + endDirection;
jsPlumb.connect({uuids:[startPoint, endPoint], editable: false});
} function createDiv(metaNode) {
return '<div class="node" id="' + obtainNodeDivId(metaNode) + '"><strong>'
+ metaNode.type + '<br/><a href="http://aliyun.com">' + metaNode.key + '</a><br/></strong></div>'
} /**
* 生成节点的 DIV id
* divId = nodeType.toUpperCase + "-" + key
* key 可能为 IP , 其中的 . 将被替换成 ZZZ , 因为 jquery id 选择器中 . 属于转义字符.
* eg. {type: 'VM', key: '1.1.1.1' }, divId = 'VM-1ZZZ1ZZZ1ZZZ1'
*/
function obtainNodeDivId(metaNode) {
return metaNode.type.toUpperCase() + '-' + transferKey(metaNode.key);
} function transferKey(key) {
return key.replace(/\./g, 'ZZZ');
} function revTransferKey(value) {
return value.replace(/ZZZ/g, '.');
} /**
* 合并新的拓扑结构到原来的拓扑结构中, 新的拓扑结构中有节点与原拓扑结构中的某个节点相匹配: type-key 相等
* @param srcTopoData 原来的拓扑结构
* @param newTopoData 要添加的的拓扑结构
*/
function mergeNewTopo(srcTopoData, newTopoData) { var srcTopoData = shallowCopyTopo(srcTopoData); if (srcTopoData == null || newTopoData == null) {
return srcTopoData || newTopoData;
} var srcRoot = srcTopoData;
var newRoot = newTopoData; var newRelData = newTopoData.rel;
var i=0, newRelLen = relLength(newRelData); var matched = findMatched(srcRoot, newRoot);
if (matched == null) {
// 没有找到匹配的节点, 直接返回原有的拓扑结构
return srcTopoData;
}
matched.rel = matched.rel.concat(newRelData);
return srcTopoData;
} /**
* 在原拓扑结构中查找与新拓扑结构根节点 newRootData 匹配的节点
* @param srcRootData 原拓扑结构
* @param newRootData 新拓扑结构的根节点
* @returns 原拓扑结构中与新拓扑结构根节点匹配的节点 or null if not found
*/
function findMatched(srcRootData, newRootData) {
var srcRelData = srcRootData.rel;
var i=0, srcRelLen = relLength(srcRelData);
var matched = null;
if ((srcRootData.type == newRootData.type) && (srcRootData.key == newRootData.key)) {
return srcRootData;
}
for (i=0; i<srcRelLen; i++) {
matched = findMatched(srcRelData[i], newRootData);
if (matched != null) {
return matched;
}
}
return matched;
} function relLength(relData) {
if (isArray(relData)) {
return relData.length;
}
return 0;
} function isArray(value) {
return value && (typeof value === 'object') && (typeof value.length === 'number');
} /**
* 浅复制拓扑结构
*/
function shallowCopyTopo(srcTopoData) {
return srcTopoData;
} /**
* 深复制拓扑结构
*/
function deepCopyTopo(srcTopoData) {
//TODO identical to deep copy of js json
}

topodemo.html 绘制拓扑图的客户端接口。 只要引进相应的依赖 JS,预置一个 <div id="topoRegion"></div>

<!doctype html>
<html>
<head>
<title>jsPlumb 1.5.3 - flowchart connectors demonstration - jQuery</title>
<link rel="stylesheet" href="topo-all.css">
<link rel="stylesheet" href="topo.css"> <!-- DEP -->
<script src="../jsPlumb/jquery-1.9.0-min.js"></script>
<script src="../jsPlumb/jquery-ui-1.9.2-min.js"></script> <!-- /DEP --> <!-- JS -->
<!-- support lib for bezier stuff -->
<script src="../jsPlumb/jsBezier-0.6-min.js"></script>
<!-- <a href="http://www.it165.net/pro/webjsp/" target="_blank" class="keylink">jsp</a>lumb geom functions -->
<script src="../jsPlumb/<a href="http://www.it165.net/pro/webjsp/" target="_blank" class="keylink">jsp</a>lumb-geom-0.1.js"></script>
<!-- jsplumb util -->
<script src="../jsPlumb/util.js"></script>
<!-- base DOM adapter -->
<script src="../jsPlumb/dom-adapter.js"></script>
<!-- main jsplumb engine -->
<script src="../jsPlumb/jsPlumb.js"></script>
<!-- endpoint -->
<script src="../jsPlumb/endpoint.js"></script>
<!-- connection -->
<script src="../jsPlumb/connection.js"></script>
<!-- anchors -->
<script src="../jsPlumb/anchors.js"></script>
<!-- connectors, endpoint and overlays -->
<script src="../jsPlumb/defaults.js"></script>
<!-- connector editors -->
<script src="../jsPlumb/connector-editors.js"></script>
<!-- bezier connectors -->
<script src="../jsPlumb/connectors-bezier.js"></script>
<!-- state machine connectors -->
<script src="../jsPlumb/connectors-statemachine.js"></script>
<!-- flowchart connectors -->
<script src="../jsPlumb/connectors-flowchart.js"></script>
<!-- SVG renderer -->
<script src="../jsPlumb/renderers-svg.js"></script>
<!-- canvas renderer -->
<script src="../jsPlumb/renderers-canvas.js"></script>
<!-- vml renderer -->
<script src="../jsPlumb/renderers-vml.js"></script> <!-- jquery jsPlumb adapter -->
<script src="../jsPlumb/jquery.jsPlumb.js"></script>
<!-- /JS --> <!-- demo code -->
<script src="drawtopo.js"></script> <script type="text/javascript">
jsPlumb.bind("ready", function() { // 拓扑数据结构根节点位置设置
var rootPosition = [270, 300];
var nodeTypeArray = ['VM', 'DEVICE', 'NC', 'VIP'];
var topoData = {
type: 'VM', key: '110.75.188.35',
rel: [
{
type: 'DEVICE',
key: '3-120343'
}, {
type: 'DEVICE',
key: '3-120344'
}, {
type: 'VIP',
key: '223.6.250.2',
rel: [
{ type: 'VM', key: '110.75.189.12' },
{ type: 'VM', key: '110.75.189.13' }
]
}, {
type: 'NC',
key: '10.242.192.2',
rel: [
{ type: 'VM', key: '110.75.188.132' },
{ type: 'VM', key: '110.75.188.135' }
] }
]
}; drawTopo(topoData, rootPosition, nodeTypeArray); var newTopoData = {
type: 'NC',
key: '10.242.192.2',
rel: [
{ type: 'VM', key: '110.75.188.140' }
]
}; var mergedTopoData = mergeNewTopo(topoData, newTopoData);
$('#topoRegion').empty();
drawTopo(mergedTopoData, rootPosition, nodeTypeArray); }); </script> </head>
<body> <div id="topoRegion">
</div> </body>
</html>

样式文件及依赖JS 见工程示例。 里面已经包含绘制拓扑图的最小依赖。

四、 最终效果图

源码下载地址:http://download.csdn.net/detail/shuqin1984/6488513

使用JsPlumb绘制拓扑图的通用方法的更多相关文章

  1. 使用 JsPlumb 绘制拓扑图的通用方法

    摘要: 实现 JsPlumb 绘制拓扑图的通用方法. 只要服务端返回一个符合指定格式的数据结构,就可以绘制相应的拓扑图. 难度: 中级 示例工程见:  http://download.csdn.net ...

  2. 使用 jsPlumb 绘制拓扑图 —— 异步加载与绘制的实现

    本文实现的方法可以边异步加载数据边绘制拓扑图. 有若干点需要说明一下: 1.  一次性获取所有数据并绘制拓扑图, 请参见文章: <使用 JsPlumb 绘制拓扑图的通用方法> ; 本文实现 ...

  3. 使用 jsPlumb 绘制拓扑图 —— 异步载入与绘制的实现

    本文实现的方法能够边异步载入数据边绘制拓扑图. 有若干点须要说明一下: 1.  一次性获取全部数据并绘制拓扑图. 请參见文章: <使用 JsPlumb 绘制拓扑图的通用方法> ; 本文实现 ...

  4. 使用 highchart 绘制柱状图的通用方法与接口

    本文给出使用 highchart 绘制柱状图的通用方法与接口, 只要指定相应的数据结构和配置, 就可以直接拿来使用. 一.  数据结构与基本接口   一般绘制图形, 会涉及到较复杂的数据结构, 比如使 ...

  5. 使用java泛型设计通用方法

    泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 因此我们可以利用泛型和反射来设计一些通用方法. 现在有2张表, 一张user表和一张stu ...

  6. .NET基础架构方法—DataTableToExcel通用方法

    p { display: block; margin: 3px 0 0 0; } --> .NET架构基础方法—DataTableToExcel通用方法(NPOI) 今天封装DataTaleTo ...

  7. .NET基础架构方法—DataTableToList通用方法

    p { display: block; margin: 3px 0 0 0; } --> .NET架构基础方法—DataTableToList通用方法   我们经常需要将从数据库中所读取的数据以 ...

  8. DataTable数据赋值给Model通用方法

    注:该文属本人原创,今后项目中发现该方法存在BUG会实时更新,转载记得附上原文出处,方便大家获得最新代码. 相信大家在做项目中,经常会根据不同的表new各种不同的Model,当需要对Model进行实例 ...

  9. 带毫秒的字符转换成时间(DateTime)格式的通用方法

    C#自身有更好的方式,Net任意String格式转换为DateTime类型 ====================================================== 原文 ==== ...

随机推荐

  1. BZOJ4182 : Shopping

    最后选择的一定是树上的一个连通块,考虑树分治,每次只需考虑重心必选的情况,这就变成了以重心为根的树形依赖多重背包问题. 设f[x][j]表示从根节点到x这条路径及其左边的所有节点,以及以x为根的子树的 ...

  2. Oracle 课程六之hint

    课程目标 完成本课程的学习后,您应该能够: •什么是oracle hint •Hint的使用范围 •Hint 汇总 •演示常用的hint   Hint简介 Hint是oracle 提供的一种SQL语法 ...

  3. [AngularJS 2 实践 一]My First Angular App

    最近一直在看关于AngularJS 2的资料,查看了网上和官网很多资料,接下来就根据官网教程步骤一步步搭建我的第一个Angular App AngularJS 2说明请参考:http://cnodej ...

  4. 基于visual Studio2013解决算法导论之045斐波那契堆

     题目 斐波那契堆 解决代码及点评 // 斐波那契堆.cpp : 定义控制台应用程序的入口点. // #include<iostream> #include<cstdio> ...

  5. Chapter 1 Securing Your Server and Network(12):保护链接server

    原文出处:http://blog.csdn.net/dba_huangzj/article/details/38438363.专题文件夹:http://blog.csdn.net/dba_huangz ...

  6. c语言 实验1

    实验结论 Part 1 首次运行Part 1的几个实验内容时会产生错误,原因如下 有时忘记在主函数中的每行末尾加分号 return 0 时空格错误导致程序运行失败 # include <stdi ...

  7. Kafka基本命令

    1.创建自定义的topic 在bin目录下执行: sh kafka-topics.sh --create --zookeeper hadoop01:2181 --replication-factor ...

  8. BZOJ 3561 DZY Loves Math VI

    BZOJ 3561 DZY Loves Math VI 求\(\sum_{i=1}^{n}\sum_{j=1}^{m}\text{lcm}(i,j)^{\gcd(i,j)}\),钦定\(n\leq m ...

  9. QS Network---zoj1586最小生成树

    Description Sunny Cup 2003 - Preliminary Round April 20th, 12:00 - 17:00 Problem E: QS Network In th ...

  10. springboot学习入门之五---开发Web应用之JSP篇

    转载:http://tengj.top/2017/03/13/springboot5/ 1整体结构 整体的框架结构,跟前面介绍Thymeleaf的时候差不多,只是多了webapp这个用来存放jsp的目 ...