使用JsPlumb绘制拓扑图的通用方法
转自: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 见工程示例。 里面已经包含绘制拓扑图的最小依赖。
四、 最终效果图
使用JsPlumb绘制拓扑图的通用方法的更多相关文章
- 使用 JsPlumb 绘制拓扑图的通用方法
摘要: 实现 JsPlumb 绘制拓扑图的通用方法. 只要服务端返回一个符合指定格式的数据结构,就可以绘制相应的拓扑图. 难度: 中级 示例工程见: http://download.csdn.net ...
- 使用 jsPlumb 绘制拓扑图 —— 异步加载与绘制的实现
本文实现的方法可以边异步加载数据边绘制拓扑图. 有若干点需要说明一下: 1. 一次性获取所有数据并绘制拓扑图, 请参见文章: <使用 JsPlumb 绘制拓扑图的通用方法> ; 本文实现 ...
- 使用 jsPlumb 绘制拓扑图 —— 异步载入与绘制的实现
本文实现的方法能够边异步载入数据边绘制拓扑图. 有若干点须要说明一下: 1. 一次性获取全部数据并绘制拓扑图. 请參见文章: <使用 JsPlumb 绘制拓扑图的通用方法> ; 本文实现 ...
- 使用 highchart 绘制柱状图的通用方法与接口
本文给出使用 highchart 绘制柱状图的通用方法与接口, 只要指定相应的数据结构和配置, 就可以直接拿来使用. 一. 数据结构与基本接口 一般绘制图形, 会涉及到较复杂的数据结构, 比如使 ...
- 使用java泛型设计通用方法
泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 因此我们可以利用泛型和反射来设计一些通用方法. 现在有2张表, 一张user表和一张stu ...
- .NET基础架构方法—DataTableToExcel通用方法
p { display: block; margin: 3px 0 0 0; } --> .NET架构基础方法—DataTableToExcel通用方法(NPOI) 今天封装DataTaleTo ...
- DataTable数据赋值给Model通用方法
注:该文属本人原创,今后项目中发现该方法存在BUG会实时更新,转载记得附上原文出处,方便大家获得最新代码. 相信大家在做项目中,经常会根据不同的表new各种不同的Model,当需要对Model进行实例 ...
- 带毫秒的字符转换成时间(DateTime)格式的通用方法
C#自身有更好的方式,Net任意String格式转换为DateTime类型 ====================================================== 原文 ==== ...
- C# 深拷贝通用方法
C#深拷贝通用方法(引用类型的拷贝) /// <summary> /// 深度COPY /// </summary> /// <typeparam name=" ...
随机推荐
- YII 表单验证规则
官方文档:http://www.yiichina.com/guide/form.model 类参考手册:http://www.yiichina.com/api/CValidatorhttp://www ...
- hdoj 4006 The kth great number【优先队列】
The kth great number Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65768/65768 K (Java/Oth ...
- linux下安装php的swoole扩展模块(安装后php加载不出来?)
应开发同事要求,需要安装php的扩展模块swoole.swoole是一种PHP高级Web开发框架,框架不是为了提升网站的性能,而是为了提升网站的开发效率,以最少的性能损耗,换取最大的开发效率. 假设服 ...
- mysql添加用户权限
MySQL性能调优my.cnf详解 //登录MYSQLmysql -u root -p//创建用户insert into mysql.user(Host,User,Password) values(‘ ...
- Day 3 @ RSA Conference Asia Pacific & Japan 2016 (afternoon)
13.30 hrs Keynote Security in the World-Sized Web Bruce Schneier,Chief Technology Officer, Resili ...
- Spring-接口调用
在Spring框架下实现和调用接口时,不用再代码中创建接口对象.而是依赖容器注入接口的实现对象. 1.创建接口 package service; /** * Created by xumao on 2 ...
- 搭建PHP开发环境 apache+MySQL+PHP 安装phpMyAdmin模块
该博文参考的资料来源于: http://wenku.baidu.com/view/0e4c569ddd3383c4bb4cd267.html http://www.cnblogs.com/pharen ...
- Java反射_JDBC操作数据
</pre><p>使用反射 来操作 这里是练习反射的使用</p><p>链接数据库工具类</p><p><pre name= ...
- 深入了解当前ETL中用到的一些基本技术
数据集成是把不同来源.格式和特点的数据在逻辑上或物理上有机地集中,从而为企业提供全面的数据共享,是企业商务智能.数据仓库系统的重要组成部分.ETL是企业数据集成的概念出发,简要分析了当前ETL中用到的 ...
- adt-bundle-linux-x86_64-20131030下新建project提示找不到adb和R.java问题的解决
adt-bundle-linux-x86_64-20131030下新建project提示找不到adb和R.java问题的解决 在ubuntu14.04下,搭建Android开发环境,下载官方的adt- ...