yanghao 2 тижнів тому
батько
коміт
b5d084b276

+ 10 - 0
src/router/modules/remaining.ts

@@ -385,6 +385,16 @@ const remainingRouter: AppRouteRecordRaw[] = [
           hidden: true,
           hidden: true,
           canTo: true
           canTo: true
         }
         }
+      },
+      {
+        path: 'breakdown',
+        component: () => import('@/views/researchProject/breakdown/index.vue'),
+        name: 'ResearchProjectBreakdown',
+        meta: {
+          title: '项目分解',
+          noCache: true,
+          canTo: true
+        }
       }
       }
     ]
     ]
   }
   }

+ 444 - 0
src/views/researchProject/breakdown/index.vue

@@ -0,0 +1,444 @@
+<template>
+  <ContentWrap>
+    <!-- <div class="page-heading">
+      <div>
+        <h2>项目分解</h2>
+        <p>项目生效后,项目责任人可将项目拆解为多个子任务,每个子任务最多分解三个孙子任务。</p>
+      </div>
+      <el-tag type="success" effect="light"><Icon icon="ep:circle-check" /> 项目已生效</el-tag>
+    </div> -->
+
+    <el-form ref="queryFormRef" :model="queryParams" inline class="project-filter">
+      <el-form-item label="项目名称" prop="projectName">
+        <el-input
+          v-model="queryParams.projectName"
+          clearable
+          placeholder="请输入项目名称"
+          class="!w-240px"
+          @keyup.enter="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="项目负责人" prop="projectLeader">
+        <el-input
+          v-model="queryParams.projectLeader"
+          clearable
+          placeholder="请输入负责人"
+          class="!w-200px"
+          @keyup.enter="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="handleQuery"><Icon icon="ep:search" /> 搜索</el-button>
+        <el-button @click="resetQuery"><Icon icon="ep:refresh" /> 重置</el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <ContentWrap>
+    <zm-table
+      v-loading="loading"
+      :data="filteredProjects"
+      row-key="id"
+      highlight-current-row
+      class="project-table"
+      @current-change="selectProject"
+    >
+      <zm-table-column label="项目名称" prop="projectName" min-width="280" show-overflow-tooltip />
+      <zm-table-column label="项目负责人" prop="projectLeader" width="140" />
+      <zm-table-column label="项目类型" prop="projectType" min-width="180" />
+      <zm-table-column label="生效日期" prop="effectiveDate" width="130" />
+      <zm-table-column label="任务数" width="90" align="center"
+        ><template #default="{ row }">{{ countTasks(row.tasks) }}</template></zm-table-column
+      >
+      <zm-table-column label="操作" width="120" fixed="right" align="center"
+        ><template #default="{ row }"
+          ><zm-button link type="primary" @click="selectProject(row)">分解任务</zm-button></template
+        ></zm-table-column
+      >
+    </zm-table>
+    <Pagination
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      :total="filteredProjects.length"
+    />
+  </ContentWrap>
+
+  <ContentWrap v-if="selectedProject" class="task-panel">
+    <div class="task-heading">
+      <div>
+        <div class="task-title-row"
+          ><h3>{{ selectedProject.projectName }}</h3
+          ><el-tag type="success" size="small">已生效</el-tag></div
+        >
+        <span
+          >项目负责人:{{ selectedProject.projectLeader }} · 已分解
+          {{ countTasks(selectedProject.tasks) }} 个任务</span
+        >
+      </div>
+      <el-button type="primary" @click="openTaskForm()"
+        ><Icon icon="ep:plus" /> 新增一级任务</el-button
+      >
+    </div>
+
+    <el-empty v-if="!selectedProject.tasks.length" description="暂未分解任务"
+      ><el-button type="primary" @click="openTaskForm()"
+        ><Icon icon="ep:plus" /> 新增一级任务</el-button
+      ></el-empty
+    >
+    <zm-table v-else :data="selectedProject.tasks" row-key="id" default-expand-all border>
+      <zm-table-column label="任务名称" min-width="250" tree-node>
+        <template #default="{ row }"
+          ><div class="task-name"
+            ><span>{{ row.name }}</span
+            ><el-tag v-if="row.level === 3" type="info" effect="plain" size="small"
+              >末级</el-tag
+            ></div
+          ></template
+        >
+      </zm-table-column>
+      <zm-table-column label="开始时间" prop="startDate" width="130" />
+      <zm-table-column label="结束时间" prop="endDate" width="130" />
+      <zm-table-column label="责任人" prop="owner" width="120" />
+      <zm-table-column label="优先级" width="100" align="center"
+        ><template #default="{ row }"
+          ><el-tag :type="priorityTagType(row.priority)">{{ row.priority }}</el-tag></template
+        ></zm-table-column
+      >
+      <zm-table-column label="任务描述" prop="description" min-width="220" show-overflow-tooltip />
+      <zm-table-column label="操作" width="220" fixed="right" align="center">
+        <template #default="{ row }">
+          <el-button
+            v-if="row.level === 1"
+            link
+            type="primary"
+            :disabled="row.children.length >= MAX_CHILDREN"
+            @click="openTaskForm(row)"
+            >新增子任务</el-button
+          >
+          <el-button v-else link type="info" disabled>不可再分解</el-button>
+          <el-button link type="primary" @click="openTaskForm(row, true)">编辑</el-button>
+          <el-button link type="danger" @click="removeTask(row)">删除</el-button>
+        </template>
+      </zm-table-column>
+    </zm-table>
+  </ContentWrap>
+
+  <el-dialog v-model="taskDialogVisible" :title="taskDialogTitle" width="720px" destroy-on-close>
+    <el-alert v-if="parentTask" type="info" :closable="false" class="parent-alert"
+      >当前父任务:{{ parentTask.name }},最多可添加 {{ MAX_CHILDREN }} 个孙子任务(已添加
+      {{ parentTask.children.length }} 个)</el-alert
+    >
+    <el-form ref="taskFormRef" :model="taskForm" :rules="taskRules" label-width="92px">
+      <el-form-item label="任务名称" prop="name"
+        ><el-input
+          v-model="taskForm.name"
+          maxlength="100"
+          show-word-limit
+          placeholder="请输入任务名称"
+      /></el-form-item>
+      <el-row :gutter="18">
+        <el-col :span="12"
+          ><el-form-item label="开始时间" prop="startDate"
+            ><el-date-picker
+              v-model="taskForm.startDate"
+              type="date"
+              value-format="YYYY-MM-DD"
+              placeholder="请选择开始时间"
+              class="w-full" /></el-form-item
+        ></el-col>
+        <el-col :span="12"
+          ><el-form-item label="结束时间" prop="endDate"
+            ><el-date-picker
+              v-model="taskForm.endDate"
+              type="date"
+              value-format="YYYY-MM-DD"
+              placeholder="请选择结束时间"
+              class="w-full" /></el-form-item
+        ></el-col>
+      </el-row>
+      <el-row :gutter="18">
+        <el-col :span="12"
+          ><el-form-item label="责任人" prop="owner"
+            ><el-input v-model="taskForm.owner" placeholder="请输入责任人" /></el-form-item
+        ></el-col>
+        <el-col :span="12"
+          ><el-form-item label="任务优先级" prop="priority"
+            ><el-radio-group v-model="taskForm.priority"
+              ><el-radio-button v-for="item in priorities" :key="item" :label="item">{{
+                item
+              }}</el-radio-button></el-radio-group
+            ></el-form-item
+          ></el-col
+        >
+      </el-row>
+      <el-form-item label="任务描述" prop="description"
+        ><el-input
+          v-model="taskForm.description"
+          type="textarea"
+          :rows="4"
+          maxlength="500"
+          show-word-limit
+          placeholder="请输入任务描述"
+      /></el-form-item>
+    </el-form>
+    <template #footer
+      ><el-button @click="taskDialogVisible = false">取消</el-button
+      ><el-button type="primary" :loading="submitLoading" @click="submitTask"
+        >保存任务</el-button
+      ></template
+    >
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { computed, reactive, ref } from 'vue'
+import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
+
+defineOptions({ name: 'ResearchProjectBreakdown' })
+const MAX_CHILDREN = 3
+const priorities = ['高', '中', '低'] as const
+type Priority = (typeof priorities)[number]
+type Task = {
+  id: number
+  name: string
+  startDate: string
+  endDate: string
+  owner: string
+  description: string
+  priority: Priority
+  level: number
+  children: Task[]
+}
+type Project = {
+  id: number
+  projectName: string
+  projectLeader: string
+  projectType: string
+  effectiveDate: string
+  tasks: Task[]
+}
+
+const createTask = (level = 1, data: Partial<Task> = {}): Task => ({
+  id: Date.now() + Math.floor(Math.random() * 1000),
+  name: '',
+  startDate: '',
+  endDate: '',
+  owner: '',
+  description: '',
+  priority: '中',
+  level,
+  children: [],
+  ...data
+})
+const projects = ref<Project[]>([
+  {
+    id: 1,
+    projectName: '高温高压井下工具可靠性研究',
+    projectLeader: '张明远',
+    projectType: '产品、工具和设备研发类',
+    effectiveDate: '2026-03-01',
+    tasks: [
+      createTask(1, {
+        id: 101,
+        name: '完成高温高压工况分析',
+        startDate: '2026-03-05',
+        endDate: '2026-04-10',
+        owner: '李强',
+        description: '梳理典型工况并输出分析报告。',
+        priority: '高',
+        children: [
+          createTask(2, {
+            id: 102,
+            name: '收集现场运行数据',
+            startDate: '2026-03-05',
+            endDate: '2026-03-20',
+            owner: '王璐',
+            description: '整理近三年现场运行数据。',
+            priority: '中',
+            children: []
+          })
+        ]
+      })
+    ]
+  },
+  {
+    id: 2,
+    projectName: '智能钻井参数优化方法研究',
+    projectLeader: '赵海宁',
+    projectType: '基础研究类',
+    effectiveDate: '2026-04-15',
+    tasks: []
+  }
+])
+const loading = ref(false)
+const queryFormRef = ref<FormInstance>()
+const queryParams = reactive({ pageNo: 1, pageSize: 10, projectName: '', projectLeader: '' })
+const filteredProjects = computed(() =>
+  projects.value.filter(
+    (project) =>
+      (!queryParams.projectName || project.projectName.includes(queryParams.projectName)) &&
+      (!queryParams.projectLeader || project.projectLeader.includes(queryParams.projectLeader))
+  )
+)
+const selectedProject = ref<Project | undefined>(projects.value[0])
+const taskDialogVisible = ref(false)
+const taskDialogTitle = ref('新增一级任务')
+const taskFormRef = ref<FormInstance>()
+const submitLoading = ref(false)
+const editingTask = ref<Task>()
+const parentTask = ref<Task>()
+const taskForm = reactive(createTask())
+const taskRules: FormRules = {
+  name: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
+  startDate: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
+  endDate: [{ required: true, message: '请选择结束时间', trigger: 'change' }],
+  owner: [{ required: true, message: '请输入责任人', trigger: 'blur' }],
+  priority: [{ required: true, message: '请选择任务优先级', trigger: 'change' }],
+  description: [{ required: true, message: '请输入任务描述', trigger: 'blur' }]
+}
+
+const countTasks = (tasks: Task[]): number =>
+  tasks.reduce((total, task) => total + 1 + countTasks(task.children), 0)
+const handleQuery = () => (queryParams.pageNo = 1)
+const resetQuery = () => {
+  queryFormRef.value?.resetFields()
+  handleQuery()
+}
+const selectProject = (project?: Project) => {
+  if (project) selectedProject.value = project
+}
+const priorityTagType = (priority: Priority): 'danger' | 'warning' | 'info' =>
+  (({ 高: 'danger', 中: 'warning', 低: 'info' }) as const)[priority]
+const resetTaskForm = (task: Task) => {
+  Object.assign(taskForm, task, { children: [] })
+}
+const openTaskForm = (task?: Task, editing = false) => {
+  if (!selectedProject.value) return
+  if (!editing && task && (task.level !== 1 || task.children.length >= MAX_CHILDREN)) {
+    ElMessage.warning(
+      task.level !== 1 ? '孙子任务不能继续分解' : `每个子任务最多添加 ${MAX_CHILDREN} 个孙子任务`
+    )
+    return
+  }
+  editingTask.value = editing ? task : undefined
+  parentTask.value = !editing && task ? task : undefined
+  taskDialogTitle.value = editing ? '编辑任务' : task ? '新增孙子任务' : '新增一级任务'
+  resetTaskForm(editing && task ? task : createTask(task ? 2 : 1))
+  taskDialogVisible.value = true
+}
+const submitTask = async () => {
+  const valid = await taskFormRef.value?.validate().catch(() => false)
+  if (!valid) return
+  if (!taskForm.startDate || !taskForm.endDate || taskForm.startDate > taskForm.endDate) {
+    ElMessage.warning('结束时间不能早于开始时间')
+    return
+  }
+  if (!selectedProject.value) return
+  submitLoading.value = true
+  if (editingTask.value) Object.assign(editingTask.value, taskForm)
+  else {
+    if (
+      parentTask.value &&
+      (parentTask.value.level !== 1 || parentTask.value.children.length >= MAX_CHILDREN)
+    ) {
+      submitLoading.value = false
+      ElMessage.warning(
+        parentTask.value.level !== 1
+          ? '孙子任务不能继续分解'
+          : `每个子任务最多添加 ${MAX_CHILDREN} 个孙子任务`
+      )
+      return
+    }
+    const task = createTask(taskForm.level, { ...taskForm, id: Date.now() })
+    if (parentTask.value) parentTask.value.children.push(task)
+    else selectedProject.value.tasks.push(task)
+  }
+  submitLoading.value = false
+  taskDialogVisible.value = false
+  ElMessage.success(editingTask.value ? '任务已更新' : '任务已添加')
+}
+const removeTask = async (task: Task) => {
+  if (!selectedProject.value) return
+  const confirmed = await ElMessageBox.confirm(
+    task.children.length
+      ? '删除任务将同时删除其全部下级任务,确定继续吗?'
+      : '确定删除这个任务吗?',
+    '删除确认',
+    { type: 'warning', confirmButtonText: '确定删除', cancelButtonText: '取消' }
+  )
+    .then(() => true)
+    .catch(() => false)
+  if (!confirmed) return
+  const remove = (tasks: Task[]): boolean => {
+    const index = tasks.findIndex((item) => item.id === task.id)
+    if (index >= 0) {
+      tasks.splice(index, 1)
+      return true
+    }
+    return tasks.some((item) => remove(item.children))
+  }
+  remove(selectedProject.value.tasks)
+  ElMessage.success('任务已删除')
+}
+</script>
+
+<style scoped lang="scss">
+.page-heading,
+.task-heading,
+.task-title-row {
+  display: flex;
+  align-items: center;
+}
+.page-heading,
+.task-heading {
+  justify-content: space-between;
+}
+.page-heading {
+  margin-bottom: 20px;
+}
+.page-heading h2,
+.task-heading h3 {
+  margin: 0;
+  color: var(--el-text-color-primary);
+}
+.page-heading p,
+.task-heading span {
+  margin: 6px 0 0;
+  color: var(--el-text-color-secondary);
+  font-size: 13px;
+}
+.page-heading :deep(.el-tag .iconify) {
+  margin-right: 4px;
+}
+.project-filter {
+  margin-bottom: -15px;
+}
+.task-panel {
+  margin-top: 16px;
+}
+.task-heading {
+  margin-bottom: 18px;
+}
+.task-title-row {
+  gap: 10px;
+}
+.task-title-row h3 {
+  font-size: 18px;
+}
+.task-name {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+.parent-alert {
+  margin-bottom: 18px;
+}
+@media (max-width: 900px) {
+  .page-heading,
+  .task-heading {
+    align-items: flex-start;
+    flex-direction: column;
+    gap: 14px;
+  }
+}
+</style>

+ 547 - 15
src/views/researchProject/wbs/index.vue

@@ -1,30 +1,562 @@
 <template>
 <template>
   <div>
   <div>
+    <vxe-switch v-model="treeConfig.showIcon" />
+
     <vxe-gantt v-bind="ganttOptions" />
     <vxe-gantt v-bind="ganttOptions" />
   </div>
   </div>
 </template>
 </template>
 
 
 <script setup>
 <script setup>
 import { reactive } from 'vue'
 import { reactive } from 'vue'
+import { VxeGanttTaskType } from 'vxe-gantt'
+
+const treeConfig = reactive({
+  transform: true, // 自动将列表转为树结构
+  rowField: 'newId', // 自定义树节点关联的主键
+  parentField: 'newParentId', // 自定义树节点关联的父节点的字段名
+  showIcon: true // 是否显示展开按钮
+})
 const ganttOptions = reactive({
 const ganttOptions = reactive({
   border: true,
   border: true,
   height: 600,
   height: 600,
+  columnConfig: {
+    resizable: true
+  },
+  treeConfig,
+  taskConfig: {
+    titleField: 'newTitle', // 自定义标题字段名
+    startField: 'start', // 自定义开始日期字段名
+    endField: 'end', // 自定义结束期字段名
+    progressField: 'progress', // 自定义进度值字段名
+    typeField: 'type' // 自定义渲染类型字段名
+  },
+  taskBarSubviewConfig: {
+    showOverview: true, // 是否显示任务总览,当子任务被展开后自动显示任务总览
+    // 自定义子任务视图任务条的样式
+    barStyle({ row }) {
+      if (row.flag === 1) {
+        return {
+          top: '-12px', // 偏移位置,计划于实际不重叠
+          backgroundColor: '#40d9eff'
+        }
+      }
+      if (row.flag === 2) {
+        return {
+          top: '12px', // 偏移位置,计划于实际不重叠
+          backgroundColor: '#31d231'
+        }
+      }
+    }
+  },
+  taskBarConfig: {
+    showContent: true, // 是否在任务条显示内容
+    moveable: true, // 是否允许拖拽任务移动日期
+    resizable: true, // 是否允许拖拽任务调整日期
+    barStyle({ row }) {
+      return {
+        round: true, // 圆角
+        bgColor: row.flag === 1 ? '#40d9eff' : '#31d231' // 已完成部分任务条的背景颜色
+      }
+    }
+  },
+  taskViewConfig: {
+    tableStyle: {
+      width: 200
+    },
+    viewStyle: {
+      border: 'inner'
+    }
+  },
   columns: [
   columns: [
-    { field: 'name', title: '任务名称', minWidth: 200 },
-    { field: 'start', title: '开始时间', width: 100 },
-    { field: 'end', title: '结束时间', width: 100 }
+    { field: 'newTitle', title: '任务名称', minWidth: 160, treeNode: true },
+    { field: 'planStartDate', title: '计划开始日期', width: 140 },
+    { field: 'planEndDate', title: '实际 结束日期', width: 140 },
+    { field: 'actualStartDate', title: '计划开始日期', width: 140 },
+    { field: 'actualEndDate', title: '实际结束日期', width: 140 }
   ],
   ],
-  data: [
-    { id: 10001, name: '任务R790', start: '2024-03-01', end: '2024-03-04', progress: 3 },
-    { id: 10002, name: '任务C30456572349', start: '2024-03-03', end: '2024-03-08', progress: 10 },
-    { id: 10003, name: '任务E563', start: '2024-03-03', end: '2024-03-11', progress: 90 },
-    { id: 10004, name: '任务P687', start: '2024-03-05', end: '2024-03-11', progress: 15 },
-    { id: 10005, name: '任务L8412430903', start: '2024-03-08', end: '2024-03-15', progress: 100 },
-    { id: 10006, name: '任务B567', start: '2024-03-10', end: '2024-03-21', progress: 5 },
-    { id: 10007, name: '任务V513802134450', start: '2024-03-15', end: '2024-03-24', progress: 70 },
-    { id: 10008, name: '任务G110', start: '2024-03-20', end: '2024-03-29', progress: 50 },
-    { id: 10009, name: '任务I802', start: '2024-03-19', end: '2024-03-20', progress: 5 },
-    { id: 10010, name: '任务K778', start: '2024-03-12', end: '2024-03-20', progress: 10 }
-  ]
+  data: []
 })
 })
+
+// 将实际和计划一分二
+const handleMultiTask = (list) => {
+  const multiRest = []
+  list.forEach((item) => {
+    if (item.hasChild) {
+      const groupRow = {
+        ...item,
+        newTitle: `${item.title}`,
+        flag: 3,
+        newId: `g${item.id}`,
+        newParentId: item.parentId ? `g${item.parentId}` : null,
+        start: '',
+        end: '',
+        progress: 0,
+        type: VxeGanttTaskType.Subview
+      }
+      multiRest.push(groupRow)
+    } else {
+      // 计划
+      const planRow = {
+        ...item,
+        newTitle: `计划:${item.title}`,
+        flag: 1,
+        newId: `p${item.id}`,
+        newParentId: item.parentId ? `g${item.parentId}` : null,
+        start: item.planStartDate,
+        end: item.planEndDate,
+        progress: 0
+      }
+      // 实际
+      const actualRow = {
+        ...item,
+        newTitle: `实际:${item.title}`,
+        flag: 2,
+        newId: `a${item.id}`,
+        newParentId: item.parentId ? `g${item.parentId}` : null,
+        start: item.actualStartDate,
+        end: item.actualEndDate,
+        progress: 0
+      }
+      multiRest.push(planRow, actualRow)
+    }
+  })
+  return multiRest
+}
+
+const loadList = () => {
+  const list = [
+    {
+      id: 10001,
+      parentId: null,
+      title: '任务1',
+      planStartDate: '',
+      planEndDate: '',
+      actualStartDate: '',
+      actualEndDate: '',
+      planProgress: 90,
+      actualProgress: 80,
+      hasChild: true
+    },
+    {
+      id: 10002,
+      parentId: 10001,
+      title: '任务2',
+      planStartDate: '2024-02-27',
+      planEndDate: '2024-03-03',
+      actualStartDate: '2024-03-01',
+      actualEndDate: '2024-03-02',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10003,
+      parentId: null,
+      title: '任务3',
+      planStartDate: '',
+      planEndDate: '',
+      actualStartDate: '',
+      actualEndDate: '',
+      planProgress: 90,
+      actualProgress: 80,
+      hasChild: true
+    },
+    {
+      id: 10004,
+      parentId: 10003,
+      title: '任务4',
+      planStartDate: '2024-03-01',
+      planEndDate: '2024-03-02',
+      actualStartDate: '2024-03-03',
+      actualEndDate: '2024-03-04',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10005,
+      parentId: 10003,
+      title: '任务5',
+      planStartDate: '2024-03-03',
+      planEndDate: '2024-03-05',
+      actualStartDate: '2024-03-04',
+      actualEndDate: '2024-03-07',
+      planProgress: 90,
+      actualProgress: 80,
+      hasChild: true
+    },
+    {
+      id: 10006,
+      parentId: 10003,
+      title: '任务6',
+      planStartDate: '2024-03-19',
+      planEndDate: '2024-03-22',
+      actualStartDate: '2024-03-17',
+      actualEndDate: '2024-03-20',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10007,
+      parentId: 10005,
+      title: '任务7',
+      planStartDate: '2024-03-17',
+      planEndDate: '2024-03-20',
+      actualStartDate: '2024-03-19',
+      actualEndDate: '2024-03-22',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10008,
+      parentId: null,
+      title: '任务8',
+      planStartDate: '',
+      planEndDate: '',
+      actualStartDate: '',
+      actualEndDate: '',
+      planProgress: 90,
+      actualProgress: 80,
+      hasChild: true
+    },
+    {
+      id: 10009,
+      parentId: 10008,
+      title: '任务9',
+      planStartDate: '2024-03-02',
+      planEndDate: '2024-03-03',
+      actualStartDate: '2024-03-02',
+      actualEndDate: '2024-03-03',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10010,
+      parentId: 10008,
+      title: '任务10',
+      planStartDate: '2024-03-05',
+      planEndDate: '2024-03-07',
+      actualStartDate: '2024-03-05',
+      actualEndDate: '2024-03-09',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10011,
+      parentId: 10008,
+      title: '任务11',
+      planStartDate: '2024-03-08',
+      planEndDate: '2024-03-11',
+      actualStartDate: '2024-03-11',
+      actualEndDate: '2024-03-15',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10012,
+      parentId: null,
+      title: '任务12',
+      planStartDate: '',
+      planEndDate: '',
+      actualStartDate: '',
+      actualEndDate: '',
+      planProgress: 90,
+      actualProgress: 80,
+      hasChild: true
+    },
+    {
+      id: 10013,
+      parentId: 10012,
+      title: '任务13',
+      planStartDate: '2024-03-04',
+      planEndDate: '2024-03-05',
+      actualStartDate: '2024-03-06',
+      actualEndDate: '2024-03-08',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10014,
+      parentId: 10012,
+      title: '任务14',
+      planStartDate: '2024-03-06',
+      planEndDate: '2024-03-07',
+      actualStartDate: '2024-03-03',
+      actualEndDate: '2024-03-05',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10015,
+      parentId: 10012,
+      title: '任务15',
+      planStartDate: '2024-03-09',
+      planEndDate: '2024-03-12',
+      actualStartDate: '2024-03-10',
+      actualEndDate: '2024-03-11',
+      planProgress: 90,
+      actualProgress: 80,
+      hasChild: true
+    },
+    {
+      id: 10016,
+      parentId: 10012,
+      title: '任务16',
+      planStartDate: '2024-03-13',
+      planEndDate: '2024-03-15',
+      actualStartDate: '2024-03-17',
+      actualEndDate: '2024-03-18',
+      planProgress: 90,
+      actualProgress: 80,
+      hasChild: true
+    },
+    {
+      id: 10017,
+      parentId: 10012,
+      title: '任务17',
+      planStartDate: '2024-03-17',
+      planEndDate: '2024-03-21',
+      actualStartDate: '2024-03-18',
+      actualEndDate: '2024-03-19',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10018,
+      parentId: null,
+      title: '任务18',
+      planStartDate: '2024-03-11',
+      planEndDate: '2024-03-12',
+      actualStartDate: '2024-03-12',
+      actualEndDate: '2024-03-15',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10019,
+      parentId: null,
+      title: '任务19',
+      planStartDate: '2024-03-15',
+      planEndDate: '2024-03-17',
+      actualStartDate: '2024-03-17',
+      actualEndDate: '2024-03-20',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10020,
+      parentId: null,
+      title: '任务20',
+      planStartDate: '',
+      planEndDate: '',
+      actualStartDate: '',
+      actualEndDate: '',
+      planProgress: 90,
+      actualProgress: 80,
+      hasChild: true
+    },
+    {
+      id: 10021,
+      parentId: 10020,
+      title: '任务21',
+      planStartDate: '2024-03-18',
+      planEndDate: '2024-03-21',
+      actualStartDate: '2024-03-19',
+      actualEndDate: '2024-03-21',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10022,
+      parentId: 10020,
+      title: '任务22',
+      planStartDate: '2024-03-23',
+      planEndDate: '2024-03-25',
+      actualStartDate: '2024-03-23',
+      actualEndDate: '2024-03-25',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10023,
+      parentId: 10020,
+      title: '任务23',
+      planStartDate: '2024-03-27',
+      planEndDate: '2024-04-02',
+      actualStartDate: '2024-03-27',
+      actualEndDate: '2024-03-28',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10024,
+      parentId: 10020,
+      title: '任务24',
+      planStartDate: '2024-04-04',
+      planEndDate: '2024-04-06',
+      actualStartDate: '2024-04-02',
+      actualEndDate: '2024-04-05',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10025,
+      parentId: 10020,
+      title: '任务25',
+      planStartDate: '2024-04-07',
+      planEndDate: '2024-04-11',
+      actualStartDate: '2024-04-08',
+      actualEndDate: '2024-04-13',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10026,
+      parentId: null,
+      title: '任务26',
+      planStartDate: '2024-04-05',
+      planEndDate: '2024-04-08',
+      actualStartDate: '2024-04-07',
+      actualEndDate: '2024-04-10',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10027,
+      parentId: null,
+      title: '任务27',
+      planStartDate: '',
+      planEndDate: '',
+      actualStartDate: '',
+      actualEndDate: '',
+      planProgress: 90,
+      actualProgress: 80,
+      hasChild: true
+    },
+    {
+      id: 10028,
+      parentId: 10027,
+      title: '任务28',
+      planStartDate: '2024-03-10',
+      planEndDate: '2024-03-13',
+      actualStartDate: '2024-03-13',
+      actualEndDate: '2024-03-14',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10029,
+      parentId: 10027,
+      title: '任务29',
+      planStartDate: '2024-03-14',
+      planEndDate: '2024-03-18',
+      actualStartDate: '2024-03-16',
+      actualEndDate: '2024-03-18',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10030,
+      parentId: 10027,
+      title: '任务30',
+      planStartDate: '2024-03-19',
+      planEndDate: '2024-03-25',
+      actualStartDate: '2024-03-20',
+      actualEndDate: '2024-03-26',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10031,
+      parentId: null,
+      title: '任务31',
+      planStartDate: '2024-03-22',
+      planEndDate: '2024-03-24',
+      actualStartDate: '2024-03-19',
+      actualEndDate: '2024-03-20',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10032,
+      parentId: null,
+      title: '任务32',
+      planStartDate: '',
+      planEndDate: '',
+      actualStartDate: '',
+      actualEndDate: '',
+      planProgress: 90,
+      actualProgress: 80,
+      hasChild: true
+    },
+    {
+      id: 10033,
+      parentId: 10032,
+      title: '任务33',
+      planStartDate: '2024-03-09',
+      planEndDate: '2024-03-14',
+      actualStartDate: '2024-03-11',
+      actualEndDate: '2024-03-15',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10034,
+      parentId: 10032,
+      title: '任务34',
+      planStartDate: '2024-03-15',
+      planEndDate: '2024-03-21',
+      actualStartDate: '2024-03-16',
+      actualEndDate: '2024-03-19',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10035,
+      parentId: 10015,
+      title: '任务35',
+      planStartDate: '2024-03-16',
+      planEndDate: '2024-03-18',
+      actualStartDate: '2024-03-17',
+      actualEndDate: '2024-03-19',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10036,
+      parentId: 10015,
+      title: '任务36',
+      planStartDate: '2024-03-12',
+      planEndDate: '2024-03-14',
+      actualStartDate: '2024-03-13',
+      actualEndDate: '2024-03-15',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10037,
+      parentId: 10016,
+      title: '任务37',
+      planStartDate: '2024-03-19',
+      planEndDate: '2024-03-22',
+      actualStartDate: '2024-03-21',
+      actualEndDate: '2024-03-23',
+      planProgress: 90,
+      actualProgress: 80
+    },
+    {
+      id: 10038,
+      parentId: 10016,
+      title: '任务38',
+      planStartDate: '2024-03-23',
+      planEndDate: '2024-03-25',
+      actualStartDate: '2024-03-24',
+      actualEndDate: '2024-03-27',
+      planProgress: 90,
+      actualProgress: 80
+    }
+  ]
+  const multiRest = handleMultiTask(list)
+  ganttOptions.data = multiRest
+}
+
+loadList()
 </script>
 </script>