From e3fc89bb84fc32e0048cbd53ee96e26f01a8881f Mon Sep 17 00:00:00 2001
From: ZN <zhang_12370@163.com>
Date: 星期一, 09 三月 2026 13:14:35 +0800
Subject: [PATCH] feat(项目类型): 优化项目类型对话框的附件上传和步骤排序功能

---
 src/views/projectManagement/projectType/components/ProjectTypeDialog.vue |  161 ++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 122 insertions(+), 39 deletions(-)

diff --git a/src/views/projectManagement/projectType/components/ProjectTypeDialog.vue b/src/views/projectManagement/projectType/components/ProjectTypeDialog.vue
index 6e3df3c..d73f464 100644
--- a/src/views/projectManagement/projectType/components/ProjectTypeDialog.vue
+++ b/src/views/projectManagement/projectType/components/ProjectTypeDialog.vue
@@ -32,10 +32,13 @@
         <div class="info-item">
           <span class="item-label">闄勪欢</span>
           <el-upload
-            action="#"
-            :auto-upload="false"
-            :show-file-list="false"
-            @change="handleFileChange"
+            :action="uploadUrl"
+            :headers="uploadHeaders"
+            :on-success="handleUploadSuccess"
+            :on-remove="handleRemove"
+            :file-list="form.attachmentList"
+            name="files"
+            multiple
           >
             <el-button type="primary">涓婁紶闄勪欢</el-button>
           </el-upload>
@@ -43,11 +46,20 @@
       </div>
 
       <!-- 姝ラ閰嶇疆琛ㄦ牸 -->
+       <p class="top-tip">璇锋寜鐓ч『搴忛厤缃」鐩樁娈碉紝鎷栨嫿<b>姝ラ</b>鎺掑簭鍗冲彲</p>
       <div class="step-table-container">
-        <el-table :data="form.savePlanNodeList" border style="width: 100%">
-          <el-table-column label="姝ラ" width="80" align="center">
+        <el-table 
+          :data="form.savePlanNodeList" 
+          border 
+          style="width: 100%" 
+          row-key="id"
+          class="drag-table"
+        >
+          <el-table-column label="姝ラ" width="80" align="center" class-name="drag-handle">
             <template #default="scope">
-              {{ scope.$index + 1 }}
+              <div class="step-index" style="cursor: move;">
+                {{ scope.$index + 1 }}
+              </div>
             </template>
           </el-table-column>
           
@@ -118,28 +130,6 @@
               <el-input v-model="scope.row.workContent" placeholder="璇疯緭鍏�" />
             </template>
           </el-table-column>
-
-          <el-table-column label="鎿嶄綔" width="180" align="center">
-            <template #default="scope">
-              <el-button
-                link
-                type="primary"
-                :disabled="scope.$index === 0"
-                @click="moveStep(scope.$index, -1)"
-              >涓婄Щ</el-button>
-              <el-button
-                link
-                type="primary"
-                :disabled="scope.$index === form.savePlanNodeList.length - 1"
-                @click="moveStep(scope.$index, 1)"
-              >涓嬬Щ</el-button>
-              <el-button
-                link
-                type="danger"
-                @click="removeStep(scope.$index)"
-              >鍒犻櫎</el-button>
-            </template>
-          </el-table-column>
         </el-table>
 
         <div class="add-row-btn" @click="addStep">
@@ -158,10 +148,12 @@
 </template>
 
 <script setup>
-import { ref, watch, onMounted } from 'vue';
+import { ref, watch, onMounted, nextTick } from 'vue';
 import { Plus, QuestionFilled } from '@element-plus/icons-vue';
 import { userListNoPageByTenantId } from '@/api/system/user';
 import { ElMessage } from 'element-plus';
