huminmin
15 小时以前 8f9e65a587ee29b607010f83025456ee28c9b0d6
src/views/oaSystem/projectManagement/components/milestoneList.vue
@@ -2,18 +2,21 @@
<template>
  <div class="milestone-list-container">
    <el-timeline>
      <el-timeline-item
        v-for="milestone in milestoneList"
        :key="milestone.phaseId"
        :timestamp="milestone.endDate"
      >
      <el-timeline-item v-for="milestone in milestoneList"
                        :key="milestone.phaseId"
                        :timestamp="milestone.endDate">
        <el-card>
          <template #header>
            <div class="card-header">
              <span>{{ milestone.phaseName }}</span>
              <div class="milestone-actions">
                <el-button type="text" size="small" @click="handleEdit(milestone)">编辑</el-button>
                <el-button type="text" size="small" @click="handleDelete(milestone)" danger>删除</el-button>
                <el-button type="text"
                           size="small"
                           @click="handleEdit(milestone)">编辑</el-button>
                <el-button type="text"
                           size="small"
                           @click="handleDelete(milestone)"
                           danger>删除</el-button>
              </div>
            </div>
          </template>
@@ -26,66 +29,67 @@
        </el-card>
      </el-timeline-item>
    </el-timeline>
    <!-- 无里程碑时的提示 -->
    <div v-if="milestoneList.length === 0" class="empty-tip">
    <div v-if="milestoneList.length === 0"
         class="empty-tip">
      <el-empty description="暂无里程碑数据" />
    </div>
    <!-- 编辑里程碑对话框 -->
    <el-dialog
      v-model="dialogVisible"
      :title="'编辑里程碑: ' + (form.phaseName || '')"
      width="600px"
      :close-on-click-modal="false"
    >
      <el-form
        ref="formRef"
        :model="form"
        :rules="rules"
        label-width="100px"
      >
        <el-form-item label="里程碑名称" prop="phaseName">
          <el-input v-model="form.phaseName" placeholder="请输入里程碑名称" />
    <el-dialog v-model="dialogVisible"
               :title="'编辑里程碑: ' + (form.phaseName || '')"
               width="600px"
               :close-on-click-modal="false">
      <el-form ref="formRef"
               :model="form"
               :rules="rules"
               label-width="100px">
        <el-form-item label="里程碑名称"
                      prop="phaseName">
          <el-input v-model="form.phaseName"
                    placeholder="请输入里程碑名称" />
        </el-form-item>
        <el-row :gutter="20">
      <el-col :span="12">
        <el-form-item label="开始日期" prop="startDate">
          <el-date-picker
            v-model="form.startDate"
            type="date"
            format="YYYY-MM-DD"
            value-format="YYYY-MM-DD"
            placeholder="选择开始日期"
            style="width: 100%"
          />
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="结束日期" prop="endDate">
          <el-date-picker
            v-model="form.endDate"
            type="date"
            format="YYYY-MM-DD"
            value-format="YYYY-MM-DD"
            placeholder="选择结束日期"
            style="width: 100%"
          />
        </el-form-item>
      </el-col>
    </el-row>
        <el-form-item label="状态" prop="status">
          <el-select v-model="form.status" placeholder="请选择状态">
            <el-option label="未开始" value="notStarted" />
            <el-option label="已完成" value="completed" />
            <el-option label="已延迟" value="delayed" />
          <el-col :span="12">
            <el-form-item label="开始日期"
                          prop="startDate">
              <el-date-picker v-model="form.startDate"
                              type="date"
                              format="YYYY-MM-DD"
                              value-format="YYYY-MM-DD"
                              placeholder="选择开始日期"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="结束日期"
                          prop="endDate">
              <el-date-picker v-model="form.endDate"
                              type="date"
                              format="YYYY-MM-DD"
                              value-format="YYYY-MM-DD"
                              placeholder="选择结束日期"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="状态"
                      prop="status">
          <el-select v-model="form.status"
                     placeholder="请选择状态">
            <el-option label="未开始"
                       value="notStarted" />
            <el-option label="已完成"
                       value="completed" />
            <el-option label="已延迟"
                       value="delayed" />
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitEditForm">确定</el-button>
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary" @click="submitEditForm">确定</el-button>
        </div>
      </template>
    </el-dialog>
