From 5333935ae59999c47653122a669f4326f0173c1c Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期四, 08 一月 2026 14:47:02 +0800
Subject: [PATCH] 确认按钮在左边

---
 src/views/oaSystem/projectManagement/components/taskTree.vue | 1505 ++++++++++++++++++++++++++++++---------------------------
 1 files changed, 783 insertions(+), 722 deletions(-)

diff --git a/src/views/oaSystem/projectManagement/components/taskTree.vue b/src/views/oaSystem/projectManagement/components/taskTree.vue
index 11e3ae8..ddcdb54 100644
--- a/src/views/oaSystem/projectManagement/components/taskTree.vue
+++ b/src/views/oaSystem/projectManagement/components/taskTree.vue
@@ -2,69 +2,98 @@
   <div class="task-tree-container">
     <!-- 浠诲姟鏍戞搷浣滄寜閽� -->
     <div class="tree-actions mb10">
-      <el-button type="primary" size="small" icon="Plus" @click="handleAddTask">娣诲姞浠诲姟</el-button>
-      <el-button type="success" size="small" icon="RefreshRight" @click="refreshTree">鍒锋柊</el-button>
-      <el-button type="info" size="small" icon="Filter" @click="toggleFilter">
+      <el-button type="primary"
+                 size="small"
+                 icon="Plus"
+                 @click="handleAddTask">娣诲姞浠诲姟</el-button>
+      <el-button type="success"
+                 size="small"
+                 icon="RefreshRight"
+                 @click="refreshTree">鍒锋柊</el-button>
+      <el-button type="info"
+                 size="small"
+                 icon="Filter"
+                 @click="toggleFilter">
         {{ showFilter ? '闅愯棌绛涢��' : '鏄剧ず绛涢��' }}
       </el-button>
     </div>
-
     <!-- 绛涢�夋潯浠� -->
-    <div v-if="showFilter" class="filter-section mb10">
-      <el-form :inline="true" :model="filterParams">
+    <div v-if="showFilter"
+         class="filter-section mb10">
+      <el-form :inline="true"
+               :model="filterParams">
         <el-form-item label="浠诲姟鐘舵��">
-          <el-select v-model="filterParams.status" placeholder="鍏ㄩ儴" clearable style="width: 120px">
-            <el-option label="鏈紑濮�" value="notStarted" />
-            <el-option label="杩涜涓�" value="inProgress" />
-            <el-option label="宸插畬鎴�" value="completed" />
-            <el-option label="宸查�炬湡" value="overdue" />
+          <el-select v-model="filterParams.status"
+                     placeholder="鍏ㄩ儴"
+                     clearable
+                     style="width: 120px">
+            <el-option label="鏈紑濮�"
+                       value="notStarted" />
+            <el-option label="杩涜涓�"
+                       value="inProgress" />
+            <el-option label="宸插畬鎴�"
+                       value="completed" />
+            <el-option label="宸查�炬湡"
+                       value="overdue" />
           </el-select>
         </el-form-item>
         <el-form-item label="璐熻矗浜�">
-          <el-input v-model="filterParams.assignee" placeholder="杈撳叆璐熻矗浜�" clearable style="width: 150px" />
+          <el-input v-model="filterParams.assignee"
+                    placeholder="杈撳叆璐熻矗浜�"
+                    clearable
+                    style="width: 150px" />
         </el-form-item>
         <el-form-item>
-          <el-button type="primary" size="small" @click="filterTree">绛涢��</el-button>
-          <el-button size="small" @click="resetFilter">閲嶇疆</el-button>
+          <el-button type="primary"
+                     size="small"
+                     @click="filterTree">绛涢��</el-button>
+          <el-button size="small"
+                     @click="resetFilter">閲嶇疆</el-button>
         </el-form-item>
       </el-form>
     </div>
-
     <!-- 浠诲姟鏍� -->
     <div class="tree-content">
-      <el-tree
-        v-loading="loading"
-        :data="taskTreeData"
-        :props="defaultProps"
-        :expand-on-click-node="false"
-        node-key="nodeId"
-        ref="treeRef"
-        @node-contextmenu="handleContextMenu"
-        @node-click="handleNodeClick"
-      >
+      <el-tree v-loading="loading"
+               :data="taskTreeData"
+               :props="defaultProps"
+               :expand-on-click-node="false"
+               node-key="nodeId"
+               ref="treeRef"
+               @node-contextmenu="handleContextMenu"
+               @node-click="handleNodeClick">
         <template #default="{ node, data }">
           <!-- 鑺傜偣鍐呭 -->
-          <div class="tree-node-content" :class="{ 'phase-node': data.type === 'phase', 'task-node': data.type === 'task' }">
+          <div class="tree-node-content"
+               :class="{ 'phase-node': data.type === 'phase', 'task-node': data.type === 'task' }">
             <!-- 鑺傜偣鍥炬爣 -->
             <div class="node-icon">
-              <i v-if="data.type === 'phase'" class="el-icon-folder text-primary" />
-              <i v-else-if="data.status === 'completed'" class="el-icon-circle-check text-success" />
-              <i v-else-if="data.status === 'inProgress'" class="el-icon-circle-check text-primary" />
-              <i v-else-if="data.status === 'overdue'" class="el-icon-alarm-clock text-danger" />
-              <i v-else class="el-icon-circle-close text-gray-400" />
+              <i v-if="data.type === 'phase'"
+                 class="el-icon-folder text-primary" />
+              <i v-else-if="data.status === 'completed'"
+                 class="el-icon-circle-check text-success" />
+              <i v-else-if="data.status === 'inProgress'"
+                 class="el-icon-circle-check text-primary" />
+              <i v-else-if="data.status === 'overdue'"
+                 class="el-icon-alarm-clock text-danger" />
+              <i v-else
+                 class="el-icon-circle-close text-gray-400" />
             </div>
-
             <!-- 鑺傜偣鏍囬鍜屾弿杩� -->
             <div class="node-info">
-              <div class="node-title" :class="{ 'overdue-title': data.type === 'task' && data.status === 'overdue' }">
+              <div class="node-title"
+                   :class="{ 'overdue-title': data.type === 'task' && data.status === 'overdue' }">
                 {{ node.label }}
-                <span v-if="data.type === 'task' && data.priority === 'high'" class="priority-tag">楂樹紭</span>
-                <span v-else-if="data.type === 'task' && data.priority === 'medium'" class="priority-tag medium">涓紭</span>
+                <span v-if="data.type === 'task' && data.priority === 'high'"
+                      class="priority-tag">楂樹紭</span>
+                <span v-else-if="data.type === 'task' && data.priority === 'medium'"
+                      class="priority-tag medium">涓紭</span>
               </div>
-              <div v-if="data.description" class="node-description">{{ data.description }}</div>
-              
+              <div v-if="data.description"
+                   class="node-description">{{ data.description }}</div>
               <!-- 浠诲姟鍏冧俊鎭� -->
