From e19cd654c14f000d7f77b2d23e016429678b1e58 Mon Sep 17 00:00:00 2001
From: zouyu <2723363702@qq.com>
Date: 星期一, 20 四月 2026 16:36:12 +0800
Subject: [PATCH] 用户管理表格支持拖拽排序

---
 src/views/system/user/index.vue            |  117 +++++++++++++++++++++++++++------------
 src/api/system/user.js                     |    2 
 src/views/performance/competency/index.vue |   37 ++++++++++--
 3 files changed, 113 insertions(+), 43 deletions(-)

diff --git a/src/api/system/user.js b/src/api/system/user.js
index 9dc4395..d56b254 100644
--- a/src/api/system/user.js
+++ b/src/api/system/user.js
@@ -216,7 +216,7 @@
 // 鏇存柊鐢ㄦ埛鎺掑簭
 export function updateUserSort(data) {
   return request({
-    url: "/system/user/updateUserSort",
+    url: "/system/newUser/updateUserSort",
     method: "post",
     data:data
   });
diff --git a/src/views/performance/competency/index.vue b/src/views/performance/competency/index.vue
index defb703..e5476d5 100644
--- a/src/views/performance/competency/index.vue
+++ b/src/views/performance/competency/index.vue
@@ -46,6 +46,7 @@
       </el-form-item>
     </el-form>
     <el-table
+      ref="competencyTable"
       v-if="refreshTable"
       v-loading="loading"
       :data="recordList"
@@ -127,12 +128,12 @@
       levelDictList:[],
       tableHeight:0,
       downloadLoading:false,
-      postList:[]
+      postList:[],
+      layoutTimer: null
     };
   },
   created() {
     this.getLevelDict()
-    this.getTableHeader();
     this.getList();
     this.getPostList()
     this.$nextTick(()=>{
@@ -141,11 +142,29 @@
   },
   mounted() {
     window.addEventListener("resize", this.getTableHeight);
+    this.scheduleTableLayout()
   },
   beforeDestroy() {
     window.removeEventListener("resize", this.getTableHeight);
+    if (this.layoutTimer) {
+      clearTimeout(this.layoutTimer)
+      this.layoutTimer = null
+    }
   },
   methods: {
+    scheduleTableLayout() {
+      if (this.layoutTimer) {
+        clearTimeout(this.layoutTimer)
+      }
+      this.layoutTimer = setTimeout(() => {
+        this.$nextTick(() => {
+          const table = this.$refs.competencyTable
+          if (table && typeof table.doLayout === 'function') {
+            table.doLayout()
+          }
+        })
+      }, 0)
+    },
     getPostList(){
       optionSelect().then(res=>{
         if(res.code===200){
@@ -171,6 +190,7 @@
       const innerHeight = window.innerHeight;
       const otherHeight = 50+46+40+51;
       this.tableHeight = innerHeight - otherHeight;
+      this.scheduleTableLayout()
     },
     changeSkillLevel(row, itemId){
       const configObj = {...row[itemId]}
@@ -196,20 +216,25 @@
     getTableHeader() {
       listConfig({ isEnable: true }).then((response) => {
         this.tableHeaderList = this.handleTree(response.data, "id");
+        this.scheduleTableLayout()
       });
     },
     /** 鏌ヨ鍒楄〃 */
     getList() {
       this.loading = true
-      this.getTableHeader();
-      getPageList(this.queryParams).then(res=>{
-        if(res.code===200){
-          this.recordList = res.data
+      Promise.all([
+        listConfig({ isEnable: true }),
+        getPageList(this.queryParams)
+      ]).then(([headerRes, listRes]) => {
+        this.tableHeaderList = this.handleTree(headerRes.data, "id");
+        if(listRes.code===200){
+          this.recordList = listRes.data
         }
       }).catch(error=>{
         console.log(error)
       }).finally(() => {
           this.loading = false;
+          this.scheduleTableLayout()
         });
     },
     /** 鎼滅储鎸夐挳鎿嶄綔 */
diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue
index f7f14d0..5fcb8ba 100644
--- a/src/views/system/user/index.vue
+++ b/src/views/system/user/index.vue
@@ -47,7 +47,7 @@
             </div>
           </div>
           <el-col>
-            <el-table row-id="userId" ref="dragTable" v-loading="loading" :data="userList" :header-cell-style="{ background: '#f8f8f9', color: '#515a6e' }" border>
+            <el-table ref="dragTable" v-loading="loading" row-key="userId" :data="userList" :header-cell-style="{ background: '#f8f8f9', color: '#515a6e' }" border>
               <el-table-column label="搴忓彿" align="center" type="index" />
               <el-table-column label="濮撳悕" align="center" key="nickName" prop="nickName" :show-overflow-tooltip="true" />
               <el-table-column label="璐﹀彿" align="center" key="userName" prop="userName" :show-overflow-tooltip="true" />
@@ -471,6 +471,7 @@
         nickName: '',
       },
       sortTable: null,
+      sortSaving: false,
     };
   },
   watch: {
@@ -486,61 +487,102 @@
       this.initPassword = response.msg;
     });
   },
-  mounted(){
-    // 鎸傝浇鍚庡垵濮嬪寲鎷栨嫿
-    this.initDrag()
+  beforeDestroy() {
+    this.destroyDrag()
   },
   methods: {
-    //琛ㄦ牸琛屾嫋鎷芥帓搴�
+    // 琛ㄦ牸琛屾嫋鎷芥帓搴�
+    destroyDrag() {
+      if (this.sortTable) {
+        this.sortTable.destroy()
+        this.sortTable = null
+      }
+    },
+    syncDragDisabledState() {
+      if (this.sortTable) {
+        this.sortTable.option('disabled', this.loading || this.sortSaving)
+      }
+    },
     initDrag() {
+      this.destroyDrag()
+      if (!this.$refs.dragTable || !this.userList || this.userList.length === 0) {
+        return
+      }
+
       // 鑾峰彇 el-table 鐨� tbody 鍏冪礌锛堟嫋鎷界殑鐩爣瀹瑰櫒锛�
       const tbody = this.$refs.dragTable.$el.querySelector(
         '.el-table__body-wrapper tbody'
       )
+      if (!tbody) {
+        return
+      }
 
       // 鍒濆鍖� Sortable
-      this.sortable = Sortable.create(tbody, {
+      this.sortTable = Sortable.create(tbody, {
         animation: 150, // 鎷栨嫿鍔ㄧ敾杩囨浮鏃堕暱
         ghostClass: 'sortable-ghost', // 鎷栨嫿鍗犱綅绗︽牱寮�
         chosenClass: 'sortable-chosen', // 閫変腑琛屾牱寮�
         dragClass: 'sortable-drag', // 鎷栨嫿鍏冪礌鏍峰紡
+        disabled: this.loading || this.sortSaving,
         // 鎷栨嫿缁撴潫瑙﹀彂锛堟牳蹇冮�昏緫锛�
-        onEnd: ({ oldIndex, newIndex }) => {
-          // oldIndex锛氬師绱㈠紩锛宯ewIndex锛氭柊绱㈠紩
-          const defNum = (this.queryParams.pageNum-1)*this.queryParams.pageSize
-          // const row = this.userList[oldIndex]
-          // let sort = newIndex+defNum;//鎺掑簭涓嬫爣
-          const row = this.userList.splice(oldIndex, 1)[0]
-          this.userList.splice(newIndex, 0, row)
-          const data = this.userList.map(item=>{return {userId:item.userId,userName:item.userName}})
-          console.log(data)
-          console.log(this.userList)
-          // 璋冪敤鎺ュ彛鏇存柊鎺掑簭
-          // let data = {
-          //   userId:row.userId,
-          //   sort: sort
-          // }
-          // updateUserSort(data).then(res=>{
-          //   if(res.code===200){
-          //     this.$message.success("鏇存柊鎴愬姛")
-          //     this.$nextTick(()=>{
-          //       this.getList()
-          //     })
-          //   }
-          // })
+        onEnd: async({ oldIndex, newIndex }) => {
+          if (
+            this.loading ||
+            this.sortSaving ||
+            oldIndex === newIndex ||
+            oldIndex === undefined ||
+            newIndex === undefined
+          ) {
+            return
+          }
 
-        },
+          const previousList = [...this.userList]
+          const row = this.userList.splice(oldIndex, 1)[0]
+          if (!row) {
+            this.userList = previousList
+            return
+          }
+          this.userList.splice(newIndex, 0, row)
+
+          const pageOffset =
+            (this.queryParams.pageNum - 1) * this.queryParams.pageSize
+          const data = this.userList.map((item, index) => ({
+            id: item.userId,
+            sort: pageOffset + index + 1
+          }))
+
+          this.sortSaving = true
+          this.syncDragDisabledState()
+          try {
+            await updateUserSort(data)
+            this.$message.success('鏇存柊鎺掑簭鎴愬姛')
+          } catch (error) {
+            this.userList = previousList
+            this.$message.error('鏇存柊鎺掑簭澶辫触')
+          } finally {
+            this.sortSaving = false
+            this.$nextTick(() => {
+              this.syncDragDisabledState()
+            })
+          }
+        }
       })
     },
     /** 鏌ヨ鐢ㄦ埛鍒楄〃 */
     getList() {
-      this.loading = true;
+      this.loading = true
+      this.syncDragDisabledState()
       listUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
-        this.userList = response.rows;
-        this.total = response.total;
-        this.loading = false;
-      }
-      );
+        this.userList = response.rows
+        this.total = response.total
+        this.loading = false
+        this.$nextTick(() => {
+          this.initDrag()
+        })
+      }).catch(() => {
+        this.loading = false
+        this.destroyDrag()
+      })
     },
     // 鎵撳紑娣诲姞鏋舵瀯寮规
     addSchema() {
@@ -958,6 +1000,9 @@
 </script>
 
 <style scoped lang="scss">
+:deep(.el-table__body-wrapper tbody tr) {
+  cursor: move;
+}
 :deep(.sortable-ghost) {
   opacity: 0.8;
   background: #f0f9eb;

--
Gitblit v1.9.3