This commit is contained in:
熊潇 2025-06-25 18:59:39 +08:00
commit 9cc8ed5d1b
10 changed files with 1574 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

BIN
WechatIMG493.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

124
index.html Normal file
View File

@ -0,0 +1,124 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>D3 Flowchart</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
body {
font-family: sans-serif;
}
</style>
</head>
<body>
<svg id="flowchart" width="1400" height="900"></svg>
<script>
const nodes = [
// Overall Goal
{ id: 'G1', x: 550, y: 20, width: 300, height: 50, color: '#2E8B57' }, // darkseagreen
// Main Pillars
{ id: 'P1', x: 50, y: 150, width: 300, height: 50, color: 'black' },
{ id: 'P2', x: 550, y: 150, width: 300, height: 50, color: 'black' },
{ id: 'P3', x: 1050, y: 150, width: 300, height: 50, color: 'black' },
// Column 1
{ id: 'W1', x: 250, y: 300, width: 150, height: 50, color: 'white', stroke: 'black' },
{ id: 'N1e', x: 50, y: 250, width: 150, height: 50, color: 'lightgrey' },
{ id: 'N1f', x: 50, y: 320, width: 150, height: 50, color: 'lightgrey' },
{ id: 'N1b_2d', x: 250, y: 400, width: 150, height: 70, color: 'lightgrey' },
{ id: 'W2', x: 50, y: 600, width: 150, height: 70, color: 'white', stroke: 'black' },
{ id: 'N1c', x: 50, y: 450, width: 150, height: 50, color: 'lightgrey' },
{ id: 'N1d', x: 50, y: 520, width: 150, height: 50, color: 'lightgrey' },
{ id: 'N1a', x: 250, y: 520, width: 150, height: 50, color: 'lightgrey' },
// Column 2
{ id: 'W3', x: 450, y: 300, width: 150, height: 50, color: 'white', stroke: 'black' },
{ id: 'N2a', x: 450, y: 400, width: 150, height: 70, color: 'lightgrey' },
{ id: 'W4', x: 650, y: 520, width: 150, height: 70, color: 'white', stroke: 'black' },
{ id: 'N2b', x: 450, y: 520, width: 150, height: 50, color: 'lightgrey' },
{ id: 'N2c', x: 650, y: 650, width: 150, height: 50, color: 'lightgrey' },
// Column 3
{ id: 'W5', x: 850, y: 300, width: 150, height: 70, color: 'white', stroke: 'black' },
{ id: 'W6', x: 1050, y: 300, width: 150, height: 50, color: 'white', stroke: 'black' },
{ id: 'W7', x: 1250, y: 300, width: 150, height: 50, color: 'white', stroke: 'black' },
{ id: 'N3a', x: 850, y: 450, width: 150, height: 70, color: 'lightgrey' },
{ id: 'N3b', x: 1050, y: 450, width: 150, height: 70, color: 'lightgrey' },
{ id: 'N3c', x: 1250, y: 450, width: 150, height: 70, color: 'lightgrey' }
];
const links = [
{ source: 'P1', target: 'G1', path: 'M200,150 V95 H700 V70' },
{ source: 'P2', target: 'G1', path: 'M700,150 V70' },
{ source: 'P3', target: 'G1', path: 'M1200,150 V95 H700 V70' },
{ source: 'W1', target: 'P1', path: 'M325,300 V225 H200 V200' },
{ source: 'N1e', target: 'W1', path: 'M125,275 H250' },
{ source: 'N1f', target: 'W1', path: 'M125,345 H250' },
{ source: 'N1b_2d', target: 'W1', path: 'M325,400 V350' },
{ source: 'W2', target: 'P1', path: 'M125,600 V225 H200 V200' },
{ source: 'N1c', target: 'W2', path: 'M125,475 V580 H125 V600' },
{ source: 'N1d', target: 'W2', path: 'M125,545 V600' },
{ source: 'N1a', target: 'W2', path: 'M325,545 H200 V600' },
{ source: 'W3', target: 'P2', path: 'M525,300 V225 H700 V200' },
{ source: 'N1b_2d', target: 'W3', path: 'M400,435 H450' },
{ source: 'N2a', target: 'W3', path: 'M525,400 V350' },
{ source: 'W4', target: 'P2', path: 'M725,520 V225 H700 V200' },
{ source: 'N2a', target: 'W4', path: 'M525,470 V555 H650' },
{ source: 'N2b', target: 'W4', path: 'M525,545 H650' },
{ source: 'N2c', target: 'W4', path: 'M725,650 V590' },
{ source: 'W5', target: 'P3', path: 'M925,300 V225 H1200 V200' },
{ source: 'N3a', target: 'W5', path: 'M925,450 V370' },
{ source: 'W6', target: 'P3', path: 'M1125,300 V225 H1200 V200' },
{ source: 'N3b', target: 'W6', path: 'M1125,450 V350' },
{ source: 'W7', target: 'P3', path: 'M1325,300 V225 H1200 V200' },
{ source: 'N3c', target: 'W7', path: 'M1325,450 V350' },
{ source: 'W1', target: 'W3', path: 'M400,325 H450' },
{ source: 'N2a', target: 'N1b_2d', path: 'M450,435 H400' },
{ source: 'W2', target: 'W4', path: 'M200,635 H650' }
];
const svg = d3.select("#flowchart");
// Define arrow marker
svg.append("defs").append("marker")
.attr("id", "arrow")
.attr("viewBox", "0 -5 10 10")
.attr("refX", 8)
.attr("refY", 0)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5")
.attr("fill", "black");
// Draw links
svg.append("g")
.selectAll("path")
.data(links)
.enter().append("path")
.attr("d", d => d.path)
.attr("stroke", "black")
.attr("stroke-width", 1.5)
.attr("fill", "none")
.attr("marker-end", "url(#arrow)");
// Draw nodes
const nodeGroup = svg.append("g")
.selectAll("g")
.data(nodes)
.enter().append("g")
.attr("transform", d => `translate(${d.x}, ${d.y})`);
nodeGroup.append("rect")
.attr("width", d => d.width)
.attr("height", d => d.height)
.attr("fill", d => d.color)
.attr("stroke", d => d.stroke || "none")
.attr("stroke-width", 1.5);
</script>
</body>
</html>