@@ -93,197 +97,203 @@
</template>
<script setup>
import { ref, onMounted, watch, reactive } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { getProject, listProjectPhase, updateProjectPhase,delProjectPhase } from '@/api/oaSystem/projectManagement';
  import { ref, onMounted, watch, reactive } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import {
    getProject,
    listProjectPhase,
    updateProjectPhase,
    delProjectPhase,
  } from "@/api/oaSystem/projectManagement";
const props = defineProps({
  projectId: {
    type: String,
    required: true
  }
});
const emit = defineEmits(['refresh']);
const milestoneList = ref([]);
const dialogVisible = ref(false);
const formRef = ref(null);
const form = reactive({
  phaseId: '',
  phaseName: '',
  startDate: '',
  endDate: '',
  status: 'notStarted',
  projectId: props.projectId
});
// 表单验证规则
const rules = {
  phaseName: [
    { required: true, message: '请输入里程碑名称', trigger: 'blur' },
    { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
  ],
  status: [
    { required: true, message: '请选择状态', trigger: 'change' }
  ]
};
// 获取里程碑列表
const getMilestoneList = async () => {
  try {
    listProjectPhase(props.projectId).then(res => {
      milestoneList.value = res.data.rows || res.data;
      // 按目标日期排序
      // milestoneList.value.sort((a, b) => new Date(a.endDate) - new Date(b.endDate));
    })
  } catch (error) {
    ElMessage.error('获取里程碑列表失败');
    console.error('获取里程碑列表失败:', error);
  }
};
// 编辑里程碑
const handleEdit = (milestone) => {
  // 复制里程碑数据到表单
  Object.assign(form, {
    phaseId: milestone.phaseId,
    phaseName: milestone.phaseName,
    description: milestone.description,
    endDate: milestone.endDate,
    status: milestone.status,
    projectId: props.projectId
  const props = defineProps({
    projectId: {
      type: String,
      required: true,
    },
  });
  dialogVisible.value = true;
};
// 提交编辑表单
const submitEditForm = async () => {
  try {
    await formRef.value.validate();
    // 发送更新请求
    const res = await updateProjectPhase(form);
    if (res.code === 200) {
      ElMessage.success('里程碑编辑成功');
      dialogVisible.value = false;
      getMilestoneList(); // 刷新列表
      emit('refresh'); // 通知父组件刷新
    } else {
      ElMessage.error(res.msg || '里程碑编辑失败');
    }
  } catch (error) {
    if (error.name === 'ValidationError') {
      // 表单验证失败,Element Plus会自动提示
      return;
    }
    ElMessage.error('里程碑编辑失败');
    console.error('编辑里程碑失败:', error);
  }
};
  const emit = defineEmits(["refresh"]);
// 删除里程碑
const handleDelete = (milestone) => {
  ElMessageBox.confirm(
    `确定要删除里程碑 "${milestone.phaseName}" 吗?删除后将无法恢复。`,
    '删除确认',
    {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
  const milestoneList = ref([]);
  const dialogVisible = ref(false);
  const formRef = ref(null);
  const form = reactive({
    phaseId: "",
    phaseName: "",
    startDate: "",
    endDate: "",
    status: "notStarted",
    projectId: props.projectId,
  });
  // 表单验证规则
  const rules = {
    phaseName: [
      { required: true, message: "请输入里程碑名称", trigger: "blur" },
      { min: 2, max: 50, message: "长度在 2 到 50 个字符", trigger: "blur" },
    ],
    status: [{ required: true, message: "请选择状态", trigger: "change" }],
  };
  // 获取里程碑列表
  const getMilestoneList = async () => {
    try {
      listProjectPhase(props.projectId).then(res => {
        milestoneList.value = res.data.rows || res.data;
        // 按目标日期排序
        // milestoneList.value.sort((a, b) => new Date(a.endDate) - new Date(b.endDate));
      });
    } catch (error) {
      ElMessage.error("获取里程碑列表失败");
      console.error("获取里程碑列表失败:", error);
    }
  )
    .then(async () => {
      try {
        // 调用删除API
        const res = await delProjectPhase(milestone.phaseId);
        if (res.code === 200) {
          ElMessage.success('里程碑删除成功');
          getMilestoneList(); // 刷新列表
          emit('refresh'); // 通知父组件刷新
        } else {
          ElMessage.error(res.msg || '里程碑删除失败');
        }
      } catch (error) {
        ElMessage.error('里程碑删除失败');
        console.error('删除里程碑失败:', error);
      }
    })
    .catch(() => {
      // 用户取消删除
      ElMessage.info('已取消删除');
  };
  // 编辑里程碑
  const handleEdit = milestone => {
    // 复制里程碑数据到表单
    Object.assign(form, {
      phaseId: milestone.phaseId,
      phaseName: milestone.phaseName,
      description: milestone.description,
      endDate: milestone.endDate,
      status: milestone.status,
      projectId: props.projectId,
    });
};
// 获取状态标签类型
const getStatusType = (status) => {
  const statusTypeMap = {
    notStarted: 'info',
    completed: 'success',
    delayed: 'danger'
    dialogVisible.value = true;
  };
  return statusTypeMap[status] || 'default';
};
// 获取状态文本
const getStatusText = (status) => {
  const statusTextMap = {
    notStarted: '未开始',
    completed: '已完成',
    delayed: '已延迟'
  // 提交编辑表单
  const submitEditForm = async () => {
    try {
      await formRef.value.validate();
      // 发送更新请求
      const res = await updateProjectPhase(form);
      if (res.code === 200) {
        ElMessage.success("里程碑编辑成功");
        dialogVisible.value = false;
        getMilestoneList(); // 刷新列表
        emit("refresh"); // 通知父组件刷新
      } else {
        ElMessage.error(res.msg || "里程碑编辑失败");
      }
    } catch (error) {
      if (error.name === "ValidationError") {
        // 表单验证失败,Element Plus会自动提示
        return;
      }
      ElMessage.error("里程碑编辑失败");
      console.error("编辑里程碑失败:", error);
    }
  };
  return statusTextMap[status] || status;
};
// 监听项目ID变化
watch(() => props.projectId, () => {
  if (props.projectId) {
    getMilestoneList();
  }
});
  // 删除里程碑
  const handleDelete = milestone => {
    ElMessageBox.confirm(
      `确定要删除里程碑 "${milestone.phaseName}" 吗?删除后将无法恢复。`,
      "删除确认",
      {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }
    )
      .then(async () => {
        try {
          // 调用删除API
          const res = await delProjectPhase(milestone.phaseId);
// 初始化
onMounted(() => {
  if (props.projectId) {
    getMilestoneList();
  }
});
          if (res.code === 200) {
            ElMessage.success("里程碑删除成功");
            getMilestoneList(); // 刷新列表
            emit("refresh"); // 通知父组件刷新
          } else {
            ElMessage.error(res.msg || "里程碑删除失败");
          }
        } catch (error) {
          ElMessage.error("里程碑删除失败");
          console.error("删除里程碑失败:", error);
        }
      })
      .catch(() => {
        // 用户取消删除
        ElMessage.info("已取消删除");
      });
  };
  // 获取状态标签类型
  const getStatusType = status => {
    const statusTypeMap = {
      notStarted: "info",
      completed: "success",
      delayed: "danger",
    };
    return statusTypeMap[status] || "default";
  };
  // 获取状态文本
  const getStatusText = status => {
    const statusTextMap = {
      notStarted: "未开始",
      completed: "已完成",
      delayed: "已延迟",
    };
    return statusTextMap[status] || status;
  };
  // 监听项目ID变化
  watch(
    () => props.projectId,
    () => {
      if (props.projectId) {
        getMilestoneList();
      }
    }
  );
  // 初始化
  onMounted(() => {
    if (props.projectId) {
      getMilestoneList();
    }
  });
</script>
<style scoped>
.milestone-list-container {
  padding: 10px 0;
}
  .milestone-list-container {
    padding: 10px 0;
  }
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
  .card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
.milestone-actions {
  display: flex;
  gap: 10px;
}
  .milestone-actions {
    display: flex;
    gap: 10px;
  }
.milestone-content {
  padding: 10px 0;
}
  .milestone-content {
    padding: 10px 0;
  }
.milestone-status {
  margin-top: 10px;
}
  .milestone-status {
    margin-top: 10px;
  }
.empty-tip {
  margin-top: 40px;
  text-align: center;
}
  .empty-tip {
    margin-top: 40px;
    text-align: center;
  }
.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
  .dialog-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
  }
</style>