2025-06-25 18:59:39 +08:00

252 lines
7.1 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>US Critical Minerals Security Flowchart</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
body { background: #f8f8f8; }
.node rect {
rx: 8px;
ry: 8px;
stroke: #333;
stroke-width: 1.5px;
}
.node text {
font-family: 'Segoe UI', Arial, sans-serif;
font-size: 15px;
fill: #222;
pointer-events: none;
}
.group-title {
font-size: 18px;
font-weight: bold;
fill: #fff;
}
.group-bg {
fill: #111;
rx: 8px;
ry: 8px;
}
.group-kpi {
font-size: 12px;
fill: #bdbdbd;
}
.arrow {
fill: none;
stroke: #888;
stroke-width: 2px;
marker-end: url(#arrowhead);
}
.main-goal {
fill: #4caf50;
font-size: 20px;
font-weight: bold;
}
.main-goal-bg {
fill: #d0e8d0;
rx: 8px;
ry: 8px;
stroke: #4caf50;
stroke-width: 2px;
}
.subnode {
fill: #fff;
stroke: #bbb;
stroke-width: 1.2px;
}
.highlight {
fill: #e0e0e0;
}
.gray {
fill: #f5f5f5;
}
.light {
fill: #e8f5e9;
}
</style>
</head>
<body>
<svg id="flowchart" width="1100" height="700"></svg>
<script>
const svg = d3.select("#flowchart");
const width = +svg.attr("width");
const height = +svg.attr("height");
// 箭头定义
svg.append("defs").append("marker")
.attr("id", "arrowhead")
.attr("viewBox", "0 -5 10 10")
.attr("refX", 18)
.attr("refY", 0)
.attr("markerWidth", 7)
.attr("markerHeight", 7)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5")
.attr("fill", "#888");
// 主目标
svg.append("rect")
.attr("class", "main-goal-bg")
.attr("x", 370)
.attr("y", 30)
.attr("width", 360)
.attr("height", 60);
svg.append("text")
.attr("class", "main-goal")
.attr("x", 550)
.attr("y", 60)
.attr("text-anchor", "middle")
.text("US critical minerals security");
svg.append("text")
.attr("x", 390)
.attr("y", 50)
.attr("font-size", 13)
.attr("fill", "#333")
.text("OVERALL GOAL:");
// 分组数据
const groups = [
{
title: "1",
subtitle: "Rebuild US critical minerals industry competitiveness",
kpi: "KPIs: Average time from discovery to construction; no. of workers in CM industry; no. of projects in pipeline",
x: 60, y: 120, w: 320, h: 80,
color: "#111",
nodes: [
{ id: "1e", text: "Develop critical minerals workforce", x: 80, y: 230, w: 180, h: 40 },
{ id: "1f", text: "Increase investment in R&D for new tech and AI usage", x: 80, y: 280, w: 220, h: 40 },
{ id: "1c", text: "Improve permitting and regulations", x: 80, y: 330, w: 180, h: 40 },
{ id: "1d", text: "Support domestic exploration with funding, data, and AI tools", x: 80, y: 380, w: 240, h: 40 },
{ id: "1b2d", text: "Support secondary processing expansion", x: 250, y: 230, w: 200, h: 40 },
{ id: "1a", text: "Increase domestic financing and incentives", x: 250, y: 280, w: 200, h: 40 },
{ id: "expand", text: "Expand US critical minerals project pipeline", x: 250, y: 330, w: 220, h: 40, highlight: true },
{ id: "incprod", text: "Increase domestic production", x: 250, y: 380, w: 180, h: 40, highlight: true }
]
},
{
title: "2",
subtitle: "Reduce import reliance from non-allied countries",
kpi: "KPIs: Percentage of US critical minerals imported from or processed by non-allied countries",
x: 410, y: 120, w: 320, h: 80,
color: "#222",
nodes: [
{ id: "2a", text: "Expand the role and use of existing multilateral arrangements", x: 430, y: 230, w: 250, h: 40 },
{ id: "2b", text: "Expand US government financing tools", x: 430, y: 280, w: 220, h: 40 },
{ id: "2c", text: "Support exploration and processing data initiatives", x: 430, y: 330, w: 250, h: 40 },
{ id: "alliedprod", text: "Increase allied production", x: 700, y: 230, w: 180, h: 40, highlight: true },
{ id: "alliedpipe", text: "Expand allied critical minerals project pipeline", x: 700, y: 280, w: 220, h: 40, highlight: true }
]
},
{
title: "3",
subtitle: "Reduce the risk of critical minerals market disruptions",
kpi: "KPIs: No. of months of critical mineral reserves; percentage of value chain data collected",
x: 760, y: 120, w: 320, h: 80,
color: "#111",
nodes: [
{ id: "mon", text: "Monitor critical minerals value chain vulnerabilities", x: 780, y: 230, w: 260, h: 40 },
{ id: "3a", text: "Develop a real-time value chain mapping and monitoring system", x: 780, y: 280, w: 300, h: 40 },
{ id: "supply", text: "Reduce supply disruption risks", x: 780, y: 330, w: 200, h: 40 },
{ id: "3b", text: "Expand purpose and use of National Defense Stockpile", x: 780, y: 380, w: 300, h: 40 },
{ id: "price", text: "Reduce price disruption risks", x: 780, y: 430, w: 200, h: 40 },
{ id: "3c", text: "Identify and deploy targeted market incentives", x: 780, y: 480, w: 300, h: 40 }
]
}
];
// 画分组背景和标题
groups.forEach(g => {
svg.append("rect")
.attr("class", "group-bg")
.attr("x", g.x)
.attr("y", g.y)
.attr("width", g.w)
.attr("height", g.h)
.attr("fill", g.color);
svg.append("text")
.attr("class", "group-title")
.attr("x", g.x + 18)
.attr("y", g.y + 28)
.text(g.title);
svg.append("text")
.attr("x", g.x + 45)
.attr("y", g.y + 28)
.attr("font-size", 16)
.attr("fill", "#fff")
.text(g.subtitle);
svg.append("text")
.attr("class", "group-kpi")
.attr("x", g.x + 10)
.attr("y", g.y + 55)
.text(g.kpi);
});
// 画节点
groups.forEach(g => {
g.nodes.forEach(n => {
svg.append("rect")
.attr("class", n.highlight ? "subnode highlight" : "subnode")
.attr("x", n.x)
.attr("y", n.y)
.attr("width", n.w)
.attr("height", n.h);
svg.append("text")
.attr("x", n.x + n.w / 2)
.attr("y", n.y + n.h / 2 + 5)
.attr("text-anchor", "middle")
.attr("font-size", 14)
.attr("fill", "#222")
.text(n.text);
});
});
// 连线数据(部分示例,实际可根据需要补充)
const links = [
// group 1
{ from: "1f", to: "1e" },
{ from: "1c", to: "expand" },
{ from: "1d", to: "expand" },
{ from: "1b2d", to: "incprod" },
{ from: "1a", to: "incprod" },
{ from: "expand", to: "incprod" },
// group 2
{ from: "2a", to: "alliedprod" },
{ from: "2b", to: "alliedpipe" },
{ from: "2c", to: "alliedpipe" },
{ from: "alliedpipe", to: "alliedprod" },
// group 3
{ from: "mon", to: "3a" },
{ from: "supply", to: "3b" },
{ from: "price", to: "3c" }
];
// 节点id到中心点映射
const nodeCenters = {};
groups.forEach(g => {
g.nodes.forEach(n => {
nodeCenters[n.id] = {
x: n.x + n.w / 2,
y: n.y + n.h / 2
};
});
});
// 画连线
links.forEach(l => {
const from = nodeCenters[l.from];
const to = nodeCenters[l.to];
if (from && to) {
svg.append("path")
.attr("class", "arrow")
.attr("d", `M${from.x},${from.y+20} C${from.x},${from.y+40} ${to.x},${to.y-40} ${to.x},${to.y-20}`);
}
});
</script>