feat: 后端全量更新 - 含所有本次需求
- Contract.php: 返回合约账户余额(balance_contract) - My.php: 地址管理增加BTC/ETH - AppContract.php: 一键平仓(closeall) - AppProxy.php: 代理专属注册链接 + 分级权限(L1/L2) - site.php: 手续费减半(0.018→0.009) - agent_permission_setup.sql: 代理权限SQL - crypto_news_crawler.py: 新闻自动采集脚本
This commit is contained in:
+403
@@ -0,0 +1,403 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Virtualized Packed Groups Layout</title>
|
||||
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
|
||||
<meta name="description" content="Quickly layout and show part of a large graph of nested groups." />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="sample">
|
||||
<div id="myDiagramDiv" style="width:100%; height:800px; border: solid 1px black"></div>
|
||||
<p>
|
||||
Node data in Model: <span id="myMessage1"></span>.
|
||||
Actual Nodes in Diagram: <span id="myMessage2"></span>.<br />
|
||||
Link data in model: <span id="myMessage3"></span>.
|
||||
Actual Links in Diagram: <span id="myMessage4"></span>.
|
||||
</p>
|
||||
<p>
|
||||
This uses the <a>VirtualizedPackedLayout</a> extension,
|
||||
defined <a href="VirtualizedPackedLayout.ts">VirtualizedPackedLayout.ts</a>,
|
||||
to quickly layout a large graph consisting of
|
||||
nested groups.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<script type="module" id="code">
|
||||
import * as go from "../release/go-module.js";
|
||||
import { VirtualizedPackedLayout } from "./VirtualizedPackedLayout.js";
|
||||
|
||||
if (window.goSamples) window.goSamples(); // init for these samples -- you don't need to call this
|
||||
const $ = go.GraphObject.make;
|
||||
|
||||
// This custom layout that applies to myWholeModel.
|
||||
// It customizes the VirtualizedPackedLayout to account for Groups.
|
||||
class VirtualizedPackedGroupsLayout extends VirtualizedPackedLayout {
|
||||
constructor() {
|
||||
super();
|
||||
this.isOngoing = false;
|
||||
this.model = null; // must be set when initializing Diagram and myWholeModel
|
||||
this.sortMode = VirtualizedPackedLayout.Area;
|
||||
this.hasCircularNodes = true;
|
||||
this.topLevelNodes = [];
|
||||
}
|
||||
|
||||
doLayout() { // ignore arg
|
||||
if (!this.model) return;
|
||||
var nodes = this.model.nodeDataArray;
|
||||
var topGroups = this.model.topGroups;
|
||||
var maxdiam = 0;
|
||||
if (Array.isArray(topGroups)) {
|
||||
for (var i = 0; i < topGroups.length; i++) {
|
||||
var g = topGroups[i];
|
||||
this.walkGroups(g);
|
||||
maxdiam = Math.max(maxdiam, Math.max(g.bounds.width, g.bounds.height));
|
||||
}
|
||||
}
|
||||
this.topLevelNodes.length = 0;
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var n = nodes[i];
|
||||
if (n.group === undefined) this.topLevelNodes.push(n);
|
||||
maxdiam = Math.max(maxdiam, Math.max(n.bounds.width, n.bounds.height));
|
||||
}
|
||||
this.spacing = Math.max(50, maxdiam * 0.2);
|
||||
this.performLayout(this.topLevelNodes); // only top-level nodes
|
||||
this.diagram.fixedBounds = this.actualBounds;
|
||||
}
|
||||
|
||||
// depth-first walk
|
||||
walkGroups(g) {
|
||||
if (!g || !g.isGroup || !g._members) throw new Error("not a group data: " + g);
|
||||
var mems = g._members;
|
||||
if (Array.isArray(mems) && mems.length > 0) {
|
||||
var maxdiam = 0;
|
||||
for (var i = 0; i < mems.length; i++) {
|
||||
var n = mems[i];
|
||||
if (n.isGroup) {
|
||||
this.walkGroups(n);
|
||||
}
|
||||
maxdiam = Math.max(maxdiam, Math.max(n.bounds.width, n.bounds.height));
|
||||
}
|
||||
this.spacing = Math.max(50, maxdiam * 0.2);
|
||||
this.performLayout(mems);
|
||||
g.bounds = this.actualBounds.copy();
|
||||
} else {
|
||||
//!!!???@@@ this needs to be customized to account for your chosen Group template
|
||||
g.bounds = new go.Rect(0, 0, 50, 50);
|
||||
}
|
||||
}
|
||||
|
||||
// override moveNode to handle groups
|
||||
moveNode(node, nx, ny) {
|
||||
const dx = nx - node.bounds.x;
|
||||
const dy = ny - node.bounds.y;
|
||||
this.shiftNode(node, dx, dy);
|
||||
}
|
||||
|
||||
shiftNode(node, dx, dy) {
|
||||
node.bounds.x += dx;
|
||||
node.bounds.y += dy;
|
||||
if (node.isGroup) {
|
||||
var mems = node._members;
|
||||
if (Array.isArray(mems)) {
|
||||
for (var i = 0; i < mems.length; i++) {
|
||||
var n = mems[i];
|
||||
this.shiftNode(n, dx, dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end VirtualizedPackedGroupsLayout
|
||||
|
||||
// The Diagram just shows what should be visible in the viewport.
|
||||
// Its model does NOT include node data for the whole graph, but only that
|
||||
// which might be visible in the viewport.
|
||||
const myDiagram =
|
||||
$(go.Diagram, "myDiagramDiv",
|
||||
{
|
||||
"animationManager.isEnabled": false, // don't have any unnecessary initial scrolling
|
||||
initialScale: 0.25,
|
||||
layout: new VirtualizedPackedGroupsLayout(),
|
||||
"InitialLayoutCompleted": function(e) { // initial scroll so that we see some nodes
|
||||
var first = null;
|
||||
var arr = myWholeModel.nodeDataArray;
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
var d = arr[i];
|
||||
if (!d.isGroup) { first = d; break; }
|
||||
}
|
||||
if (first) {
|
||||
e.diagram.centerRect(first.bounds);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function fillBinding(depth) { if (depth >= myColors.length) depth = 0; return "rgba(" + myColors[depth] + ",0.1)"; }
|
||||
function strokeBinding(depth) { if (depth >= myColors.length) depth = 0; return "rgb(" + myColors[depth] + ")"; }
|
||||
|
||||
var myColors = ["0,0,0", "0,255,0", "255,0,0", "0,0,255"];
|
||||
var myLayoutFactors = [16, 8, 4, 2];
|
||||
|
||||
myDiagram.nodeTemplate =
|
||||
$(go.Node, "Auto",
|
||||
{ isLayoutPositioned: false }, // optimization
|
||||
new go.Binding("position", "bounds", function(b) { return b.position; }),
|
||||
{ width: 50, height: 50 }, // in cooperation with the load function, below
|
||||
$(go.Shape, "Circle",
|
||||
{
|
||||
spot1: go.Spot.TopLeft, spot2: go.Spot.BottomRight,
|
||||
portId: "", fill: "white", stroke: "gray"
|
||||
},
|
||||
new go.Binding("fill", "depth", fillBinding),
|
||||
new go.Binding("stroke", "depth", strokeBinding)),
|
||||
$(go.TextBlock,
|
||||
new go.Binding("text", "key"))
|
||||
);
|
||||
|
||||
myDiagram.groupTemplate =
|
||||
$(go.Group, "Auto",
|
||||
{ isLayoutPositioned: false }, // optimization
|
||||
// note no Placeholder and no .layout, since VirtualizedPackedGroupsLayout will compute everything
|
||||
new go.Binding("position", "bounds", function(b) { return new go.Point(b.x - b.width * 0.05, b.y - b.height * 0.05); }),
|
||||
new go.Binding("desiredSize", "bounds", function(b) { return new go.Size(b.size.width * 1.1, b.size.height * 1.1); }),
|
||||
$(go.Shape, "Ellipse",
|
||||
{
|
||||
spot1: new go.Spot(0.05, 0.05), spot2: new go.Spot(0.95, 0.95),
|
||||
portId: "", fill: "white", stroke: "gray"
|
||||
},
|
||||
new go.Binding("fill", "depth", fillBinding),
|
||||
new go.Binding("stroke", "depth", strokeBinding)),
|
||||
$(go.TextBlock,
|
||||
new go.Binding("text", "key"))
|
||||
);
|
||||
|
||||
// This model includes all of the data
|
||||
const myWholeModel =
|
||||
$(go.GraphLinksModel); // must match the model used by the Diagram, below
|
||||
|
||||
// The virtualized layout works on the full model, not on the Diagram Nodes and Links
|
||||
myDiagram.layout.model = myWholeModel;
|
||||
|
||||
// Do not set myDiagram.model = myWholeModel -- that would create a zillion Nodes and Links!
|
||||
// In the future Diagram may have built-in support for virtualization.
|
||||
// For now, we have to implement virtualization ourselves by having the Diagram's model
|
||||
// be different than the "real" model.
|
||||
myDiagram.model = // this only holds nodes that should be in the viewport
|
||||
$(go.GraphLinksModel); // must match the model, above
|
||||
|
||||
// for now, we have to implement virtualization ourselves
|
||||
myDiagram.isVirtualized = true;
|
||||
myDiagram.addDiagramListener("ViewportBoundsChanged", onViewportChanged);
|
||||
|
||||
// This is a status message
|
||||
const myLoading =
|
||||
$(go.Part, // this has to set the location or position explicitly
|
||||
{ location: new go.Point(0, 0), scale: 4 },
|
||||
$(go.TextBlock, "loading...",
|
||||
{ stroke: "red", font: "20pt sans-serif" }));
|
||||
|
||||
// temporarily add the status indicator
|
||||
myDiagram.add(myLoading);
|
||||
|
||||
// Allow the myLoading indicator to be shown now,
|
||||
// but allow objects added in load to also be considered part of the initial Diagram.
|
||||
// If you are not going to add temporary initial Parts, don't call delayInitialization.
|
||||
myDiagram.delayInitialization(load);
|
||||
|
||||
|
||||
// The following code creates a large randomized graph with nested groups in myWholeModel.
|
||||
|
||||
function load() {
|
||||
// create a lot of data for the myWholeModel
|
||||
addGraph(myWholeModel, 123456, 50, 4, 1.0);
|
||||
|
||||
// remove the status indicator
|
||||
myDiagram.remove(myLoading);
|
||||
}
|
||||
|
||||
function addGraph(model, totnodes, maxmembers, maxdepth, percentgroup) {
|
||||
model.topGroups = []; // add this property to GraphLinksModel
|
||||
addGraphInternal(model, totnodes, maxmembers, maxdepth, percentgroup, 0, null);
|
||||
}
|
||||
function addGraphInternal(model, totnodes, maxmembers, maxdepth, percentgroup, depth, groupdata) {
|
||||
// groupdata may be null for top-level nodes
|
||||
var nkey = model.nodeDataArray.length;
|
||||
if (nkey >= totnodes) return;
|
||||
var numnodes = Math.floor(Math.random() * (maxmembers - 1)) + 2;
|
||||
if (nkey + numnodes >= totnodes) numnodes = totnodes - nkey;
|
||||
var nodes = [];
|
||||
var links = [];
|
||||
for (var i = 0; i < numnodes; i++) {
|
||||
var data = { key: nkey + i, bounds: undefined, depth: depth };
|
||||
if (groupdata) {
|
||||
if (!groupdata.isGroup || !groupdata._members) {
|
||||
throw new Error("not a group data: " + groupdata);
|
||||
}
|
||||
// initially no .bounds property for group data
|
||||
data.group = groupdata.key;
|
||||
groupdata._members.push(data);
|
||||
}
|
||||
if (depth < maxdepth && Math.random() < percentgroup) {
|
||||
data.isGroup = true;
|
||||
data._members = [];
|
||||
if (!groupdata) model.topGroups.push(data); // only remember top-level groups
|
||||
} else {
|
||||
//!!!???@@@ this needs to be customized to account for your chosen Node template
|
||||
data.bounds = new go.Rect(0, 0, 50, 50);
|
||||
}
|
||||
nodes.push(data);
|
||||
if (i > 0) links.push({ from: nkey, to: nkey + i });
|
||||
}
|
||||
for (var i = 1; i <= numnodes / 3; i++) {
|
||||
// additional links between nodes other than the first one
|
||||
var from = Math.floor(Math.random() * (numnodes - 1)) + 1;
|
||||
var to = Math.floor(Math.random() * (numnodes - 1)) + 1;
|
||||
links.push({ from: nodes[from].key, to: nodes[to].key });
|
||||
}
|
||||
model.addNodeDataCollection(nodes);
|
||||
model.addLinkDataCollection(links);
|
||||
for (var i = 0; i < numnodes; i++) {
|
||||
var data = nodes[i];
|
||||
if (data.isGroup) {
|
||||
addGraphInternal(model, totnodes, maxmembers, maxdepth, percentgroup, depth + 1, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The following functions implement virtualization of the Diagram
|
||||
// Assume data.bounds is a Rect of the area occupied by the Node in document coordinates.
|
||||
|
||||
// The normal mechanism for determining the size of the document depends on all of the
|
||||
// Nodes and Links existing, so we need to use a function that depends only on the model data.
|
||||
function computeDocumentBounds(model) {
|
||||
var b = new go.Rect();
|
||||
var ndata = model.nodeDataArray;
|
||||
for (var i = 0; i < ndata.length; i++) {
|
||||
var d = ndata[i];
|
||||
if (!d.bounds) continue;
|
||||
if (i === 0) {
|
||||
b.set(d.bounds);
|
||||
} else {
|
||||
b.unionRect(d.bounds);
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
// As the user scrolls or zooms, make sure the Parts (Nodes and Links) exist in the viewport.
|
||||
function onViewportChanged(e) {
|
||||
var diagram = e.diagram;
|
||||
// make sure there are Nodes for each node data that is in the viewport
|
||||
// or that is connected to such a Node
|
||||
var viewb = diagram.viewportBounds; // the new viewportBounds
|
||||
var model = diagram.model; // assume a GraphLinksModel
|
||||
|
||||
var oldskips = diagram.skipsUndoManager;
|
||||
diagram.skipsUndoManager = true;
|
||||
|
||||
var b = new go.Rect();
|
||||
var ndata = myWholeModel.nodeDataArray;
|
||||
for (var i = 0; i < ndata.length; i++) {
|
||||
var n = ndata[i];
|
||||
if (!n.bounds) continue;
|
||||
if (n.bounds.intersectsRect(viewb)) {
|
||||
model.addNodeData(n);
|
||||
}
|
||||
}
|
||||
|
||||
var ldata = myWholeModel.linkDataArray;
|
||||
for (var i = 0; i < ldata.length; i++) {
|
||||
var l = ldata[i];
|
||||
var fromkey = myWholeModel.getFromKeyForLinkData(l);
|
||||
if (fromkey === undefined) continue;
|
||||
var from = myWholeModel.findNodeDataForKey(fromkey);
|
||||
if (from === null || !from.bounds) continue;
|
||||
|
||||
var tokey = myWholeModel.getToKeyForLinkData(l);
|
||||
if (tokey === undefined) continue;
|
||||
var to = myWholeModel.findNodeDataForKey(tokey);
|
||||
if (to === null || !to.bounds) continue;
|
||||
|
||||
b.set(from.bounds);
|
||||
b.unionRect(to.bounds);
|
||||
if (b.intersectsRect(viewb)) {
|
||||
// also make sure both connected nodes are present,
|
||||
// so that link routing is authentic
|
||||
model.addNodeData(from);
|
||||
model.addNodeData(to);
|
||||
model.addLinkData(l);
|
||||
var link = diagram.findLinkForData(l);
|
||||
if (link !== null) {
|
||||
// do this now to avoid delayed routing outside of transaction
|
||||
link.fromNode.ensureBounds();
|
||||
link.toNode.ensureBounds();
|
||||
link.updateRoute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diagram.skipsUndoManager = oldskips;
|
||||
|
||||
if (myRemoveTimer === null) {
|
||||
// only remove offscreen nodes after a delay
|
||||
myRemoveTimer = setTimeout(function() { removeOffscreen(diagram); }, 3000);
|
||||
}
|
||||
|
||||
updateCounts(); // only for this sample
|
||||
}
|
||||
|
||||
// occasionally remove Parts that are offscreen from the Diagram
|
||||
var myRemoveTimer = null;
|
||||
|
||||
function removeOffscreen(diagram) {
|
||||
myRemoveTimer = null;
|
||||
|
||||
var viewb = diagram.viewportBounds;
|
||||
var model = diagram.model;
|
||||
var remove = []; // collect for later removal
|
||||
var removeLinks = new go.Set(); // links connected to a node data to remove
|
||||
var it = diagram.nodes;
|
||||
while (it.next()) {
|
||||
var n = it.value;
|
||||
var d = n.data;
|
||||
if (d === null) continue;
|
||||
if (!n.actualBounds.intersectsRect(viewb) && !n.isSelected) {
|
||||
// even if the node is out of the viewport, keep it if it is selected or
|
||||
// if any link connecting with the node is still in the viewport
|
||||
if (!n.linksConnected.any(function(l) { return l.actualBounds.intersectsRect(viewb); })) {
|
||||
remove.push(d);
|
||||
if (model instanceof go.GraphLinksModel) {
|
||||
removeLinks.addAll(n.linksConnected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (remove.length > 0) {
|
||||
var oldskips = diagram.skipsUndoManager;
|
||||
diagram.skipsUndoManager = true;
|
||||
model.removeNodeDataCollection(remove);
|
||||
if (model instanceof go.GraphLinksModel) {
|
||||
removeLinks.each(function(l) { if (!l.isSelected) model.removeLinkData(l.data); });
|
||||
}
|
||||
diagram.skipsUndoManager = oldskips;
|
||||
}
|
||||
|
||||
updateCounts(); // only for this sample
|
||||
}
|
||||
// end of virtualized Diagram
|
||||
|
||||
// This function is only used in this sample to demonstrate the effects of the virtualization.
|
||||
// In a real application you would delete this function and all calls to it.
|
||||
function updateCounts() {
|
||||
document.getElementById("myMessage1").textContent = myWholeModel.nodeDataArray.length;
|
||||
document.getElementById("myMessage2").textContent = myDiagram.nodes.count;
|
||||
document.getElementById("myMessage3").textContent = myWholeModel.linkDataArray.length;
|
||||
document.getElementById("myMessage4").textContent = myDiagram.links.count;
|
||||
}
|
||||
|
||||
window.myDiagram = myDiagram; // Attach to the window for console debugging
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user