TrustDataSpace/assets/js/pages/mods/mod4-ops/data-path-tree.js
sichan 987bef5737 + feat:
1. 数据产品详情页
2. 数据资产化树状图
3. 数据运营页
2025-02-03 14:51:07 +08:00

313 lines
10 KiB
JavaScript

// 定义数据资产路径树的数据结构
const treeData = {
name: "数据资产入表",
children: [
{
name: "数据采集",
description: "从各种来源收集原始数据",
children: [
{
name: "结构化数据",
description: "数据库、Excel等格式化数据",
children: [
{
name: "数据库抽取",
description: "使用ETL工具从数据库中抽取数据"
},
{
name: "文件导入",
description: "导入Excel、CSV等文件数据"
}
]
},
{
name: "非结构化数据",
description: "文档、图片、视频等非结构化数据",
children: [
{
name: "文本解析",
description: "解析文档、日志等文本数据"
},
{
name: "媒体处理",
description: "处理图片、视频等媒体数据"
}
]
}
]
},
{
name: "数据处理",
description: "对采集的数据进行清洗和转换",
children: [
{
name: "数据清洗",
description: "去除异常值和重复数据",
children: [
{
name: "质量检查",
description: "检查数据完整性和准确性"
},
{
name: "数据修复",
description: "修复和补充缺失数据"
}
]
},
{
name: "数据转换",
description: "转换数据格式和结构",
children: [
{
name: "格式标准化",
description: "统一数据格式和编码"
},
{
name: "结构转换",
description: "调整数据结构以适应目标系统"
}
]
}
]
},
{
name: "数据入表",
description: "将处理后的数据导入目标系统",
children: [
{
name: "数据映射",
description: "建立源数据和目标表的字段映射",
children: [
{
name: "字段匹配",
description: "确定源字段和目标字段的对应关系"
},
{
name: "转换规则",
description: "定义数据转换和计算规则"
}
]
},
{
name: "数据加载",
description: "将数据加载到目标表中",
children: [
{
name: "增量更新",
description: "只更新变化的数据"
},
{
name: "全量替换",
description: "完全替换目标表数据"
}
]
}
]
}
]
};
// 定义节点详细信息数据
const nodeDetails = {
"数据资产入表": {
description: "数据资产入表是将各类数据规范化并导入系统的完整流程",
steps: [
"制定数据采集计划",
"确定数据处理规则",
"设计数据存储结构",
"实施数据导入流程"
],
notes: [
"确保数据质量和完整性",
"注意数据安全和隐私保护",
"建立数据版本控制机制"
],
docs: [
"《数据资产管理规范》",
"《数据质量控制指南》",
"《数据安全管理制度》"
]
},
"数据采集": {
description: "从各种来源收集原始数据的过程",
steps: [
"识别数据源",
"建立采集通道",
"制定采集策略",
"执行数据采集"
],
notes: [
"确保数据源的可靠性",
"建立数据采集的监控机制",
"注意采集频率的控制"
],
docs: [
"《数据采集规范》",
"《数据源接入指南》"
]
},
"结构化数据": {
description: "处理来自数据库、Excel等格式化数据源的数据",
steps: [
"连接数据源",
"提取目标数据",
"验证数据格式",
"临时存储数据"
],
notes: [
"检查数据完整性",
"处理特殊字符",
"注意数据量大小"
],
docs: [
"《结构化数据处理指南》",
"《数据库连接配置手册》"
]
},
// ... 为每个节点添加详细信息
};
// 初始化D3树形图
document.addEventListener('DOMContentLoaded', () => {
// 设置SVG尺寸和边距
const margin = {top: 60, right: 120, bottom: 60, left: 120};
const width = 1200 - margin.left - margin.right;
const height = 800 - margin.top - margin.bottom;
// 创建SVG容器
const svg = d3.select('#techTree')
.append('svg')
.attr('width', '100%')
.attr('height', '100%')
.attr('viewBox', `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// 创建树形布局
const tree = d3.tree()
.size([width, height])
.separation((a, b) => (a.parent == b.parent ? 2 : 3));
// 创建层级数据
const root = d3.hierarchy(treeData);
// 计算节点位置
tree(root);
// 创建连接线 - 使用贝塞尔曲线
const link = svg.selectAll('.link')
.data(root.links())
.enter()
.append('path')
.attr('class', 'link')
.attr('d', d3.linkHorizontal()
.x(d => d.y)
.y(d => d.x));
// 创建节点组
const node = svg.selectAll('.node')
.data(root.descendants())
.enter()
.append('g')
.attr('class', d => `node${d.children ? ' node--internal' : ' node--leaf'}`)
.attr('transform', d => `translate(${d.y},${d.x})`);
// 添加节点矩形
node.append('rect')
.attr('width', 160)
.attr('height', 60)
.attr('x', -80)
.attr('y', -30)
.attr('rx', 8)
.attr('ry', 8);
// 添加节点文本
node.append('text')
.attr('dy', '0.35em')
.attr('text-anchor', 'middle')
.each(function(d) {
const text = d3.select(this);
const words = d.data.name.split('');
const lineHeight = 1.1;
const maxLength = 8;
let line = [];
let tspans = [];
words.forEach((word, i) => {
line.push(word);
if (line.length === maxLength || i === words.length - 1) {
tspans.push(line.join(''));
line = [];
}
});
tspans.forEach((tspan, i) => {
text.append('tspan')
.attr('x', 0)
.attr('dy', i === 0 ? -0.5 * (tspans.length - 1) * lineHeight + 'em' : lineHeight + 'em')
.text(tspan);
});
});
// 获取详情面板元素
const detailTitle = document.getElementById('detailTitle');
const detailDescription = document.getElementById('detailDescription');
const detailSteps = document.getElementById('detailSteps');
const detailNotes = document.getElementById('detailNotes');
const detailDocs = document.getElementById('detailDocs');
// 修改节点点击事件
node.on('click', (event, d) => {
const details = nodeDetails[d.data.name] || {
description: d.data.description || "暂无详细描述",
steps: [],
notes: [],
docs: []
};
// 更新详情面板内容
detailTitle.textContent = d.data.name;
detailDescription.textContent = details.description;
// 更新步骤列表
detailSteps.innerHTML = details.steps.map(step => `<li>${step}</li>`).join('');
// 更新注意事项
detailNotes.innerHTML = details.notes.map(note => `<li>${note}</li>`).join('');
// 更新相关文档
detailDocs.innerHTML = details.docs.map(doc => `<li>${doc}</li>`).join('');
// 添加active类到当前节点
svg.selectAll('.node').classed('active', false);
d3.select(event.currentTarget).classed('active', true);
});
// 添加缩放功能
const zoom = d3.zoom()
.scaleExtent([0.5, 2])
.on('zoom', (event) => {
svg.attr('transform', event.transform);
});
// 初始化缩放
d3.select('#techTree svg')
.call(zoom)
.call(zoom.transform, d3.zoomIdentity.translate(margin.left, margin.top));
// 绑定按钮事件
document.getElementById('zoomIn').addEventListener('click', () => {
d3.select('#techTree svg').transition()
.call(zoom.scaleBy, 1.2);
});
document.getElementById('zoomOut').addEventListener('click', () => {
d3.select('#techTree svg').transition()
.call(zoom.scaleBy, 0.8);
});
document.getElementById('resetView').addEventListener('click', () => {
d3.select('#techTree svg').transition()
.call(zoom.transform, d3.zoomIdentity.translate(margin.left, margin.top));
});
});