314 lines
7.9 KiB
HTML
314 lines
7.9 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<title>G6 Tree View</title>
|
|
<script src="/ui/assets/g6.js"></script>
|
|
<style>
|
|
#tree_container {
|
|
width: 1100px;
|
|
height: 800px;
|
|
margin: 0 auto;
|
|
border: 1px solid #ddd;
|
|
}
|
|
|
|
.g6-tooltip {
|
|
border: 1px solid #e2e2e2;
|
|
border-radius: 4px;
|
|
font-size: 12px;
|
|
color: #545454;
|
|
background-color: rgba(255, 255, 255, 0.9);
|
|
padding: 10px 8px;
|
|
box-shadow: rgb(174, 174, 174) 0px 0px 10px;
|
|
}
|
|
|
|
.control-panel {
|
|
padding: 10px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
button {
|
|
margin: 5px;
|
|
padding: 5px 10px;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="control-panel">
|
|
<button id="add-btn">添加节点</button>
|
|
<button id="edit-btn">编辑选中节点</button>
|
|
<button id="delete-btn">删除选中节点</button>
|
|
<button id="refresh-btn">刷新树</button>
|
|
</div>
|
|
<div id="tree_container"></div>
|
|
|
|
<script>
|
|
// 全局变量
|
|
let graph = null;
|
|
let selectedNode = null;
|
|
|
|
// 将后端数据转换为G6可用的格式
|
|
function convertToG6Format(data) {
|
|
const map = {};
|
|
const roots = [];
|
|
|
|
// 创建所有节点
|
|
data.forEach(item => {
|
|
map[item.id] = {
|
|
id: item.id,
|
|
label: item.name,
|
|
style: {
|
|
fill: '#91d5ff',
|
|
stroke: '#40a9ff'
|
|
},
|
|
children: []
|
|
};
|
|
});
|
|
|
|
// 构建树结构
|
|
data.forEach(item => {
|
|
const node = map[item.id];
|
|
if (!item.parent_id || item.parent_id === '' || item.parent_id === '-1') {
|
|
roots.push(node);
|
|
} else {
|
|
const parent = map[item.parent_id];
|
|
if (parent) {
|
|
if (!parent.children) parent.children = [];
|
|
parent.children.push(node);
|
|
}
|
|
}
|
|
});
|
|
|
|
// 返回根节点数组的第一个作为主树
|
|
return roots.length > 0 ? { id: 'root', label: 'Root', children: roots } : {};
|
|
}
|
|
|
|
// 初始化G6图形
|
|
function initGraph() {
|
|
// 注册自定义节点,美化显示效果
|
|
window.G6.registerNode('tree-node', {
|
|
draw(cfg, group) {
|
|
const keyShape = group.addShape('rect', {
|
|
attrs: {
|
|
x: 0,
|
|
y: -15,
|
|
width: 120,
|
|
height: 30,
|
|
radius: 4,
|
|
fill: cfg.style ? cfg.style.fill : '#91d5ff',
|
|
stroke: cfg.style ? cfg.style.stroke : '#40a9ff',
|
|
cursor: 'pointer'
|
|
},
|
|
name: 'node-keyshape',
|
|
});
|
|
|
|
group.addShape('text', {
|
|
attrs: {
|
|
text: cfg.label,
|
|
x: 10,
|
|
y: 0,
|
|
fontSize: 14,
|
|
fill: '#333',
|
|
textAlign: 'left',
|
|
textBaseline: 'middle',
|
|
cursor: 'pointer'
|
|
},
|
|
name: 'node-label'
|
|
});
|
|
|
|
return keyShape;
|
|
}
|
|
});
|
|
|
|
// 创建G6实例
|
|
graph = new window.G6.Graph({
|
|
container: 'tree_container',
|
|
width: 1100,
|
|
height: 800,
|
|
fitView: true,
|
|
animate: true,
|
|
modes: {
|
|
default: ['drag-canvas', 'zoom-canvas', {
|
|
type: 'drag-node',
|
|
enableDelegate: true
|
|
}]
|
|
},
|
|
layout: {
|
|
type: 'compactBox',
|
|
direction: 'LR',
|
|
getId: function getId(d) {
|
|
return d.id;
|
|
},
|
|
getHeight: function getHeight() {
|
|
return 16;
|
|
},
|
|
getWidth: function getWidth() {
|
|
return 16;
|
|
},
|
|
getVGap: function getVGap() {
|
|
return 40;
|
|
},
|
|
getHGap: function getHGap() {
|
|
return 100;
|
|
}
|
|
},
|
|
defaultNode: {
|
|
type: 'tree-node'
|
|
},
|
|
nodeStateStyles: {
|
|
selected: {
|
|
fill: '#d9f7be',
|
|
stroke: '#73d13d'
|
|
}
|
|
}
|
|
});
|
|
|
|
// 节点点击事件
|
|
graph.on('node:click', evt => {
|
|
const { item } = evt;
|
|
const nodeId = item._cfg.id;
|
|
|
|
// 取消之前选中状态
|
|
if (selectedNode) {
|
|
graph.setItemState(selectedNode, 'selected', false);
|
|
}
|
|
|
|
// 设置新选中状态
|
|
graph.setItemState(item, 'selected', true);
|
|
selectedNode = item;
|
|
|
|
console.log('选中节点:', nodeId);
|
|
});
|
|
|
|
// 注册按钮事件
|
|
document.getElementById('add-btn').addEventListener('click', addNode);
|
|
document.getElementById('edit-btn').addEventListener('click', editNode);
|
|
document.getElementById('delete-btn').addEventListener('click', deleteNode);
|
|
document.getElementById('refresh-btn').addEventListener('click', fetchAndRenderTree);
|
|
return graph;
|
|
}
|
|
|
|
// 获取数据并渲染树
|
|
async function fetchAndRenderTree() {
|
|
try {
|
|
const response = await fetch('http://127.0.0.1:5002/api/tree/');
|
|
const result = await response.json();
|
|
|
|
if (result.code === 0) {
|
|
const treeData = convertToG6Format(result.data);
|
|
graph.data(treeData);
|
|
graph.render();
|
|
|
|
// 自动适应画布
|
|
setTimeout(() => {
|
|
graph.fitView();
|
|
}, 500);
|
|
} else {
|
|
console.error('获取数据失败:', result.message);
|
|
}
|
|
} catch (error) {
|
|
console.error('获取数据错误:', error);
|
|
}
|
|
}
|
|
|
|
// 添加节点
|
|
function addNode() {
|
|
const parentId = selectedNode ? selectedNode._cfg.id : '-1';
|
|
const newNodeName = prompt('请输入新节点名称:');
|
|
|
|
if (newNodeName) {
|
|
fetch('http://127.0.0.1:5002/api/tree/', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
name: newNodeName,
|
|
parent_id: parentId
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
alert('节点添加成功');
|
|
fetchAndRenderTree();
|
|
})
|
|
.catch(error => {
|
|
console.error('添加节点失败:', error);
|
|
});
|
|
}
|
|
}
|
|
|
|
// 编辑节点
|
|
function editNode() {
|
|
if (!selectedNode) {
|
|
alert('请先选择一个节点');
|
|
return;
|
|
}
|
|
|
|
const nodeId = selectedNode._cfg.id;
|
|
const newName = prompt('请输入新的节点名称:', selectedNode._cfg.model.label);
|
|
|
|
if (newName) {
|
|
fetch(`http://127.0.0.1:5002/api/tree/${nodeId}`, {
|
|
method: 'PATCH',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
name: newName
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
alert('节点更新成功');
|
|
fetchAndRenderTree();
|
|
})
|
|
.catch(error => {
|
|
console.error('更新节点失败:', error);
|
|
});
|
|
}
|
|
}
|
|
|
|
// 删除节点
|
|
function deleteNode() {
|
|
if (!selectedNode) {
|
|
alert('请先选择一个节点');
|
|
return;
|
|
}
|
|
|
|
const nodeId = selectedNode._cfg.id;
|
|
const confirmDelete = confirm('确定删除此节点吗?');
|
|
|
|
if (confirmDelete) {
|
|
fetch(`http://127.0.0.1:5002/api/tree/${nodeId}`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
cascade: false
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
alert('节点删除成功');
|
|
selectedNode = null;
|
|
fetchAndRenderTree();
|
|
})
|
|
.catch(error => {
|
|
console.error('删除节点失败:', error);
|
|
});
|
|
}
|
|
}
|
|
|
|
// 页面加载完成后初始化图形并加载数据
|
|
$node.addEventListener('DOMContentLoaded', () => {
|
|
graph = initGraph();
|
|
fetchAndRenderTree();
|
|
});
|
|
</script>
|
|
</body>
|
|
|
|
</html> |