修复查看器:滚动条+缩放/展开按钮+初始视图
This commit is contained in:
+116
-81
@@ -6,16 +6,21 @@
|
||||
<title>金地商置股权架构图</title>
|
||||
<style>
|
||||
* { margin:0; padding:0; box-sizing:border-box; }
|
||||
body { font-family: "PingFang SC","Microsoft YaHei",sans-serif; background:#f5f7fa; }
|
||||
.header { background:#1e6fd9; color:#fff; padding:12px 16px; display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
|
||||
.header h1 { font-size:17px; font-weight:600; white-space:nowrap; }
|
||||
.search-box { flex:1; min-width:200px; max-width:400px; position:relative; }
|
||||
.search-box input { width:100%; padding:8px 34px 8px 12px; border:none; border-radius:6px; font-size:14px; outline:none; }
|
||||
.search-box .clear { position:absolute; right:8px; top:50%; transform:translateY(-50%); cursor:pointer; color:#999; display:none; font-size:18px; }
|
||||
.stats { font-size:12px; opacity:.9; white-space:nowrap; }
|
||||
.legend { font-size:12px; opacity:.9; white-space:nowrap; }
|
||||
#chart { width:100%; height:calc(100vh - 56px); }
|
||||
.tip { position:fixed; bottom:16px; left:50%; transform:translateX(-50%); background:rgba(0,0,0,.75); color:#fff; padding:8px 16px; border-radius:20px; font-size:13px; z-index:99; display:none; }
|
||||
html,body { height:100%; font-family:"PingFang SC","Microsoft YaHei",sans-serif; background:#f5f7fa; overflow:hidden; }
|
||||
.header { background:#1e6fd9; color:#fff; padding:10px 14px; display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
|
||||
.header h1 { font-size:16px; font-weight:600; white-space:nowrap; }
|
||||
.search-box { flex:1; min-width:160px; max-width:360px; position:relative; }
|
||||
.search-box input { width:100%; padding:7px 30px 7px 10px; border:none; border-radius:6px; font-size:13px; outline:none; }
|
||||
.search-box .clear { position:absolute; right:8px; top:50%; transform:translateY(-50%); cursor:pointer; color:#999; display:none; font-size:16px; }
|
||||
.stats { font-size:12px; opacity:.95; white-space:nowrap; }
|
||||
.btn-group { display:flex; gap:6px; flex-wrap:wrap; }
|
||||
.btn { background:rgba(255,255,255,.18); color:#fff; border:1px solid rgba(255,255,255,.35); border-radius:5px;
|
||||
padding:5px 10px; font-size:12px; cursor:pointer; transition:background .15s; }
|
||||
.btn:hover { background:rgba(255,255,255,.32); }
|
||||
#wrap { position:relative; overflow:auto; background:#fff; }
|
||||
#chart { width:100%; height:100%; }
|
||||
.tip { position:fixed; bottom:16px; left:50%; transform:translateX(-50%); background:rgba(0,0,0,.78); color:#fff;
|
||||
padding:8px 16px; border-radius:20px; font-size:13px; z-index:99; display:none; max-width:80%; text-align:center; }
|
||||
.matched { font-weight:700; color:#e6a700; }
|
||||
</style>
|
||||
</head>
|
||||
@@ -23,13 +28,19 @@
|
||||
<div class="header">
|
||||
<h1>🏢 金地商置股权架构图</h1>
|
||||
<div class="search-box">
|
||||
<input id="search" type="text" placeholder="🔍 搜索公司名称(如:威新 / 金地 / 置业)">
|
||||
<input id="search" type="text" placeholder="🔍 搜索公司名称">
|
||||
<span class="clear" id="clearBtn">×</span>
|
||||
</div>
|
||||
<div class="stats" id="stats"></div>
|
||||
<div class="legend">🟦 根公司 ⬜ 子公司 🔍 点击节点展开/收起 滚轮缩放</div>
|
||||
<div class="btn-group">
|
||||
<button class="btn" id="btnZoomIn">🔍 放大</button>
|
||||
<button class="btn" id="btnZoomOut">🔍 缩小</button>
|
||||
<button class="btn" id="btnReset">🔄 重置</button>
|
||||
<button class="btn" id="btnExpandAll">📂 全部展开</button>
|
||||
<button class="btn" id="btnCollapseAll">📁 全部收起</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chart"></div>
|
||||
<div id="wrap"><div id="chart"></div></div>
|
||||
<div class="tip" id="tip"></div>
|
||||
|
||||
<script>
|
||||
@@ -84,55 +95,103 @@ var treeData = [{"name": "上海信能度尔信息科技有限公司", "org2": "
|
||||
var relCount = 630;
|
||||
var companyCount = 668;
|
||||
|
||||
// 统计节点数
|
||||
function countNodes(n) { return 1 + (n.children||[]).reduce((s,c)=>s+countNodes(c),0); }
|
||||
document.getElementById('stats').textContent = '共 ' + companyCount + ' 家公司 · ' + relCount + ' 条持股关系';
|
||||
function countNodes(n) { return 1 + (n.children||[]).reduce(function(s,c){return s+countNodes(c);},0); }
|
||||
var totalNodes = treeData.reduce(function(s,n){return s+countNodes(n);},0);
|
||||
var depth = 7;
|
||||
var maxW = 121;
|
||||
document.getElementById('stats').textContent = companyCount + ' 家公司 · ' + relCount + ' 条关系 · ' + depth + ' 层';
|
||||
|
||||
var chart = echarts.init(document.getElementById('chart'));
|
||||
window.addEventListener('resize', function(){ chart.resize(); });
|
||||
var wrap = document.getElementById('wrap');
|
||||
var chartEl = document.getElementById('chart');
|
||||
var chart = echarts.init(chartEl);
|
||||
|
||||
// ===== 画布尺寸 =====
|
||||
var BASE_W = Math.max(1600, maxW * 50);
|
||||
var BASE_H = Math.max(800, depth * 140);
|
||||
var zoom = 1.0;
|
||||
var MIN_ZOOM = 0.3, MAX_ZOOM = 4.0;
|
||||
|
||||
function applySize() {
|
||||
chartEl.style.width = (BASE_W * zoom) + 'px';
|
||||
chartEl.style.height = (BASE_H * zoom) + 'px';
|
||||
chart.resize();
|
||||
}
|
||||
|
||||
// 为每个节点加 children 数量标签
|
||||
function annotate(n) {
|
||||
n.value = [n.name, (n.children||[]).length];
|
||||
n.label = { formatter: '{b}' };
|
||||
(n.children||[]).forEach(annotate);
|
||||
return n;
|
||||
}
|
||||
treeData.forEach(annotate);
|
||||
|
||||
var option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
triggerOn: 'mousemove',
|
||||
formatter: function(p) {
|
||||
if (p.data && p.data.org2) return '<b>' + p.data.name + '</b><br>所属:' + p.data.org2;
|
||||
return p.data ? p.data.name : '';
|
||||
}
|
||||
},
|
||||
// 初始: 只展开第一层
|
||||
function markCollapsed(ns, isRoot) {
|
||||
ns.forEach(function(n){
|
||||
if (!isRoot && n.children && n.children.length) n.collapsed = true;
|
||||
if (n.children) markCollapsed(n.children, false);
|
||||
});
|
||||
}
|
||||
markCollapsed(treeData, true);
|
||||
|
||||
var baseOption = {
|
||||
tooltip: { trigger:'item', triggerOn:'mousemove',
|
||||
formatter: function(p){ return p.data ? '<b>'+p.data.name+'</b>'+(p.data.org2?'<br>所属:'+p.data.org2:'') : ''; } },
|
||||
series: [{
|
||||
type: 'tree',
|
||||
data: treeData,
|
||||
top: '8%', bottom: '8%', left: '6%', right: '18%',
|
||||
symbol: 'circle', symbolSize: 8,
|
||||
initialTreeDepth: 1, // 默认只展开第一层(分层展开)
|
||||
expandAndCollapse: true, // 点击展开/收起
|
||||
roam: true, // 滚轮缩放 + 拖拽
|
||||
label: { position: 'left', verticalAlign: 'middle', align: 'right', fontSize: 12, color: '#333' },
|
||||
leaves: { label: { position: 'right', verticalAlign: 'middle', align: 'left' } },
|
||||
emphasis: { focus: 'descendant' },
|
||||
lineStyle: { color: '#999', width: 1.2, curveness: 0.2 },
|
||||
itemStyle: { color: function(p){ return p.data.children && p.data.children.length ? '#1e6fd9' : '#aab'; } }
|
||||
type:'tree', data: treeData, orient:'TB',
|
||||
top:'4%', bottom:'4%', left:'2%', right:'2%',
|
||||
symbol:'circle', symbolSize:7,
|
||||
initialTreeDepth:1, expandAndCollapse:true, roam:true,
|
||||
label:{ position:'bottom', fontSize:11, color:'#333', formatter:'{b}', distance:4 },
|
||||
leaves:{ label:{ position:'bottom', fontSize:11, color:'#333' } },
|
||||
emphasis:{ focus:'descendant' },
|
||||
lineStyle:{ color:'#999', width:1.2, curveness:0.15 },
|
||||
itemStyle:{ color:function(p){ return (p.data.children&&p.data.children.length)?'#1e6fd9':'#aab'; } }
|
||||
}]
|
||||
};
|
||||
chart.setOption(option);
|
||||
chart.setOption(baseOption);
|
||||
applySize();
|
||||
|
||||
// ===== 搜索功能 =====
|
||||
window.addEventListener('resize', function(){ chart.resize(); });
|
||||
|
||||
// ===== 缩放按钮 =====
|
||||
function setZoom(z) {
|
||||
zoom = Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, z));
|
||||
applySize();
|
||||
showTip('缩放 ' + Math.round(zoom*100) + '%');
|
||||
}
|
||||
document.getElementById('btnZoomIn').onclick = function(){ setZoom(zoom * 1.25); };
|
||||
document.getElementById('btnZoomOut').onclick = function(){ setZoom(zoom / 1.25); };
|
||||
document.getElementById('btnReset').onclick = function(){ zoom = 1.0; applySize(); showTip('已重置视图'); };
|
||||
|
||||
// ===== 全部展开/收起 =====
|
||||
function collectParents(ns, arr) {
|
||||
ns.forEach(function(n){
|
||||
if (n.children && n.children.length) { arr.push(n.name); collectParents(n.children, arr); }
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
function expandAll() {
|
||||
chart.dispatchAction({ type:'treeExpandAndCollapse', seriesIndex:0, name: collectParents(treeData, []) });
|
||||
showTip('已全部展开');
|
||||
}
|
||||
function collapseAll() {
|
||||
var names = [];
|
||||
treeData.forEach(function(n){ if(n.children) n.children.forEach(function(c){ names.push(c.name); }); });
|
||||
chart.dispatchAction({ type:'treeExpandAndCollapse', seriesIndex:0, name: names });
|
||||
showTip('已全部收起');
|
||||
}
|
||||
document.getElementById('btnExpandAll').onclick = expandAll;
|
||||
document.getElementById('btnCollapseAll').onclick = collapseAll;
|
||||
|
||||
// ===== 搜索 =====
|
||||
var searchInput = document.getElementById('search');
|
||||
var clearBtn = document.getElementById('clearBtn');
|
||||
var tip = document.getElementById('tip');
|
||||
var lastQuery = '';
|
||||
var tipTimer = null;
|
||||
function showTip(msg){ tip.textContent=msg; tip.style.display='block'; clearTimeout(tipTimer); tipTimer=setTimeout(function(){tip.style.display='none';},3000); }
|
||||
|
||||
function showTip(msg) { tip.textContent = msg; tip.style.display = 'block'; setTimeout(function(){ tip.style.display='none'; }, 3000); }
|
||||
var allNames = [];
|
||||
(function collect(ns){ ns.forEach(function(n){ allNames.push(n.name); if(n.children) collect(n.children); }); })(treeData);
|
||||
|
||||
function findPath(name, nodes, path) {
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
@@ -145,70 +204,46 @@ function findPath(name, nodes, path) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 收集所有公司名 → 搜索建议
|
||||
var allNames = [];
|
||||
(function collect(ns){ ns.forEach(function(n){ allNames.push(n.name); if(n.children) collect(n.children); }); })(treeData);
|
||||
|
||||
// 展开路径(把某节点的所有祖先展开)
|
||||
function expandPath(path) {
|
||||
var expanded = [];
|
||||
for (var i=0;i<path.length-1;i++) {
|
||||
expanded.push(path[i].name);
|
||||
}
|
||||
chart.dispatchAction({
|
||||
type: 'treeExpandAndCollapse',
|
||||
seriesIndex: 0,
|
||||
name: expanded
|
||||
});
|
||||
chart.setOption({ series:[{ data: treeData }] });
|
||||
var names = path.slice(0, -1).map(function(n){ return n.name; });
|
||||
if (names.length) chart.dispatchAction({ type:'treeExpandAndCollapse', seriesIndex:0, name: names });
|
||||
}
|
||||
|
||||
searchInput.addEventListener('input', function() {
|
||||
var q = searchInput.value.trim().toLowerCase();
|
||||
clearBtn.style.display = q ? 'block' : 'none';
|
||||
if (!q) {
|
||||
// 清空搜索:重置为初始视图
|
||||
chart.setOption({ series:[{ data: treeData }] });
|
||||
chart.setOption({ series:[{ data: treeData, label:{formatter:'{b}', rich:{}} }] });
|
||||
return;
|
||||
}
|
||||
var matches = allNames.filter(function(n){ return n.toLowerCase().indexOf(q) !== -1; });
|
||||
if (matches.length === 0) { showTip('未找到包含「' + q + '」的公司'); return; }
|
||||
if (matches.length > 50) { showTip('找到 ' + matches.length + ' 家,仅显示前 50 家,请更精确'); matches = matches.slice(0,50); }
|
||||
// 高亮匹配项(通过 tooltip 提示 + 展开第一个匹配的路径)
|
||||
if (matches.length === 0) { showTip('未找到包含「'+q+'」的公司'); return; }
|
||||
if (matches.length > 50) { showTip('找到 '+matches.length+' 家,仅展示前 50,请更精确'); matches = matches.slice(0,50); }
|
||||
var first = matches[0];
|
||||
var path = findPath(first, treeData, []);
|
||||
if (path) {
|
||||
expandPath(path);
|
||||
if (path) expandPath(path);
|
||||
showTip('找到 ' + matches.length + ' 家:' + matches.slice(0,5).join('、') + (matches.length>5?'…':''));
|
||||
}
|
||||
// 高亮所有匹配节点
|
||||
chart.setOption({
|
||||
series: [{
|
||||
data: treeData,
|
||||
label: {
|
||||
formatter: function(p){
|
||||
if (p.data && p.data.name.toLowerCase().indexOf(q) !== -1) return '{matched|' + p.data.name + '}';
|
||||
if (p.data && p.data.name.toLowerCase().indexOf(q) !== -1) return '{matched|'+p.data.name+'}';
|
||||
return p.data ? p.data.name : '';
|
||||
},
|
||||
rich: { matched: { color:'#e6a700', fontWeight:'bold' } }
|
||||
rich: { matched:{ color:'#e6a700', fontWeight:'bold' } }
|
||||
}
|
||||
}]
|
||||
});
|
||||
});
|
||||
clearBtn.onclick = function(){ searchInput.value=''; clearBtn.style.display='none'; chart.setOption({ series:[{ data: treeData, label:{formatter:'{b}', rich:{}} }] }); };
|
||||
|
||||
clearBtn.addEventListener('click', function(){
|
||||
searchInput.value = '';
|
||||
clearBtn.style.display = 'none';
|
||||
chart.setOption({ series:[{ data: treeData }] });
|
||||
});
|
||||
|
||||
// 点击空白处提示
|
||||
chart.on('click', function(p){
|
||||
if (p.data && p.data.children && p.data.children.length) {
|
||||
showTip('「' + p.data.name + '」' + (p.data.children.length) + ' 家子公司');
|
||||
showTip('「'+p.data.name+'」共 ' + p.data.children.length + ' 家子公司');
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(function(){ zoom=1.0; applySize(); }, 200);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user