From db42d47f5692ef64e5436c5a6d29dcb537b44596 Mon Sep 17 00:00:00 2001
From: zouyu <2723363702@qq.com>
Date: 星期一, 26 一月 2026 16:36:13 +0800
Subject: [PATCH] 浪潮对接单点登录:mis调整

---
 src/views/oaSystem/projectManagement/components/milestoneList.vue |  289 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 289 insertions(+), 0 deletions(-)

diff --git a/src/views/oaSystem/projectManagement/components/milestoneList.vue b/src/views/oaSystem/projectManagement/components/milestoneList.vue
new file mode 100644
index 0000000..47b0027
--- /dev/null
+++ b/src/views/oaSystem/projectManagement/components/milestoneList.vue
@@ -0,0 +1,289 @@
+// ... existing code ...
+<template>
+  <div class="milestone-list-container">
+    <el-timeline>
+      <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>
+              </div>
+            </div>
+          </template>
+          <div class="milestone-content">
+            <p>{{ milestone.description }}</p>
+            <div class="milestone-status">
+              <el-tag :type="getStatusType(milestone.status)">{{ getStatusText(milestone.status) }}</el-tag>
+            </div>
+          </div>
+        </el-card>
+      </el-timeline-item>
+    </el-timeline>
+    
+    <!-- 鏃犻噷绋嬬鏃剁殑鎻愮ず -->
+    <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-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-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary" @click="submitEditForm">纭畾</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</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';
+
+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
+  });
+  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') {
+      // 琛ㄥ崟楠岃瘉澶辫触锛孍lement Plus浼氳嚜鍔ㄦ彁绀�
+      return;
+    }
+    ElMessage.error('閲岀▼纰戠紪杈戝け璐�');
+    console.error('缂栬緫閲岀▼纰戝け璐�:', error);
+  }
+};
+
+// 鍒犻櫎閲岀▼纰�
+const handleDelete = (milestone) => {
+  ElMessageBox.confirm(
+    `纭畾瑕佸垹闄ら噷绋嬬 "${milestone.phaseName}" 鍚楋紵鍒犻櫎鍚庡皢鏃犳硶鎭㈠銆俙,
+    '鍒犻櫎纭',
+    {
+      confirmButtonText: '纭畾',
+      cancelButtonText: '鍙栨秷',
+      type: 'warning'
+    }
+  )
+    .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 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;
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.milestone-actions {
+  display: flex;
+  gap: 10px;
+}
+
+.milestone-content {
+  padding: 10px 0;
+}
+
+.milestone-status {
+  margin-top: 10px;
+}
+
+.empty-tip {
+  margin-top: 40px;
+  text-align: center;
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+</style>

--
Gitblit v1.9.3