-              <div v-if="data.type === 'task'" class="task-meta">
+              <div v-if="data.type === 'task'"
+                   class="task-meta">
                 <span class="meta-item">
                   <i class="el-icon-user"></i>
                   {{ data.assigneeName || '鏈垎閰�' }}
@@ -75,97 +104,115 @@
                 </span>
               </div>
             </div>
-
             <!-- 浠诲姟杩涘害鏉� -->
-            <div v-if="data.type === 'task'" class="task-progress">
-              <el-progress :percentage="data.progress || 0" :stroke-width="4" :show-text="false" />
+            <div v-if="data.type === 'task'"
+                 class="task-progress">
+              <el-progress :percentage="data.progress || 0"
+                           :stroke-width="4"
+                           :show-text="false" />
             </div>
-
             <!-- 鎿嶄綔鎸夐挳 -->
             <div class="node-actions">
-              <el-button
-                v-if="data.type === 'task'"
-                type="text"
-                size="small"
-                icon="Edit"
-                @click.stop="handleEditTask(data)"
-                v-hasPermi="['oaSystem:task:edit']"
-              />
-              <el-button
-                v-if="data.type === 'phase'"
-                type="text"
-                size="small"
-                icon="Plus"
-                @click.stop="handleAddTaskUnderPhase(data)"
-                v-hasPermi="['oaSystem:task:add']"
-              />
-              <el-button
-                type="text"
-                size="small"
-                icon="Delete"
-                @click.stop="handleDeleteNode(data)"
-                v-hasPermi="['oaSystem:task:remove']"
-              />
+              <el-button v-if="data.type === 'task'"
+                         type="text"
+                         size="small"
+                         icon="Edit"
+                         @click.stop="handleEditTask(data)"
+                         v-hasPermi="['oaSystem:task:edit']" />
+              <el-button v-if="data.type === 'phase'"
+                         type="text"
+                         size="small"
+                         icon="Plus"
+                         @click.stop="handleAddTaskUnderPhase(data)"
+                         v-hasPermi="['oaSystem:task:add']" />
+              <el-button type="text"
+                         size="small"
+                         icon="Delete"
+                         @click.stop="handleDeleteNode(data)"
+                         v-hasPermi="['oaSystem:task:remove']" />
             </div>
           </div>
         </template>
       </el-tree>
     </div>
-
     <!-- 鍙抽敭鑿滃崟 -->
-    <div v-if="showContextMenu" :style="contextMenuStyle" class="context-menu">
+    <div v-if="showContextMenu"
+         :style="contextMenuStyle"
+         class="context-menu">
       <el-menu @select="handleContextMenuSelect">
-        <el-menu-item v-if="selectedNode.type === 'task'" index="edit">缂栬緫浠诲姟</el-menu-item>
-        <el-menu-item v-if="selectedNode.type === 'phase'" index="addTask">娣诲姞瀛愪换鍔�</el-menu-item>
+        <el-menu-item v-if="selectedNode.type === 'task'"
+                      index="edit">缂栬緫浠诲姟</el-menu-item>
+        <el-menu-item v-if="selectedNode.type === 'phase'"
+                      index="addTask">娣诲姞瀛愪换鍔�</el-menu-item>
         <el-menu-item index="delete">鍒犻櫎</el-menu-item>
         <el-menu-item index="expandAll">灞曞紑鍏ㄩ儴</el-menu-item>
         <el-menu-item index="collapseAll">鏀惰捣鍏ㄩ儴</el-menu-item>
       </el-menu>
     </div>
-
     <!-- 浠诲姟琛ㄥ崟瀵硅瘽妗� -->
-    <el-dialog :title="dialogTitle" v-model="dialogOpen" width="600px" append-to-body>
-      <el-form ref="taskFormRef" :model="taskForm" :rules="taskRules" label-width="80px">
-        <el-form-item label="浠诲姟鍚嶇О" prop="taskName">
-          <el-input v-model="taskForm.taskName" placeholder="璇疯緭鍏ヤ换鍔″悕绉�" />
+    <el-dialog :title="dialogTitle"
+               v-model="dialogOpen"
+               width="600px"
+               append-to-body>
+      <el-form ref="taskFormRef"
+               :model="taskForm"
+               :rules="taskRules"
+               label-width="80px">
+        <el-form-item label="浠诲姟鍚嶇О"
+                      prop="taskName">
+          <el-input v-model="taskForm.taskName"
+                    placeholder="璇疯緭鍏ヤ换鍔″悕绉�" />
         </el-form-item>
-        <el-form-item label="璐熻矗浜�" prop="assigneeId">
-          <el-input v-model="taskForm.assigneeId" placeholder="璇疯緭鍏ヨ礋璐d汉ID" />
+        <el-form-item label="璐熻矗浜�"
+                      prop="assigneeId">
+          <el-input v-model="taskForm.assigneeId"
+                    placeholder="璇疯緭鍏ヨ礋璐d汉ID" />
         </el-form-item>
-        <el-form-item label="寮�濮嬫棩鏈�" prop="startDate">
-          <el-date-picker
-            v-model="taskForm.startDate"
-            type="date"
-            placeholder="閫夋嫨寮�濮嬫棩鏈�"
-            style="width: 100%"
-          />
+        <el-form-item label="寮�濮嬫棩鏈�"
+                      prop="startDate">
+          <el-date-picker v-model="taskForm.startDate"
+                          type="date"
+                          placeholder="閫夋嫨寮�濮嬫棩鏈�"
+                          style="width: 100%" />
         </el-form-item>
-        <el-form-item label="缁撴潫鏃ユ湡" prop="endDate">
-          <el-date-picker
-            v-model="taskForm.endDate"
-            type="date"
-            placeholder="閫夋嫨缁撴潫鏃ユ湡"
-            style="width: 100%"
-          />
+        <el-form-item label="缁撴潫鏃ユ湡"
+                      prop="endDate">
+          <el-date-picker v-model="taskForm.endDate"
+                          type="date"
+                          placeholder="閫夋嫨缁撴潫鏃ユ湡"
+                          style="width: 100%" />
         </el-form-item>
-        <el-form-item label="浼樺厛绾�" prop="priority">
-          <el-select v-model="taskForm.priority" placeholder="閫夋嫨浼樺厛绾�">
-            <el-option label="浣�" value="low" />
-            <el-option label="涓�" value="medium" />
-            <el-option label="楂�" value="high" />
+        <el-form-item label="浼樺厛绾�"
+                      prop="priority">
+          <el-select v-model="taskForm.priority"
+                     placeholder="閫夋嫨浼樺厛绾�">
+            <el-option label="浣�"
+                       value="low" />
+            <el-option label="涓�"
+                       value="medium" />
+            <el-option label="楂�"
+                       value="high" />
           </el-select>
         </el-form-item>
-        <el-form-item label="杩涘害" prop="progress">
-          <el-input-number v-model="taskForm.progress" :min="0" :max="100" style="width: 100%" />
+        <el-form-item label="杩涘害"
+                      prop="progress">
+          <el-input-number v-model="taskForm.progress"
+                           :min="0"
+                           :max="100"
+                           style="width: 100%" />
         </el-form-item>
