Browse Source

feat(ChapterPanel): 实现章节树形结构管理和右键菜单功能

- 添加el-tree组件实现章节树形展示
- 实现右键菜单功能用于添加章节
- 添加章节对话框支持新增章节到树中
- 更新README.md中已完成的任务标记
YourName 3 tuần trước cách đây
mục cha
commit
4acc697e7d
2 tập tin đã thay đổi với 131 bổ sung10 xóa
  1. 2 2
      README.md
  2. 129 8
      src/components/ChapterPanel.vue

+ 2 - 2
README.md

@@ -47,8 +47,8 @@ npm install epubjs
 ```
 
 ### 近期目标(v0.1)
-- [ ] 实现基础文本编辑器
-- [ ] 完成章节树形结构管理
+- [x] 实现基础文本编辑器
+- [x] 完成章节树形结构管理
 
 ### 中期目标(v0.5)
 - [ ] 添加元数据编辑面板

+ 129 - 8
src/components/ChapterPanel.vue

@@ -1,9 +1,36 @@
 <template>
   <el-tabs v-model="activeTab" class="chapter-tabs">
     <el-tab-pane label="章节列表" name="chapters">
-      <div class="chapter-list">
-        章节列表
+      <div class="tree-container" @contextmenu="handleContextMenu">
+        <el-tree
+          class="chapter-list"
+          :data="chapterData"
+          node-key="id"
+          :props="{ label: 'label', children: 'children' }"
+          default-expand-all
+        ></el-tree>
       </div>
+
+      <!-- 右键菜单 -->
+      <div
+        v-show="contextMenuVisible"
+        class="custom-context-menu"
+        :style="{ top: contextMenuPosition.y + 'px', left: contextMenuPosition.x + 'px' }"
+        @click.stop
+      >
+        <ul>
+          <li @click="openAddDialog">添加章节</li>
+        </ul>
+      </div>
+
+      <!-- 添加章节对话框 -->
+      <el-dialog title="添加章节" v-model="showAddDialog" width="300px">
+        <el-input v-model="newChapterName" placeholder="输入章节名称"></el-input>
+        <template #footer>
+          <el-button @click="showAddDialog = false">取消</el-button>
+          <el-button type="primary" @click="addChapter">确定</el-button>
+        </template>
+      </el-dialog>
     </el-tab-pane>
     <el-tab-pane label="书籍信息" name="info">
       <div class="book-info">
@@ -14,16 +41,110 @@
 </template>
 
 <script setup>
-import { ref } from 'vue';
-import { ElTabs, ElTabPane } from 'element-plus';
+import { ref, onUnmounted, watch } from 'vue';
+import { ElTabs, ElTabPane, ElTree, ElInput, ElButton, ElDialog } from 'element-plus';
 const activeTab = ref('chapters');
+const contextMenuVisible = ref(false);
+const contextMenuPosition = ref({ x: 0, y: 0 });
+const currentNode = ref(null);
+const chapterData = ref([{
+  id: 1,
+  label: '第一章',
+  children: [{
+    id: 2,
+    label: '1.1 节'
+  }]
+}]);
+const showAddDialog = ref(false);
+const newChapterName = ref('');
+
+// 处理右键菜单事件
+const handleContextMenu = (event) => {
+  event.preventDefault();
+  console.log('右键菜单事件触发');
+  console.log('事件目标:', event.target);
+  console.log('当前鼠标位置:', { x: event.clientX, y: event.clientY });
+  contextMenuPosition.value = { x: event.clientX, y: event.clientY };
+  contextMenuVisible.value = true;
+  console.log('设置contextMenuVisible为true');
+  // 阻止事件冒泡
+  event.stopPropagation();
+};
+
+
+// 监控contextMenuVisible变化
+watch(contextMenuVisible, (newValue) => {
+  console.log('contextMenuVisible变化:', newValue);
+});
+
+// 打开添加章节对话框
+const openAddDialog = () => {
+  contextMenuVisible.value = false;
+  showAddDialog.value = true;
+  newChapterName.value = '';
+};
+
+// 添加章节
+const addChapter = () => {
+  if (!newChapterName.value.trim()) {
+    console.log('章节名称不能为空');
+    return;
+  }
+
+  const newChapter = {
+    id: Date.now(),
+    label: newChapterName.value.trim(),
+    children: []
+  };
+
+  if (currentNode.value) {
+    // 如果有选中节点,添加为子节点
+    if (!currentNode.value.children) {
+      currentNode.value.children = [];
+    }
+    currentNode.value.children.push(newChapter);
+  } else {
+    // 如果没有选中节点,添加到根节点
+    chapterData.value.push(newChapter);
+  }
+
+  showAddDialog.value = false;
+  console.log('章节添加成功');
+};
+
+// 点击其他区域关闭菜单
+const handleClickOutside = (e) => {
+  const contextMenu = document.querySelector('.custom-context-menu');
+  if (contextMenu && !contextMenu.contains(e.target)) {
+    contextMenuVisible.value = false;
+  }
+};
+
+document.addEventListener('click', handleClickOutside);
+
+onUnmounted(() => {
+  document.removeEventListener('click', handleClickOutside);
+});
 </script>
 
 <style scoped>
 .chapter-tabs { height: 100%; }
-.chapter-list { padding: 10px; }
-.chapter-item { padding: 8px 12px; margin-bottom: 4px; cursor: pointer; border-radius: 4px; transition: background-color 0.2s; }
-.chapter-item:hover { background-color: #f5f5f5; }
+.tree-container {
+  height: 80vh;
+  overflow: auto;
+}
+.chapter-list { width: 100%; }
+.custom-context-menu {
+  position: fixed;
+  z-index: 9999;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+  min-width: 100px;
+}
+ul { list-style: none; padding: 0; margin: 0; }
+li { padding: 6px 12px; cursor: pointer; }
+li:hover { background-color: #f5f5f5; }
 .book-info { padding: 15px; }
-.info-item { margin-bottom: 10px; }
 </style>