Files
dsProject/dsLightRag/static/mind_map.html
2025-08-14 15:45:08 +08:00

347 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>初中数学典型题思维导图</title>
<!-- 引入jsmind资源 -->
<link rel="stylesheet" type="text/css" href="./jsmind/jsmind.css" />
<script type="text/javascript" src="./jsmind/jsmind.js"></script>
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
body {
display: flex;
flex-direction: column;
background-color: #f5f5f5;
}
.controls {
margin: 10px;
padding: 10px;
background-color: #e9ecef;
border-radius: 4px;
}
#mindmap-container {
flex: 1;
width: 100%;
overflow: hidden;
}
button { margin-right: 10px; padding: 6px 12px; cursor: pointer; }
#status { margin-left: 10px; color: #666; }
/* 增强的强制右对齐样式 */
.jm-mindmap-container { justify-content: flex-end !important; }
.jm-node-container { flex-direction: row-reverse !important; }
.jm-node-wrap { float: right !important; }
.jm-line-container { justify-content: flex-end !important; }
.jm-right { display: flex !important; }
.jm-left { display: none !important; }
/* 根节点子节点强制右对齐终极方案 */
.jm-root > .jm-node-container > .jm-node-wrap {
float: right !important;
clear: right !important;
position: relative !important;
left: auto !important;
right: 0 !important;
}
/* 修改连接线起点位置 */
.jm-root > .jm-node-container > .jm-line-container {
justify-content: flex-end !important;
padding-left: 0 !important;
padding-right: 20px !important;
}
/* 掌握状态复选框样式 */
.mastery-checkbox { display: none; }
.mastery-checkbox + .mastery-icon { margin-right: 8px; cursor: pointer; }
.mastery-checkbox:not(:checked) + .mastery-icon::before { content: '❌'; color: #ff4d4f; }
.mastery-checkbox:checked + .mastery-icon::before { content: '✅'; color: #52c41a; }
</style>
</head>
<body>
<div class="controls">
<button onclick="addNode()">添加节点</button>
<button onclick="deleteNode()">删除节点</button>
<button onclick="saveMindMap()">保存数据</button>
<button onclick="resetMindMap()">重置画布</button>
<span id="status"></span>
</div>
<div id="mindmap-container"></div>
<script>
// 思维导图初始化配置
const mindMapOptions = {
container: 'mindmap-container',
theme: 'primary',
editable: true,
default_direction: 'right',
mode: 'full',
support_html: true // 添加HTML支持
};
// 初中数学典型题与解题思路数据
// 将军饮马问题及变形数据
const initialData = {
"meta": { "name": "将军饮马问题及变形解题思路", "author": "jsmind" },
"format": "node_tree",
"data": {
"id": "root",
"topic": "<a href='https://mp.weixin.qq.com/s?__biz=MzI1NjYzNjE1NQ==&mid=2247540913&idx=2&sn=7a061ec4c7dbbcc94b8bf2fa7d93f4c9&chksm=ea21c525dd564c33b578037e893e5190b92841f3db0837191864591bb3da0e10d8af7ce5da10&scene=27' target='_blank'>将军饮马问题及变形</a>",
"direction": "right",
"children": [
{
"id": "basic_model",
"topic": "<input type='checkbox' class='mastery-checkbox' data-id='basic_model'> <span class='mastery-icon'></span> 【类型一】 两定一动基本型",
"direction": "right",
"children": [
{ "id": "basic_feature", "topic": "特点两定点A、B位于直线l同侧在l上找一点P使PA+PB最短", "direction": "right" },
{ "id": "basic_solution", "topic": "解题思路作A关于直线l的对称点A'连接A'B交l于点PP即为所求", "direction": "right" },
{ "id": "basic_case", "topic": "典型案例牧马人从A地出发到河边饮水后到B地怎样走路径最短", "direction": "right" }
]
},
{
"id": "two_points_one_line",
"topic": "<input type='checkbox' class='mastery-checkbox' data-id='two_points_one_line'> <span class='mastery-icon'></span> 【类型二】 两次对称型",
"direction": "right",
"children": [
{ "id": "two_points_feature", "topic": "特点定点A、B位于平面内有两条相交直线l、m在l、m上分别找点P、Q使AP+PQ+QB最短", "direction": "right" },
{ "id": "two_points_solution", "topic": "解题思路分别作A关于l的对称点A'B关于m的对称点B'连接A'B'交l于P交m于Q", "direction": "right" },
{ "id": "two_points_case", "topic": "典型案例:台球两次反弹后撞击目标球,求反弹路径", "direction": "right" }
]
},
{
"id": "polygon_model",
"topic": "<input type='checkbox' class='mastery-checkbox' data-id='polygon_model'> <span class='mastery-icon'></span> 【类型三】 平移型",
"direction": "right",
"children": [
{ "id": "polygon_feature", "topic": "特点:在多边形边上找一点或多点,使路径之和最短", "direction": "right" },
{ "id": "polygon_solution", "topic": "解题思路:利用多边形对称性,通过多次作对称点转化为直线距离问题", "direction": "right" },
{ "id": "polygon_case", "topic": "典型案例矩形ABCD中E为AB上一点在BC、CD上分别找点P、Q使EP+PQ+QA最短", "direction": "right" }
]
},
{
"id": "angle_model",
"topic": "<input type='checkbox' class='mastery-checkbox' data-id='angle_model'> <span class='mastery-icon'></span> 【类型四】 点到直线垂线段最短",
"direction": "right",
"children": [
{ "id": "angle_feature", "topic": "特点定点A在∠MON内部在OM、ON上分别找点B、C使△ABC周长最短", "direction": "right" },
{ "id": "angle_solution", "topic": "解题思路分别作A关于OM、ON的对称点A'、A''连接A'A''交OM于B交ON于C", "direction": "right" },
{ "id": "angle_case", "topic": "典型案例:锐角三角形内一点到三边距离之和最短问题", "direction": "right" }
]
}
,
{
"id": "angle_model5",
"topic": "<input type='checkbox' class='mastery-checkbox' data-id='angle_model'> <span class='mastery-icon'></span> 【类型五】 三动点“将军饮马”问题",
"direction": "right",
"children": [
{ "id": "angle_feature", "topic": "特点定点A在∠MON内部在OM、ON上分别找点B、C使△ABC周长最短", "direction": "right" },
{ "id": "angle_solution", "topic": "解题思路分别作A关于OM、ON的对称点A'、A''连接A'A''交OM于B交ON于C", "direction": "right" },
{ "id": "angle_case", "topic": "典型案例:锐角三角形内一点到三边距离之和最短问题", "direction": "right" }
]
}
,
{
"id": "angle_model6",
"topic": "<input type='checkbox' class='mastery-checkbox' data-id='angle_model'> <span class='mastery-icon'></span> 【类型六】 相对运动思想的运用",
"direction": "right",
"children": [
{ "id": "angle_feature", "topic": "特点定点A在∠MON内部在OM、ON上分别找点B、C使△ABC周长最短", "direction": "right" },
{ "id": "angle_solution", "topic": "解题思路分别作A关于OM、ON的对称点A'、A''连接A'A''交OM于B交ON于C", "direction": "right" },
{ "id": "angle_case", "topic": "典型案例:锐角三角形内一点到三边距离之和最短问题", "direction": "right" }
]
}
,
{
"id": "angle_model7",
"topic": "<input type='checkbox' class='mastery-checkbox' data-id='angle_model'> <span class='mastery-icon'></span> 【类型七】 先找“河”,再“饮马”",
"direction": "right",
"children": [
{ "id": "angle_feature", "topic": "特点定点A在∠MON内部在OM、ON上分别找点B、C使△ABC周长最短", "direction": "right" },
{ "id": "angle_solution", "topic": "解题思路分别作A关于OM、ON的对称点A'、A''连接A'A''交OM于B交ON于C", "direction": "right" },
{ "id": "angle_case", "topic": "典型案例:锐角三角形内一点到三边距离之和最短问题", "direction": "right" }
]
}
]
}
};
// 创建思维导图实例
const jm = new jsMind(mindMapOptions);
// 初始化函数
function init() {
// 彻底清除所有可能相关的本地存储数据
localStorage.removeItem('mindMapData');
localStorage.removeItem('savedMindMap');
localStorage.removeItem('jsmind_state');
// 尝试从本地存储加载数据
const savedData = localStorage.getItem('mindMapData');
if (savedData) {
try {
const data = JSON.parse(savedData);
jm.show(data);
updateStatus('已加载保存的数据');
return;
} catch (e) {
updateStatus('加载保存数据失败,使用初始数据');
}
}
// 使用初始数据
jm.show(initialData);
}
// 状态更新函数
function updateStatus(message) {
const statusElement = document.getElementById('status');
if (statusElement) {
statusElement.textContent = message;
setTimeout(() => { statusElement.textContent = ''; }, 3000);
}
}
// 保存思维导图数据到本地存储
function saveMindMapData() {
const mindData = jm.get_data();
localStorage.setItem('mindMapData', JSON.stringify(mindData));
}
// 添加节点功能
function addNode() {
const selectedNode = jm.get_selected_node();
if (!selectedNode) {
updateStatus('请先选择一个节点');
return;
}
const newNodeId = Date.now().toString();
const newNodeText = prompt('请输入新节点内容:', '新节点');
if (!newNodeText) return;
// 关键修复使用jsmind原生支持的side参数移除direction和position
jm.add_node(selectedNode, newNodeId, newNodeText, {
side: 'right' // 这是jsmind库控制节点位置的原生参数
});
saveMindMapData();
updateStatus('节点添加成功');
}
// 删除节点功能
function deleteNode() {
const selectedNode = jm.get_selected_node();
if (!selectedNode || selectedNode.isroot) {
updateStatus('不能删除根节点');
return;
}
jm.remove_node(selectedNode.id);
saveMindMapData();
updateStatus('节点已删除');
}
// 保存思维导图
function saveMindMap() {
saveMindMapData();
updateStatus('思维导图已保存到本地存储');
}
// 重置思维导图
function resetMindMap() {
if (confirm('确定要重置思维导图吗?当前数据将丢失')) {
localStorage.removeItem('mindMapData');
jm.show(initialData);
updateStatus('思维导图已重置');
}
}
// 页面加载完成后初始化
window.onload = function() {
init();
// Fixed: Use reliable timeout instead of unsupported event listener
setTimeout(initMasteryCheckboxes, 50);
};
// 初始化掌握状态复选框
function initMasteryCheckboxes() {
// 从本地存储加载掌握状态
const masteryStates = JSON.parse(localStorage.getItem('masteryStates') || '{}');
// 添加重试机制确保节点加载完成
const initializeNodes = () => {
let nodesInitialized = 0;
const totalNodes = Object.keys(masteryStates).length;
Object.keys(masteryStates).forEach(id => {
const checkbox = document.querySelector(`.mastery-checkbox[data-id='${id}']`);
if (checkbox) {
checkbox.checked = masteryStates[id];
// 重试获取节点直到成功或超时
let attempts = 0;
const maxAttempts = 5;
const tryGetNode = () => {
const node = jm.get_node(id);
if (node) {
nodesInitialized++;
if (checkbox.checked) {
jm.collapse_node(node);
// 确保子节点也被收起
if (node.children && node.children.length > 0) {
node.children.forEach(childId => {
const childNode = jm.get_node(childId);
if (childNode) jm.collapse_node(childNode);
});
}
} else {
jm.expand_node(node);
}
} else if (attempts < maxAttempts) {
attempts++;
setTimeout(tryGetNode, 200); // 200ms后重试
}
};
tryGetNode();
}
});
};
initializeNodes();
// 为所有复选框添加事件监听
document.addEventListener('click', function(e) {
if (e.target.classList.contains('mastery-icon')) {
const checkbox = e.target.previousElementSibling;
if (checkbox && checkbox.classList.contains('mastery-checkbox')) {
checkbox.checked = !checkbox.checked;
saveMasteryState(checkbox.dataset.id, checkbox.checked);
const node = jm.get_node(checkbox.dataset.id);
if (node) {
if (checkbox.checked) {
jm.collapse_node(node);
// 收起所有子节点
if (node.children && node.children.length > 0) {
node.children.forEach(childId => {
const childNode = jm.get_node(childId);
if (childNode) jm.collapse_node(childNode);
});
}
} else {
jm.expand_node(node);
}
}
}
}
});
}
// 保存掌握状态到本地存储
function saveMasteryState(modelId, isMastered) {
const masteryStates = JSON.parse(localStorage.getItem('masteryStates') || '{}');
masteryStates[modelId] = isMastered;
localStorage.setItem('masteryStates', JSON.stringify(masteryStates));
}
</script>
</body>
</html>