-        <el-form-item label="鎻忚堪" prop="description">
-          <el-input v-model="taskForm.description" type="textarea" placeholder="璇疯緭鍏ヤ换鍔℃弿杩�" />
+        <el-form-item label="鎻忚堪"
+                      prop="description">
+          <el-input v-model="taskForm.description"
+                    type="textarea"
+                    placeholder="璇疯緭鍏ヤ换鍔℃弿杩�" />
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="submitTaskForm">纭畾</el-button>
           <el-button @click="dialogOpen = false">鍙栨秷</el-button>
-          <el-button type="primary" @click="submitTaskForm">纭畾</el-button>
         </div>
       </template>
     </el-dialog>
@@ -173,662 +220,676 @@
 </template>
 
 <script setup>
-import { ref, reactive, computed, watch, onMounted } from 'vue';
-import { ElMessage, ElMessageBox, ElMenu, ElMenuItem } from 'element-plus';
-// import { getProject, addTask, updateTask, deleteTask, deletePhase } from '@/api/oaSystem/projectManagement';
+  import { ref, reactive, computed, watch, onMounted } from "vue";
+  import { ElMessage, ElMessageBox, ElMenu, ElMenuItem } from "element-plus";
+  // import { getProject, addTask, updateTask, deleteTask, deletePhase } from '@/api/oaSystem/projectManagement';
 
