ui
This commit is contained in:
@@ -0,0 +1,314 @@
|
||||
<!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>
|
||||
Reference in New Issue
Block a user