+import { getToken } from '@/utils/auth';
+import Sortable from 'sortablejs';
 
 const props = defineProps({
   modelValue: Boolean,
@@ -174,12 +166,17 @@
 const visible = ref(false);
 const formRef = ref(null);
 const userOptions = ref([]);
+const uploadHeaders = { Authorization: "Bearer " + getToken() };
+// 涓婁紶鍦板潃
+const uploadUrl = import.meta.env.VITE_APP_BASE_API + "/basic/customer-follow/upload";
+let sortable = null;
 
 const form = ref({
   id: undefined,
   name: '',
   description: '',
   attachmentIds: [],
+  attachmentList: [],
   savePlanNodeList: []
 });
 
@@ -192,21 +189,70 @@
   visible.value = val;
   if (val) {
     if (props.data) {
-      // 缂栬緫妯″紡
-      form.value = JSON.parse(JSON.stringify(props.data));
-      if (!form.value.savePlanNodeList || form.value.savePlanNodeList.length === 0) {
+      // 缂栬緫妯″紡 - 鍥炴樉鏁版嵁
+      form.value = {
+        id: props.data.id,
+        name: props.data.name,
+        description: props.data.description,
+        attachmentIds: [],
+        attachmentList: props.data.attachmentList || [],
+        savePlanNodeList: []
+      };
+      
+      // 鍥炴樉闄勪欢ID
+      if (form.value.attachmentList && form.value.attachmentList.length > 0) {
+        form.value.attachmentIds = form.value.attachmentList.map(item => item.id);
+      }
+      
+      // 鍥炴樉姝ラ鑺傜偣
+      if (props.data.planNodeList && props.data.planNodeList.length > 0) {
+        form.value.savePlanNodeList = props.data.planNodeList.map(node => ({
+          id: node.id,
+          projectManagementPlanId: node.projectManagementPlanId,
+          sort: node.sort,
+          name: node.name,
+          leaderId: node.leaderId,
+          leaderName: node.leaderName,
+          estimatedDuration: node.estimatedDuration,
+          hourlyRate: node.hourlyRate,
+          workContent: node.workContent
+        }));
+      } else {
         form.value.savePlanNodeList = [createDefaultNode()];
       }
     } else {
       // 鏂板妯″紡
       resetForm();
     }
+    // 鍒濆鍖栨嫋鎷�
+    nextTick(() => {
+      initSortable();
+    });
   }
 });
 
 watch(visible, (val) => {
   emit('update:modelValue', val);
 });
+
+/** 鍒濆鍖栨嫋鎷� */
+function initSortable() {
+  const el = document.querySelector('.drag-table .el-table__body-wrapper tbody');
+  if (!el) return;
+  
+  if (sortable) {
+    sortable.destroy();
+  }
+
+  sortable = Sortable.create(el, {
+    handle: '.drag-handle',
+    animation: 150,
+    onEnd: ({ newIndex, oldIndex }) => {
+      const targetRow = form.value.savePlanNodeList.splice(oldIndex, 1)[0];
+      form.value.savePlanNodeList.splice(newIndex, 0, targetRow);
+    }
+  });
+}
 
 /** 鍒涘缓榛樿鑺傜偣瀵硅薄 */
 function createDefaultNode() {
@@ -227,6 +273,7 @@
     name: '',
     description: '',
     attachmentIds: [],
+    attachmentList: [],
     savePlanNodeList: [createDefaultNode()]
   };
   if (formRef.value) {
@@ -254,12 +301,38 @@
   }
 }
 
-/** 澶勭悊鏂囦欢鍙樺寲 */
-function handleFileChange(file) {
-  // 杩欓噷瀹炵幇鏂囦欢涓婁紶閫昏緫锛岃幏鍙� attachmentId
-  ElMessage.info('姝e湪涓婁紶: ' + file.name);
-  // 妯℃嫙涓婁紶鎴愬姛
-  // form.value.attachmentIds.push(newId);
+/** 澶勭悊鏂囦欢涓婁紶鎴愬姛 */
+function handleUploadSuccess(response, file, fileList) {
+  if (response.code === 200) {
+    ElMessage.success('涓婁紶鎴愬姛');
+    // 鍋囪鍚庣杩斿洖鐨勬暟鎹粨鏋勪腑鍖呭惈鏂囦欢ID鍜孶RL绛変俊鎭�
+    // 杩欓噷闇�瑕佹牴鎹疄闄呮帴鍙h繑鍥炵粨鏋勮繘琛岃皟鏁�
+    // 閫氬父 response.data 鍖呭惈鏂囦欢淇℃伅
+    const newFile = response.data;
+    if (newFile && newFile.id) {
+       form.value.attachmentIds.push(newFile.id);
+       form.value.attachmentList.push({
+         name: file.name,
+         url: newFile.url,
+         id: newFile.id
+       });
+    }
+  } else {
+    ElMessage.error(response.msg || '涓婁紶澶辫触');
+  }
+}
+
+/** 澶勭悊鏂囦欢绉婚櫎 */
+function handleRemove(file) {
+  const index = form.value.attachmentList.findIndex(item => item.name === file.name);
+  if (index !== -1) {
+    const fileId = form.value.attachmentList[index].id;
+    form.value.attachmentList.splice(index, 1);
+    const idIndex = form.value.attachmentIds.indexOf(fileId);
+    if (idIndex !== -1) {
+      form.value.attachmentIds.splice(idIndex, 1);
+    }
+  }
 }
 
 /** 娣诲姞姝ラ */
@@ -294,6 +367,10 @@
   try {
     const valid = await formRef.value.validate();
     if (valid) {
+      // 鎻愪氦鍓嶈嚜鍔ㄥ~鍏� sort 瀛楁锛屾寜褰撳墠鏁扮粍椤哄簭鎺掑簭
+      form.value.savePlanNodeList.forEach((node, index) => {
+        node.sort = index;
+      });
       emit('submit', form.value);
     }
   } catch (error) {
@@ -382,4 +459,10 @@
   gap: 15px;
   padding-top: 10px;
 }
+.top-tip {
+  
+  font-size: 14px;
+  color: #606266;
+  margin:0 0 10px 10px;
+}
 </style>

--
Gitblit v1.9.3