-const props = defineProps({
-  projectId: {
-    type: String,
-    required: true
-  }
-});
-
-const emit = defineEmits(['refresh']);
-
-// 缁勪欢鐘舵��
-const loading = ref(false);
-const treeRef = ref();
-const showContextMenu = ref(false);
-const contextMenuStyle = ref({});
-const selectedNode = ref({});
-const showFilter = ref(false);
-const dialogOpen = ref(false);
-const dialogTitle = ref('');
-const taskFormRef = ref();
-
-// 绛涢�夊弬鏁�
-const filterParams = reactive({
-  status: '',
-  assignee: ''
-});
-
-// 浠诲姟琛ㄥ崟鏁版嵁
-const taskForm = reactive({
-  taskId: undefined,
-  taskName: '',
-  description: '',
-  startDate: '',
-  endDate: '',
-  assigneeId: '',
-  assigneeName: '',
-  status: 'notStarted',
-  progress: 0,
-  priority: 'medium',
-  phaseId: '',
-  projectId: props.projectId
-});
-
-// 琛ㄥ崟楠岃瘉瑙勫垯
-const taskRules = {
-  taskName: [
-    { required: true, message: '浠诲姟鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' },
-    { min: 2, max: 50, message: '浠诲姟鍚嶇О闀垮害鍦� 2 鍒� 50 涓瓧绗�', trigger: 'blur' }
-  ],
-  startDate: [
-    { required: true, message: '寮�濮嬫棩鏈熶笉鑳戒负绌�', trigger: 'change' }
-  ],
-  endDate: [
-    { required: true, message: '缁撴潫鏃ユ湡涓嶈兘涓虹┖', trigger: 'change' }
-  ],
-  assigneeId: [
-    { required: true, message: '璐熻矗浜轰笉鑳戒负绌�', trigger: 'blur' }
-  ],
-  progress: [
-    { required: true, message: '杩涘害涓嶈兘涓虹┖', trigger: 'blur' },
-    { type: 'number', min: 0, max: 100, message: '杩涘害蹇呴』鍦� 0 鍒� 100 涔嬮棿', trigger: 'blur' }
-  ]
-};
-
-// 浠诲姟鏍戞暟鎹�
-const rawTaskTreeData = ref([]);
-
-// 妯℃嫙浠诲姟鏁版嵁
-const mockTaskData = {
-  'PRJ2023001': [
-    {
-      phaseId: 'PHASE001',
-      phaseName: '闇�姹傚垎鏋�',
-      startDate: '2023-11-01',
-      endDate: '2023-11-15',
-      status: 'completed',
-      tasks: [
-        {
-          taskId: 'TASK001',
-          taskName: '闇�姹傝皟鐮�',
-          description: '璋冪爺鐢ㄦ埛闇�姹傚拰涓氬姟娴佺▼',
-          startDate: '2023-11-01',
-          endDate: '2023-11-05',
-          assigneeId: 'USER001',
-          assigneeName: '寮犱笁',
-          status: 'completed',
-          progress: 100,
-          priority: 'medium'
-        },
-        {
-          taskId: 'TASK002',
-          taskName: '闇�姹傛枃妗g紪鍐�',
-          description: '缂栧啓璇︾粏鐨勯渶姹傝鏍艰鏄庝功',
-          startDate: '2023-11-06',
-          endDate: '2023-11-15',
-          assigneeId: 'USER002',
-          assigneeName: '鏉庡洓',
-          status: 'completed',
-          progress: 100,
-          priority: 'high'
-        }
-      ]
+  const props = defineProps({
+    projectId: {
+      type: String,
+      required: true,
     },
-    {
-      phaseId: 'PHASE002',
-      phaseName: '绯荤粺璁捐',
-      startDate: '2023-11-16',
-      endDate: '2023-12-10',
-      status: 'completed',
-      tasks: [
-        {
-          taskId: 'TASK003',
-          taskName: '绯荤粺鏋舵瀯璁捐',
-          description: '璁捐绯荤粺鏁翠綋鏋舵瀯',
-          startDate: '2023-11-16',
-          endDate: '2023-11-25',
-          assigneeId: 'USER003',
-          assigneeName: '鐜嬩簲',
-          status: 'completed',
-          progress: 100,
-          priority: 'high'
-        },
-        {
-          taskId: 'TASK004',
-          taskName: '鏁版嵁搴撹璁�',
-          description: '璁捐鏁版嵁搴撹〃缁撴瀯鍜屽叧绯�',
-          startDate: '2023-11-26',
-          endDate: '2023-12-10',
-          assigneeId: 'USER004',
-          assigneeName: '璧靛叚',
-          status: 'completed',
-          progress: 100,
-          priority: 'medium'
-        }
-      ]
-    },
-    {
-      phaseId: 'PHASE003',
-      phaseName: '寮�鍙戝疄鐜�',
-      startDate: '2023-12-11',
-      endDate: '2024-01-31',
-      status: 'inProgress',
-      tasks: [
-        {
-          taskId: 'TASK005',
-          taskName: '鍓嶇寮�鍙�',
-          description: '寮�鍙戠敤鎴风晫闈㈠拰浜や簰閫昏緫',
-          startDate: '2023-12-11',
-          endDate: '2024-01-15',
-          assigneeId: 'USER005',
-          assigneeName: '閽变竷',
-          status: 'inProgress',
-          progress: 70,
-          priority: 'high'
-        },
-        {
-          taskId: 'TASK006',
-          taskName: '鍚庣寮�鍙�',
-          description: '寮�鍙戜笟鍔¢�昏緫鍜孉PI鎺ュ彛',
-          startDate: '2023-12-11',
-          endDate: '2024-01-20',
-          assigneeId: 'USER006',
-          assigneeName: '瀛欏叓',
-          status: 'inProgress',
-          progress: 60,
-          priority: 'high'
-        }
-      ]
-    }
-  ],
-  // 榛樿鏁版嵁
-  default: [
-    {
-      phaseId: 'PHASE_DEFAULT1',
-      phaseName: '鍑嗗闃舵',
-      startDate: '2023-01-01',
-      endDate: '2023-03-31',
-      status: 'completed',
-      tasks: [
-        {
-          taskId: 'TASK_DEFAULT1',
-          taskName: '椤圭洰鍚姩',
-          description: '鍙紑椤圭洰鍚姩浼氳',
-          startDate: '2023-01-01',
-          endDate: '2023-01-05',
-          assigneeId: 'USER_DEFAULT1',
-          assigneeName: '璐熻矗浜篈',
-          status: 'completed',
-          progress: 100,
-          priority: 'high'
-        }
-      ]
-    },
-    {
-      phaseId: 'PHASE_DEFAULT2',
-      phaseName: '鎵ц闃舵',
-      startDate: '2023-04-01',
-      endDate: '2023-09-30',
-      status: 'inProgress',
-      tasks: [
-        {
-          taskId: 'TASK_DEFAULT2',
-          taskName: '鏍稿績鍔熻兘寮�鍙�',
-          description: '寮�鍙戠郴缁熸牳蹇冨姛鑳芥ā鍧�',
-          startDate: '2023-04-01',
-          endDate: '2023-06-30',
-          assigneeId: 'USER_DEFAULT2',
-          assigneeName: '璐熻矗浜築',
-          status: 'inProgress',
-          progress: 50,
-          priority: 'high'
-        }
-      ]
-    }
-  ]
-};
-
-const taskTreeData = computed(() => {
-  // 搴旂敤绛涢�夋潯浠�
-  if (!showFilter.value || (!filterParams.status && !filterParams.assignee)) {
-    return rawTaskTreeData.value;
-  }
-
-  // 娣辨嫹璐濆師濮嬫暟鎹互閬垮厤淇敼
-  const filteredData = JSON.parse(JSON.stringify(rawTaskTreeData.value));
-  
-  // 閫掑綊绛涢�夎妭鐐�
-  const filterNodes = (nodes) => {
-    const result = [];
-    
-    nodes.forEach(node => {
-      // 瀵逛簬闃舵鑺傜偣锛屾鏌ュ叾瀛愪换鍔℃槸鍚︾鍚堢瓫閫夋潯浠�
-      if (node.type === 'phase' && node.children) {
-        const filteredChildren = filterNodes(node.children);
-        if (filteredChildren.length > 0) {
-          // 淇濈暀鑷冲皯鏈変竴涓瓙浠诲姟绗﹀悎鏉′欢鐨勯樁娈�
-          node.children = filteredChildren;
-          result.push(node);
-        }
-      }
-      // 瀵逛簬浠诲姟鑺傜偣锛岀洿鎺ュ簲鐢ㄧ瓫閫夋潯浠�
-      else if (node.type === 'task') {
-        const statusMatch = !filterParams.status || node.status === filterParams.status;
-        const assigneeMatch = !filterParams.assignee || 
-          (node.assigneeName && node.assigneeName.includes(filterParams.assignee));
-        
-        if (statusMatch && assigneeMatch) {
-          result.push(node);
-        }
-      }
-    });
-    
-    return result;
-  };
-  
-  return filterNodes(filteredData);
-});
-
-// 鏍戣妭鐐归厤缃�
-const defaultProps = {
-  children: 'children',
-  label: (data) => {
-    if (data.type === 'phase') {
-      return `${data.phaseName}`;
-    } else {
-      return `${data.taskName}`;
-    }
-  }
-};
-
-// 鍔犺浇浠诲姟鏍戞暟鎹�
-const loadTaskTree = async () => {
-  loading.value = true;
-  // try {
-  //   const { data } = await getProject(props.projectId);
-  //   rawTaskTreeData.value = buildTaskTree(data.phases || []);
-  // } catch (error) {
-  //   ElMessage.error('鍔犺浇浠诲姟鏍戝け璐�');
-  //   console.error('鍔犺浇浠诲姟鏍戝け璐�:', error);
-  // } finally {
-  //   loading.value = false;
-  // }
-  try {
-    // 妯℃嫙缃戠粶寤惰繜
-    await new Promise(resolve => setTimeout(resolve, 500));
-    
-    // 浣跨敤妯℃嫙鏁版嵁鏇夸唬API璇锋眰
-    const phases = mockTaskData[props.projectId] || mockTaskData.default;
-    rawTaskTreeData.value = buildTaskTree(phases);
-  } catch (error) {
-    ElMessage.error('鍔犺浇浠诲姟鏍戝け璐�');
-    console.error('鍔犺浇浠诲姟鏍戝け璐�:', error);
-  } finally {
-    loading.value = false;
-  }
-};
-
-// 鏋勫缓浠诲姟鏍�
-const buildTaskTree = (phases) => {
-  return phases.map(phase => ({
-    nodeId: phase.phaseId,
-    phaseId: phase.phaseId,
-    phaseName: phase.phaseName,
-    type: 'phase',
-    children: (phase.tasks || []).map(task => ({
-      nodeId: task.taskId,
-      taskId: task.taskId,
-      taskName: task.taskName,
-      description: task.description,
-      startDate: task.startDate,
-      endDate: task.endDate,
-      assigneeId: task.assigneeId,
-      assigneeName: task.assigneeName,
-      status: task.status,
-      progress: task.progress,
-      priority: task.priority,
-      phaseId: task.phaseId,
-      projectId: props.projectId,
-      type: 'task'
-    }))
-  }));
-};
-
-// 鏍煎紡鍖栨棩鏈熻寖鍥�
-const formatDateRange = (startDate, endDate) => {
-  if (!startDate || !endDate) return '';
-  return `${startDate} - ${endDate}`;
-};
-
-// 鍒锋柊鏍�
-const refreshTree = () => {
-  loadTaskTree();
-  // 閫氱煡鐖剁粍浠跺埛鏂版暟鎹�
-  emit('refresh');
-};
-
-// 鍒囨崲绛涢�夐潰鏉�
-const toggleFilter = () => {
-  showFilter.value = !showFilter.value;
-};
-
-// 搴旂敤绛涢��
-const filterTree = () => {
-  // 绛涢�夐�昏緫宸茬粡鍦╟omputed涓疄鐜�
-};
-
-// 閲嶇疆绛涢��
-const resetFilter = () => {
-  filterParams.status = '';
-  filterParams.assignee = '';
-};
-
-// 澶勭悊鑺傜偣鐐瑰嚮
-const handleNodeClick = (data, node) => {
-  // 鍒囨崲灞曞紑/鏀惰捣鐘舵��
-  if (data.type === 'phase') {
-    node.expanded = !node.expanded;
-  }
-};
-
-// 澶勭悊鍙抽敭鑿滃崟
-const handleContextMenu = (event, data) => {
-  event.preventDefault();
-  selectedNode.value = data;
-  contextMenuStyle.value = {
-    position: 'fixed',
-    left: `${event.clientX}px`,
-    top: `${event.clientY}px`,
-    zIndex: 1000
-  };
-  showContextMenu.value = true;
-};
-
-// 澶勭悊鍙抽敭鑿滃崟閫夋嫨
-const handleContextMenuSelect = (index) => {
-  showContextMenu.value = false;
-  switch (index) {
-    case 'edit':
-      if (selectedNode.value.type === 'task') {
-        handleEditTask(selectedNode.value);
-      }
-      break;
-    case 'addTask':
-      if (selectedNode.value.type === 'phase') {
-        handleAddTaskUnderPhase(selectedNode.value);
-      }
-      break;
-    case 'delete':
-      handleDeleteNode(selectedNode.value);
-      break;
-    case 'expandAll':
-      treeRef.value?.expandAll();
-      break;
-    case 'collapseAll':
-      treeRef.value?.collapseAll();
-      break;
-  }
-};
-
-// 娣诲姞浠诲姟
-const handleAddTask = () => {
-  resetTaskForm();
-  dialogTitle.value = '娣诲姞浠诲姟';
-  dialogOpen.value = true;
-};
-
-// 鍦ㄦ寚瀹氶樁娈典笅娣诲姞浠诲姟
-const handleAddTaskUnderPhase = (phase) => {
-  resetTaskForm();
-  taskForm.phaseId = phase.phaseId;
-  dialogTitle.value = '娣诲姞瀛愪换鍔�';
-  dialogOpen.value = true;
-};
-
-// 缂栬緫浠诲姟
-const handleEditTask = (task) => {
-  resetTaskForm();
-  Object.assign(taskForm, { ...task });
-  dialogTitle.value = '缂栬緫浠诲姟';
-  dialogOpen.value = true;
-};
-
-// 鍒犻櫎鑺傜偣
-const handleDeleteNode = async (node) => {
-  const confirmMessage = node.type === 'phase' 
-    ? `纭畾瑕佸垹闄ら樁娈� "${node.phaseName}" 鍙婂叾鎵�鏈夊瓙浠诲姟鍚楋紵` 
-    : `纭畾瑕佸垹闄や换鍔� "${node.taskName}" 鍚楋紵`;
-  
-  await ElMessageBox.confirm(confirmMessage, '纭鎿嶄綔', {
-    confirmButtonText: '纭畾',
-    cancelButtonText: '鍙栨秷',
-    type: 'warning'
-  }).catch(() => {
-    throw new Error('鍙栨秷鍒犻櫎');
   });
-  
-  try {
-    if (node.type === 'phase') {
-      await deletePhase(node.phaseId);
-    } else {
-      await deleteTask(node.taskId);
+
+  const emit = defineEmits(["refresh"]);
+
+  // 缁勪欢鐘舵��
+  const loading = ref(false);
+  const treeRef = ref();
+  const showContextMenu = ref(false);
+  const contextMenuStyle = ref({});
+  const selectedNode = ref({});
+  const showFilter = ref(false);
+  const dialogOpen = ref(false);
+  const dialogTitle = ref("");
+  const taskFormRef = ref();
+
+  // 绛涢�夊弬鏁�
+  const filterParams = reactive({
+    status: "",
+    assignee: "",
+  });
+
+  // 浠诲姟琛ㄥ崟鏁版嵁
+  const taskForm = reactive({
+    taskId: undefined,
+    taskName: "",
+    description: "",
+    startDate: "",
+    endDate: "",
+    assigneeId: "",
+    assigneeName: "",
+    status: "notStarted",
+    progress: 0,
+    priority: "medium",
+    phaseId: "",
+    projectId: props.projectId,
+  });
+
+  // 琛ㄥ崟楠岃瘉瑙勫垯
+  const taskRules = {
+    taskName: [
+      { required: true, message: "浠诲姟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" },
+      {
+        min: 2,
+        max: 50,
+        message: "浠诲姟鍚嶇О闀垮害鍦� 2 鍒� 50 涓瓧绗�",
+        trigger: "blur",
+      },
+    ],
+    startDate: [
+      { required: true, message: "寮�濮嬫棩鏈熶笉鑳戒负绌�", trigger: "change" },
+    ],
+    endDate: [{ required: true, message: "缁撴潫鏃ユ湡涓嶈兘涓虹┖", trigger: "change" }],
+    assigneeId: [{ required: true, message: "璐熻矗浜轰笉鑳戒负绌�", trigger: "blur" }],
+    progress: [
+      { required: true, message: "杩涘害涓嶈兘涓虹┖", trigger: "blur" },
+      {
+        type: "number",
+        min: 0,
+        max: 100,
+        message: "杩涘害蹇呴』鍦� 0 鍒� 100 涔嬮棿",
+        trigger: "blur",
+      },
+    ],
+  };
+
+  // 浠诲姟鏍戞暟鎹�
+  const rawTaskTreeData = ref([]);
+
+  // 妯℃嫙浠诲姟鏁版嵁
+  const mockTaskData = {
+    PRJ2023001: [
+      {
+        phaseId: "PHASE001",
+        phaseName: "闇�姹傚垎鏋�",
+        startDate: "2023-11-01",
+        endDate: "2023-11-15",
+        status: "completed",
+        tasks: [
+          {
+            taskId: "TASK001",
+            taskName: "闇�姹傝皟鐮�",
+            description: "璋冪爺鐢ㄦ埛闇�姹傚拰涓氬姟娴佺▼",
+            startDate: "2023-11-01",
+            endDate: "2023-11-05",
+            assigneeId: "USER001",
+            assigneeName: "寮犱笁",
+            status: "completed",
+            progress: 100,
+            priority: "medium",
+          },
+          {
+            taskId: "TASK002",
+            taskName: "闇�姹傛枃妗g紪鍐�",
+            description: "缂栧啓璇︾粏鐨勯渶姹傝鏍艰鏄庝功",
+            startDate: "2023-11-06",
+            endDate: "2023-11-15",
+            assigneeId: "USER002",
+            assigneeName: "鏉庡洓",
+            status: "completed",
+            progress: 100,
+            priority: "high",
+          },
+        ],
+      },
+      {
+        phaseId: "PHASE002",
+        phaseName: "绯荤粺璁捐",
+        startDate: "2023-11-16",
+        endDate: "2023-12-10",
+        status: "completed",
+        tasks: [
+          {
+            taskId: "TASK003",
+            taskName: "绯荤粺鏋舵瀯璁捐",
+            description: "璁捐绯荤粺鏁翠綋鏋舵瀯",
+            startDate: "2023-11-16",
+            endDate: "2023-11-25",
+            assigneeId: "USER003",
+            assigneeName: "鐜嬩簲",
+            status: "completed",
+            progress: 100,
+            priority: "high",
+          },
+          {
+            taskId: "TASK004",
+            taskName: "鏁版嵁搴撹璁�",
+            description: "璁捐鏁版嵁搴撹〃缁撴瀯鍜屽叧绯�",
+            startDate: "2023-11-26",
+            endDate: "2023-12-10",
+            assigneeId: "USER004",
+            assigneeName: "璧靛叚",
+            status: "completed",
+            progress: 100,
+            priority: "medium",
+          },
+        ],
+      },
+      {
+        phaseId: "PHASE003",
+        phaseName: "寮�鍙戝疄鐜�",
+        startDate: "2023-12-11",
+        endDate: "2024-01-31",
+        status: "inProgress",
+        tasks: [
+          {
+            taskId: "TASK005",
+            taskName: "鍓嶇寮�鍙�",
+            description: "寮�鍙戠敤鎴风晫闈㈠拰浜や簰閫昏緫",
+            startDate: "2023-12-11",
+            endDate: "2024-01-15",
+            assigneeId: "USER005",
+            assigneeName: "閽变竷",
+            status: "inProgress",
+            progress: 70,
+            priority: "high",
+          },
+          {
+            taskId: "TASK006",
+            taskName: "鍚庣寮�鍙�",
+            description: "寮�鍙戜笟鍔¢�昏緫鍜孉PI鎺ュ彛",
+            startDate: "2023-12-11",
+            endDate: "2024-01-20",
+            assigneeId: "USER006",
+            assigneeName: "瀛欏叓",
+            status: "inProgress",
+            progress: 60,
+            priority: "high",
+          },
+        ],
+      },
+    ],
+    // 榛樿鏁版嵁
+    default: [
+      {
+        phaseId: "PHASE_DEFAULT1",
+        phaseName: "鍑嗗闃舵",
+        startDate: "2023-01-01",
+        endDate: "2023-03-31",
+        status: "completed",
+        tasks: [
+          {
+            taskId: "TASK_DEFAULT1",
+            taskName: "椤圭洰鍚姩",
+            description: "鍙紑椤圭洰鍚姩浼氳",
+            startDate: "2023-01-01",
+            endDate: "2023-01-05",
+            assigneeId: "USER_DEFAULT1",
+            assigneeName: "璐熻矗浜篈",
+            status: "completed",
+            progress: 100,
+            priority: "high",
+          },
+        ],
+      },
+      {
+        phaseId: "PHASE_DEFAULT2",
+        phaseName: "鎵ц闃舵",
+        startDate: "2023-04-01",
+        endDate: "2023-09-30",
+        status: "inProgress",
+        tasks: [
+          {
+            taskId: "TASK_DEFAULT2",
+            taskName: "鏍稿績鍔熻兘寮�鍙�",
+            description: "寮�鍙戠郴缁熸牳蹇冨姛鑳芥ā鍧�",
+            startDate: "2023-04-01",
+            endDate: "2023-06-30",
+            assigneeId: "USER_DEFAULT2",
+            assigneeName: "璐熻矗浜築",
+            status: "inProgress",
+            progress: 50,
+            priority: "high",
+          },
+        ],
+      },
+    ],
+  };
+
+  const taskTreeData = computed(() => {
+    // 搴旂敤绛涢�夋潯浠�
+    if (!showFilter.value || (!filterParams.status && !filterParams.assignee)) {
+      return rawTaskTreeData.value;
     }
-    ElMessage.success('鍒犻櫎鎴愬姛');
-    refreshTree();
-  } catch (error) {
-    if (error.message !== '鍙栨秷鍒犻櫎') {
-      ElMessage.error('鍒犻櫎澶辫触');
-      console.error('鍒犻櫎澶辫触:', error);
+
+    // 娣辨嫹璐濆師濮嬫暟鎹互閬垮厤淇敼
+    const filteredData = JSON.parse(JSON.stringify(rawTaskTreeData.value));
+
+    // 閫掑綊绛涢�夎妭鐐�
+    const filterNodes = nodes => {
+      const result = [];
+
+      nodes.forEach(node => {
+        // 瀵逛簬闃舵鑺傜偣锛屾鏌ュ叾瀛愪换鍔℃槸鍚︾鍚堢瓫閫夋潯浠�
+        if (node.type === "phase" && node.children) {
+          const filteredChildren = filterNodes(node.children);
+          if (filteredChildren.length > 0) {
+            // 淇濈暀鑷冲皯鏈変竴涓瓙浠诲姟绗﹀悎鏉′欢鐨勯樁娈�
+            node.children = filteredChildren;
+            result.push(node);
+          }
+        }
+        // 瀵逛簬浠诲姟鑺傜偣锛岀洿鎺ュ簲鐢ㄧ瓫閫夋潯浠�
+        else if (node.type === "task") {
+          const statusMatch =
+            !filterParams.status || node.status === filterParams.status;
+          const assigneeMatch =
+            !filterParams.assignee ||
+            (node.assigneeName &&
+              node.assigneeName.includes(filterParams.assignee));
+
+          if (statusMatch && assigneeMatch) {
+            result.push(node);
+          }
+        }
+      });
+
+      return result;
+    };
+
+    return filterNodes(filteredData);
+  });
+
+  // 鏍戣妭鐐归厤缃�
+  const defaultProps = {
+    children: "children",
+    label: data => {
+      if (data.type === "phase") {
+        return `${data.phaseName}`;
+      } else {
+        return `${data.taskName}`;
+      }
+    },
+  };
+
+  // 鍔犺浇浠诲姟鏍戞暟鎹�
+  const loadTaskTree = async () => {
+    loading.value = true;
+    // try {
+    //   const { data } = await getProject(props.projectId);
+    //   rawTaskTreeData.value = buildTaskTree(data.phases || []);
+    // } catch (error) {
+    //   ElMessage.error('鍔犺浇浠诲姟鏍戝け璐�');
+    //   console.error('鍔犺浇浠诲姟鏍戝け璐�:', error);
+    // } finally {
+    //   loading.value = false;
+    // }
+    try {
+      // 妯℃嫙缃戠粶寤惰繜
+      await new Promise(resolve => setTimeout(resolve, 500));
+
+      // 浣跨敤妯℃嫙鏁版嵁鏇夸唬API璇锋眰
+      const phases = mockTaskData[props.projectId] || mockTaskData.default;
+      rawTaskTreeData.value = buildTaskTree(phases);
+    } catch (error) {
+      ElMessage.error("鍔犺浇浠诲姟鏍戝け璐�");
+      console.error("鍔犺浇浠诲姟鏍戝け璐�:", error);
+    } finally {
+      loading.value = false;
     }
-  }
-};
+  };
 
-// 閲嶇疆浠诲姟琛ㄥ崟
-const resetTaskForm = () => {
-  taskForm.taskId = undefined;
-  taskForm.taskName = '';
-  taskForm.description = '';
-  taskForm.startDate = '';
-  taskForm.endDate = '';
-  taskForm.assigneeId = '';
-  taskForm.assigneeName = '';
-  taskForm.status = 'notStarted';
-  taskForm.progress = 0;
-  taskForm.priority = 'medium';
-  taskForm.phaseId = '';
-  taskForm.projectId = props.projectId;
-  
-  if (taskFormRef.value) {
-    taskFormRef.value.resetFields();
-  }
-};
+  // 鏋勫缓浠诲姟鏍�
+  const buildTaskTree = phases => {
+    return phases.map(phase => ({
+      nodeId: phase.phaseId,
+      phaseId: phase.phaseId,
+      phaseName: phase.phaseName,
+      type: "phase",
+      children: (phase.tasks || []).map(task => ({
+        nodeId: task.taskId,
+        taskId: task.taskId,
+        taskName: task.taskName,
+        description: task.description,
+        startDate: task.startDate,
+        endDate: task.endDate,
+        assigneeId: task.assigneeId,
+        assigneeName: task.assigneeName,
+        status: task.status,
+        progress: task.progress,
+        priority: task.priority,
+        phaseId: task.phaseId,
+        projectId: props.projectId,
+        type: "task",
+      })),
+    }));
+  };
 
-// 鎻愪氦浠诲姟琛ㄥ崟
-const submitTaskForm = async () => {
-  try {
-    await taskFormRef.value.validate();
-    
-    if (taskForm.taskId) {
-      await updateTask(taskForm);
-      ElMessage.success('淇敼浠诲姟鎴愬姛');
-    } else {
-      await addTask(taskForm);
-      ElMessage.success('娣诲姞浠诲姟鎴愬姛');
-    }
-    
-    dialogOpen.value = false;
-    refreshTree();
-  } catch (error) {
-    console.error('鎻愪氦琛ㄥ崟澶辫触:', error);
-  }
-};
+  // 鏍煎紡鍖栨棩鏈熻寖鍥�
+  const formatDateRange = (startDate, endDate) => {
+    if (!startDate || !endDate) return "";
+    return `${startDate} - ${endDate}`;
+  };
 
-// 鐐瑰嚮鍏朵粬鍖哄煙鍏抽棴鍙抽敭鑿滃崟
-document.addEventListener('click', () => {
-  if (showContextMenu.value) {
-    showContextMenu.value = false;
-  }
-});
-
-// 鐩戝惉椤圭洰ID鍙樺寲
-watch(() => props.projectId, (newProjectId) => {
-  if (newProjectId) {
+  // 鍒锋柊鏍�
+  const refreshTree = () => {
     loadTaskTree();
-  }
-});
+    // 閫氱煡鐖剁粍浠跺埛鏂版暟鎹�
+    emit("refresh");
+  };
 
-// 鍒濆鍖�
-onMounted(() => {
-  loadTaskTree();
-});
+  // 鍒囨崲绛涢�夐潰鏉�
+  const toggleFilter = () => {
+    showFilter.value = !showFilter.value;
+  };
+
+  // 搴旂敤绛涢��
+  const filterTree = () => {
+    // 绛涢�夐�昏緫宸茬粡鍦╟omputed涓疄鐜�
+  };
+
+  // 閲嶇疆绛涢��
+  const resetFilter = () => {
+    filterParams.status = "";
+    filterParams.assignee = "";
+  };
+
+  // 澶勭悊鑺傜偣鐐瑰嚮
+  const handleNodeClick = (data, node) => {
+    // 鍒囨崲灞曞紑/鏀惰捣鐘舵��
+    if (data.type === "phase") {
+      node.expanded = !node.expanded;
+    }
+  };
+
+  // 澶勭悊鍙抽敭鑿滃崟
+  const handleContextMenu = (event, data) => {
+    event.preventDefault();
+    selectedNode.value = data;
+    contextMenuStyle.value = {
+      position: "fixed",
+      left: `${event.clientX}px`,
+      top: `${event.clientY}px`,
+      zIndex: 1000,
+    };
+    showContextMenu.value = true;
+  };
+
+  // 澶勭悊鍙抽敭鑿滃崟閫夋嫨
+  const handleContextMenuSelect = index => {
+    showContextMenu.value = false;
+    switch (index) {
+      case "edit":
+        if (selectedNode.value.type === "task") {
+          handleEditTask(selectedNode.value);
+        }
+        break;
+      case "addTask":
+        if (selectedNode.value.type === "phase") {
+          handleAddTaskUnderPhase(selectedNode.value);
+        }
+        break;
+      case "delete":
+        handleDeleteNode(selectedNode.value);
+        break;
+      case "expandAll":
+        treeRef.value?.expandAll();
+        break;
+      case "collapseAll":
+        treeRef.value?.collapseAll();
+        break;
+    }
+  };
+
+  // 娣诲姞浠诲姟
+  const handleAddTask = () => {
+    resetTaskForm();
+    dialogTitle.value = "娣诲姞浠诲姟";
+    dialogOpen.value = true;
+  };
+
+  // 鍦ㄦ寚瀹氶樁娈典笅娣诲姞浠诲姟
+  const handleAddTaskUnderPhase = phase => {
+    resetTaskForm();
+    taskForm.phaseId = phase.phaseId;
+    dialogTitle.value = "娣诲姞瀛愪换鍔�";
+    dialogOpen.value = true;
+  };
+
+  // 缂栬緫浠诲姟
+  const handleEditTask = task => {
+    resetTaskForm();
+    Object.assign(taskForm, { ...task });
+    dialogTitle.value = "缂栬緫浠诲姟";
+    dialogOpen.value = true;
+  };
+
+  // 鍒犻櫎鑺傜偣
+  const handleDeleteNode = async node => {
+    const confirmMessage =
+      node.type === "phase"
+        ? `纭畾瑕佸垹闄ら樁娈� "${node.phaseName}" 鍙婂叾鎵�鏈夊瓙浠诲姟鍚楋紵`
+        : `纭畾瑕佸垹闄や换鍔� "${node.taskName}" 鍚楋紵`;
+
+    await ElMessageBox.confirm(confirmMessage, "纭鎿嶄綔", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    }).catch(() => {
+      throw new Error("鍙栨秷鍒犻櫎");
+    });
+
+    try {
+      if (node.type === "phase") {
+        await deletePhase(node.phaseId);
+      } else {
+        await deleteTask(node.taskId);
+      }
+      ElMessage.success("鍒犻櫎鎴愬姛");
+      refreshTree();
+    } catch (error) {
+      if (error.message !== "鍙栨秷鍒犻櫎") {
+        ElMessage.error("鍒犻櫎澶辫触");
+        console.error("鍒犻櫎澶辫触:", error);
+      }
+    }
+  };
+
+  // 閲嶇疆浠诲姟琛ㄥ崟
+  const resetTaskForm = () => {
+    taskForm.taskId = undefined;
+    taskForm.taskName = "";
+    taskForm.description = "";
+    taskForm.startDate = "";
+    taskForm.endDate = "";
+    taskForm.assigneeId = "";
+    taskForm.assigneeName = "";
+    taskForm.status = "notStarted";
+    taskForm.progress = 0;
+    taskForm.priority = "medium";
+    taskForm.phaseId = "";
+    taskForm.projectId = props.projectId;
+
+    if (taskFormRef.value) {
+      taskFormRef.value.resetFields();
+    }
+  };
+
+  // 鎻愪氦浠诲姟琛ㄥ崟
+  const submitTaskForm = async () => {
+    try {
+      await taskFormRef.value.validate();
+
+      if (taskForm.taskId) {
+        await updateTask(taskForm);
+        ElMessage.success("淇敼浠诲姟鎴愬姛");
+      } else {
+        await addTask(taskForm);
+        ElMessage.success("娣诲姞浠诲姟鎴愬姛");
+      }
+
+      dialogOpen.value = false;
+      refreshTree();
+    } catch (error) {
+      console.error("鎻愪氦琛ㄥ崟澶辫触:", error);
+    }
+  };
+
+  // 鐐瑰嚮鍏朵粬鍖哄煙鍏抽棴鍙抽敭鑿滃崟
+  document.addEventListener("click", () => {
+    if (showContextMenu.value) {
+      showContextMenu.value = false;
+    }
+  });
+
+  // 鐩戝惉椤圭洰ID鍙樺寲
+  watch(
+    () => props.projectId,
+    newProjectId => {
+      if (newProjectId) {
+        loadTaskTree();
+      }
+    }
+  );
+
+  // 鍒濆鍖�
+  onMounted(() => {
+    loadTaskTree();
+  });
 </script>
 
 <style scoped>
-.task-tree-container {
-  padding: 10px;
-}
+  .task-tree-container {
+    padding: 10px;
+  }
 
-.tree-actions {
-  display: flex;
-  gap: 10px;
-  align-items: center;
-}
+  .tree-actions {
+    display: flex;
+    gap: 10px;
+    align-items: center;
+  }
 
-.filter-section {
-  background: #f5f7fa;
-  padding: 10px;
-  border-radius: 4px;
-}
+  .filter-section {
+    background: #f5f7fa;
+    padding: 10px;
+    border-radius: 4px;
+  }
 
-.tree-content {
-  background: #fff;
-  border: 1px solid #ebeef5;
-  border-radius: 4px;
-  padding: 10px;
-  max-height: 600px;
-  overflow-y: auto;
-}
+  .tree-content {
+    background: #fff;
+    border: 1px solid #ebeef5;
+    border-radius: 4px;
+    padding: 10px;
+    max-height: 600px;
+    overflow-y: auto;
+  }
 
-.tree-node-content {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  padding: 5px 0;
-  min-height: 40px;
-}
+  .tree-node-content {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    padding: 5px 0;
+    min-height: 40px;
+  }
 
-.phase-node {
-  font-weight: bold;
-  color: #409eff;
-}
+  .phase-node {
+    font-weight: bold;
+    color: #409eff;
+  }
 
-.task-node {
-  color: #606266;
-}
+  .task-node {
+    color: #606266;
+  }
 
-.node-icon {
-  display: flex;
-  align-items: center;
-  width: 20px;
-}
+  .node-icon {
+    display: flex;
+    align-items: center;
+    width: 20px;
+  }
 
-.node-info {
-  flex: 1;
-  min-width: 0;
-}
+  .node-info {
+    flex: 1;
+    min-width: 0;
+  }
 
-.node-title {
-  font-weight: 500;
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  margin-bottom: 2px;
-}
+  .node-title {
+    font-weight: 500;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    margin-bottom: 2px;
+  }
 
-.overdue-title {
-  color: #f56c6c;
-  font-weight: bold;
-}
+  .overdue-title {
+    color: #f56c6c;
+    font-weight: bold;
+  }
 
-.priority-tag {
-  background: #f56c6c;
-  color: white;
-  font-size: 10px;
-  padding: 1px 4px;
-  border-radius: 2px;
-  margin-left: 5px;
-}
+  .priority-tag {
+    background: #f56c6c;
+    color: white;
+    font-size: 10px;
+    padding: 1px 4px;
+    border-radius: 2px;
+    margin-left: 5px;
+  }
 
-.priority-tag.medium {
-  background: #e6a23c;
-}
+  .priority-tag.medium {
+    background: #e6a23c;
+  }
 
-.node-description {
-  font-size: 12px;
-  color: #909399;
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
+  .node-description {
+    font-size: 12px;
+    color: #909399;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
 
-.task-meta {
-  display: flex;
-  gap: 15px;
-  font-size: 12px;
-  color: #909399;
-  margin-top: 2px;
-}
+  .task-meta {
+    display: flex;
+    gap: 15px;
+    font-size: 12px;
+    color: #909399;
+    margin-top: 2px;
+  }
 
-.meta-item {
-  display: flex;
-  align-items: center;
-  gap: 3px;
-}
+  .meta-item {
+    display: flex;
+    align-items: center;
+    gap: 3px;
+  }
 
-.task-progress {
-  width: 120px;
-  margin: 0 10px;
-}
+  .task-progress {
+    width: 120px;
+    margin: 0 10px;
+  }
 
-.node-actions {
-  display: flex;
-  gap: 5px;
-  opacity: 0;
-  transition: opacity 0.3s;
-}
+  .node-actions {
+    display: flex;
+    gap: 5px;
+    opacity: 0;
+    transition: opacity 0.3s;
+  }
 
-.tree-node-content:hover .node-actions {
-  opacity: 1;
-}
+  .tree-node-content:hover .node-actions {
+    opacity: 1;
+  }
 
-.context-menu {
-  background: white;
-  border: 1px solid #ebeef5;
-  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
-  border-radius: 4px;
-}
+  .context-menu {
+    background: white;
+    border: 1px solid #ebeef5;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 4px;
+  }
 
-.context-menu .el-menu {
-  min-width: 120px;
-  border: none;
-}
+  .context-menu .el-menu {
+    min-width: 120px;
+    border: none;
+  }
 
-.context-menu .el-menu-item {
-  padding: 0 15px;
-  height: 36px;
-  line-height: 36px;
-}
+  .context-menu .el-menu-item {
+    padding: 0 15px;
+    height: 36px;
+    line-height: 36px;
+  }
 
-.context-menu .el-menu-item:hover {
-  background-color: #f5f7fa;
-}
+  .context-menu .el-menu-item:hover {
+    background-color: #f5f7fa;
+  }
 
-.text-gray-400 {
-  color: #c0c4cc;
-}
+  .text-gray-400 {
+    color: #c0c4cc;
+  }
 </style>
\ No newline at end of file

--
Gitblit v1.9.3