389
test1-claude-4/index.html Normal file
View File

@ -0,0 +1,389 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>US Critical Minerals Security Strategy</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
body {
margin: 0;
padding: 20px;
font-family: Arial, sans-serif;
background-color: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.overall-goal {
background: #8bc34a;
color: white;
padding: 20px;
text-align: center;
font-weight: bold;
font-size: 18px;
margin-bottom: 30px;
border-radius: 8px;
}
.goal-title {
font-size: 14px;
margin-bottom: 10px;
}
.goal-text {
font-size: 24px;
}
.node {
cursor: pointer;
}
.goal-node {
fill: #2c3e50;
stroke: #2c3e50;
stroke-width: 2;
}
.action-node {
fill: #95a5a6;
stroke: #7f8c8d;
stroke-width: 1;
}
.kpi-node {
fill: white;
stroke: #2c3e50;
stroke-width: 2;
}
.node-text {
fill: white;
font-size: 12px;
text-anchor: middle;
dominant-baseline: middle;
font-weight: bold;
}
.kpi-text {
fill: #2c3e50;
font-size: 10px;
text-anchor: middle;
dominant-baseline: middle;
}
.arrow {
fill: none;
stroke: #666;
stroke-width: 2;
marker-end: url(#arrowhead);
}
.goal-number {
fill: white;
font-size: 48px;
font-weight: bold;
text-anchor: middle;
dominant-baseline: middle;
}
.goal-title-text {
fill: white;
font-size: 14px;
font-weight: bold;
text-anchor: start;
dominant-baseline: middle;
}
.goal-description {
fill: white;
font-size: 10px;
text-anchor: start;
dominant-baseline: middle;
}
</style>
</head>
<body>
<div class="container">
<div class="overall-goal">
<div class="goal-title">OVERALL GOAL</div>
<div class="goal-text">US critical minerals security</div>
</div>
<svg id="flowchart" width="1160" height="800"></svg>
</div>
<script>
const svg = d3.select("#flowchart");
const width = 1160;
const height = 800;
// 定义箭头标记
svg.append("defs").append("marker")
.attr("id", "arrowhead")
.attr("viewBox", "0 -5 10 10")
.attr("refX", 8)
.attr("refY", 0)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5")
.attr("fill", "#666");
// 主要目标节点数据
const goalNodes = [
{
id: "goal1",
x: 160,
y: 180,
width: 300,
height: 80,
number: "1",
title: "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"
},
{
id: "goal2",
x: 480,
y: 180,
width: 300,
height: 80,
number: "2",
title: "Reduce import reliance from non-allied countries",
kpi: "KPIs: Percentage of US critical minerals imported from or processed by non-allied countries"
},
{
id: "goal3",
x: 800,
y: 180,
width: 300,
height: 80,
number: "3",
title: "Reduce the risk of critical minerals market disruptions",
kpi: "KPIs: No. of months of critical mineral reserves; percentage of value chain data collected"
}
];
// 行动节点数据
const actionNodes = [
// Goal 1 actions
{ id: "1a", x: 50, y: 320, width: 120, height: 60, text: "(1a) Develop critical minerals workforce" },
{ id: "1b", x: 50, y: 400, width: 120, height: 60, text: "(1b) Increase investment in R&D for new tech and AI usage" },
{ id: "1c", x: 50, y: 480, width: 120, height: 60, text: "(1c) Improve permitting and regulations" },
{ id: "1d", x: 50, y: 560, width: 120, height: 60, text: "(1d) Support domestic exploration with funding, data, and AI tools" },
{ id: "increase", x: 240, y: 320, width: 120, height: 60, text: "Increase domestic production" },
{ id: "support", x: 240, y: 440, width: 120, height: 60, text: "(1b)/(2d) Support secondary processing expansion" },
{ id: "financing", x: 240, y: 520, width: 120, height: 60, text: "(1a) Increase domestic financing and incentives" },
{ id: "expand", x: 180, y: 640, width: 120, height: 60, text: "Expand US critical minerals project pipeline" },
// Goal 2 actions
{ id: "2a", x: 380, y: 400, width: 120, height: 60, text: "(2a) Expand the role and use of existing multilateral arrangements" },
{ id: "2b", x: 530, y: 400, width: 120, height: 60, text: "(2b) Expand US government financing tools" },
{ id: "2c", x: 460, y: 520, width: 120, height: 60, text: "(2c) Support exploration and data initiatives" },
{ id: "increase2", x: 480, y: 320, width: 120, height: 60, text: "Increase allied production" },
{ id: "expand2", x: 460, y: 580, width: 120, height: 60, text: "Expand allied critical minerals project pipeline" },
// Goal 3 actions
{ id: "3a", x: 700, y: 400, width: 120, height: 60, text: "(3a) Develop a real-time value chain mapping and monitoring system" },
{ id: "3b", x: 850, y: 400, width: 120, height: 60, text: "(3b) Expand purpose and use of National Defense Stockpile" },
{ id: "3c", x: 1000, y: 400, width: 120, height: 60, text: "(3c) Identify and deploy targeted market incentives" },
{ id: "monitor", x: 720, y: 320, width: 120, height: 60, text: "Monitor critical minerals value chain vulnerabilities" },
{ id: "reduce1", x: 850, y: 320, width: 120, height: 60, text: "Reduce supply disruption risks" },
{ id: "reduce2", x: 1000, y: 320, width: 120, height: 60, text: "Reduce price disruption risks" }
];
// 绘制主要目标节点
goalNodes.forEach(goal => {
const g = svg.append("g").attr("class", "goal-group");
// 主要矩形
g.append("rect")
.attr("x", goal.x)
.attr("y", goal.y)
.attr("width", goal.width)
.attr("height", goal.height)
.attr("class", "goal-node")
.attr("rx", 5);
// 数字圆形
g.append("circle")
.attr("cx", goal.x + 30)
.attr("cy", goal.y + 40)
.attr("r", 25)
.attr("fill", "white");
g.append("text")
.attr("x", goal.x + 30)
.attr("y", goal.y + 40)
.attr("class", "goal-number")
.attr("fill", "#2c3e50")
.text(goal.number);
// 标题文本
const titleLines = goal.title.split(' ');
const midIndex = Math.ceil(titleLines.length / 2);
const firstLine = titleLines.slice(0, midIndex).join(' ');
const secondLine = titleLines.slice(midIndex).join(' ');
g.append("text")
.attr("x", goal.x + 70)
.attr("y", goal.y + 30)
.attr("class", "goal-title-text")
.text(firstLine);
g.append("text")
.attr("x", goal.x + 70)
.attr("y", goal.y + 50)
.attr("class", "goal-title-text")
.text(secondLine);
// KPI文本框
g.append("rect")
.attr("x", goal.x)
.attr("y", goal.y + 90)
.attr("width", goal.width)
.attr("height", 40)
.attr("class", "kpi-node")
.attr("rx", 3);
// 分行显示KPI文本
const kpiLines = wrapText(goal.kpi, 50);
kpiLines.forEach((line, i) => {
g.append("text")
.attr("x", goal.x + 10)
.attr("y", goal.y + 105 + i * 12)
.attr("class", "kpi-text")
.attr("text-anchor", "start")
.text(line);
});
});
// 绘制行动节点
actionNodes.forEach(action => {
const g = svg.append("g").attr("class", "action-group");
g.append("rect")
.attr("x", action.x)
.attr("y", action.y)
.attr("width", action.width)
.attr("height", action.height)
.attr("class", "action-node")
.attr("rx", 3);
// 分行显示文本
const textLines = wrapText(action.text, 15);
textLines.forEach((line, i) => {
g.append("text")
.attr("x", action.x + action.width/2)
.attr("y", action.y + action.height/2 + (i - textLines.length/2 + 0.5) * 12)
.attr("class", "node-text")
.text(line);
});
});
// 绘制连接线
const connections = [
// 从overall goal到三个主要目标的连接
{ from: { x: 580, y: 140 }, to: { x: 310, y: 180 } },
{ from: { x: 580, y: 140 }, to: { x: 630, y: 180 } },
{ from: { x: 580, y: 140 }, to: { x: 950, y: 180 } },
// Goal 1的连接
{ from: { x: 110, y: 320 }, to: { x: 240, y: 340 } },
{ from: { x: 110, y: 400 }, to: { x: 240, y: 380 } },
{ from: { x: 110, y: 480 }, to: { x: 240, y: 460 } },
{ from: { x: 110, y: 560 }, to: { x: 180, y: 640 } },
{ from: { x: 300, y: 640 }, to: { x: 460, y: 580 } },
{ from: { x: 360, y: 340 }, to: { x: 480, y: 320 } },
{ from: { x: 310, y: 260 }, to: { x: 300, y: 320 } },
// Goal 2的连接
{ from: { x: 440, y: 400 }, to: { x: 480, y: 350 } },
{ from: { x: 590, y: 400 }, to: { x: 540, y: 350 } },
{ from: { x: 520, y: 520 }, to: { x: 520, y: 580 } },
{ from: { x: 630, y: 260 }, to: { x: 540, y: 320 } },
// Goal 3的连接
{ from: { x: 760, y: 400 }, to: { x: 780, y: 350 } },
{ from: { x: 910, y: 400 }, to: { x: 910, y: 350 } },
{ from: { x: 1060, y: 400 }, to: { x: 1060, y: 350 } },
{ from: { x: 950, y: 260 }, to: { x: 780, y: 320 } },
{ from: { x: 950, y: 260 }, to: { x: 910, y: 320 } },
{ from: { x: 950, y: 260 }, to: { x: 1060, y: 320 } }
];
connections.forEach(conn => {
svg.append("line")
.attr("class", "arrow")
.attr("x1", conn.from.x)
.attr("y1", conn.from.y)
.attr("x2", conn.to.x)
.attr("y2", conn.to.y);
});
// 从总目标到三个主要目标的向上箭头
svg.append("line")
.attr("class", "arrow")
.attr("x1", 580)
.attr("y1", 100)
.attr("x2", 310)
.attr("y2", 140)
.attr("transform", "rotate(-15 580 100)");
svg.append("line")
.attr("class", "arrow")
.attr("x1", 580)
.attr("y1", 100)
.attr("x2", 630)
.attr("y2", 140);
svg.append("line")
.attr("class", "arrow")
.attr("x1", 580)
.attr("y1", 100)
.attr("x2", 950)
.attr("y2", 140)
.attr("transform", "rotate(15 580 100)");
// 文本换行函数
function wrapText(text, maxLength) {
const words = text.split(' ');
const lines = [];
let currentLine = '';
words.forEach(word => {
if ((currentLine + word).length <= maxLength) {
currentLine += (currentLine ? ' ' : '') + word;
} else {
if (currentLine) lines.push(currentLine);
currentLine = word;
}
});
if (currentLine) lines.push(currentLine);
return lines;
}
// 添加交互性
svg.selectAll(".goal-group, .action-group")
.on("mouseover", function() {
d3.select(this).style("opacity", 0.8);
})
.on("mouseout", function() {
d3.select(this).style("opacity", 1);
})
.on("click", function() {
console.log("节点被点击");
});
</script>
</body>
</html>

16
test1/index.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>US Critical Minerals Security Strategy</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="chart-container">
<!-- 图表将通过D3.js渲染到此处 -->
</div>
<script src="script.js"></script>
</body>
</html>

239
test1/script.js Normal file
View File

@ -0,0 +1,239 @@
document.addEventListener('DOMContentLoaded', function() {
const width = document.getElementById('chart-container').clientWidth;
const height = document.getElementById('chart-container').clientHeight;
const svg = d3.select("#chart-container")
.append("svg")
.attr("width", width)
.attr("height", height);
// 添加箭头标记定义
svg.append("defs").append("marker")
.attr("id", "arrowhead")
.attr("viewBox", "0 -5 10 10")
.attr("refX", 8)
.attr("refY", 0)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5")
.attr("fill", "#666");
// 定义节点数据
const nodes = [
// 目标节点
{id: "goal", label: "OVERALL GOAL\nUS critical minerals security", x: width/2, y: 70, width: 300, height: 80, type: "goal"},
// 三大核心策略节点
{id: "strategy1", label: "1", x: 160, y: 180, width: 240, height: 100, type: "primary"},
{id: "strategy1_text", label: "Rebuild US critical minerals\nindustry competitiveness", x: 160, y: 175, width: 200, height: 30, type: "label"},
{id: "kpi1", label: "KPIs: Average time from discovery to construction; no.\nof workers in CM industry; no. of projects in pipeline", x: 160, y: 220, width: 240, height: 40, type: "kpi"},
{id: "strategy2", label: "2", x: 480, y: 180, width: 240, height: 100, type: "primary"},
{id: "strategy2_text", label: "Reduce import reliance from\nnon-allied countries", x: 480, y: 175, width: 200, height: 30, type: "label"},
{id: "kpi2", label: "KPIs: Percentage of US critical minerals imported from\nor processed by non-allied countries", x: 480, y: 220, width: 240, height: 40, type: "kpi"},
{id: "strategy3", label: "3", x: 800, y: 180, width: 240, height: 100, type: "primary"},
{id: "strategy3_text", label: "Reduce the risk of critical minerals\nmarket disruptions", x: 800, y: 175, width: 200, height: 30, type: "label"},
{id: "kpi3", label: "KPIs: No. of months of critical mineral reserves;\npercentage of value chain data collected", x: 800, y: 220, width: 240, height: 40, type: "kpi"},
// 二级节点
{id: "1e", label: "(1e) Develop\ncritical minerals\nworkforce", x: 60, y: 320, width: 100, height: 80, type: "grey"},
{id: "1d", label: "(1d) Increase\ninvestment in\nR&D for new\ntech and AI\nusage", x: 60, y: 440, width: 100, height: 80, type: "grey"},
{id: "1c", label: "(1c) Improve\npermitting and\nregulations", x: 60, y: 560, width: 100, height: 80, type: "grey"},
{id: "1b", label: "(1b)/(2d)\nSupport\nsecondary\nprocessing\nexpansion", x: 260, y: 440, width: 100, height: 80, type: "grey"},
{id: "domestic_prod", label: "Increase\ndomestic\nproduction", x: 260, y: 320, width: 100, height: 80, type: "secondary"},
{id: "allied_prod", label: "Increase allied\nproduction", x: 480, y: 320, width: 100, height: 80, type: "secondary"},
{id: "2a", label: "(2a) Expand the\nrole and use of\nexisting\nmultilateral\narrangements", x: 380, y: 440, width: 100, height: 80, type: "grey"},
{id: "2b", label: "(2b) Expand US\ngovernment\nfinancing tools", x: 580, y: 440, width: 100, height: 80, type: "grey"},
{id: "1a", label: "(1a) Increase\ndomestic\nfinancing and\nincentives", x: 260, y: 560, width: 100, height: 80, type: "grey"},
{id: "allied_pipeline", label: "Expand allied\ncritical minerals\nproject pipeline", x: 480, y: 560, width: 100, height: 80, type: "secondary"},
{id: "2c", label: "(2c) Support\nexploration and\ndata initiatives", x: 480, y: 680, width: 100, height: 80, type: "grey"},
{id: "3a", label: "(3a) Develop a\nreal-time value\nchain mapping\nand monitoring\nsystem", x: 690, y: 440, width: 100, height: 80, type: "grey"},
{id: "3b", label: "(3b) Expand\npurpose and\nuse of National\nDefense\nStockpile", x: 790, y: 440, width: 100, height: 80, type: "grey"},
{id: "3c", label: "(3c) Identify and\ndeploy targeted\nmarket\nincentives", x: 890, y: 440, width: 100, height: 80, type: "grey"},
{id: "value_chain", label: "Monitor critical\nminerals value\nchain\nvulnerabilities", x: 690, y: 320, width: 100, height: 80, type: "secondary"},
{id: "supply_risk", label: "Reduce supply\ndisruption risks", x: 790, y: 320, width: 100, height: 80, type: "secondary"},
{id: "price_risk", label: "Reduce price\ndisruption risks", x: 890, y: 320, width: 100, height: 80, type: "secondary"},
{id: "1d_pipeline", label: "Expand US\ncritical minerals\nproject pipeline", x: 160, y: 680, width: 100, height: 80, type: "secondary"}
];
// 定义连接数据
const links = [
{source: "goal", target: "strategy1"},
{source: "goal", target: "strategy2"},
{source: "goal", target: "strategy3"},
{source: "1e", target: "domestic_prod"},
{source: "domestic_prod", target: "strategy1"},
{source: "1d", target: "1e"},
{source: "1c", target: "1b"},
{source: "1b", target: "domestic_prod"},
{source: "domestic_prod", target: "allied_prod"},
{source: "allied_prod", target: "strategy2"},
{source: "2a", target: "allied_prod"},
{source: "2a", target: "2b"},
{source: "2b", target: "2a"},
{source: "1a", target: "1b"},
{source: "1a", target: "allied_pipeline"},
{source: "allied_pipeline", target: "allied_prod"},
{source: "2c", target: "allied_pipeline"},
{source: "1d", target: "1d_pipeline"},
{source: "1d_pipeline", target: "1c"},
{source: "3a", target: "value_chain"},
{source: "value_chain", target: "strategy3"},
{source: "3b", target: "supply_risk"},
{source: "supply_risk", target: "strategy3"},
{source: "3c", target: "price_risk"},
{source: "price_risk", target: "strategy3"}
];
// 绘制连接线
svg.selectAll("path.link")
.data(links)
.enter()
.append("path")
.attr("class", "link")
.attr("d", function(d) {
const source = nodes.find(n => n.id === d.source);
const target = nodes.find(n => n.id === d.target);
// 确定连接点位置
let sourceX, sourceY, targetX, targetY;
if (source.y < target.y) {
sourceX = source.x;
sourceY = source.y + source.height/2;
targetX = target.x;
targetY = target.y - target.height/2;
} else if (source.y > target.y) {
sourceX = source.x;
sourceY = source.y - source.height/2;
targetX = target.x;
targetY = target.y + target.height/2;
} else if (source.x < target.x) {
sourceX = source.x + source.width/2;
sourceY = source.y;
targetX = target.x - target.width/2;
targetY = target.y;
} else {
sourceX = source.x - source.width/2;
sourceY = source.y;
targetX = target.x + target.width/2;
targetY = target.y;
}
// 创建路径
return `M${sourceX},${sourceY}L${targetX},${targetY}`;
})
.attr("marker-end", "url(#arrowhead)");
// 绘制节点
const nodeGroups = svg.selectAll("g.node")
.data(nodes.filter(n => !n.id.includes("_text") && !n.id.includes("kpi")))
.enter()
.append("g")
.attr("class", function(d) { return "node " + d.type; });
// 添加矩形
nodeGroups.append("rect")
.attr("x", d => d.x - d.width/2)
.attr("y", d => d.y - d.height/2)
.attr("width", d => d.width)
.attr("height", d => d.height)
.attr("rx", 4)
.attr("ry", 4);
// 添加标签文本
nodeGroups.append("text")
.attr("x", d => d.x)
.attr("y", d => d.y)
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(d => d.label)
.each(function(d) {
// 处理多行文本
const text = d3.select(this);
const words = d.label.split('\n');
text.text(null);
words.forEach((word, i) => {
text.append("tspan")
.attr("x", d.x)
.attr("dy", i ? "1.2em" : "0")
.text(word);
});
});
// 添加策略标题文本
svg.selectAll("text.strategy-text")
.data(nodes.filter(n => n.id.includes("_text")))
.enter()
.append("text")
.attr("class", "strategy-text")
.attr("x", d => d.x)
.attr("y", d => d.y)
.attr("text-anchor", "middle")
.attr("fill", "white")
.each(function(d) {
// 处理多行文本
const text = d3.select(this);
const words = d.label.split('\n');
text.text(null);
words.forEach((word, i) => {
text.append("tspan")
.attr("x", d.x)
.attr("dy", i ? "1.2em" : "0")
.text(word);
});
});
// 添加KPI文本
svg.selectAll("text.kpi")
.data(nodes.filter(n => n.id.includes("kpi")))
.enter()
.append("text")
.attr("class", "kpi")
.attr("x", d => d.x)
.attr("y", d => d.y)
.attr("text-anchor", "middle")
.attr("fill", "white")
.attr("font-size", "10px")
.each(function(d) {
// 处理多行文本
const text = d3.select(this);
const words = d.label.split('\n');
text.text(null);
words.forEach((word, i) => {
text.append("tspan")
.attr("x", d.x)
.attr("dy", i ? "1.2em" : "0")
.text(word);
});
});
});

59
test1/style.css Normal file
View File

@ -0,0 +1,59 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
#chart-container {
width: 100%;
height: 800px;
position: relative;
}
.node {
fill: #ccc;
stroke: #fff;
stroke-width: 2px;
}
.node.goal {
fill: #8BC34A; /* 绿色背景,对应顶部框 */
}
.node.primary {
fill: #000;
color: white;
}
.node.secondary {
fill: #f0f0f0;
}
.node.grey {
fill: #ccc;
}
text {
font-size: 12px;
text-anchor: middle;
dominant-baseline: middle;
fill: #333;
}
.node.primary text {
fill: white;
}
.link {
fill: none;
stroke: #666;
stroke-width: 1.5px;
marker-end: url(#arrowhead);
}
.number-circle {
font-size: 24px;
font-weight: bold;
fill: white;
}

272
test2-gemini-2/index.html Normal file
View File

@ -0,0 +1,272 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>US Critical Minerals Security Flowchart</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
background-color: #f0f2f5;
margin: 0;
padding: 2rem;
}
/* --- Node Styles --- */
.node-group .node-rect {
stroke-width: 1.5;
stroke: #333;
}
.node-goal .node-rect {
fill: #8BC34A; /* A green similar to the image */
stroke: none;
}
.node-pillar .node-rect {
fill: #212121;
stroke: none;
}
.node-action .node-rect {
fill: #E0E0E0; /* A light grey */
stroke: #9E9E9E;
}
.node-intermediate .node-rect {
fill: #FFFFFF;
stroke: #424242;
}
/* --- Text Styles --- */
.node-text {
font-size: 11px;
text-anchor: middle;
fill: #000;
pointer-events: none;
}
.node-pillar .node-text, .node-goal .node-text {
fill: #fff;
}
.node-text .kpi-text {
font-size: 9px;
font-style: italic;
}
/* --- Link Styles --- */
.link {
fill: none;
stroke: #616161;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<div id="chart"></div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
// 1. Data Definition
const nodes = [
// Top Goal
{ id: 'goal', type: 'goal', width: 280, height: 50, text: ['OVERALL GOAL', 'US critical minerals security'] },
// 3 Pillars
{ id: 'p1', type: 'pillar', width: 280, height: 75, text: ['1', 'Rebuild US critical minerals', 'industry competitiveness'], kpis: ['KPIs: Average time from discovery to construction; no.', 'of workers in CM industry; no. of projects in pipeline'] },
{ id: 'p2', type: 'pillar', width: 280, height: 75, text: ['2', 'Reduce import reliance from', 'non-allied countries'], kpis: ['KPIs: Percentage of US critical minerals imported from', 'or processed by non-allied countries'] },
{ id: 'p3', type: 'pillar', width: 280, height: 75, text: ['3', 'Reduce the risk of critical minerals', 'market disruptions'], kpis: ['KPIs: No. of months of critical mineral reserves;', 'percentage of value chain data collected'] },
// Intermediate Nodes
{ id: 'int1', type: 'intermediate', width: 130, height: 40, text: ['Increase domestic', 'production'] },
{ id: 'int2', type: 'intermediate', width: 130, height: 40, text: ['Increase allied', 'production'] },
{ id: 'int3', type: 'intermediate', width: 130, height: 40, text: ['Expand US', 'critical minerals', 'project pipeline'] },
{ id: 'int4', type: 'intermediate', width: 130, height: 40, text: ['Expand allied', 'critical minerals', 'project pipeline'] },
{ id: 'int5', type: 'intermediate', width: 130, height: 50, text: ['Monitor critical', 'minerals value', 'chain', 'vulnerabilities'] },
{ id: 'int6', type: 'intermediate', width: 130, height: 40, text: ['Reduce supply', 'disruption risks'] },
{ id: 'int7', type: 'intermediate', width: 130, height: 40, text: ['Reduce price', 'disruption risks'] },
// Action Nodes (Grey)
{ id: 'a1e', type: 'action', width: 130, height: 45, text: ['(1e) Develop', 'critical minerals', 'workforce'] },
{ id: 'a1f', type: 'action', width: 130, height: 55, text: ['(1f) Increase', 'investment in', 'R&D for new', 'tech and AI', 'usage'] },
{ id: 'a1c', type: 'action', width: 130, height: 45, text: ['(1c) Improve', 'permitting and', 'regulations'] },
{ id: 'a1d', type: 'action', width: 130, height: 55, text: ['(1d) Support', 'domestic', 'exploration with', 'funding, data,', 'and AI tools'] },
{ id: 'a1a', type: 'action', width: 130, height: 45, text: ['(1a) Increase', 'domestic', 'financing and', 'incentives'] },
{ id: 'a1b/2d', type: 'action', width: 130, height: 55, text: ['(1b)/(2d)', 'Support', 'secondary', 'processing', 'expansion'] },
{ id: 'a2a', type: 'action', width: 130, height: 55, text: ['(2a) Expand the', 'role and use of', 'existing', 'multilateral', 'arrangements'] },
{ id: 'a2b', type: 'action', width: 130, height: 45, text: ['(2b) Expand US', 'government', 'financing tools'] },
{ id: 'a2c', type: 'action', width: 130, height: 45, text: ['(2c) Support', 'exploration and', 'data initiatives'] },
{ id: 'a3a', type: 'action', width: 130, height: 55, text: ['(3a) Develop a', 'real-time value', 'chain mapping', 'and monitoring', 'system'] },
{ id: 'a3b', type: 'action', width: 130, height: 55, text: ['(3b) Expand', 'purpose and', 'use of National', 'Defense', 'Stockpile'] },
{ id: 'a3c', type: 'action', width: 130, height: 55, text: ['(3c) Identify and', 'deploy targeted', 'market', 'incentives'] },
];
const links = [
{ source: 'goal', target: 'p1' }, { source: 'goal', target: 'p2' }, { source: 'goal', target: 'p3' },
{ source: 'p1', target: 'int1' }, { source: 'p2', target: 'int2' },
{ source: 'p3', target: 'int5' }, { source: 'p3', target: 'int6' }, { source: 'p3', target: 'int7' },
{ source: 'int1', target: 'a1e' }, { source: 'int1', target: 'a1f' }, { source: 'int1', target: 'a1c' },
{ source: 'int1', target: 'a1b/2d' },
{ source: 'int3', target: 'a1d' },
{ source: 'a1a', target: 'int1' },
{ source: 'a1b/2d', target: 'int2' },
{ source: 'a1c', target: 'int3' }, { source: 'a1f', target: 'int3' },
{ source: 'a2a', target: 'int2' }, { source: 'a2b', target: 'a2a' },
{ source: 'a2a', target: 'int4' },
{ source: 'a2b', target: 'int4' },
{ source: 'int4', target: 'a2c' },
{ source: 'a3a', target: 'int5' }, { source: 'a3b', target: 'int6' }, { source: 'a3c', target: 'int7' },
{ source: 'int6', target: 'a3b' }
];
// 2. SVG Setup and Layout
const width = 960;
const height = 850;
const nodePositions = {
'goal': { x: width / 2, y: 50 },
'p1': { x: 150, y: 170 },
'p2': { x: width / 2, y: 170 },
'p3': { x: width - 150, y: 170 },
'int1': { x: 150, y: 320 },
'int2': { x: width / 2, y: 320 },
'int3': { x: 150, y: 510 },
'int4': { x: width / 2, y: 510 },
'int5': { x: 570, y: 320 },
'int6': { x: 710, y: 320 },
'int7': { x: 850, y: 320 },
'a1e': { x: 50, y: 420 },
'a1f': { x: 50, y: 510 },
'a1c': { x: 50, y: 600 },
'a1d': { x: 150, y: 600 },
'a1a': { x: 250, y: 420 },
'a1b/2d': { x: 250, y: 510 },
'a2a': { x: width / 2, y: 420 },
'a2b': { x: 350, y: 510 },
'a2c': { x: width / 2, y: 600 },
'a3a': { x: 570, y: 420 },
'a3b': { x: 710, y: 420 },
'a3c': { x: 850, y: 420 },
};
const svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height);
// Define arrow markers
svg.append("defs").append("marker")
.attr("id", "arrow")
.attr("viewBox", "0 -5 10 10")
.attr("refX", 8)
.attr("refY", 0)
.attr("markerWidth", 5)
.attr("markerHeight", 5)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5")
.attr("fill", "#616161");
// 3. Render Links
const linkGroup = svg.append("g").attr("class", "links");
links.forEach(link => {
const sourceNode = nodes.find(n => n.id === link.source);
const targetNode = nodes.find(n => n.id === link.target);
const sourcePos = nodePositions[link.source];
const targetPos = nodePositions[link.target];
let pathData;
const sx = sourcePos.x, sy = sourcePos.y;
const tx = targetPos.x, ty = targetPos.y;
const sourceHalfHeight = sourceNode.height / 2;
const targetHalfHeight = targetNode.height / 2;
const sourceHalfWidth = sourceNode.width / 2;
const targetHalfWidth = targetNode.width / 2;
// Simple routing for orthogonal lines
if (sx === tx) { // Vertical
pathData = `M ${sx},${sy < ty ? sy + sourceHalfHeight : sy - sourceHalfHeight} L ${tx},${sy < ty ? ty - targetHalfHeight : ty + targetHalfHeight}`;
} else if (sy === ty) { // Horizontal
pathData = `M ${sx < tx ? sx + sourceHalfWidth : sx - sourceHalfWidth},${sy} L ${tx < sx ? tx + targetHalfWidth : tx - targetHalfWidth},${ty}`;
} else { // L-shaped connectors
const midY = sy + (ty-sy)/2;
pathData = `M ${sx},${sy < ty ? sy + sourceHalfHeight : sy - sourceHalfHeight} L ${sx},${midY} L ${tx},${midY} L ${tx},${sy < ty ? ty - targetHalfHeight : ty + targetHalfHeight}`;
}
// Custom paths based on the diagram
if( (link.source === 'a1c' && link.target === 'int3') || (link.source === 'a1f' && link.target === 'int3') ) {
pathData = `M ${sx + sourceHalfWidth},${sy} L ${tx - targetHalfWidth},${ty}`;
} else if (link.source === 'a1b/2d' && link.target === 'int2') {
pathData = `M ${sx + sourceHalfWidth},${sy} L ${tx - targetHalfWidth},${ty}`;
} else if (link.source === 'a2b' && link.target === 'a2a') {
pathData = `M ${sx},${sy - sourceHalfHeight} L ${tx},${ty + targetHalfHeight}`;
} else if (link.source === 'a2a' && link.target === 'int4') {
pathData = `M ${sx},${sy + sourceHalfHeight} L ${tx},${ty - targetHalfHeight}`;
} else if (link.source === 'a2b' && link.target === 'int4') {
const midX = sx + (tx-sx)/2;
pathData = `M ${sx-sourceHalfWidth},${sy} L ${midX},${sy} L ${midX},${ty+targetHalfHeight}`;
}
linkGroup.append("path")
.attr("class", "link")
.attr("d", pathData)
.attr("marker-end", "url(#arrow)");
});
// 4. Render Nodes
const nodeGroups = svg.selectAll("g.node-group")
.data(nodes)
.enter()
.append("g")
.attr("class", d => `node-group node-${d.type}`)
.attr("transform", d => `translate(${nodePositions[d.id].x}, ${nodePositions[d.id].y})`);
// Add rectangles
nodeGroups.append("rect")
.attr("class", "node-rect")
.attr("x", d => -d.width / 2)
.attr("y", d => -d.height / 2)
.attr("width", d => d.width)
.attr("height", d => d.height)
.attr("rx", 3) // Rounded corners
.attr("ry", 3);
// Add text labels
const text = nodeGroups.append("text")
.attr("class", "node-text")
.attr("dy", d => (d.kpis ? -5 : 0)); // Adjust vertical position for nodes with KPIs
text.each(function(d) {
const el = d3.select(this);
const lineHeight = 12; // Adjust this value for line spacing
const initialY = -( (d.text.length -1) * lineHeight) / 2; // Calculate starting Y to center the block
d.text.forEach((line, i) => {
el.append("tspan")
.attr("x", 0)
.attr("y", initialY + (i * lineHeight))
.text(line)
.style("font-weight", (i === 0 && (d.type === 'pillar' || d.text[0].startsWith('(')) ? "bold" : "normal"));
});
// Add KPI text for pillar nodes
if (d.kpis) {
const kpiYstart = d.height / 2 - (d.kpis.length * 10) - 4;
d.kpis.forEach((line, i) => {
el.append("tspan")
.attr("class", "kpi-text")
.attr("x", 0)
.attr("y", kpiYstart + (i * 10))
.text(line);
});
}
});
</script>
</body>
</html>

222
test2-gemini/index.html Normal file
View File

@ -0,0 +1,222 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>美国关键矿产安全战略</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: #f0f0f0;
display: flex;
justify-content: center;
align-items: center;
margin: 0;
padding: 20px;
}
svg {
background-color: #e9e9e9;
border: 1px solid #ccc;
}
.node rect {
stroke: #333;
stroke-width: 1px;
}
.node-goal rect {
fill: #84a94b;
stroke: none;
}
.node-pillar rect {
fill: #231f20;
stroke: none;
}
.node-action rect {
fill: #cccccc;
}
.node-intermediate rect {
fill: #ffffff;
}
.node text {
font-size: 12px;
fill: #000;
pointer-events: none;
}
.node-goal text, .node-pillar text {
fill: #ffffff;
}
.kpi-text {
font-size: 10px;
font-style: italic;
}
.link {
fill: none;
stroke: #555;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<svg id="chart" width="1200" height="850"></svg>
<script>
const data = {
nodes: [
// Main Goal
{ id: 'goal', type: 'goal', x: 450, y: 20, width: 300, height: 60, text: ['OVERALL GOAL', 'US critical minerals security'] },
// Pillars
{ id: 'pillar1', type: 'pillar', x: 50, y: 120, width: 320, height: 80, text: ['1 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' },
{ id: 'pillar2', type: 'pillar', x: 440, y: 120, width: 320, height: 80, text: ['2 Reduce import reliance from', 'non-allied countries'], kpi: 'KPIs: Percentage of US critical minerals imported from or processed by non-allied countries' },
{ id: 'pillar3', type: 'pillar', x: 830, y: 120, width: 320, height: 80, text: ['3 Reduce the risk of critical minerals', 'market disruptions'], kpi: 'KPIs: No. of months of critical mineral reserves; percentage of value chain data collected' },
// Level 1 Intermediates
{ id: 'increase_domestic_prod', type: 'intermediate', x: 260, y: 300, width: 150, height: 50, text: ['Increase', 'domestic', 'production'] },
{ id: 'increase_allied_prod', type: 'intermediate', x: 480, y: 300, width: 150, height: 50, text: ['Increase allied', 'production'] },
{ id: 'monitor_vuln', type: 'intermediate', x: 750, y: 300, width: 150, height: 50, text: ['Monitor critical', 'minerals value', 'chain', 'vulnerabilities'] },
{ id: 'reduce_supply_risk', type: 'intermediate', x: 920, y: 300, width: 150, height: 50, text: ['Reduce supply', 'disruption risks'] },
{ id: 'reduce_price_risk', type: 'intermediate', x: 1090, y: 300, width: 150, height: 50, text: ['Reduce price', 'disruption risks'] },
// Level 2 Intermediates
{ id: 'expand_us_pipeline', type: 'intermediate', x: 150, y: 650, width: 150, height: 50, text: ['Expand US', 'critical minerals', 'project pipeline'] },
{ id: 'expand_allied_pipeline', type: 'intermediate', x: 550, y: 570, width: 150, height: 50, text: ['Expand allied', 'critical minerals', 'project pipeline'] },
// Actions (Grey Boxes)
{ id: '1e', type: 'action', x: 50, y: 300, width: 150, height: 50, text: ['(1e) Develop', 'critical minerals', 'workforce'] },
{ id: '1f', type: 'action', x: 50, y: 380, width: 150, height: 50, text: ['(1f) Increase', 'investment in', 'R&D for new', 'tech and AI usage'] },
{ id: '1c', type: 'action', x: 50, y: 540, width: 150, height: 50, text: ['(1c) Improve', 'permitting and', 'regulations'] },
{ id: '1d', type: 'action', x: 50, y: 650, width: 150, height: 50, text: ['(1d) Support', 'domestic', 'exploration with', 'funding, data,', 'and AI tools'] },
{ id: '1b_2d', type: 'action', x: 260, y: 460, width: 150, height: 50, text: ['(1b)/(2d)', 'Support', 'secondary', 'processing', 'expansion'] },
{ id: '1a', type: 'action', x: 260, y: 570, width: 150, height: 50, text: ['(1a) Increase', 'domestic', 'financing and', 'incentives'] },
{ id: '2a', type: 'action', x: 480, y: 460, width: 150, height: 50, text: ['(2a) Expand the', 'role and use of', 'existing', 'multilateral', 'arrangements'] },
{ id: '2b', type: 'action', x: 670, y: 460, width: 150, height: 50, text: ['(2b) Expand US', 'government', 'financing tools'] },
{ id: '2c', type: 'action', x: 550, y: 680, width: 150, height: 50, text: ['(2c) Support', 'exploration and', 'data initiatives'] },
{ id: '3a', type: 'action', x: 750, y: 460, width: 150, height: 50, text: ['(3a) Develop a', 'real-time value', 'chain mapping', 'and monitoring', 'system'] },
{ id: '3b', type: 'action', x: 920, y: 460, width: 150, height: 50, text: ['(3b) Expand', 'purpose and', 'use of National', 'Defense', 'Stockpile'] },
{ id: '3c', type: 'action', x: 1090, y: 460, width: 150, height: 50, text: ['(3c) Identify and', 'deploy targeted', 'market', 'incentives'] },
],
links: [
// Arrows
{ source: 'pillar1', target: 'increase_domestic_prod', path: 'M 210 200 V 300' },
{ source: 'pillar2', target: 'increase_allied_prod', path: 'M 600 200 V 300' },
{ source: 'pillar3', target: 'monitor_vuln', path: 'M 825 200 V 300' },
{ source: 'pillar3', target: 'reduce_supply_risk', path: 'M 995 200 V 300' },
{ source: 'pillar3', target: 'reduce_price_risk', path: 'M 1165 200 V 300' },
{ source: 'goal', target: 'pillar1', path: 'M 600 80 V 100 H 210 V 120' },
{ source: 'goal', target: 'pillar2', path: 'M 600 80 V 120' },
{ source: 'goal', target: 'pillar3', path: 'M 600 80 V 100 H 990 V 120' },
{ source: '1e', target: 'increase_domestic_prod', path: 'M 200 325 H 260' },
{ source: '1f', target: 'increase_domestic_prod', path: 'M 125 380 V 360 H 260' },
{ source: '1c', target: 'expand_us_pipeline', path: 'M 125 540 V 640 H 225' },
{ source: '1d', target: 'expand_us_pipeline', path: 'M 200 675 H 225' },
{ source: 'expand_us_pipeline', target: 'increase_domestic_prod', path: 'M 225 650 V 520 H 335 V 350' },
{ source: '1a', target: 'expand_us_pipeline', path: 'M 335 570 V 650' },
{ source: '1b_2d', target: 'increase_domestic_prod', path: 'M 335 460 V 350' },
{ source: '1b_2d', target: 'increase_allied_prod', path: 'M 410 485 H 450 V 350' },
{ source: '2a', target: 'increase_allied_prod', path: 'M 555 460 V 350' },
{ source: 'expand_allied_pipeline', target: 'increase_allied_prod', path: 'M 625 570 V 520 H 555 V 350' },
{ source: '2c', target: 'expand_allied_pipeline', path: 'M 625 680 V 620' },
{ source: '2b', target: 'expand_allied_pipeline', path: 'M 745 510 H 700 V 570' },
{ source: '2a', target: 'expand_allied_pipeline', path: 'M 555 510 H 580 V 570' },
{ source: '3a', target: 'monitor_vuln', path: 'M 825 460 V 350' },
{ source: '3b', target: 'reduce_supply_risk', path: 'M 995 460 V 350' },
{ source: '3c', target: 'reduce_price_risk', path: 'M 1165 460 V 350' },
]
};
const svg = d3.select("#chart");
// Define arrow marker
svg.append("defs").append("marker")
.attr("id", "arrow")
.attr("viewBox", "0 -5 10 10")
.attr("refX", 5)
.attr("refY", 0)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5")
.attr("fill", "#555");
// Draw links
svg.append("g")
.selectAll("path")
.data(data.links)
.enter().append("path")
.attr("class", "link")
.attr("d", d => d.path)
.attr("marker-end", "url(#arrow)");
// Draw nodes
const node = svg.append("g")
.selectAll("g")
.data(data.nodes)
.enter().append("g")
.attr("class", d => `node node-${d.type}`)
.attr("transform", d => `translate(${d.x}, ${d.y})`);
node.append("rect")
.attr("width", d => d.width)
.attr("height", d => d.height);
// Add text to nodes
node.each(function(d) {
const group = d3.select(this);
const lineHeight = 14;
const y_start = (d.height - (d.text.length * lineHeight)) / 2 + 12;
d.text.forEach((line, i) => {
group.append("text")
.attr("x", d.width / 2)
.attr("y", y_start + (i * lineHeight))
.attr("text-anchor", "middle")
.text(line);
});
if (d.kpi) {
const kpiLines = wrapText(d.kpi, d.width - 20);
const kpi_y_start = 45;
kpiLines.forEach((line, i) => {
group.append("text")
.attr("class", "kpi-text")
.attr("x", d.width / 2)
.attr("y", kpi_y_start + (i * 12))
.attr("text-anchor", "middle")
.text(line);
});
}
});
// Text wrapping function for KPIs
function wrapText(text, width) {
const words = text.split(/\s+/).reverse();
let word;
const lines = [];
let line = [];
let tspan = d3.select(document.createElement("tspan"));
const testSvg = d3.select("body").append("svg").attr("width", 0).attr("height", 0);
const textElement = testSvg.append("text").attr("class", "kpi-text");
while (word = words.pop()) {
line.push(word);
textElement.text(line.join(" "));
if (textElement.node().getComputedTextLength() > width && line.length > 1) {
line.pop();
lines.push(line.join(" "));
line = [word];
}
}
lines.push(line.join(" "));
testSvg.remove();
return lines;
}
</script>
</body>
</html>

252
test2-gpt-4.1/index.html Normal file
View File

@ -0,0 +1,252 @@
<!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>