feat(frontend): use custom rich-text editor for node editing and hide blue selection outline when editing; intercept MindElixir dblclick to fire showRichTextEditor; refine styles to suppress outline in editing mode; build dist for Django serving

This commit is contained in:
lixinran 2025-10-15 21:07:59 +08:00
parent 318974511c
commit 327c58151b
10 changed files with 317 additions and 274 deletions

View File

@ -2,8 +2,11 @@ from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('mindmap.urls')),
# Serve built frontend at root
path('', TemplateView.as_view(template_name='index.html'), name='spa-index'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -23,8 +23,8 @@
flex-direction: column;
}
</style>
<script type="module" crossorigin src="/assets/index-baaa8619.js"></script>
<link rel="stylesheet" href="/assets/index-a2fbcb1e.css">
<script type="module" crossorigin src="/assets/index-b0cb73ba.js"></script>
<link rel="stylesheet" href="/assets/index-f016b388.css">
</head>
<body>
<div id="app"></div>

View File

@ -1,5 +1,5 @@
<template>
<div class="mindmap-container">
<div class="mindmap-container" :class="{ 'is-editing': showRichTextEditor }">
<!-- 欢迎页面 -->
<div v-if="showWelcome" class="welcome-page">
<div class="welcome-content" :class="{ 'ai-sidebar-collapsed': isAISidebarCollapsed }">
@ -2212,11 +2212,22 @@ const centerNodeAndEdit = async (nodeId) => {
console.log('✅ 节点已平滑居中显示');
}
//
//
setTimeout(() => {
if (mindElixir.value.beginEdit) {
mindElixir.value.beginEdit(topicElement);
console.log('✅ 节点已进入编辑状态');
try {
const nodeIdPure = (nodeId || '').replace(/^me/, '');
const nodeObj = mindElixir.value.getNodeById
? mindElixir.value.getNodeById(nodeIdPure)
: null;
const openTarget = nodeObj || { id: nodeIdPure, topic: topicElement?.textContent || '' };
if (typeof openCustomEditModal === 'function') {
openCustomEditModal(openTarget, topicElement);
} else if (typeof openRichTextEditor === 'function') {
openRichTextEditor(openTarget, topicElement);
}
console.log('✅ 已打开自定义富文本编辑器');
} catch (e) {
console.warn('打开自定义编辑器失败,忽略。', e);
}
}, 350); // 0.3s + 50ms
} else {
@ -3543,8 +3554,27 @@ const saveNodeEditToAPI = async (node) => {
//
const openEditModal = (node) => {
console.log("打开编辑模态框:", node);
// Mind Elixir
// 使 inline
try {
const nodeId = node?.id || node?.nodeObj?.id;
let nodeElement = null;
if (nodeId) {
nodeElement = document.querySelector(`[data-nodeid="me${nodeId}"]`) ||
document.querySelector(`[data-id="${nodeId}"]`) ||
document.querySelector(`.topic[data-id="${nodeId}"]`);
}
//
if (typeof openCustomEditModal === 'function') {
openCustomEditModal(node?.nodeObj || node, nodeElement);
} else if (typeof openRichTextEditor === 'function') {
openRichTextEditor(node?.nodeObj || node, nodeElement);
}
} catch (e) {
console.warn('打开自定义编辑器失败,回退到内置编辑。', e);
if (mindElixir.value?.editText) {
mindElixir.value.editText(node);
}
}
};
//
@ -5315,6 +5345,11 @@ const findNodeInData = (nodeData, targetId) => {
overflow: visible;
}
/* 打开自定义编辑器时,隐藏内联选中蓝色描边以减少干扰 */
.mindmap-container.is-editing :deep(.map-container .selected) {
outline: none !important;
}
/* 保存控制按钮样式 */
.save-controls {
position: absolute;

View File

@ -20,6 +20,10 @@
outline: 2px solid var(--selected);
outline-offset: 1px;
}
// 当外层容器处于自定义编辑模式时隐藏选中描边
:global(.is-editing) & .selected {
outline: none !important;
}
.hyper-link {
text-decoration: none;
margin-left: 0.3em;

View File

@ -97,9 +97,10 @@ export default function (mind: MindElixirInstance) {
}
// 如果没有图片或富文本内容,则进入普通编辑模式
if (mind.editable) {
mind.beginEdit(target)
}
// 使用宿主应用的自定义编辑器(如果已集成)
// 通过事件总线通知外层打开富文本编辑器
mind.bus.fire('showRichTextEditor', topic.nodeObj, topic)
return
}
// 处理其他双击事件