<template>
<view class="mindmap-container">
<view class="canvas">
<MindNode :node="rootNode"
:selectedId="selectedId"
@node-toggle="toggleNode"
@add-child="addChildNode"
@edit-text="editNodeText"/>
</view>
</view>
</template>
<script>
import MindNode from '@/components/MindNode/MindNode.vue';
export default {
components: { MindNode },
data() {
return {
rootNode: null,
selectedId: 'root',
};
},
created() {
this.initRootNode();
},
methods: {
initRootNode() {
this.rootNode = {
id: 'root',
content: '中心主题',
expanded: true,
level: 0,
position: { x: 0, y: 5 },
children: []
};
},
addChildNode(parent) {
// 层级限制:最多10层(根节点为0级)[^3]
if (parent.level >= 9) {
uni.showToast({
title: '已达到最大层级(10层)',
icon: 'none'
});
return;
}
const childCount = parent.children.length;
const newNode = {
id: `node-${Date.now()}`,
content: '新节点',
expanded: true,
level: parent.level + 1,
position: {
x: 180,
y: parent.position.y + (childCount * 60)
},
children: []
};
parent.children.push(newNode);
this.selectedId = newNode.id;
},
toggleNode(node) {
node.expanded = !node.expanded;
this.$forceUpdate();
},
// 编辑节点文本
editNodeText({ node, newText }) {
node.content = newText;
this.$forceUpdate();
}
}
};
</script>
<style scoped>
.mindmap-container {
position: relative;
width: 100vw;
height: 100vh;
overflow: auto;
background: #f8f9fa;
}
.canvas {
position: relative;
padding: 100px;
min-height: 100vh;
min-width: 100vw;
}
</style>
<template>
<view class="node-container" :style="{
left: `${node.position.x}px`,
top: `${node.position.y}px`,
zIndex: 1000 - node.level
}">
<!-- 节点主体 -->
<view class="node-card"
:class="['level-' + node.level, { 'node-selected': node.id === selectedId }]">
<!-- 文本编辑区域 -->
<view v-if="isEditing" class="edit-container">
<input v-model="editText" class="edit-input" auto-focus @blur="saveEdit"
@keyup.enter="saveEdit" />
<view class="edit-btns">
<view class="btn-confirm" @click="saveEdit">✓</view>
<view class="btn-cancel" @click="cancelEdit">✕</view>
</view>
</view>
<!-- 显示文本区域 -->
<view v-else class="content-container" @click="startEdit">
<text class="node-content">{{ node.content }}</text>
</view>
<!-- 操作按钮 -->
<view class="node-actions">
<!-- 添加子节点按钮(层级限制) -->
<view v-if="node.level < 9" class="action-btn" @click.stop="$emit('add-child', node)">
➕
</view>
<!-- 折叠/展开按钮 -->
<view v-if="node.children.length" class="action-btn"
@click.stop="$emit('node-toggle', node)">
{{ node.expanded ? '−' : '+' }}
</view>
</view>
</view>
<!-- 连接线 -->
<view v-if="node.expanded" v-for="child in node.children" :key="child.id"
class="connection">
<svg class="connector-svg">
<path :d="connectorPath(node, child)" stroke="#bbc3cc" stroke-width="1.5" fill="none"/>
</svg>
</view>
<!-- 递归渲染子节点 -->
<MindNode v-for="child in node.children"
v-show="node.expanded"
:key="child.id"
:node="child"
:selectedId="selectedId"
@node-toggle="$emit('node-toggle', $event)"
@add-child="$emit('add-child', $event)"
@edit-text="$emit('edit-text', $event)"/>
</view>
</template>
<script>
export default {
props: {
node: Object,
selectedId: String
},
data() {
return {
isEditing: false,
editText: ''
};
},
methods: {
connectorPath(parent, child) {
const startX = 120;
const startY = 24;
const endX = child.position.x - parent.position.x;
const endY = child.position.y - parent.position.y + 24;
const controlX1 = startX + (endX - startX) * 0.5;
return `M${startX},${startY}
C${controlX1},${startY}
${controlX1},${endY}
${endX},${endY}`;
},
// 启动文本编辑
startEdit() {
this.editText = this.node.content;
this.isEditing = true;
},
// 保存编辑
saveEdit() {
if (this.editText.trim()) {
this.$emit('edit-text', {
node: this.node,
newText: this.editText
});
}
this.isEditing = false;
},
// 取消编辑
cancelEdit() {
this.isEditing = false;
}
}
};
</script>
<style scoped>
.node-container {
position: absolute;
transition: all 0.3s;
}
.node-card {
position: relative;
display: flex;
align-items: center;
min-width: 120px;
height: 48px;
padding: 0 12px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
background: white;
z-index: 1;
}
.content-container {
flex: 1;
padding: 5px 0;
}
.node-content {
font-size: 14px;
line-height: 1.4;
}
.edit-container {
flex: 1;
display: flex;
flex-direction: column;
}
.edit-input {
flex: 1;
border: 1px solid #409eff;
border-radius: 4px;
padding: 4px 8px;
font-size: 14px;
}
.edit-btns {
display: flex;
justify-content: flex-end;
margin-top: 4px;
}
.btn-confirm, .btn-cancel {
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-left: 6px;
font-weight: bold;
cursor: pointer;
}
.btn-confirm {
background: #4CAF50;
color: white;
}
.btn-cancel {
background: #f56c6c;
color: white;
}
.node-actions {
display: flex;
margin-left: 8px;
}
.action-btn {
width: 24px;
height: 24px;
border-radius: 50%;
background: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
margin-left: 6px;
font-weight: bold;
cursor: pointer;
}
.action-btn:hover {
background: #e0e0e0;
}
.connection {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 0;
}
.connector-svg {
position: absolute;
width: 100%;
height: 100%;
overflow: visible;
}
.level-0 .node-card {
background: #ffeb3b;
border: 2px solid #ffc107;
}
</style>
根据我给出的代码做出更改,将根节点的置于水平方向的中央且生成的子节点跟随移动,给子节点添加自动避让